accounting: add error messages for errors

Replaced "return err" cases with wrapped errors to attach the info which may be
useful when debugging.
This commit is contained in:
Boris Nagaev 2025-10-02 10:10:08 -03:00
parent b12a858407
commit 3431cd8c9e
No known key found for this signature in database
7 changed files with 128 additions and 49 deletions

View file

@ -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)

View file

@ -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 := "<nil>"
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
}

View file

@ -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

View file

@ -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

View file

@ -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...)

View file

@ -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...)
}

View file

@ -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)