diff --git a/autopilot/agent.go b/autopilot/agent.go index 9aef4f9f4b..d9c35a685f 100644 --- a/autopilot/agent.go +++ b/autopilot/agent.go @@ -648,7 +648,7 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32, // to open channels to. scores, err = chooseN(numChans, scores) if err != nil { - return fmt.Errorf("unable to make weighted choice: %v", + return fmt.Errorf("unable to make weighted choice: %w", err) } diff --git a/autopilot/combinedattach.go b/autopilot/combinedattach.go index 0a7e3557b6..b43856d242 100644 --- a/autopilot/combinedattach.go +++ b/autopilot/combinedattach.go @@ -84,7 +84,7 @@ func (c *WeightedCombAttachment) NodeScores(g ChannelGraph, chans []LocalChannel g, chans, chanSize, nodes, ) if err != nil { - return nil, fmt.Errorf("unable to get sub score: %v", + return nil, fmt.Errorf("unable to get sub score: %w", err) } diff --git a/autopilot/manager.go b/autopilot/manager.go index cf30aca82c..e5999f5182 100644 --- a/autopilot/manager.go +++ b/autopilot/manager.go @@ -341,7 +341,7 @@ func (m *Manager) queryHeuristics(nodes map[NodeID]struct{}, localState bool) ( m.cfg.PilotCfg.Graph, totalChans, chanSize, nodes, ) if err != nil { - return nil, fmt.Errorf("unable to get sub score: %v", + return nil, fmt.Errorf("unable to get sub score: %w", err) } diff --git a/brontide/listener.go b/brontide/listener.go index e3f93a665d..ba210853c4 100644 --- a/brontide/listener.go +++ b/brontide/listener.go @@ -91,7 +91,7 @@ func (l *Listener) listen() { // rejectedConnErr is a helper function that prepends the remote address of the // failed connection attempt to the original error message. func rejectedConnErr(err error, remoteAddr string) error { - return fmt.Errorf("unable to accept connection from %v: %v", remoteAddr, + return fmt.Errorf("unable to accept connection from %v: %w", remoteAddr, err) } diff --git a/cert/selfsigned.go b/cert/selfsigned.go index 748eeba779..c956afb004 100644 --- a/cert/selfsigned.go +++ b/cert/selfsigned.go @@ -261,7 +261,7 @@ func GenCertPair(org string, tlsExtraIPs, tlsExtraDomains []string, &template, &priv.PublicKey, priv, ) if err != nil { - return nil, nil, fmt.Errorf("failed to create certificate: %v", + return nil, nil, fmt.Errorf("failed to create certificate: %w", err) } @@ -270,13 +270,13 @@ func GenCertPair(org string, tlsExtraIPs, tlsExtraDomains []string, certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}, ) if err != nil { - return nil, nil, fmt.Errorf("failed to encode certificate: %v", + return nil, nil, fmt.Errorf("failed to encode certificate: %w", err) } keybytes, err := x509.MarshalECPrivateKey(priv) if err != nil { - return nil, nil, fmt.Errorf("unable to encode privkey: %v", + return nil, nil, fmt.Errorf("unable to encode privkey: %w", err) } keyBuf := &bytes.Buffer{} @@ -284,7 +284,7 @@ func GenCertPair(org string, tlsExtraIPs, tlsExtraDomains []string, keyBuf, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keybytes}, ) if err != nil { - return nil, nil, fmt.Errorf("failed to encode private key: %v", + return nil, nil, fmt.Errorf("failed to encode private key: %w", err) } diff --git a/chainntnfs/bitcoindnotify/bitcoind.go b/chainntnfs/bitcoindnotify/bitcoind.go index d268c8374a..e7fa4f5243 100644 --- a/chainntnfs/bitcoindnotify/bitcoind.go +++ b/chainntnfs/bitcoindnotify/bitcoind.go @@ -792,7 +792,7 @@ func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, // proceed with fallback methods. jsonErr, ok := err.(*btcjson.RPCError) if !ok || jsonErr.Code != btcjson.ErrRPCNoTxInfo { - return nil, fmt.Errorf("unable to query for txid %v: %v", + return nil, fmt.Errorf("unable to query for txid %v: %w", outpoint.Hash, err) } } diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go index 430a106614..6615be984f 100644 --- a/chainntnfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -886,7 +886,7 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, // proceed with fallback methods. jsonErr, ok := err.(*btcjson.RPCError) if !ok || jsonErr.Code != btcjson.ErrRPCNoTxInfo { - return nil, fmt.Errorf("unable to query for txid %v: %v", + return nil, fmt.Errorf("unable to query for txid %v: %w", outpoint.Hash, err) } } diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index 0f2fe27e45..906bfbfbe1 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -483,12 +483,12 @@ func GetCommonBlockAncestorHeight(chainConn ChainConn, reorgHash, for reorgHash != chainHash { reorgHeader, err := chainConn.GetBlockHeader(&reorgHash) if err != nil { - return 0, fmt.Errorf("unable to get header for hash=%v: %v", + return 0, fmt.Errorf("unable to get header for hash=%v: %w", reorgHash, err) } chainHeader, err := chainConn.GetBlockHeader(&chainHash) if err != nil { - return 0, fmt.Errorf("unable to get header for hash=%v: %v", + return 0, fmt.Errorf("unable to get header for hash=%v: %w", chainHash, err) } reorgHash = reorgHeader.PrevBlock @@ -497,7 +497,7 @@ func GetCommonBlockAncestorHeight(chainConn ChainConn, reorgHash, verboseHeader, err := chainConn.GetBlockHeaderVerbose(&chainHash) if err != nil { - return 0, fmt.Errorf("unable to get verbose header for hash=%v: %v", + return 0, fmt.Errorf("unable to get verbose header for hash=%v: %w", chainHash, err) } @@ -719,7 +719,7 @@ func ConfDetailsFromTxIndex(chainConn TxIndexConn, r ConfRequest, } return nil, TxNotFoundIndex, - fmt.Errorf("unable to query for txid %v: %v", + fmt.Errorf("unable to query for txid %v: %w", r.TxID, err) } @@ -728,13 +728,13 @@ func ConfDetailsFromTxIndex(chainConn TxIndexConn, r ConfRequest, rawTx, err := hex.DecodeString(rawTxRes.Hex) if err != nil { return nil, TxNotFoundIndex, - fmt.Errorf("unable to deserialize tx %v: %v", + fmt.Errorf("unable to deserialize tx %v: %w", r.TxID, err) } var tx wire.MsgTx if err := tx.Deserialize(bytes.NewReader(rawTx)); err != nil { return nil, TxNotFoundIndex, - fmt.Errorf("unable to deserialize tx %v: %v", + fmt.Errorf("unable to deserialize tx %v: %w", r.TxID, err) } @@ -759,13 +759,13 @@ func ConfDetailsFromTxIndex(chainConn TxIndexConn, r ConfRequest, if err != nil { return nil, TxNotFoundIndex, fmt.Errorf("unable to get block hash %v for "+ - "historical dispatch: %v", rawTxRes.BlockHash, err) + "historical dispatch: %w", rawTxRes.BlockHash, err) } block, err := chainConn.GetBlock(blockHash) if err != nil { return nil, TxNotFoundIndex, fmt.Errorf("unable to get block with hash %v for "+ - "historical dispatch: %v", blockHash, err) + "historical dispatch: %w", blockHash, err) } // In the modern chain (the only one we really care about for LN), the diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index b2e00da80e..9a9e8a7052 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -582,7 +582,7 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ // can compute the current block hash. blockHash, err := n.p2pNode.GetBlockHash(int64(scanHeight)) if err != nil { - return nil, fmt.Errorf("unable to get header for height=%v: %v", + return nil, fmt.Errorf("unable to get header for height=%v: %w", scanHeight, err) } @@ -601,7 +601,7 @@ func (n *NeutrinoNotifier) historicalConfDetails(confRequest chainntnfs.ConfRequ ) if err != nil { return nil, fmt.Errorf("unable to retrieve regular filter for "+ - "height=%v: %v", scanHeight, err) + "height=%v: %w", scanHeight, err) } // In the case that the filter exists, we'll attempt to see if diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 02da263912..1aa5538f3b 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -730,11 +730,11 @@ func NewChainControl(walletConfig lnwallet.Config, lnWallet, err := lnwallet.NewLightningWallet(walletConfig) if err != nil { - return nil, ccCleanup, fmt.Errorf("unable to create wallet: %v", + return nil, ccCleanup, fmt.Errorf("unable to create wallet: %w", err) } if err := lnWallet.Startup(); err != nil { - return nil, ccCleanup, fmt.Errorf("unable to create wallet: %v", + return nil, ccCleanup, fmt.Errorf("unable to create wallet: %w", err) } diff --git a/channeldb/codec.go b/channeldb/codec.go index a79ef8558f..7ee7a5fabf 100644 --- a/channeldb/codec.go +++ b/channeldb/codec.go @@ -78,7 +78,7 @@ func WriteElement(w io.Writer, element interface{}) error { if e.PubKey != nil { if err := binary.Write(w, byteOrder, true); err != nil { - return fmt.Errorf("error writing serialized element: %s", err) + return fmt.Errorf("error writing serialized element: %w", err) } return WriteElement(w, e.PubKey) diff --git a/channeldb/db.go b/channeldb/db.go index aff70d4626..d2759dafe9 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -643,7 +643,7 @@ func (c *ChannelStateDB) fetchNodeChannels(chainBucket kvdb.RBucket) ( oChannel, err := fetchOpenChannel(chanBucket, &outPoint) if err != nil { return fmt.Errorf("unable to read channel data for "+ - "chan_point=%v: %v", outPoint, err) + "chan_point=%v: %w", outPoint, err) } oChannel.Db = c diff --git a/channeldb/graph.go b/channeldb/graph.go index 8ad90f8083..b90ea9a050 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -1497,9 +1497,15 @@ func (c *ChannelGraph) pruneGraphNodes(nodes kvdb.RwBucket, // If we reach this point, then there are no longer any edges // that connect this node, so we can delete it. if err := c.deleteLightningNode(nodes, nodePubKey[:]); err != nil { - log.Warnf("Unable to prune node %x from the "+ - "graph: %v", nodePubKey, err) - continue + if errors.Is(err, ErrGraphNodeNotFound) || + errors.Is(err, ErrGraphNodesNotFound) { + + log.Warnf("Unable to prune node %x from the "+ + "graph: %v", nodePubKey, err) + continue + } + + return err } log.Infof("Pruned unconnected node %x from channel graph", diff --git a/channeldb/migration21/current/current_codec.go b/channeldb/migration21/current/current_codec.go index c78319a4f7..15b939dba0 100644 --- a/channeldb/migration21/current/current_codec.go +++ b/channeldb/migration21/current/current_codec.go @@ -83,7 +83,7 @@ func WriteElement(w io.Writer, element interface{}) error { if e.PubKey != nil { if err := binary.Write(w, byteOrder, true); err != nil { - return fmt.Errorf("error writing serialized element: %s", err) + return fmt.Errorf("error writing serialized element: %w", err) } return WriteElement(w, e.PubKey) diff --git a/channeldb/migration21/legacy/legacy_codec.go b/channeldb/migration21/legacy/legacy_codec.go index 8416ff39b6..ee5414525b 100644 --- a/channeldb/migration21/legacy/legacy_codec.go +++ b/channeldb/migration21/legacy/legacy_codec.go @@ -81,7 +81,7 @@ func WriteElement(w io.Writer, element interface{}) error { if e.PubKey != nil { if err := binary.Write(w, byteOrder, true); err != nil { - return fmt.Errorf("error writing serialized element: %s", err) + return fmt.Errorf("error writing serialized element: %w", err) } return WriteElement(w, e.PubKey) diff --git a/channeldb/migration25/migration.go b/channeldb/migration25/migration.go index 257d8272c5..53bff36420 100644 --- a/channeldb/migration25/migration.go +++ b/channeldb/migration25/migration.go @@ -93,7 +93,7 @@ func findOpenChannels(openChanBucket kvdb.RBucket) ([]*OpenChannel, error) { // open channels as they don't have any revocation logs and // their current commitments reflect the initial balances. if err := FetchChanCommitments(chanBucket, c); err != nil { - return fmt.Errorf("unable to fetch chan commits: %v", + return fmt.Errorf("unable to fetch chan commits: %w", err) } diff --git a/channeldb/migration27/migration.go b/channeldb/migration27/migration.go index a6f7b1a96b..85b1d52c99 100644 --- a/channeldb/migration27/migration.go +++ b/channeldb/migration27/migration.go @@ -79,7 +79,7 @@ func findHistoricalChannels(historicalBucket kvdb.RBucket) ([]*OpenChannel, // Try to fetch channel info in old format. err = fetchChanInfoCompatible(chanBucket, c, true) if err != nil { - return fmt.Errorf("%s: fetch chan info got: %v", + return fmt.Errorf("%s: fetch chan info got: %w", c.FundingOutpoint, err) } diff --git a/channeldb/migration30/migration.go b/channeldb/migration30/migration.go index 406cc291a4..83a9cee8fb 100644 --- a/channeldb/migration30/migration.go +++ b/channeldb/migration30/migration.go @@ -254,7 +254,7 @@ func writeRevocationLogs(openChanBucket kvdb.RwBucket, logEntrykey := mig24.MakeLogKey(entry.commitHeight) err = logBucket.Put(logEntrykey[:], b.Bytes()) if err != nil { - return fmt.Errorf("putRevocationLog err: %v", + return fmt.Errorf("putRevocationLog err: %w", err) } } diff --git a/channeldb/migration_01_to_11/codec.go b/channeldb/migration_01_to_11/codec.go index a87f6eda72..ca2f5cff6d 100644 --- a/channeldb/migration_01_to_11/codec.go +++ b/channeldb/migration_01_to_11/codec.go @@ -70,7 +70,7 @@ func WriteElement(w io.Writer, element interface{}) error { if e.PubKey != nil { if err := binary.Write(w, byteOrder, true); err != nil { - return fmt.Errorf("error writing serialized element: %s", err) + return fmt.Errorf("error writing serialized element: %w", err) } return WriteElement(w, e.PubKey) diff --git a/channeldb/migration_01_to_11/graph.go b/channeldb/migration_01_to_11/graph.go index 0f36a78edf..9035e3e6f9 100644 --- a/channeldb/migration_01_to_11/graph.go +++ b/channeldb/migration_01_to_11/graph.go @@ -1140,7 +1140,7 @@ func deserializeChanEdgePolicy(r io.Reader, node, err := fetchLightningNode(nodes, pub[:]) if err != nil { - return nil, fmt.Errorf("unable to fetch node: %x, %v", + return nil, fmt.Errorf("unable to fetch node: %x, %w", pub[:], err) } edge.Node = &node diff --git a/channeldb/migration_01_to_11/migrations.go b/channeldb/migration_01_to_11/migrations.go index 6ae25e2101..5027034cb0 100644 --- a/channeldb/migration_01_to_11/migrations.go +++ b/channeldb/migration_01_to_11/migrations.go @@ -516,7 +516,7 @@ func MigratePruneEdgeUpdateIndex(tx kvdb.RwTx) error { // well. edgeIndex, err := edges.CreateBucketIfNotExists(edgeIndexBucket) if err != nil { - return fmt.Errorf("error creating edge index bucket: %s", err) + return fmt.Errorf("error creating edge index bucket: %w", err) } if edgeIndex == nil { return fmt.Errorf("unable to create/fetch edge index " + @@ -546,7 +546,7 @@ func MigratePruneEdgeUpdateIndex(tx kvdb.RwTx) error { return nil }) if err != nil { - return fmt.Errorf("unable to gather existing edge policies: %v", + return fmt.Errorf("unable to gather existing edge policies: %w", err) } @@ -560,7 +560,7 @@ func MigratePruneEdgeUpdateIndex(tx kvdb.RwTx) error { return nil }) if err != nil { - return fmt.Errorf("unable to gather existing edge updates: %v", + return fmt.Errorf("unable to gather existing edge updates: %w", err) } diff --git a/channeldb/migration_01_to_11/zpay32/amountunits.go b/channeldb/migration_01_to_11/zpay32/amountunits.go index 0cc1fcdbe4..938a0b0ef1 100644 --- a/channeldb/migration_01_to_11/zpay32/amountunits.go +++ b/channeldb/migration_01_to_11/zpay32/amountunits.go @@ -136,7 +136,7 @@ func encodeAmount(msat lnwire.MilliSatoshi) (string, error) { // Should always be expressible in pico BTC. pico, err := fromMSat['p'](msat) if err != nil { - return "", fmt.Errorf("unable to express %d msat as pBTC: %v", + return "", fmt.Errorf("unable to express %d msat as pBTC: %w", msat, err) } shortened := strconv.FormatUint(pico, 10) + "p" diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index 4ad29167e8..69325c4257 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -159,7 +159,7 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, prefetchPayment(tx, paymentHash) bucket, err := createPaymentBucket(tx, paymentHash) if err != nil { - return err + return fmt.Errorf("couldn't create bucket: %w", err) } // Get the existing status of this payment, if any. @@ -178,7 +178,8 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, // Otherwise, if the error is not `ErrPaymentNotInitiated`, // we'll return the error. case !errors.Is(err, ErrPaymentNotInitiated): - return err + return fmt.Errorf("couldn't fetch payment status: %w", + err) } // Before we set our new sequence number, we check whether this @@ -190,7 +191,8 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, if seqBytes != nil { indexBucket := tx.ReadWriteBucket(paymentsIndexBucket) if err := indexBucket.Delete(seqBytes); err != nil { - return err + return fmt.Errorf("couldn't delete index "+ + "bucket: %w", err) } } @@ -201,27 +203,30 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, tx, sequenceNum, info.PaymentIdentifier, ) if err != nil { - return err + return fmt.Errorf("couldn't create payment index "+ + "entry: %w", err) } err = bucket.Put(paymentSequenceKey, sequenceNum) if err != nil { - return err + return fmt.Errorf("couldn't put sequence key: %w", err) } // Add the payment info to the bucket, which contains the // static information for this payment err = bucket.Put(paymentCreationInfoKey, infoBytes) if err != nil { - return err + return fmt.Errorf("couldn't put creation key info: %w", + err) } // We'll delete any lingering HTLCs to start with, in case we // are initializing a payment that was attempted earlier, but // left in a state where we could retry. err = bucket.DeleteNestedBucket(paymentHtlcsBucket) - if err != nil && err != kvdb.ErrBucketNotFound { - return err + if err != nil && !errors.Is(err, kvdb.ErrBucketNotFound) { + return fmt.Errorf("couldn't delete nested bucket: %w", + err) } // Also delete any lingering failure info now that we are @@ -229,7 +234,7 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, return bucket.Delete(paymentFailInfoKey) }) if err != nil { - return err + return fmt.Errorf("unable to init payment: %w", err) } return updateErr @@ -521,7 +526,7 @@ func (p *PaymentControl) Fail(paymentHash lntypes.Hash, prefetchPayment(tx, paymentHash) bucket, err := fetchPaymentBucketUpdate(tx, paymentHash) - if err == ErrPaymentNotInitiated { + if errors.Is(err, ErrPaymentNotInitiated) { updateErr = ErrPaymentNotInitiated return nil } else if err != nil { diff --git a/channeldb/payment_control_test.go b/channeldb/payment_control_test.go index 9db6fe862e..5b394edae8 100644 --- a/channeldb/payment_control_test.go +++ b/channeldb/payment_control_test.go @@ -181,7 +181,7 @@ func TestPaymentControlSwitchFail(t *testing.T) { // Attempt a final payment, which should now fail since the prior // payment succeed. err = pControl.InitPayment(info.PaymentIdentifier, info) - if err != ErrAlreadyPaid { + if !errors.Is(err, ErrAlreadyPaid) { t.Fatalf("unable to send htlc message: %v", err) } } @@ -216,9 +216,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { // payment hash, should result in error indicating that payment has // already been sent. err = pControl.InitPayment(info.PaymentIdentifier, info) - require.Equal(t, ErrPaymentExists, err, "payment control wrong "+ - "behaviour: init payment again must trigger ErrPaymentExists "+ - "error") + require.ErrorIs(t, err, ErrPaymentExists) // Record an attempt. _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) @@ -234,7 +232,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { // Sends base htlc message which initiate StatusInFlight. err = pControl.InitPayment(info.PaymentIdentifier, info) - if err != ErrPaymentInFlight { + if !errors.Is(err, ErrPaymentInFlight) { t.Fatalf("payment control wrong behaviour: " + "double sending must trigger ErrPaymentInFlight error") } @@ -253,7 +251,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { assertPaymentInfo(t, pControl, info.PaymentIdentifier, info, nil, htlc) err = pControl.InitPayment(info.PaymentIdentifier, info) - if err != ErrAlreadyPaid { + if !errors.Is(err, ErrAlreadyPaid) { t.Fatalf("unable to send htlc message: %v", err) } } diff --git a/channeldb/payments.go b/channeldb/payments.go index 15bcd83424..a1baa07f7a 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -615,7 +615,7 @@ func (d *DB) QueryPayments(query PaymentsQuery) (PaymentsResponse, error) { err = indexes.ForEach(countFn) } if err != nil { - return fmt.Errorf("error counting payments: %v", + return fmt.Errorf("error counting payments: %w", err) } diff --git a/cmd/lncli/cmd_macaroon.go b/cmd/lncli/cmd_macaroon.go index 92a96c43c3..54e03057ab 100644 --- a/cmd/lncli/cmd_macaroon.go +++ b/cmd/lncli/cmd_macaroon.go @@ -361,7 +361,7 @@ func printMacaroon(ctx *cli.Context) error { case args.Present(): macBytes, err = hex.DecodeString(args.First()) if err != nil { - return fmt.Errorf("unable to hex decode macaroon: %v", + return fmt.Errorf("unable to hex decode macaroon: %w", err) } diff --git a/cmd/lncli/cmd_open_channel.go b/cmd/lncli/cmd_open_channel.go index 45f032f53c..fd21b88d48 100644 --- a/cmd/lncli/cmd_open_channel.go +++ b/cmd/lncli/cmd_open_channel.go @@ -602,7 +602,7 @@ func openChannelPsbt(rpcCtx context.Context, ctx *cli.Context, // Recv blocks until a message or error arrives. resp, err := stream.Recv() if err == io.EOF { - srvErr <- fmt.Errorf("lnd shutting down: %v", + srvErr <- fmt.Errorf("lnd shutting down: %w", err) return } else if err != nil { @@ -685,7 +685,7 @@ func openChannelPsbt(rpcCtx context.Context, ctx *cli.Context, } fundedPsbt, err := decodePsbt(inputPsbt) if err != nil { - return fmt.Errorf("psbt decode failed: %v", + return fmt.Errorf("psbt decode failed: %w", err) } verifyMsg := &lnrpc.FundingTransitionMsg{ @@ -866,14 +866,14 @@ func batchOpenChannel(ctx *cli.Context) error { for idx, jsonChannel := range jsonChannels { pubKeyBytes, err := hex.DecodeString(jsonChannel.NodePubkey) if err != nil { - return fmt.Errorf("error parsing node pubkey hex: %v", + return fmt.Errorf("error parsing node pubkey hex: %w", err) } pendingChanBytes, err := hex.DecodeString( jsonChannel.PendingChanID, ) if err != nil { - return fmt.Errorf("error parsing pending chan ID: %v", + return fmt.Errorf("error parsing pending chan ID: %w", err) } diff --git a/cmd/lncli/cmd_payments.go b/cmd/lncli/cmd_payments.go index 1ba3b42c88..e5e153f45e 100644 --- a/cmd/lncli/cmd_payments.go +++ b/cmd/lncli/cmd_payments.go @@ -525,13 +525,13 @@ func sendPaymentRequest(ctx *cli.Context, recordID, err := strconv.ParseUint(kv[0], 10, 64) if err != nil { - return fmt.Errorf("invalid data format: %v", + return fmt.Errorf("invalid data format: %w", err) } hexValue, err := hex.DecodeString(kv[1]) if err != nil { - return fmt.Errorf("invalid data format: %v", + return fmt.Errorf("invalid data format: %w", err) } @@ -1514,7 +1514,7 @@ func forwardingHistory(ctx *cli.Context) error { case args.Present(): i, err := strconv.ParseInt(args.First(), 10, 64) if err != nil { - return fmt.Errorf("unable to decode index_offset: %v", + return fmt.Errorf("unable to decode index_offset: %w", err) } indexOffset = uint32(i) @@ -1527,7 +1527,7 @@ func forwardingHistory(ctx *cli.Context) error { case args.Present(): m, err := strconv.ParseInt(args.First(), 10, 64) if err != nil { - return fmt.Errorf("unable to decode max_events: %v", + return fmt.Errorf("unable to decode max_events: %w", err) } maxEvents = uint32(m) @@ -1616,7 +1616,7 @@ func buildRoute(ctx *cli.Context) error { for _, k := range hops { pubkey, err := route.NewVertexFromStr(k) if err != nil { - return fmt.Errorf("error parsing %v: %v", k, err) + return fmt.Errorf("error parsing %v: %w", k, err) } rpcHops = append(rpcHops, pubkey[:]) } @@ -1757,7 +1757,7 @@ func deletePayments(ctx *cli.Context) error { case singlePayment: paymentHash, err = hex.DecodeString(ctx.String("payment_hash")) if err != nil { - return fmt.Errorf("error decoding payment_hash: %v", + return fmt.Errorf("error decoding payment_hash: %w", err) } @@ -1766,7 +1766,7 @@ func deletePayments(ctx *cli.Context) error { FailedHtlcsOnly: failedHTLCsOnly, }) if err != nil { - return fmt.Errorf("error deleting single payment: %v", + return fmt.Errorf("error deleting single payment: %w", err) } diff --git a/cmd/lncli/cmd_profile.go b/cmd/lncli/cmd_profile.go index 0f350e5c2c..21666be964 100644 --- a/cmd/lncli/cmd_profile.go +++ b/cmd/lncli/cmd_profile.go @@ -150,7 +150,7 @@ func profileAdd(ctx *cli.Context) error { // All done, store the updated profile file. f.Profiles = append(f.Profiles, profile) if err = saveProfileFile(defaultProfileFile, f); err != nil { - return fmt.Errorf("error writing profile file %s: %v", + return fmt.Errorf("error writing profile file %s: %w", defaultProfileFile, err) } @@ -443,7 +443,7 @@ func profileAddMacaroon(ctx *cli.Context) error { selectedProfile.Macaroons.Jar, macEntry, ) if err = saveProfileFile(defaultProfileFile, f); err != nil { - return fmt.Errorf("error writing profile file %s: %v", + return fmt.Errorf("error writing profile file %s: %w", defaultProfileFile, err) } diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 5190cd69f7..561208f365 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -1743,7 +1743,7 @@ func getChanInfo(ctx *cli.Context) error { case ctx.Args().Present(): chanID, err = strconv.ParseUint(ctx.Args().First(), 10, 64) if err != nil { - return fmt.Errorf("error parsing chan_id: %s", err) + return fmt.Errorf("error parsing chan_id: %w", err) } default: return fmt.Errorf("chan_id argument missing") diff --git a/cmd/lncli/macaroon_jar.go b/cmd/lncli/macaroon_jar.go index 2c140fa6d9..f54f29a26c 100644 --- a/cmd/lncli/macaroon_jar.go +++ b/cmd/lncli/macaroon_jar.go @@ -67,7 +67,7 @@ func (e *macaroonEntry) loadMacaroon( macBytes, err = decryptMacaroon(parts[1], parts[2], pw) if err != nil { - return nil, fmt.Errorf("unable to decrypt macaroon: %v", + return nil, fmt.Errorf("unable to decrypt macaroon: %w", err) } } else { @@ -142,7 +142,7 @@ func decryptMacaroon(keyB64, dataB64 string, pw []byte) ([]byte, error) { key := &snacl.SecretKey{} err = key.Unmarshal(keyData) if err != nil { - return nil, fmt.Errorf("could not unmarshal encryption key: %v", + return nil, fmt.Errorf("could not unmarshal encryption key: %w", err) } @@ -155,7 +155,7 @@ func decryptMacaroon(keyB64, dataB64 string, pw []byte) ([]byte, error) { } macBytes, err := key.Decrypt(encryptedMac) if err != nil { - return nil, fmt.Errorf("could not decrypt macaroon data: %v", + return nil, fmt.Errorf("could not decrypt macaroon data: %w", err) } return macBytes, nil diff --git a/cmd/lncli/profile.go b/cmd/lncli/profile.go index 9c66805427..90ac69c0ee 100644 --- a/cmd/lncli/profile.go +++ b/cmd/lncli/profile.go @@ -224,7 +224,7 @@ func loadProfileFile(file string) (*profileFile, error) { content, err := ioutil.ReadFile(file) if err != nil { - return nil, fmt.Errorf("could not load profile file %s: %v", + return nil, fmt.Errorf("could not load profile file %s: %w", file, err) } f := &profileFile{} @@ -262,7 +262,7 @@ func (f *profileFile) unmarshalJSON(content []byte) error { func (f *profileFile) marshalJSON() ([]byte, error) { b, err := json.Marshal(f) if err != nil { - return nil, fmt.Errorf("error JSON marshalling profile: %v", + return nil, fmt.Errorf("error JSON marshalling profile: %w", err) } diff --git a/cmd/lncli/walletrpc_active.go b/cmd/lncli/walletrpc_active.go index f095601699..0d72e537da 100644 --- a/cmd/lncli/walletrpc_active.go +++ b/cmd/lncli/walletrpc_active.go @@ -1220,7 +1220,7 @@ func fundPsbt(ctx *cli.Context) error { // entry must be present. jsonMap := []byte(ctx.String("outputs")) if err := json.Unmarshal(jsonMap, &amountToAddr); err != nil { - return fmt.Errorf("error parsing outputs JSON: %v", + return fmt.Errorf("error parsing outputs JSON: %w", err) } tpl.Outputs = amountToAddr diff --git a/config_builder.go b/config_builder.go index 5195777e11..c222dcc944 100644 --- a/config_builder.go +++ b/config_builder.go @@ -567,7 +567,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, ) cleanUpTasks = append(cleanUpTasks, pccCleanup) if err != nil { - err := fmt.Errorf("unable to create partial chain control: %v", + err := fmt.Errorf("unable to create partial chain control: %w", err) d.logger.Error(err) return nil, nil, nil, err @@ -1044,7 +1044,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( if err != nil { cleanUp() - err := fmt.Errorf("unable to open %s database: %v", + err := fmt.Errorf("unable to open %s database: %w", lncfg.NSTowerClientDB, err) d.logger.Error(err) return nil, nil, err @@ -1059,7 +1059,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase( if err != nil { cleanUp() - err := fmt.Errorf("unable to open %s database: %v", + err := fmt.Errorf("unable to open %s database: %w", lncfg.NSTowerServerDB, err) d.logger.Error(err) return nil, nil, err @@ -1274,7 +1274,7 @@ func importWatchOnlyAccounts(wallet *wallet.Wallet, addrSchema, ) if err != nil { - return fmt.Errorf("could not import account %v: %v", + return fmt.Errorf("could not import account %v: %w", name, err) } } diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 0e33dc5716..017e8bdac2 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -931,7 +931,7 @@ func (b *BreachArbitrator) cleanupBreach(chanPoint *wire.OutPoint) error { // info from the database. err = b.cfg.Store.Remove(chanPoint) if err != nil { - return fmt.Errorf("unable to remove retribution from db: %v", + return fmt.Errorf("unable to remove retribution from db: %w", err) } diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 2c353bf6a5..c34314d9e7 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1647,7 +1647,7 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(now time.Time) error { return nil }) if err != nil && err != channeldb.ErrGraphNoEdgesFound { - return fmt.Errorf("unable to retrieve outgoing channels: %v", + return fmt.Errorf("unable to retrieve outgoing channels: %w", err) } @@ -1872,7 +1872,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge( // to the database. err = d.cfg.Router.AddProof(chanAnnMsg.ShortChannelID, proof) if err != nil { - err := fmt.Errorf("unable add proof to shortChanID=%v: %v", + err := fmt.Errorf("unable add proof to shortChanID=%v: %w", chanAnnMsg.ShortChannelID, err) log.Error(err) return nil, err @@ -1909,7 +1909,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement, op ...batch.SchedulerOption) error { if err := routing.ValidateNodeAnn(msg); err != nil { - return fmt.Errorf("unable to validate node announcement: %v", + return fmt.Errorf("unable to validate node announcement: %w", err) } diff --git a/discovery/message_store.go b/discovery/message_store.go index cf228eee71..10fa51623a 100644 --- a/discovery/message_store.go +++ b/discovery/message_store.go @@ -72,7 +72,7 @@ func NewMessageStore(db kvdb.Backend) (*MessageStore, error) { return err }) if err != nil { - return nil, fmt.Errorf("unable to create required buckets: %v", + return nil, fmt.Errorf("unable to create required buckets: %w", err) } diff --git a/discovery/syncer.go b/discovery/syncer.go index b567a69403..b910151cb1 100644 --- a/discovery/syncer.go +++ b/discovery/syncer.go @@ -1252,7 +1252,7 @@ func (g *GossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error query.ChainHash, query.ShortChanIDs, ) if err != nil { - return fmt.Errorf("unable to fetch chan anns for %v..., %v", + return fmt.Errorf("unable to fetch chan anns for %v..., %w", query.ShortChanIDs[0].ToUint64(), err) } diff --git a/feature/manager.go b/feature/manager.go index 991d5c6119..98317eb8f1 100644 --- a/feature/manager.go +++ b/feature/manager.go @@ -211,7 +211,7 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { fv := lnwire.NewFeatureVector(raw, lnwire.Features) err := ValidateDeps(fv) if err != nil { - return nil, fmt.Errorf("invalid feature set %v: %v", + return nil, fmt.Errorf("invalid feature set %v: %w", set, err) } } diff --git a/funding/batch.go b/funding/batch.go index 163c33b7e3..51d982ebe4 100644 --- a/funding/batch.go +++ b/funding/batch.go @@ -226,7 +226,7 @@ func (b *Batcher) BatchFund(ctx context.Context, "chan ID") } } else if _, err := rand.Read(pendingChanID[:]); err != nil { - return nil, fmt.Errorf("error making temp chan ID: %v", + return nil, fmt.Errorf("error making temp chan ID: %w", err) } @@ -265,7 +265,7 @@ func (b *Batcher) BatchFund(ctx context.Context, }, }) if err != nil { - return nil, fmt.Errorf("error parsing channel %d: %v", + return nil, fmt.Errorf("error parsing channel %d: %w", idx, err) } diff --git a/funding/manager.go b/funding/manager.go index 5196090b77..81f32a3a48 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -1123,7 +1123,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, case markedOpen: err := f.sendChannelReady(channel, lnChannel) if err != nil { - return fmt.Errorf("failed sending channelReady: %v", + return fmt.Errorf("failed sending channelReady: %w", err) } @@ -1137,7 +1137,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, ) if err != nil { return fmt.Errorf("error setting channel state to"+ - " channelReadySent: %v", err) + " channelReadySent: %w", err) } log.Debugf("Channel(%v) with ShortChanID %v: successfully "+ @@ -1207,7 +1207,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, // shutdown. err = f.deleteChannelOpeningState(&channel.FundingOutpoint) if err != nil { - return fmt.Errorf("error deleting channel state: %v", + return fmt.Errorf("error deleting channel state: %w", err) } @@ -2757,7 +2757,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, if err := c.CloseChannel( closeInfo, channeldb.ChanStatusLocalCloseInitiator, ); err != nil { - return fmt.Errorf("failed closing channel %v: %v", + return fmt.Errorf("failed closing channel %v: %w", c.FundingOutpoint, err) } @@ -4784,7 +4784,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { } if err := msg.Peer.SendMessage(true, &fundingOpen); err != nil { - e := fmt.Errorf("unable to send funding request message: %v", + e := fmt.Errorf("unable to send funding request message: %w", err) log.Errorf(e.Error()) diff --git a/go.mod b/go.mod index 6a33f3221b..42506e6909 100644 --- a/go.mod +++ b/go.mod @@ -206,4 +206,12 @@ replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-d // docs/INSTALL.md. go 1.19 +replace github.com/lightningnetwork/lnd/kvdb => ./kvdb + +replace github.com/btcsuite/btcwallet => github.com/roasbeef/btcwallet v0.11.1-0.20240307225634-ff180237e5ad + +replace github.com/btcsuite/btcwallet/walletdb => github.com/roasbeef/btcwallet/walletdb v1.1.1-0.20240307225634-ff180237e5ad + +replace github.com/btcsuite/btcwallet/wtxmgr => github.com/roasbeef/btcwallet/wtxmgr v1.0.1-0.20240307225634-ff180237e5ad + retract v0.0.2 diff --git a/go.sum b/go.sum index ccfd727723..715840cbd5 100644 --- a/go.sum +++ b/go.sum @@ -90,18 +90,12 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.10-0.20240305014015-f7c216e78ee8 h1:fkPZ7LfOCm3Nn0Y/56LOqwooYaw9cV/4jZkdHw7XGik= -github.com/btcsuite/btcwallet v0.16.10-0.20240305014015-f7c216e78ee8/go.mod h1:q+J5AzV+ymzUaBXkP099uuVh18yzCyHHXPc4rxi55mk= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc= -github.com/btcsuite/btcwallet/walletdb v1.4.1 h1:NGIGoxx3trpaWqmdOeuhju7KJKp5UM96mQL21idF6RY= -github.com/btcsuite/btcwallet/walletdb v1.4.1/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ= -github.com/btcsuite/btcwallet/wtxmgr v1.5.1 h1:2yXhMGa4DNz16Mi0e8dVoiFXKOznXlxiGLhB3hKj2uA= -github.com/btcsuite/btcwallet/wtxmgr v1.5.1/go.mod h1:tO4FBSdann0xg/Jtm0grV7t1DzpQMK8nThYVtvSJo/8= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= @@ -432,8 +426,6 @@ github.com/lightningnetwork/lnd/fn v1.0.4 h1:n4iGRRoS+XHqNbOrsXIvweps/QfWk+moO7F github.com/lightningnetwork/lnd/fn v1.0.4/go.mod h1:K9gbvdl5z4XmRcqWUVqvvVcuRKtmq9BNQ+cWYlk+vjw= github.com/lightningnetwork/lnd/healthcheck v1.2.3 h1:oqhOOy8WmIEa6RBkYKC0mmYZkhl8T2kGD97n9jpML8o= github.com/lightningnetwork/lnd/healthcheck v1.2.3/go.mod h1:eDxH3dEwV9DeBW/6inrmlVh1qBOFV0AI14EEPnGt9gc= -github.com/lightningnetwork/lnd/kvdb v1.4.5 h1:wwX3hbFTsnxEIL5X2Pszq1o3Fd2OZGdyWIMr9QrMxL8= -github.com/lightningnetwork/lnd/kvdb v1.4.5/go.mod h1:oaGL6R/qwazM7hPurg8jSPYsWw3cGEOt6YJDs5TUNos= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= @@ -532,6 +524,12 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/roasbeef/btcwallet v0.11.1-0.20240307225634-ff180237e5ad h1:wvsow2VwKa/efv0ObcsMpQnw4lypkj21vNkG0cQcCjg= +github.com/roasbeef/btcwallet v0.11.1-0.20240307225634-ff180237e5ad/go.mod h1:rROXjC/DZcEkGQBaUUBnL6q02lhR7sTgZt7LM9Kiv/4= +github.com/roasbeef/btcwallet/walletdb v1.1.1-0.20240307225634-ff180237e5ad h1:ZH/8I/jIG84zkHMwOJXsOpCtm748ITS77B4DAKAjSCI= +github.com/roasbeef/btcwallet/walletdb v1.1.1-0.20240307225634-ff180237e5ad/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ= +github.com/roasbeef/btcwallet/wtxmgr v1.0.1-0.20240307225634-ff180237e5ad h1:1eXppv9SmNBe2MbP9DzocbNAcxxA0mHs8hUnL+Tjnpc= +github.com/roasbeef/btcwallet/wtxmgr v1.0.1-0.20240307225634-ff180237e5ad/go.mod h1:tO4FBSdann0xg/Jtm0grV7t1DzpQMK8nThYVtvSJo/8= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/input/musig2.go b/input/musig2.go index 28a15af738..5891522829 100644 --- a/input/musig2.go +++ b/input/musig2.go @@ -587,7 +587,7 @@ func DeserializePartialSignature(scalarBytes []byte) (*musig2.PartialSignature, sig := &musig2.PartialSignature{} if err := sig.Decode(bytes.NewReader(scalarBytes)); err != nil { - return nil, fmt.Errorf("error decoding partial signature: %v", + return nil, fmt.Errorf("error decoding partial signature: %w", err) } diff --git a/kvdb/bolt_compact.go b/kvdb/bolt_compact.go index 6597eb2d59..b477e206b3 100644 --- a/kvdb/bolt_compact.go +++ b/kvdb/bolt_compact.go @@ -90,7 +90,7 @@ func (cmd *compacter) execute() (int64, int64, error) { Timeout: cmd.dbTimeout, }) if err != nil { - return 0, 0, fmt.Errorf("error opening source database: %v", + return 0, 0, fmt.Errorf("error opening source database: %w", err) } defer func() { @@ -105,7 +105,7 @@ func (cmd *compacter) execute() (int64, int64, error) { }) if err != nil { return 0, 0, fmt.Errorf("error opening destination database: "+ - "%v", err) + "%w", err) } defer func() { if err := dst.Close(); err != nil { @@ -122,7 +122,7 @@ func (cmd *compacter) execute() (int64, int64, error) { fi, err = os.Stat(cmd.dstPath) if err != nil { return 0, 0, fmt.Errorf("error determining destination "+ - "database size: %v", err) + "database size: %w", err) } else if fi.Size() == 0 { return 0, 0, fmt.Errorf("zero db size") } diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go index 90ca8324a8..425ba16225 100644 --- a/kvdb/postgres/db.go +++ b/kvdb/postgres/db.go @@ -28,7 +28,6 @@ func newPostgresBackend(ctx context.Context, config *Config, prefix string) ( Schema: "public", TableNamePrefix: prefix, SQLiteCmdReplacements: sqliteCmdReplacements, - WithTxLevelLock: true, } return sqlbase.NewSqlBackend(ctx, cfg) diff --git a/kvdb/postgres/db_test.go b/kvdb/postgres/db_test.go index 0378b4dbb8..3ad1a32d9b 100644 --- a/kvdb/postgres/db_test.go +++ b/kvdb/postgres/db_test.go @@ -58,5 +58,5 @@ func TestPanic(t *testing.T) { return nil }, func() {}) - require.Contains(t, err.Error(), "terminating connection") + require.Contains(t, err.Error(), "conn closed") } diff --git a/kvdb/sqlbase/db.go b/kvdb/sqlbase/db.go index f6f2c75901..6251250cc2 100644 --- a/kvdb/sqlbase/db.go +++ b/kvdb/sqlbase/db.go @@ -24,7 +24,7 @@ const ( // DefaultNumTxRetries is the default number of times we'll retry a // transaction if it fails with an error that permits transaction // repetition. - DefaultNumTxRetries = 10 + DefaultNumTxRetries = 50 // DefaultInitialRetryDelay is the default initial delay between // retries. This will be used to generate a random delay between -50% @@ -35,7 +35,7 @@ const ( DefaultInitialRetryDelay = time.Millisecond * 50 // DefaultMaxRetryDelay is the default maximum delay between retries. - DefaultMaxRetryDelay = time.Second * 5 + DefaultMaxRetryDelay = time.Second ) // Config holds a set of configuration options of a sql database connection. @@ -66,10 +66,6 @@ type Config struct { // commands. Note that the sqlite keywords to be replaced are // case-sensitive. SQLiteCmdReplacements SQLiteCmdReplacements - - // WithTxLevelLock when set will ensure that there is a transaction - // level lock. - WithTxLevelLock bool } // db holds a reference to the sql db connection. @@ -90,10 +86,6 @@ type db struct { // db is the underlying database connection instance. db *sql.DB - // lock is the global write lock that ensures single writer. This is - // only used if cfg.WithTxLevelLock is set. - lock sync.RWMutex - // table is the name of the table that contains the data for all // top-level buckets that have keys that cannot be mapped to a distinct // sql table. @@ -181,7 +173,6 @@ func (db *db) getPrefixedTableName(table string) string { func catchPanic(f func() error) (err error) { defer func() { if r := recover(); r != nil { - log.Criticalf("Caught unhandled error: %v", r) switch data := r.(type) { case error: @@ -190,6 +181,18 @@ func catchPanic(f func() error) (err error) { default: err = errors.New(fmt.Sprintf("%v", data)) } + + // Before we issue a critical log which'll cause the + // daemon to shut down, we'll first check if this is a + // DB serialization error. If so, then we don't need to + // log as we can retry safely and avoid tearing + // everything down. + if IsSerializationError(MapSQLError(err)) { + log.Tracef("Detected db serialization error "+ + "via panic: %v", err) + } else { + log.Criticalf("Caught unhandled error: %v", r) + } } }() @@ -308,26 +311,43 @@ func (db *db) executeTransaction(f func(tx walletdb.ReadWriteTx) error, return dbErr } - err = catchPanic(func() error { return f(tx) }) - if err != nil { - if rollbackErr := tx.Rollback(); rollbackErr != nil { - log.Errorf("Error rolling back tx: %v", - rollbackErr) + fnErr := catchPanic(func() error { return f(tx) }) + if fnErr != nil { + if err := attemptRollback(tx); err != nil { + return err } - return err + dbErr := MapSQLError(fnErr) + if IsSerializationError(dbErr) { + // Nothing to roll back here, since we didn't + // even get a transaction yet. + if waitBeforeRetry(i) { + continue + } + } + + return fnErr } - dbErr := tx.Commit() - if IsSerializationError(dbErr) { - _ = tx.Rollback() + commitErr := tx.Commit() + if commitErr != nil { + if err := attemptRollback(tx); err != nil { + return err + } - if waitBeforeRetry(i) { - continue + dbErr := MapSQLError(commitErr) + if IsSerializationError(dbErr) { + // The transaction failed due to a + // serialization problem, so we can retry. + if waitBeforeRetry(i) { + continue + } } + + return commitErr } - return dbErr + return nil } // If we get to this point, then we weren't able to successfully commit @@ -367,3 +387,15 @@ func (db *db) Close() error { return dbConns.Close(db.cfg.Dsn) } + +// attemptRollback attempts to roll back the transaction, and if it fails, it +// will return the error. If the transaction was already closed, it will return +// nil. +func attemptRollback(tx *readWriteTx) error { + rollbackErr := tx.Rollback() + if rollbackErr != nil && !errors.Is(rollbackErr, walletdb.ErrTxClosed) { + return fmt.Errorf("error rolling back tx: %w", rollbackErr) + } + + return nil +} diff --git a/kvdb/sqlbase/readwrite_tx.go b/kvdb/sqlbase/readwrite_tx.go index ec761931ad..18a6a682c9 100644 --- a/kvdb/sqlbase/readwrite_tx.go +++ b/kvdb/sqlbase/readwrite_tx.go @@ -5,7 +5,6 @@ package sqlbase import ( "context" "database/sql" - "sync" "github.com/btcsuite/btcwallet/walletdb" ) @@ -20,28 +19,11 @@ type readWriteTx struct { // active is true if the transaction hasn't been committed yet. active bool - - // locker is a pointer to the global db lock. - locker sync.Locker } // newReadWriteTx creates an rw transaction using a connection from the // specified pool. func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) { - locker := newNoopLocker() - if db.cfg.WithTxLevelLock { - // Obtain the global lock instance. An alternative here is to - // obtain a database lock from Postgres. Unfortunately there is - // no database-level lock in Postgres, meaning that each table - // would need to be locked individually. Perhaps an advisory - // lock could perform this function too. - locker = &db.lock - if readOnly { - locker = db.lock.RLocker() - } - } - locker.Lock() - // Start the transaction. Don't use the timeout context because it would // be applied to the transaction as a whole. If possible, mark the // transaction as read-only to make sure that potential programming @@ -54,7 +36,6 @@ func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) { }, ) if err != nil { - locker.Unlock() return nil, err } @@ -62,7 +43,6 @@ func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) { db: db, tx: tx, active: true, - locker: locker, }, nil } @@ -94,7 +74,6 @@ func (tx *readWriteTx) Rollback() error { // Unlock the transaction regardless of the error result. tx.active = false - tx.locker.Unlock() return err } @@ -162,7 +141,6 @@ func (tx *readWriteTx) Commit() error { // Unlock the transaction regardless of the error result. tx.active = false - tx.locker.Unlock() return err } @@ -204,25 +182,3 @@ func (tx *readWriteTx) Exec(query string, args ...interface{}) (sql.Result, return tx.tx.ExecContext(ctx, query, args...) } - -// noopLocker is an implementation of a no-op sync.Locker. -type noopLocker struct{} - -// newNoopLocker creates a new noopLocker. -func newNoopLocker() sync.Locker { - return &noopLocker{} -} - -// Lock is a noop. -// -// NOTE: this is part of the sync.Locker interface. -func (n *noopLocker) Lock() { -} - -// Unlock is a noop. -// -// NOTE: this is part of the sync.Locker interface. -func (n *noopLocker) Unlock() { -} - -var _ sync.Locker = (*noopLocker)(nil) diff --git a/kvdb/sqlbase/sqlerrors_postgres.go b/kvdb/sqlbase/sqlerrors_postgres.go index 3915a34f41..1b12dda66f 100644 --- a/kvdb/sqlbase/sqlerrors_postgres.go +++ b/kvdb/sqlbase/sqlerrors_postgres.go @@ -5,6 +5,7 @@ package sqlbase import ( "errors" "fmt" + "strings" "github.com/jackc/pgconn" "github.com/jackc/pgerrcode" @@ -13,6 +14,19 @@ import ( // parsePostgresError attempts to parse a postgres error as a database agnostic // SQL error. func parsePostgresError(err error) error { + if err == nil { + return nil + } + + // Sometimes the error won't be properly wrapped, so we'll need to + // inspect raw error itself to detect something we can wrap properly. + const postgresErrMsg = "could not serialize access" + if strings.Contains(err.Error(), postgresErrMsg) { + return &ErrSerializationError{ + DBError: err, + } + } + var pqErr *pgconn.PgError if !errors.As(err, &pqErr) { return nil diff --git a/kvdb/sqlbase/sqlerrors_sqlite.go b/kvdb/sqlbase/sqlerrors_sqlite.go index c291466104..9df93893a4 100644 --- a/kvdb/sqlbase/sqlerrors_sqlite.go +++ b/kvdb/sqlbase/sqlerrors_sqlite.go @@ -5,6 +5,7 @@ package sqlbase import ( "errors" "fmt" + "strings" "modernc.org/sqlite" sqlite3 "modernc.org/sqlite/lib" @@ -13,6 +14,21 @@ import ( // parseSqliteError attempts to parse a sqlite error as a database agnostic // SQL error. func parseSqliteError(err error) error { + if err == nil { + return nil + } + + // If the error isn't wrapped properly, the errors.As call with fail, + // so we'll also try to check the expected error message directly. + // This is taken from: + // https://gitlab.com/cznic/sqlite/-/blob/v1.25.0/sqlite.go#L75. + const sqliteErrMsg = "SQLITE_BUSY" + if strings.Contains(err.Error(), sqliteErrMsg) { + return &ErrSerializationError{ + DBError: err, + } + } + var sqliteErr *sqlite.Error if !errors.As(err, &sqliteErr) { return nil diff --git a/lnd.go b/lnd.go index 0034f134b3..f4ef03a078 100644 --- a/lnd.go +++ b/lnd.go @@ -91,7 +91,7 @@ func AdminAuthOptions(cfg *Config, skipMacaroons bool) ([]grpc.DialOption, mac := &macaroon.Macaroon{} if err = mac.UnmarshalBinary(macBytes); err != nil { - return nil, fmt.Errorf("unable to decode macaroon: %v", + return nil, fmt.Errorf("unable to decode macaroon: %w", err) } diff --git a/lnrpc/devrpc/dev_server.go b/lnrpc/devrpc/dev_server.go index e2b98efa76..662c0d08d9 100644 --- a/lnrpc/devrpc/dev_server.go +++ b/lnrpc/devrpc/dev_server.go @@ -252,7 +252,7 @@ func (s *Server) ImportGraph(ctx context.Context, } if err := graphDB.AddLightningNode(node); err != nil { - return nil, fmt.Errorf("unable to add node %v: %v", + return nil, fmt.Errorf("unable to add node %v: %w", rpcNode.PubKey, err) } @@ -285,7 +285,7 @@ func (s *Server) ImportGraph(ctx context.Context, edge.ChannelPoint = *channelPoint if err := graphDB.AddChannelEdge(edge); err != nil { - return nil, fmt.Errorf("unable to add edge %v: %v", + return nil, fmt.Errorf("unable to add edge %v: %w", rpcEdge.ChanPoint, err) } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 7ed683f24e..b5e2428730 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -1140,7 +1140,7 @@ func toPairSnapshot(pairResult *PairHistory) (*routing.MissionControlPairSnapsho pairResult.History.FailTime, ) if err != nil { - return nil, fmt.Errorf("%v invalid failure: %v", pairPrefix, + return nil, fmt.Errorf("%v invalid failure: %w", pairPrefix, err) } @@ -1150,7 +1150,7 @@ func toPairSnapshot(pairResult *PairHistory) (*routing.MissionControlPairSnapsho pairResult.History.SuccessTime, ) if err != nil { - return nil, fmt.Errorf("%v invalid success: %v", pairPrefix, + return nil, fmt.Errorf("%v invalid success: %w", pairPrefix, err) } @@ -1265,7 +1265,7 @@ func (s *Server) subscribePayment(identifier lntypes.Hash) ( sub, err := router.Tower.SubscribePayment(identifier) switch { - case err == channeldb.ErrPaymentNotInitiated: + case errors.Is(err, channeldb.ErrPaymentNotInitiated): return nil, status.Error(codes.NotFound, err.Error()) case err != nil: return nil, err diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index a84ea68ffd..c29d495eb0 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -710,14 +710,14 @@ func (s *Server) VerifyMessage(_ context.Context, // for Schnorr signatures. pubkey, err := schnorr.ParsePubKey(in.Pubkey) if err != nil { - return nil, fmt.Errorf("unable to parse pubkey: %v", + return nil, fmt.Errorf("unable to parse pubkey: %w", err) } sigParsed, err := schnorr.ParseSignature(in.Signature) if err != nil { return nil, fmt.Errorf("can't parse Schnorr "+ - "signature: %v", err) + "signature: %w", err) } var digest []byte @@ -746,7 +746,7 @@ func (s *Server) VerifyMessage(_ context.Context, } sig, err := wireSig.ToSignature() if err != nil { - return nil, fmt.Errorf("failed to convert from wire format: %v", + return nil, fmt.Errorf("failed to convert from wire format: %w", err) } @@ -874,7 +874,7 @@ func (s *Server) MuSig2CombineKeys(_ context.Context, // Are there any tweaks to apply to the combined public key? tweaks, err := UnmarshalTweaks(in.Tweaks, in.TaprootTweak) if err != nil { - return nil, fmt.Errorf("error unmarshaling tweak options: %v", + return nil, fmt.Errorf("error unmarshaling tweak options: %w", err) } @@ -1014,7 +1014,7 @@ func (s *Server) MuSig2CreateSession(_ context.Context, // Are there any tweaks to apply to the combined public key? tweaks, err := UnmarshalTweaks(in.Tweaks, in.TaprootTweak) if err != nil { - return nil, fmt.Errorf("error unmarshaling tweak options: %v", + return nil, fmt.Errorf("error unmarshaling tweak options: %w", err) } @@ -1139,7 +1139,7 @@ func (s *Server) MuSig2CombineSig(_ context.Context, in.OtherPartialSignatures, ) if err != nil { - return nil, fmt.Errorf("error parsing partial signatures: %v", + return nil, fmt.Errorf("error parsing partial signatures: %w", err) } diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 3e71070aae..253d585094 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -1043,7 +1043,7 @@ func (w *WalletKit) BumpFee(ctx context.Context, // with an unconfirmed transaction. _, currentHeight, err := w.cfg.Chain.GetBestBlock() if err != nil { - return nil, fmt.Errorf("unable to retrieve current height: %v", + return nil, fmt.Errorf("unable to retrieve current height: %w", err) } @@ -2604,7 +2604,7 @@ func (w *WalletKit) ImportTapscript(_ context.Context, taprootScope := waddrmgr.KeyScopeBIP0086 addr, err := w.cfg.Wallet.ImportTaprootScript(taprootScope, tapscript) if err != nil { - return nil, fmt.Errorf("error importing script into wallet: %v", + return nil, fmt.Errorf("error importing script into wallet: %w", err) } diff --git a/lnrpc/wtclientrpc/wtclient.go b/lnrpc/wtclientrpc/wtclient.go index 459ce3240c..79e4287a6e 100644 --- a/lnrpc/wtclientrpc/wtclient.go +++ b/lnrpc/wtclientrpc/wtclient.go @@ -208,7 +208,7 @@ func (c *WatchtowerClient) AddTower(ctx context.Context, c.cfg.Resolver, ) if err != nil { - return nil, fmt.Errorf("invalid address %v: %v", req.Address, err) + return nil, fmt.Errorf("invalid address %v: %w", req.Address, err) } towerAddr := &lnwire.NetAddress{ diff --git a/lntest/bitcoind_common.go b/lntest/bitcoind_common.go index 9ce962dc26..e10bfed54d 100644 --- a/lntest/bitcoind_common.go +++ b/lntest/bitcoind_common.go @@ -192,7 +192,7 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, client, err := rpcclient.New(&rpcCfg, nil) if err != nil { _ = cleanUp() - return nil, nil, fmt.Errorf("unable to create rpc client: %v", + return nil, nil, fmt.Errorf("unable to create rpc client: %w", err) } diff --git a/lntest/node/harness_node.go b/lntest/node/harness_node.go index 71d55d9eac..fd873c2a23 100644 --- a/lntest/node/harness_node.go +++ b/lntest/node/harness_node.go @@ -294,14 +294,14 @@ func (hn *HarnessNode) ReadMacaroon(macPath string, timeout time.Duration) ( err := wait.NoError(func() error { macBytes, err := ioutil.ReadFile(macPath) if err != nil { - return fmt.Errorf("error reading macaroon file: %v", + return fmt.Errorf("error reading macaroon file: %w", err) } newMac := &macaroon.Macaroon{} if err = newMac.UnmarshalBinary(macBytes); err != nil { return fmt.Errorf("error unmarshalling macaroon "+ - "file: %v", err) + "file: %w", err) } mac = newMac @@ -619,7 +619,7 @@ func (hn *HarnessNode) cleanup() error { if hn.Cfg.backupDBDir != "" { err := os.RemoveAll(hn.Cfg.backupDBDir) if err != nil { - return fmt.Errorf("unable to remove backup dir: %v", + return fmt.Errorf("unable to remove backup dir: %w", err) } } diff --git a/lnwallet/btcwallet/psbt.go b/lnwallet/btcwallet/psbt.go index e655300c1f..1bed4b42c3 100644 --- a/lnwallet/btcwallet/psbt.go +++ b/lnwallet/btcwallet/psbt.go @@ -449,7 +449,7 @@ func signSegWitV0(in *psbt.PInput, tx *wire.MsgTx, in.SighashType, privKey, ) if err != nil { - return fmt.Errorf("error signing input %d: %v", idx, err) + return fmt.Errorf("error signing input %d: %w", idx, err) } in.PartialSigs = append(in.PartialSigs, &psbt.PartialSig{ PubKey: pubKeyBytes, @@ -471,7 +471,7 @@ func signSegWitV1KeySpend(in *psbt.PInput, tx *wire.MsgTx, privKey, ) if err != nil { - return fmt.Errorf("error signing taproot input %d: %v", idx, + return fmt.Errorf("error signing taproot input %d: %w", idx, err) } @@ -491,7 +491,7 @@ func signSegWitV1ScriptSpend(in *psbt.PInput, tx *wire.MsgTx, in.WitnessUtxo.PkScript, leaf, in.SighashType, privKey, ) if err != nil { - return fmt.Errorf("error signing taproot script input %d: %v", + return fmt.Errorf("error signing taproot script input %d: %w", idx, err) } diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index 5132f12966..885fb7b465 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -192,7 +192,7 @@ func (i *PsbtIntent) FundingParams() (btcutil.Address, int64, *psbt.Packet, // Encode the address in the human-readable bech32 format. addr, err := script.Address(i.netParams) if err != nil { - return nil, 0, nil, fmt.Errorf("unable to encode address: %v", + return nil, 0, nil, fmt.Errorf("unable to encode address: %w", err) } @@ -204,7 +204,7 @@ func (i *PsbtIntent) FundingParams() (btcutil.Address, int64, *psbt.Packet, packet, err = psbt.New(nil, nil, 2, 0, nil) if err != nil { return nil, 0, nil, fmt.Errorf("unable to create "+ - "PSBT: %v", err) + "PSBT: %w", err) } } packet.UnsignedTx.TxOut = append(packet.UnsignedTx.TxOut, out) diff --git a/lnwallet/rpcwallet/rpcwallet.go b/lnwallet/rpcwallet/rpcwallet.go index 50a38cd946..62c9d52d0b 100644 --- a/lnwallet/rpcwallet/rpcwallet.go +++ b/lnwallet/rpcwallet/rpcwallet.go @@ -533,12 +533,12 @@ func (r *RPCKeyRing) SignMessageSchnorr(keyLoc keychain.KeyLocator, if err != nil { considerShutdown(err) return nil, fmt.Errorf("error signing message in remote "+ - "signer instance: %v", err) + "signer instance: %w", err) } sigParsed, err := schnorr.ParseSignature(resp.Signature) if err != nil { - return nil, fmt.Errorf("can't parse schnorr signature: %v", + return nil, fmt.Errorf("can't parse schnorr signature: %w", err) } return sigParsed, nil @@ -634,7 +634,7 @@ func (r *RPCKeyRing) ComputeInputScript(tx *wire.MsgTx, // input. sig, err := r.remoteSign(tx, signDesc, witnessProgram) if err != nil { - return nil, fmt.Errorf("error signing with remote instance: %v", + return nil, fmt.Errorf("error signing with remote instance: %w", err) } @@ -740,7 +740,7 @@ func (r *RPCKeyRing) MuSig2CreateSession(bipVersion input.MuSig2Version, resp.TaprootInternalKey, ) if err != nil { - return nil, fmt.Errorf("error parsing internal key: %v", + return nil, fmt.Errorf("error parsing internal key: %w", err) } } @@ -1261,7 +1261,7 @@ func connectRPC(hostPort, tlsCertPath, macaroonPath string, certBytes, err := ioutil.ReadFile(tlsCertPath) if err != nil { - return nil, fmt.Errorf("error reading TLS cert file %v: %v", + return nil, fmt.Errorf("error reading TLS cert file %v: %w", tlsCertPath, err) } @@ -1273,7 +1273,7 @@ func connectRPC(hostPort, tlsCertPath, macaroonPath string, macBytes, err := ioutil.ReadFile(macaroonPath) if err != nil { - return nil, fmt.Errorf("error reading macaroon file %v: %v", + return nil, fmt.Errorf("error reading macaroon file %v: %w", macaroonPath, err) } mac := &macaroon.Macaroon{} @@ -1297,7 +1297,7 @@ func connectRPC(hostPort, tlsCertPath, macaroonPath string, defer cancel() conn, err := grpc.DialContext(ctxt, hostPort, opts...) if err != nil { - return nil, fmt.Errorf("unable to connect to RPC server: %v", + return nil, fmt.Errorf("unable to connect to RPC server: %w", err) } diff --git a/peer/brontide.go b/peer/brontide.go index 189fb16605..6ac438284d 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -3318,7 +3318,7 @@ func (p *Brontide) handleInitMsg(msg *lnwire.Init) error { // those presented in the local features fields. err := msg.Features.Merge(msg.GlobalFeatures) if err != nil { - return fmt.Errorf("unable to merge legacy global features: %v", + return fmt.Errorf("unable to merge legacy global features: %w", err) } diff --git a/routing/missioncontrol_store.go b/routing/missioncontrol_store.go index a6c98571fd..dd60d9346b 100644 --- a/routing/missioncontrol_store.go +++ b/routing/missioncontrol_store.go @@ -75,7 +75,7 @@ func newMissionControlStore(db kvdb.Backend, maxRecords int, err := kvdb.Update(db, func(tx kvdb.RwTx) error { resultsBucket, err := tx.CreateTopLevelBucket(resultsKey) if err != nil { - return fmt.Errorf("cannot create results bucket: %v", + return fmt.Errorf("cannot create results bucket: %w", err) } diff --git a/routing/router.go b/routing/router.go index 4abfe54b4a..50dcc63f79 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2442,7 +2442,8 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( // control. paySession, err := r.cfg.SessionSource.NewPaymentSession(payment) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("error creating payment session: "+ + "%w", err) } // Record this payment hash with the ControlTower, ensuring it is not diff --git a/rpcperms/middleware_handler.go b/rpcperms/middleware_handler.go index 9b1d77a541..2962037874 100644 --- a/rpcperms/middleware_handler.go +++ b/rpcperms/middleware_handler.go @@ -418,7 +418,7 @@ func NewMessageInterceptionRequest(ctx context.Context, case proto.Message: req.ProtoSerialized, err = proto.Marshal(t) if err != nil { - return nil, fmt.Errorf("cannot marshal proto msg: %v", + return nil, fmt.Errorf("cannot marshal proto msg: %w", err) } req.ProtoTypeName = string(proto.MessageName(t)) diff --git a/rpcserver.go b/rpcserver.go index d55c97257a..7376399d39 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1889,7 +1889,7 @@ func newPsbtAssembler(req *lnrpc.OpenChannelRequest, normalizedMinConfs int32, bytes.NewReader(psbtShim.BasePsbt), false, ) if err != nil { - return nil, fmt.Errorf("error parsing base PSBT: %v", + return nil, fmt.Errorf("error parsing base PSBT: %w", err) } } @@ -2114,7 +2114,7 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest, in.CloseAddress, r.cfg.ActiveNetParams.Params, ) if err != nil { - return nil, fmt.Errorf("error parsing upfront shutdown: %v", + return nil, fmt.Errorf("error parsing upfront shutdown: %w", err) } @@ -6460,7 +6460,7 @@ func (r *rpcServer) StopDaemon(_ context.Context, // otherwise some funds wouldn't be picked up. isRecoveryMode, progress, err := r.server.cc.Wallet.GetRecoveryInfo() if err != nil { - return nil, fmt.Errorf("unable to get wallet recovery info: %v", + return nil, fmt.Errorf("unable to get wallet recovery info: %w", err) } if isRecoveryMode && progress < 1 { @@ -7142,7 +7142,7 @@ func (r *rpcServer) ForwardingHistory(ctx context.Context, } timeSlice, err := r.server.miscDB.ForwardingLog().Query(eventQuery) if err != nil { - return nil, fmt.Errorf("unable to query forwarding log: %v", + return nil, fmt.Errorf("unable to query forwarding log: %w", err) } @@ -7944,7 +7944,7 @@ func (r *rpcServer) FundingStateStep(ctx context.Context, false, ) if err != nil { - return nil, fmt.Errorf("error parsing psbt: %v", + return nil, fmt.Errorf("error parsing psbt: %w", err) } diff --git a/sqldb/sqlerrors.go b/sqldb/sqlerrors.go index a939ec499f..6cc1b1cf87 100644 --- a/sqldb/sqlerrors.go +++ b/sqldb/sqlerrors.go @@ -5,6 +5,7 @@ package sqldb import ( "errors" "fmt" + "strings" "github.com/jackc/pgconn" "github.com/jackc/pgerrcode" @@ -21,6 +22,10 @@ var ( // MapSQLError attempts to interpret a given error as a database agnostic SQL // error. func MapSQLError(err error) error { + if err == nil { + return nil + } + // Attempt to interpret the error as a sqlite error. var sqliteErr *sqlite.Error if errors.As(err, &sqliteErr) { @@ -33,6 +38,26 @@ func MapSQLError(err error) error { return parsePostgresError(pqErr) } + // Sometimes the error won't be properly wrapped, so we'll need to + // inspect raw error itself to detect something we can wrap properly. + // This handles a postgres variant of the error. + const postgresErrMsg = "could not serialize access" + if strings.Contains(err.Error(), postgresErrMsg) { + return &ErrSerializationError{ + DBError: err, + } + } + + // We'll also attempt to catch this for sqlite, that uses a slightly + // different error message. This is taken from: + // https://gitlab.com/cznic/sqlite/-/blob/v1.25.0/sqlite.go#L75. + const sqliteErrMsg = "SQLITE_BUSY" + if strings.Contains(err.Error(), sqliteErrMsg) { + return &ErrSerializationError{ + DBError: err, + } + } + // Return original error if it could not be classified as a database // specific error. return err diff --git a/walletunlocker/service.go b/walletunlocker/service.go index d896c2d88a..bde5c7a560 100644 --- a/walletunlocker/service.go +++ b/walletunlocker/service.go @@ -882,7 +882,7 @@ func (u *UnlockerService) ChangePassword(ctx context.Context, err = macaroonService.Close() if err != nil { - return nil, fmt.Errorf("could not close macaroon service: %v", + return nil, fmt.Errorf("could not close macaroon service: %w", err) } diff --git a/watchtower/wtclient/session_negotiator.go b/watchtower/wtclient/session_negotiator.go index 65d5060aec..aed91c603b 100644 --- a/watchtower/wtclient/session_negotiator.go +++ b/watchtower/wtclient/session_negotiator.go @@ -480,7 +480,7 @@ func (n *sessionNegotiator) tryAddress(sessionKey keychain.SingleKeyECDH, err = n.cfg.DB.CreateClientSession(dbClientSession) if err != nil { - return fmt.Errorf("unable to persist ClientSession: %v", + return fmt.Errorf("unable to persist ClientSession: %w", err) } diff --git a/watchtower/wtclient/session_queue.go b/watchtower/wtclient/session_queue.go index 7864105159..41ae28f2fa 100644 --- a/watchtower/wtclient/session_queue.go +++ b/watchtower/wtclient/session_queue.go @@ -527,7 +527,7 @@ func (q *sessionQueue) nextStateUpdate() (*wtwire.StateUpdate, bool, hint, encBlob, err := task.craftSessionPayload(q.cfg.Signer) if err != nil { // TODO(conner): mark will not send - err := fmt.Errorf("unable to craft session payload: %v", + err := fmt.Errorf("unable to craft session payload: %w", err) return nil, false, wtdb.BackupID{}, err } @@ -665,7 +665,7 @@ func (q *sessionQueue) sendStateUpdate(conn wtserver.Peer, switch { case err == wtdb.ErrUnallocatedLastApplied: // TODO(conner): borked watchtower - err = fmt.Errorf("unable to ack seqnum=%d: %v", + err = fmt.Errorf("unable to ack seqnum=%d: %w", stateUpdate.SeqNum, err) q.log.Errorf("SessionQueue(%v) failed to ack update: %v", q.ID(), err) @@ -673,14 +673,14 @@ func (q *sessionQueue) sendStateUpdate(conn wtserver.Peer, case err == wtdb.ErrLastAppliedReversion: // TODO(conner): borked watchtower - err = fmt.Errorf("unable to ack seqnum=%d: %v", + err = fmt.Errorf("unable to ack seqnum=%d: %w", stateUpdate.SeqNum, err) q.log.Errorf("SessionQueue(%s) failed to ack update: %v", q.ID(), err) return err case err != nil: - err = fmt.Errorf("unable to ack seqnum=%d: %v", + err = fmt.Errorf("unable to ack seqnum=%d: %w", stateUpdate.SeqNum, err) q.log.Errorf("SessionQueue(%s) failed to ack update: %v", q.ID(), err) diff --git a/zpay32/amountunits.go b/zpay32/amountunits.go index f53f3ff00e..8dcfb7c101 100644 --- a/zpay32/amountunits.go +++ b/zpay32/amountunits.go @@ -136,7 +136,7 @@ func encodeAmount(msat lnwire.MilliSatoshi) (string, error) { // Should always be expressible in pico BTC. pico, err := fromMSat['p'](msat) if err != nil { - return "", fmt.Errorf("unable to express %d msat as pBTC: %v", + return "", fmt.Errorf("unable to express %d msat as pBTC: %w", msat, err) } shortened := strconv.FormatUint(pico, 10) + "p"