From 3431cd8c9e26e5c818cabfc0397d070f2949a2de Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Thu, 2 Oct 2025 10:10:08 -0300 Subject: [PATCH] accounting: add error messages for errors Replaced "return err" cases with wrapped errors to attach the info which may be useful when debugging. --- accounting/categories.go | 8 ++++-- accounting/conversions.go | 22 +++++++++++++--- accounting/entries.go | 53 +++++++++++++++++++++++++++------------ accounting/filter.go | 8 ++++-- accounting/off_chain.go | 34 +++++++++++++++++-------- accounting/on_chain.go | 49 +++++++++++++++++++++++++----------- accounting/report.go | 3 ++- 7 files changed, 128 insertions(+), 49 deletions(-) diff --git a/accounting/categories.go b/accounting/categories.go index 6eeee3e..c2dbffa 100644 --- a/accounting/categories.go +++ b/accounting/categories.go @@ -1,6 +1,9 @@ package accounting -import "regexp" +import ( + "fmt" + "regexp" +) // CustomCategory describes a custom category which can be used to identify // special case groups of transactions. @@ -24,7 +27,8 @@ func NewCustomCategory(name string, regexes []string) (*CustomCategory, error) { for _, regex := range regexes { exp, err := regexp.Compile(regex) if err != nil { - return nil, err + return nil, fmt.Errorf("category %v: compiling regex "+ + "%v failed: %w", name, regex, err) } category.Regexes = append(category.Regexes, exp) diff --git a/accounting/conversions.go b/accounting/conversions.go index 8e882dc..04ae995 100644 --- a/accounting/conversions.go +++ b/accounting/conversions.go @@ -2,6 +2,7 @@ package accounting import ( "context" + "fmt" "time" "github.com/btcsuite/btcd/btcutil" @@ -44,12 +45,18 @@ func getConversion(ctx context.Context, startTime, endTime time.Time, err := utils.ValidateTimeRange(startTime, endTime) if err != nil { - return nil, err + return nil, fmt.Errorf("conversion: invalid time range [%v,%v): %w", + startTime, endTime, err) } fiatClient, err := fiat.NewPriceSource(priceCfg) if err != nil { - return nil, err + backend := "" + if priceCfg != nil { + backend = priceCfg.Backend.String() + } + return nil, fmt.Errorf("conversion: initialising price "+ + "source backend %v failed: %w", backend, err) } // Get price data for our relevant period. We get pricing for the whole @@ -57,12 +64,19 @@ func getConversion(ctx context.Context, startTime, endTime time.Time, // calls we need to make to our external data source. prices, err := fiatClient.GetPrices(ctx, startTime, endTime) if err != nil { - return nil, err + return nil, fmt.Errorf("conversion: fetching prices for "+ + "range [%v,%v) failed: %w", startTime, endTime, err) } // Create a wrapper function which can be used to get individual price // points from our set of price data as we create our report. return func(ts time.Time) (*fiat.Price, error) { - return fiat.GetPrice(prices, ts) + price, err := fiat.GetPrice(prices, ts) + if err != nil { + return nil, fmt.Errorf("conversion: fetching price "+ + "at %v failed: %w", ts, err) + } + + return price, nil }, nil } diff --git a/accounting/entries.go b/accounting/entries.go index d6456e1..d944d64 100644 --- a/accounting/entries.go +++ b/accounting/entries.go @@ -80,7 +80,8 @@ func channelOpenEntries(channel channelInfo, tx lndclient.Transaction, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v channel %v: creating open entry "+ + "failed: %w", tx.TxHash, channel.channelID, err) } // If we did not initiate opening the channel, we can just return the @@ -101,7 +102,9 @@ func channelOpenEntries(channel channelInfo, tx lndclient.Transaction, FeeReference(tx.TxHash), note, category, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v channel %v: creating channel "+ + "open fee entry failed: %w", tx.TxHash, + channel.channelID, err) } return []*HarmonyEntry{openEntry, feeEntry}, nil @@ -135,7 +138,9 @@ func closedChannelEntries(channel closedChannelInfo, tx lndclient.Transaction, tx.TxHash, note, category, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v channel %v: creating channel "+ + "close entry failed: %w", tx.TxHash, channel.channelID, + err) } switch channel.initiator { @@ -172,7 +177,9 @@ func closedChannelEntries(channel closedChannelInfo, tx lndclient.Transaction, fees, err := u.getFee(tx.Tx.TxHash()) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v channel %v: fetching on-chain "+ + "close fees failed: %w", tx.TxHash, channel.channelID, + err) } // Our fees are provided as a positive amount in sats. Convert this to @@ -185,7 +192,9 @@ func closedChannelEntries(channel closedChannelInfo, tx lndclient.Transaction, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v channel %v: creating channel "+ + "close fee entry failed: %w", tx.TxHash, + channel.channelID, err) } return []*HarmonyEntry{closeEntry, feeEntry}, nil @@ -201,7 +210,8 @@ func sweepEntries(tx lndclient.Transaction, u entryUtils) ([]*HarmonyEntry, erro tx.TxHash, tx.Label, category, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating sweep entry failed: %w", + tx.TxHash, err) } // If we do not have a fee lookup function set, we log a warning that @@ -216,7 +226,8 @@ func sweepEntries(tx lndclient.Transaction, u entryUtils) ([]*HarmonyEntry, erro fee, err := u.getFee(tx.Tx.TxHash()) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: fetching sweep fee failed: %w", + tx.TxHash, err) } feeEntry, err := newHarmonyEntry( @@ -225,7 +236,8 @@ func sweepEntries(tx lndclient.Transaction, u entryUtils) ([]*HarmonyEntry, erro u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating sweep fee entry "+ + "failed: %w", tx.TxHash, err) } return []*HarmonyEntry{txEntry, feeEntry}, nil @@ -267,7 +279,8 @@ func createOnchainFeeEntry(tx lndclient.Transaction, category string, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating on-chain fee entry "+ + "failed: %w", tx.TxHash, err) } return feeEntry, nil @@ -315,7 +328,9 @@ func onChainEntries(tx lndclient.Transaction, note := utxoManagementFeeNote(tx.TxHash) feeEntry, err := createOnchainFeeEntry(tx, category, note, u) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating utxo "+ + "management fee entry failed: %w", tx.TxHash, + err) } return []*HarmonyEntry{feeEntry}, nil @@ -326,7 +341,8 @@ func onChainEntries(tx lndclient.Transaction, tx.Label, category, true, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating on-chain transaction "+ + "entry failed: %w", tx.TxHash, err) } // If we did not pay any fees, we can just return a single entry. @@ -336,7 +352,8 @@ func onChainEntries(tx lndclient.Transaction, feeEntry, err := createOnchainFeeEntry(tx, category, "", u) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating on-chain fee entry "+ + "failed: %w", tx.TxHash, err) } return []*HarmonyEntry{txEntry, feeEntry}, nil @@ -451,7 +468,8 @@ func paymentEntry(payment paymentInfo, paidToSelf bool, ref, note, "", false, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("payment %v: creating payment entry "+ + "failed: %w", payment.Hash, err) } // If we paid no fees (possible for payments to our direct peer), then @@ -468,7 +486,8 @@ func paymentEntry(payment paymentInfo, paidToSelf bool, feeRef, note, "", false, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("payment %v: creating payment fee "+ + "entry failed: %w", payment.Hash, err) } return []*HarmonyEntry{paymentEntry, feeEntry}, nil } @@ -502,7 +521,8 @@ func forwardingEntry(forward lndclient.ForwardingEvent, false, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("forward %v: creating forwarding "+ + "entry failed: %w", txid, err) } // If we did not earn any fees, return the forwarding entry. @@ -515,7 +535,8 @@ func forwardingEntry(forward lndclient.ForwardingEvent, EntryTypeForwardFee, txid, "", "", "", false, u.getFiat, ) if err != nil { - return nil, err + return nil, fmt.Errorf("forward %v: creating forwarding fee "+ + "entry failed: %w", txid, err) } return []*HarmonyEntry{fwdEntry, feeEntry}, nil diff --git a/accounting/filter.go b/accounting/filter.go index 10f6465..3bc585c 100644 --- a/accounting/filter.go +++ b/accounting/filter.go @@ -149,7 +149,9 @@ func preProcessPayments(payments []lndclient.Payment, payment.PaymentRequest, decode, ) if err != nil && err != errNoPaymentRequest { - return nil, err + return nil, fmt.Errorf("payment %v: retrieving "+ + "payment request details failed: %w", + payment.Hash, err) } destination, err := paymentHtlcDestination(payment) @@ -214,7 +216,9 @@ func paymentHtlcDestination(payment lndclient.Payment) (*route.Vertex, error) { lastHop := hops[len(hops)-1] lastHopPubkey, err := route.NewVertexFromStr(lastHop.PubKey) if err != nil { - return nil, err + return nil, fmt.Errorf("payment %v: parsing last hop "+ + "pubkey %v failed: %w", payment.Hash, lastHop.PubKey, + err) } return &lastHopPubkey, nil diff --git a/accounting/off_chain.go b/accounting/off_chain.go index 6e72012..7b55c22 100644 --- a/accounting/off_chain.go +++ b/accounting/off_chain.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lntypes" @@ -48,7 +49,9 @@ func OffChainReport(ctx context.Context, cfg *OffChainConfig) (Report, error) { cfg.PriceSourceCfg, ) if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: init conversion "+ + "lookup for range [%v,%v) failed: %w", cfg.StartTime, + cfg.EndTime, err) } return offChainReportWithPrices(cfg, getPrice) @@ -62,7 +65,8 @@ func offChainReportWithPrices(cfg *OffChainConfig, getPrice fiatPrice) (Report, invoices, err := cfg.ListInvoices() if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: listing invoices "+ + "failed: %w", err) } filteredInvoices := filterInvoices(cfg.StartTime, cfg.EndTime, invoices) @@ -71,25 +75,31 @@ func offChainReportWithPrices(cfg *OffChainConfig, getPrice fiatPrice) (Report, payments, err := cfg.ListPayments() if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: listing payments "+ + "failed: %w", err) } preProcessed, err := preProcessPayments(payments, cfg.DecodePayReq) if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: preprocessing %d "+ + "payments failed: %w", len(payments), err) } // Get a list of all the payments we made to ourselves. paymentsToSelf, err := getCircularPayments(cfg.OwnPubKey, preProcessed) if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: identifying "+ + "circular payments for node %v failed: %w", + cfg.OwnPubKey, err) } filteredPayments := filterPayments( cfg.StartTime, cfg.EndTime, preProcessed, ) if err := sanityCheckDuplicates(filteredPayments); err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: duplicate payment "+ + "hashes detected in range [%v,%v): %w", cfg.StartTime, + cfg.EndTime, err) } log.Infof("Retrieved: %v payments, %v filtered, %v circular", @@ -99,7 +109,8 @@ func offChainReportWithPrices(cfg *OffChainConfig, getPrice fiatPrice) (Report, // are already supplied over the relevant range for our query. forwards, err := cfg.ListForwards() if err != nil { - return nil, err + return nil, fmt.Errorf("off-chain report: listing forwards "+ + "failed: %w", err) } log.Infof("Retrieved: %v forwards", len(forwards)) @@ -133,7 +144,8 @@ func offChainReport(invoices []lndclient.Invoice, payments []paymentInfo, entry, err := invoiceEntry(invoice, toSelf, utils) if err != nil { - return nil, err + return nil, fmt.Errorf("invoice %v: creating entry "+ + "failed: %w", invoice.Hash, err) } reports = append(reports, entry) @@ -146,7 +158,8 @@ func offChainReport(invoices []lndclient.Invoice, payments []paymentInfo, entries, err := paymentEntry(payment, toSelf, utils) if err != nil { - return nil, err + return nil, fmt.Errorf("payment %v: creating entries "+ + "failed: %w", payment.Hash, err) } reports = append(reports, entries...) @@ -155,7 +168,8 @@ func offChainReport(invoices []lndclient.Invoice, payments []paymentInfo, for _, forward := range forwards { entries, err := forwardingEntry(forward, utils) if err != nil { - return nil, err + return nil, fmt.Errorf("forward at %v: creating "+ + "entries failed: %w", forward.Timestamp, err) } reports = append(reports, entries...) diff --git a/accounting/on_chain.go b/accounting/on_chain.go index 7a1fa0b..8ef1bf8 100644 --- a/accounting/on_chain.go +++ b/accounting/on_chain.go @@ -2,6 +2,7 @@ package accounting import ( "context" + "fmt" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" @@ -23,12 +24,15 @@ func OnChainReport(ctx context.Context, cfg *OnChainConfig) (Report, error) { cfg.PriceSourceCfg, ) if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: init conversion "+ + "lookup for range [%v,%v) failed: %w", cfg.StartTime, + cfg.EndTime, err) } info, err := getOnChainInfo(cfg, getPrice) if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: gathering on-chain "+ + "data failed: %w", err) } return onChainReport(info) @@ -93,7 +97,8 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation onChainTxns, err := cfg.OnChainTransactions() if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: listing on-chain "+ + "transactions failed: %w", err) } // Filter our on chain transactions by start and end time. If we have @@ -101,7 +106,9 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation // early. info.txns, err = filterOnChain(cfg.StartTime, cfg.EndTime, onChainTxns) if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: filtering "+ + "transactions for range [%v,%v) failed: %w", + cfg.StartTime, cfg.EndTime, err) } if len(info.txns) == 0 { @@ -115,7 +122,8 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation // closing channels that are awaiting resolution). pending, err := cfg.PendingChannels() if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: listing pending "+ + "channels failed: %w", err) } // We add our pending force close channels to opened and closed channels @@ -171,7 +179,8 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation // other on chain transactions. openRPCChannels, err := cfg.OpenChannels() if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: listing open "+ + "channels failed: %w", err) } for _, channel := range openRPCChannels { @@ -179,7 +188,9 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation channel.ChannelPoint, ) if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: parsing open "+ + "channel point %v failed: %w", + channel.ChannelPoint, err) } init := lndclient.InitiatorLocal @@ -201,7 +212,8 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation // on chain transactions. closedRPCChannels, err := cfg.ClosedChannels() if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: listing closed "+ + "channels failed: %w", err) } // Add our already closed channels open and closed transactions to our @@ -212,7 +224,9 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation closed.ChannelPoint, ) if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: parsing "+ + "closed channel point %v failed: %w", + closed.ChannelPoint, err) } inf := newChannelInfo( @@ -234,7 +248,8 @@ func getOnChainInfo(cfg *OnChainConfig, getPrice fiatPrice) (*onChainInformation // identify them separately to other on chain transactions. sweeps, err := cfg.ListSweeps() if err != nil { - return nil, err + return nil, fmt.Errorf("on-chain report: listing sweep "+ + "transactions failed: %w", err) } for _, sweep := range sweeps { @@ -260,7 +275,9 @@ func onChainReport(info *onChainInformation) ( openChannel, txn, info.entryUtils, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating "+ + "channel open entries failed: %w", + txn.TxHash, err) } report = append(report, entries...) @@ -274,7 +291,9 @@ func onChainReport(info *onChainInformation) ( channelClose, txn, info.entryUtils, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating "+ + "channel close entries failed: %w", + txn.TxHash, err) } report = append(report, entries...) @@ -289,7 +308,8 @@ func onChainReport(info *onChainInformation) ( txn, info.entryUtils, ) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating sweep "+ + "entries failed: %w", txn.TxHash, err) } report = append(report, entries...) @@ -300,7 +320,8 @@ func onChainReport(info *onChainInformation) ( // closes, we create a generic on chain entry for it. entries, err := onChainEntries(txn, info.entryUtils) if err != nil { - return nil, err + return nil, fmt.Errorf("tx %v: creating generic "+ + "on-chain entries failed: %w", txn.TxHash, err) } report = append(report, entries...) } diff --git a/accounting/report.go b/accounting/report.go index 0a0ff74..c02c4c7 100644 --- a/accounting/report.go +++ b/accounting/report.go @@ -78,7 +78,8 @@ func newHarmonyEntry(ts time.Time, amountMsat int64, e EntryType, txid, btcPrice, err := convert(ts) if err != nil { - return nil, err + return nil, fmt.Errorf("fiat conversion at %v failed: %w", ts, + err) } amtMsat := lnwire.MilliSatoshi(absAmt)