Merge pull request from yyforyongyu/fix-fee-estimator

chainfee: allow specifying min relay feerate from the API source
This commit is contained in:
Oliver Gugger 2024-07-25 11:40:18 -06:00 committed by GitHub
commit b40f165310
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 298 additions and 435 deletions

@ -27,19 +27,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnutils"
)
// Swapper is an interface that allows the chanbackup.SubSwapper to update the
@ -278,9 +279,8 @@ func (s *SubSwapper) backupUpdater() {
)
for i, closedChan := range chanUpdate.ClosedChans {
log.Debugf("Removing channel %v from backup "+
"state", newLogClosure(func() string {
return chanUpdate.ClosedChans[i].String()
}))
"state", lnutils.NewLogClosure(
chanUpdate.ClosedChans[i].String))
delete(s.backupState, closedChan)

@ -4,9 +4,9 @@ import (
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnutils"
)
// ChannelRestorer is an interface that allows the Recover method to map the
@ -63,9 +63,8 @@ func Recover(backups []Single, restorer ChannelRestorer,
log.Infof("Attempting to connect to node=%x (addrs=%v) to "+
"restore ChannelPoint(%v)",
backup.RemoteNodePub.SerializeCompressed(),
newLogClosure(func() string {
return spew.Sdump(backups[i].Addresses)
}), backup.FundingOutpoint)
lnutils.SpewLogClosure(backups[i].Addresses),
backup.FundingOutpoint)
err = peerConnector.ConnectPeer(
backup.RemoteNodePub, backup.Addresses,

@ -13,13 +13,13 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -732,9 +732,8 @@ justiceTxBroadcast:
}
finalTx := justiceTxs.spendAll
brarLog.Debugf("Broadcasting justice tx: %v", newLogClosure(func() string {
return spew.Sdump(finalTx)
}))
brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure(
finalTx))
// We'll now attempt to broadcast the transaction which finalized the
// channel's retribution against the cheating counter party.
@ -857,9 +856,7 @@ Loop:
brarLog.Debugf("Broadcasting justice tx "+
"spending commitment outs: %v",
newLogClosure(func() string {
return spew.Sdump(tx)
}))
lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label)
if err != nil {
@ -874,9 +871,7 @@ Loop:
brarLog.Debugf("Broadcasting justice tx "+
"spending HTLC outs: %v",
newLogClosure(func() string {
return spew.Sdump(tx)
}))
lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label)
if err != nil {
@ -891,9 +886,7 @@ Loop:
brarLog.Debugf("Broadcasting justice tx "+
"spending second-level HTLC output: %v",
newLogClosure(func() string {
return spew.Sdump(tx)
}))
lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label)
if err != nil {

@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -1254,7 +1255,7 @@ func (c *chainWatcher) dispatchContractBreach(spendEvent *chainntnfs.SpendDetail
spendHeight := uint32(spendEvent.SpendingHeight)
log.Debugf("Punishment breach retribution created: %v",
newLogClosure(func() string {
lnutils.NewLogClosure(func() string {
retribution.KeyRing.LocalHtlcKey = nil
retribution.KeyRing.RemoteHtlcKey = nil
retribution.KeyRing.ToLocalKey = nil

@ -14,7 +14,6 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
@ -24,6 +23,7 @@ import (
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sweep"
@ -465,10 +465,8 @@ func (c *ChannelArbitrator) Start(state *chanArbStartState) error {
}
log.Debugf("Starting ChannelArbitrator(%v), htlc_set=%v, state=%v",
c.cfg.ChanPoint, newLogClosure(func() string {
return spew.Sdump(c.activeHTLCs)
}), state.currentState,
)
c.cfg.ChanPoint, lnutils.SpewLogClosure(c.activeHTLCs),
state.currentState)
// Set our state from our starting state.
c.state = state.currentState
@ -958,10 +956,7 @@ func (c *ChannelArbitrator) stateStep(
// Otherwise, we'll log that we checked the HTLC actions as the
// commitment transaction has already been broadcast.
log.Tracef("ChannelArbitrator(%v): logging chain_actions=%v",
c.cfg.ChanPoint,
newLogClosure(func() string {
return spew.Sdump(chainActions)
}))
c.cfg.ChanPoint, lnutils.SpewLogClosure(chainActions))
// Depending on the type of trigger, we'll either "tunnel"
// through to a farther state, or just proceed linearly to the
@ -1096,10 +1091,7 @@ func (c *ChannelArbitrator) stateStep(
// channel resolution state.
log.Infof("Broadcasting force close transaction %v, "+
"ChannelPoint(%v): %v", closeTx.TxHash(),
c.cfg.ChanPoint,
newLogClosure(func() string {
return spew.Sdump(closeTx)
}))
c.cfg.ChanPoint, lnutils.SpewLogClosure(closeTx))
// At this point, we'll now broadcast the commitment
// transaction itself.
@ -1224,9 +1216,7 @@ func (c *ChannelArbitrator) stateStep(
if len(pktsToSend) != 0 {
log.Debugf("ChannelArbitrator(%v): sending "+
"resolution message=%v", c.cfg.ChanPoint,
newLogClosure(func() string {
return spew.Sdump(pktsToSend)
}))
lnutils.SpewLogClosure(pktsToSend))
err := c.cfg.DeliverResolutionMsg(pktsToSend...)
if err != nil {
@ -2741,11 +2731,7 @@ func (c *ChannelArbitrator) notifyContractUpdate(upd *ContractUpdate) {
c.unmergedSet[upd.HtlcKey] = newHtlcSet(upd.Htlcs)
log.Tracef("ChannelArbitrator(%v): fresh set of htlcs=%v",
c.cfg.ChanPoint,
newLogClosure(func() string {
return spew.Sdump(upd)
}),
)
c.cfg.ChanPoint, lnutils.SpewLogClosure(upd))
}
// updateActiveHTLCs merges the unmerged set of HTLCs from the link with

@ -51,19 +51,3 @@ func UseBreachLogger(logger btclog.Logger) {
func UseNurseryLogger(logger btclog.Logger) {
utxnLog = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -18,6 +18,7 @@ import (
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
)
@ -947,10 +948,7 @@ func (u *UtxoNursery) waitForSweepConf(classHeight uint32,
func (u *UtxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
utxnLog.Infof("Publishing CLTV-delayed HTLC output using timeout tx "+
"(txid=%v): %v", baby.timeoutTx.TxHash(),
newLogClosure(func() string {
return spew.Sdump(baby.timeoutTx)
}),
)
lnutils.SpewLogClosure(baby.timeoutTx))
// We'll now broadcast the HTLC transaction, then wait for it to be
// confirmed before transitioning it to kindergarten.

@ -14,8 +14,8 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil/bech32"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tor"
"github.com/miekg/dns"
@ -431,10 +431,7 @@ search:
}
log.Tracef("Retrieved SRV records from dns seed: %v",
newLogClosure(func() string {
return spew.Sdump(addrs)
}),
)
lnutils.SpewLogClosure(addrs))
// Next, we'll need to issue an A record request for each of
// the nodes, skipping it if nothing comes back.

@ -27,19 +27,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations
// so don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -55,6 +55,24 @@
# Improvements
## Functional Updates
* A new field, `min_relay_feerate`, is [now
expected](https://github.com/lightningnetwork/lnd/pull/8891) in the response
from querying the external fee estimation URL. The new response should have
the format,
```json
{
"fee_by_block_target": {
"2": 5076,
"3": 4228,
"26": 4200
},
"min_relay_feerate": 1000
}
```
All units are `sats/kvB`. If the new field `min_relay_feerate` is not set,
the default floor feerate (1012 sats/kvB) will be used.
## RPC Updates
* [`xImportMissionControl`](https://github.com/lightningnetwork/lnd/pull/8779)

@ -12,7 +12,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/batch"
"github.com/lightningnetwork/lnd/chainntnfs"
@ -1432,7 +1431,7 @@ func (b *Builder) processUpdate(msg interface{},
}
log.Tracef("New channel update applied: %v",
newLogClosure(func() string { return spew.Sdump(msg) }))
lnutils.SpewLogClosure(msg))
b.stats.incNumChannelUpdates()
default:

@ -29,19 +29,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -5,11 +5,11 @@ import (
"fmt"
"sync"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -800,9 +800,8 @@ func (cm *circuitMap) CommitCircuits(circuits ...*PaymentCircuit) (
inKeys = append(inKeys, circuit.Incoming)
}
log.Tracef("Committing fresh circuits: %v", newLogClosure(func() string {
return spew.Sdump(inKeys)
}))
log.Tracef("Committing fresh circuits: %v", lnutils.SpewLogClosure(
inKeys))
actions := &CircuitFwdActions{}
@ -948,9 +947,8 @@ func (cm *circuitMap) OpenCircuits(keystones ...Keystone) error {
return nil
}
log.Tracef("Opening finalized circuits: %v", newLogClosure(func() string {
return spew.Sdump(keystones)
}))
log.Tracef("Opening finalized circuits: %v", lnutils.SpewLogClosure(
keystones))
// Check that all keystones correspond to committed-but-unopened
// circuits.
@ -1078,9 +1076,8 @@ func (cm *circuitMap) CloseCircuit(outKey CircuitKey) (*PaymentCircuit, error) {
// circuit was already cleaned up at a different point in time.
func (cm *circuitMap) DeleteCircuits(inKeys ...CircuitKey) error {
log.Tracef("Deleting resolved circuits: %v", newLogClosure(func() string {
return spew.Sdump(inKeys)
}))
log.Tracef("Deleting resolved circuits: %v", lnutils.SpewLogClosure(
inKeys))
var (
closingCircuits = make(map[CircuitKey]struct{})

@ -13,7 +13,6 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btclog"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/channeldb"
@ -25,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
@ -2536,13 +2536,9 @@ func (l *channelLink) updateCommitTx() error {
l.log.Tracef("revocation window exhausted, unable to send: "+
"%v, pend_updates=%v, dangling_closes%v",
l.channel.PendingLocalUpdateCount(),
newLogClosure(func() string {
return spew.Sdump(l.openedCircuits)
}),
newLogClosure(func() string {
return spew.Sdump(l.closedCircuits)
}),
)
lnutils.SpewLogClosure(l.openedCircuits),
lnutils.SpewLogClosure(l.closedCircuits))
return nil
} else if err != nil {
return err

@ -31,19 +31,3 @@ func UseLogger(logger btclog.Logger) {
log = logger
hop.UseLogger(logger)
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
@ -613,9 +614,8 @@ func (s *Switch) SendHTLC(firstHop lnwire.ShortChannelID, attemptID uint64,
func (s *Switch) UpdateForwardingPolicies(
chanPolicies map[wire.OutPoint]models.ForwardingPolicy) {
log.Tracef("Updating link policies: %v", newLogClosure(func() string {
return spew.Sdump(chanPolicies)
}))
log.Tracef("Updating link policies: %v", lnutils.SpewLogClosure(
chanPolicies))
s.indexMtx.RLock()
@ -1213,9 +1213,8 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
)
log.Warnf("unable to find err source for "+
"outgoing_link=%v, errors=%v",
packet.outgoingChanID, newLogClosure(func() string {
return spew.Sdump(linkErrs)
}))
packet.outgoingChanID,
lnutils.SpewLogClosure(linkErrs))
}
log.Tracef("incoming HTLC(%x) violated "+
@ -1994,10 +1993,9 @@ out:
continue
}
log.Tracef("Acked %d settle fails: %v", len(s.pendingSettleFails),
newLogClosure(func() string {
return spew.Sdump(s.pendingSettleFails)
}))
log.Tracef("Acked %d settle fails: %v",
len(s.pendingSettleFails),
lnutils.SpewLogClosure(s.pendingSettleFails))
// Reset the pendingSettleFails buffer while keeping acquired
// memory.

@ -27,19 +27,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string // nolint:unused
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure { // nolint:unused
return logClosure(c)
}

@ -15,11 +15,11 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/netann"
"github.com/lightningnetwork/lnd/routing"
@ -473,10 +473,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
}
log.Tracef("[addinvoice] adding new invoice %v",
newLogClosure(func() string {
return spew.Sdump(newInvoice)
}),
)
lnutils.SpewLogClosure(newInvoice))
// With all sanity checks passed, write the invoice to the database.
_, err = cfg.AddInvoice(ctx, newInvoice, paymentHash)

@ -27,19 +27,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -33,6 +33,9 @@ type WebFeeService interface {
// target.
SetFeeRate(feeRate chainfee.SatPerKWeight, conf uint32)
// SetMinRelayFeerate sets a min relay feerate.
SetMinRelayFeerate(fee chainfee.SatPerKVByte)
// Reset resets the fee rate map to the default value.
Reset()
}
@ -52,8 +55,9 @@ const (
type FeeService struct {
*testing.T
feeRateMap map[uint32]uint32
url string
feeRateMap map[uint32]uint32
minRelayFeerate chainfee.SatPerKVByte
url string
srv *http.Server
wg sync.WaitGroup
@ -79,6 +83,7 @@ func NewFeeService(t *testing.T) *FeeService {
f.feeRateMap = map[uint32]uint32{
feeServiceTarget: DefaultFeeRateSatPerKw,
}
f.minRelayFeerate = chainfee.FeePerKwFloor.FeePerKVByte()
listenAddr := fmt.Sprintf(":%v", port)
mux := http.NewServeMux()
@ -113,10 +118,9 @@ func (f *FeeService) handleRequest(w http.ResponseWriter, _ *http.Request) {
defer f.lock.Unlock()
bytes, err := json.Marshal(
struct {
Fees map[uint32]uint32 `json:"fee_by_block_target"`
}{
Fees: f.feeRateMap,
chainfee.WebAPIResponse{
FeeByBlockTarget: f.feeRateMap,
MinRelayFeerate: f.minRelayFeerate,
},
)
require.NoErrorf(f, err, "cannot serialize estimates")
@ -143,6 +147,14 @@ func (f *FeeService) SetFeeRate(fee chainfee.SatPerKWeight, conf uint32) {
f.feeRateMap[conf] = uint32(fee.FeePerKVByte())
}
// SetMinRelayFeerate sets a min relay feerate.
func (f *FeeService) SetMinRelayFeerate(fee chainfee.SatPerKVByte) {
f.lock.Lock()
defer f.lock.Unlock()
f.minRelayFeerate = fee
}
// Reset resets the fee rate map to the default value.
func (f *FeeService) Reset() {
f.lock.Lock()

@ -860,6 +860,12 @@ func (h *HarnessTest) SetFeeEstimateWithConf(
h.feeService.SetFeeRate(fee, conf)
}
// SetMinRelayFeerate sets a min relay fee rate to be returned from fee
// estimator.
func (h *HarnessTest) SetMinRelayFeerate(fee chainfee.SatPerKVByte) {
h.feeService.SetMinRelayFeerate(fee)
}
// validateNodeState checks that the node doesn't have any uncleaned states
// which will affect its following tests.
func (h *HarnessTest) validateNodeState(hn *node.HarnessNode) error {

27
lnutils/log.go Normal file

@ -0,0 +1,27 @@
package lnutils
import "github.com/davecgh/go-spew/spew"
// LogClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type LogClosure func() string
// String invokes the underlying function and returns the result.
func (c LogClosure) String() string {
return c()
}
// NewLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func NewLogClosure(c func() string) LogClosure {
return LogClosure(c)
}
// SpewLogClosure takes an interface and returns the string of it created from
// `spew.Sdump` in a LogClosure.
func SpewLogClosure(a any) LogClosure {
return func() string {
return spew.Sdump(a)
}
}

@ -10,10 +10,12 @@ import (
"net"
"net/http"
"sync"
"sync/atomic"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/rpcclient"
"github.com/lightningnetwork/lnd/lnutils"
)
const (
@ -628,9 +630,10 @@ var _ Estimator = (*BitcoindEstimator)(nil)
// implementation of this interface in order to allow the WebAPIEstimator to
// be fully generic in its logic.
type WebAPIFeeSource interface {
// GetFeeMap will query the web API, parse the response and return a
// map of confirmation targets to sat/kw fees.
GetFeeMap() (map[uint32]uint32, error)
// GetFeeInfo will query the web API, parse the response into a
// WebAPIResponse which contains a map of confirmation targets to
// sat/kw fees and min relay feerate.
GetFeeInfo() (WebAPIResponse, error)
}
// SparseConfFeeSource is an implementation of the WebAPIFeeSource that utilizes
@ -642,30 +645,43 @@ type SparseConfFeeSource struct {
URL string
}
// WebAPIResponse is the response returned by the fee estimation API.
type WebAPIResponse struct {
// FeeByBlockTarget is a map of confirmation targets to sat/kvb fees.
FeeByBlockTarget map[uint32]uint32 `json:"fee_by_block_target"`
// MinRelayFeerate is the minimum relay fee in sat/kvb.
MinRelayFeerate SatPerKVByte `json:"min_relay_feerate"`
}
// parseResponse attempts to parse the body of the response generated by the
// above query URL. Typically this will be JSON, but the specifics are left to
// the WebAPIFeeSource implementation.
func (s SparseConfFeeSource) parseResponse(r io.Reader) (
map[uint32]uint32, error) {
WebAPIResponse, error) {
type jsonResp struct {
FeeByBlockTarget map[uint32]uint32 `json:"fee_by_block_target"`
}
resp := jsonResp{
resp := WebAPIResponse{
FeeByBlockTarget: make(map[uint32]uint32),
MinRelayFeerate: 0,
}
jsonReader := json.NewDecoder(r)
if err := jsonReader.Decode(&resp); err != nil {
return nil, err
return WebAPIResponse{}, err
}
return resp.FeeByBlockTarget, nil
if resp.MinRelayFeerate == 0 {
log.Errorf("No min relay fee rate available, using default %v",
FeePerKwFloor)
resp.MinRelayFeerate = FeePerKwFloor.FeePerKVByte()
}
return resp, nil
}
// GetFeeMap will query the web API, parse the response and return a map of
// confirmation targets to sat/kw fees.
func (s SparseConfFeeSource) GetFeeMap() (map[uint32]uint32, error) {
// GetFeeInfo will query the web API, parse the response and return a map of
// confirmation targets to sat/kw fees and min relay feerate in a parsed
// response.
func (s SparseConfFeeSource) GetFeeInfo() (WebAPIResponse, error) {
// Rather than use the default http.Client, we'll make a custom one
// which will allow us to control how long we'll wait to read the
// response from the service. This way, if the service is down or
@ -688,20 +704,20 @@ func (s SparseConfFeeSource) GetFeeMap() (map[uint32]uint32, error) {
if err != nil {
log.Errorf("unable to query web api for fee response: %v",
err)
return nil, err
return WebAPIResponse{}, err
}
defer resp.Body.Close()
// Once we've obtained the response, we'll instruct the WebAPIFeeSource
// to parse out the body to obtain our final result.
feesByBlockTarget, err := s.parseResponse(resp.Body)
parsedResp, err := s.parseResponse(resp.Body)
if err != nil {
log.Errorf("unable to parse fee api response: %v", err)
return nil, err
return WebAPIResponse{}, err
}
return feesByBlockTarget, nil
return parsedResp, nil
}
// A compile-time assertion to ensure that SparseConfFeeSource implements the
@ -711,8 +727,8 @@ var _ WebAPIFeeSource = (*SparseConfFeeSource)(nil)
// WebAPIEstimator is an implementation of the Estimator interface that
// queries an HTTP-based fee estimation from an existing web API.
type WebAPIEstimator struct {
started sync.Once
stopped sync.Once
started atomic.Bool
stopped atomic.Bool
// apiSource is the backing web API source we'll use for our queries.
apiSource WebAPIFeeSource
@ -726,6 +742,7 @@ type WebAPIEstimator struct {
// rather than re-querying the API, to prevent an inadvertent DoS attack.
feesMtx sync.Mutex
feeByBlockTarget map[uint32]uint32
minRelayFeerate SatPerKVByte
// noCache determines whether the web estimator should cache fee
// estimates.
@ -777,6 +794,12 @@ func NewWebAPIEstimator(api WebAPIFeeSource, noCache bool,
func (w *WebAPIEstimator) EstimateFeePerKW(numBlocks uint32) (
SatPerKWeight, error) {
// If the estimator hasn't been started yet, we'll return an error as
// we can't provide a fee estimate.
if !w.started.Load() {
return 0, fmt.Errorf("estimator not started")
}
if numBlocks > MaxBlockTarget {
numBlocks = MaxBlockTarget
} else if numBlocks < minBlockTarget {
@ -816,28 +839,33 @@ func (w *WebAPIEstimator) EstimateFeePerKW(numBlocks uint32) (
//
// NOTE: This method is part of the Estimator interface.
func (w *WebAPIEstimator) Start() error {
log.Infof("Starting Web API fee estimator...")
// Return an error if it's already been started.
if w.started.Load() {
return fmt.Errorf("web API fee estimator already started")
}
defer w.started.Store(true)
// During startup we'll query the API to initialize the fee map.
w.updateFeeEstimates()
// No update loop is needed when we don't cache.
if w.noCache {
return nil
}
var err error
w.started.Do(func() {
log.Infof("Starting web API fee estimator")
feeUpdateTimeout := w.randomFeeUpdateTimeout()
feeUpdateTimeout := w.randomFeeUpdateTimeout()
log.Infof("Web API fee estimator using update timeout of %v",
feeUpdateTimeout)
log.Infof("Web API fee estimator using update timeout of %v",
feeUpdateTimeout)
w.updateFeeTicker = time.NewTicker(feeUpdateTimeout)
w.updateFeeTicker = time.NewTicker(feeUpdateTimeout)
w.updateFeeEstimates()
w.wg.Add(1)
go w.feeUpdateManager()
w.wg.Add(1)
go w.feeUpdateManager()
})
return err
return nil
}
// Stop stops any spawned goroutines and cleans up the resources used by the
@ -845,19 +873,22 @@ func (w *WebAPIEstimator) Start() error {
//
// NOTE: This method is part of the Estimator interface.
func (w *WebAPIEstimator) Stop() error {
log.Infof("Stopping web API fee estimator")
if w.stopped.Swap(true) {
return fmt.Errorf("web API fee estimator already stopped")
}
// Update loop is not running when we don't cache.
if w.noCache {
return nil
}
w.stopped.Do(func() {
log.Infof("Stopping web API fee estimator")
w.updateFeeTicker.Stop()
w.updateFeeTicker.Stop()
close(w.quit)
w.wg.Wait()
close(w.quit)
w.wg.Wait()
})
return nil
}
@ -866,7 +897,19 @@ func (w *WebAPIEstimator) Stop() error {
//
// NOTE: This method is part of the Estimator interface.
func (w *WebAPIEstimator) RelayFeePerKW() SatPerKWeight {
return FeePerKwFloor
if !w.started.Load() {
log.Error("WebAPIEstimator not started")
}
// Get fee estimates now if we don't refresh periodically.
if w.noCache {
w.updateFeeEstimates()
}
log.Infof("Web API returning %v for min relay feerate",
w.minRelayFeerate)
return w.minRelayFeerate.FeePerKWeight()
}
// randomFeeUpdateTimeout returns a random timeout between minFeeUpdateTimeout
@ -956,14 +999,21 @@ func (w *WebAPIEstimator) getCachedFee(numBlocks uint32) (uint32, error) {
func (w *WebAPIEstimator) updateFeeEstimates() {
// Once we've obtained the response, we'll instruct the WebAPIFeeSource
// to parse out the body to obtain our final result.
feesByBlockTarget, err := w.apiSource.GetFeeMap()
resp, err := w.apiSource.GetFeeInfo()
if err != nil {
log.Errorf("unable to get fee response: %v", err)
return
}
log.Debugf("Received response from source: %s", lnutils.NewLogClosure(
func() string {
resp, _ := json.Marshal(resp)
return string(resp)
}))
w.feesMtx.Lock()
w.feeByBlockTarget = feesByBlockTarget
w.feeByBlockTarget = resp.FeeByBlockTarget
w.minRelayFeerate = resp.MinRelayFeerate
w.feesMtx.Unlock()
}

@ -107,17 +107,20 @@ func TestSparseConfFeeSource(t *testing.T) {
2: 42,
3: 54321,
}
testJSON := map[string]map[uint32]uint32{
"fee_by_block_target": testFees,
testMinRelayFee := SatPerKVByte(1000)
testResp := WebAPIResponse{
MinRelayFeerate: testMinRelayFee,
FeeByBlockTarget: testFees,
}
jsonResp, err := json.Marshal(testJSON)
jsonResp, err := json.Marshal(testResp)
require.NoError(t, err, "unable to marshal JSON API response")
reader := bytes.NewReader(jsonResp)
// Finally, ensure the expected map is returned without error.
fees, err := feeSource.parseResponse(reader)
resp, err := feeSource.parseResponse(reader)
require.NoError(t, err, "unable to parse API response")
require.Equal(t, testFees, fees, "unexpected fee map returned")
require.Equal(t, testResp, resp, "unexpected resp returned")
// Test parsing an improperly formatted JSON API response.
badFees := map[string]uint32{"hi": 12345, "hello": 42, "satoshi": 54321}
@ -131,6 +134,45 @@ func TestSparseConfFeeSource(t *testing.T) {
require.Error(t, err, "expected error when parsing bad JSON")
}
// TestFeeSourceCompatibility checks that when a fee source doesn't return a
// `min_relay_feerate` field in its response, the floor feerate is used.
//
// NOTE: Field `min_relay_feerate` was added in v0.18.3.
func TestFeeSourceCompatibility(t *testing.T) {
t.Parallel()
// Test that GenQueryURL returns the URL as is.
url := "test"
feeSource := SparseConfFeeSource{URL: url}
// Test parsing a properly formatted JSON API response.
//
// Create the resp without the `min_relay_feerate` field.
testFees := map[uint32]uint32{
1: 12345,
}
testResp := struct {
// FeeByBlockTarget is a map of confirmation targets to sat/kvb
// fees.
FeeByBlockTarget map[uint32]uint32 `json:"fee_by_block_target"`
}{
FeeByBlockTarget: testFees,
}
jsonResp, err := json.Marshal(testResp)
require.NoError(t, err, "unable to marshal JSON API response")
reader := bytes.NewReader(jsonResp)
// Ensure the expected map is returned without error.
resp, err := feeSource.parseResponse(reader)
require.NoError(t, err, "unable to parse API response")
require.Equal(t, testResp.FeeByBlockTarget, resp.FeeByBlockTarget,
"unexpected resp returned")
// Expect the floor feerate to be used.
require.Equal(t, FeePerKwFloor.FeePerKVByte(), resp.MinRelayFeerate)
}
// TestWebAPIFeeEstimator checks that the WebAPIFeeEstimator returns fee rates
// as expected.
func TestWebAPIFeeEstimator(t *testing.T) {
@ -194,25 +236,28 @@ func TestWebAPIFeeEstimator(t *testing.T) {
// This will create a `feeByBlockTarget` map with the following values,
// - 2: 4000 sat/kb
// - 6: 2000 sat/kb.
feeRateResp := map[uint32]uint32{
feeRates := map[uint32]uint32{
minTarget: maxFeeRate,
maxTarget: minFeeRate,
}
resp := WebAPIResponse{
FeeByBlockTarget: feeRates,
}
// Create a mock fee source and mock its returned map.
feeSource := &mockFeeSource{}
feeSource.On("GetFeeMap").Return(feeRateResp, nil)
feeSource.On("GetFeeInfo").Return(resp, nil)
estimator, _ := NewWebAPIEstimator(
feeSource, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
)
// Test that requesting a fee when no fees have been cached won't fail.
// Test that when the estimator is not started, an error is returned.
feeRate, err := estimator.EstimateFeePerKW(5)
require.NoErrorf(t, err, "expected no error")
require.Equalf(t, FeePerKwFloor, feeRate, "expected fee rate floor "+
"returned when no cached fee rate found")
require.Error(t, err, "expected an error")
require.Zero(t, feeRate, "expected zero fee rate")
// Start the estimator.
require.NoError(t, estimator.Start(), "unable to start fee estimator")
for _, tc := range testCases {
@ -234,7 +279,7 @@ func TestWebAPIFeeEstimator(t *testing.T) {
exp := SatPerKVByte(tc.expectedFeeRate).FeePerKWeight()
require.Equalf(t, exp, est, "target %v failed, fee "+
"map is %v", tc.target, feeRateResp)
"map is %v", tc.target, feeRate)
})
}

@ -12,10 +12,10 @@ type mockFeeSource struct {
// WebAPIFeeSource interface.
var _ WebAPIFeeSource = (*mockFeeSource)(nil)
func (m *mockFeeSource) GetFeeMap() (map[uint32]uint32, error) {
func (m *mockFeeSource) GetFeeInfo() (WebAPIResponse, error) {
args := m.Called()
return args.Get(0).(map[uint32]uint32), args.Error(1)
return args.Get(0).(WebAPIResponse), args.Error(1)
}
// MockEstimator implements the `Estimator` interface and is used by

@ -53,7 +53,7 @@ func (s SatPerKVByte) FeePerKWeight() SatPerKWeight {
// String returns a human-readable string of the fee rate.
func (s SatPerKVByte) String() string {
return fmt.Sprintf("%v sat/kb", int64(s))
return fmt.Sprintf("%v sat/kvb", int64(s))
}
// SatPerKWeight represents a fee rate in sat/kw.

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
@ -869,10 +870,7 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen
// With the closing transaction crafted, we'll now broadcast it
// to the network.
chancloserLog.Infof("Broadcasting cooperative close tx: %v",
newLogClosure(func() string {
return spew.Sdump(closeTx)
}),
)
lnutils.SpewLogClosure(closeTx))
// Create a close channel label.
chanID := c.cfg.Channel.ShortChanID()

@ -23,19 +23,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
chancloserLog = logger
}
// logClosure is used to provide a closure over expensive logging operations
// so they aren't performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -29,6 +29,7 @@ import (
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
@ -1869,10 +1870,7 @@ func (lc *LightningChannel) restoreCommitState(
lc.localCommitChain.addCommitment(localCommit)
lc.log.Tracef("starting local commitment: %v",
newLogClosure(func() string {
return spew.Sdump(lc.localCommitChain.tail())
}),
)
lnutils.SpewLogClosure(lc.localCommitChain.tail()))
// We'll also do the same for the remote commitment chain.
remoteCommit, err := lc.diskCommitToMemCommit(
@ -1885,10 +1883,7 @@ func (lc *LightningChannel) restoreCommitState(
lc.remoteCommitChain.addCommitment(remoteCommit)
lc.log.Tracef("starting remote commitment: %v",
newLogClosure(func() string {
return spew.Sdump(lc.remoteCommitChain.tail())
}),
)
lnutils.SpewLogClosure(lc.remoteCommitChain.tail()))
var (
pendingRemoteCommit *commitment
@ -1920,10 +1915,7 @@ func (lc *LightningChannel) restoreCommitState(
lc.remoteCommitChain.addCommitment(pendingRemoteCommit)
lc.log.Debugf("pending remote commitment: %v",
newLogClosure(func() string {
return spew.Sdump(lc.remoteCommitChain.tip())
}),
)
lnutils.SpewLogClosure(lc.remoteCommitChain.tip()))
// We'll also re-create the set of commitment keys needed to
// fully re-derive the state.
@ -3245,12 +3237,8 @@ func (lc *LightningChannel) fetchParent(entry *PaymentDescriptor,
return nil, fmt.Errorf("unable to find parent entry "+
"%d in %v update log: %v\nUpdatelog: %v",
entry.ParentIndex, logName,
newLogClosure(func() string {
return spew.Sdump(entry)
}), newLogClosure(func() string {
return spew.Sdump(updateLog)
}),
)
lnutils.SpewLogClosure(entry),
lnutils.SpewLogClosure(updateLog))
// The parent add height should never be zero at this point. If
// that's the case we probably forgot to send a new commitment.
@ -4249,10 +4237,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
"their_balance=%v, commit_tx: %v",
newCommitView.ourBalance,
newCommitView.theirBalance,
newLogClosure(func() string {
return spew.Sdump(newCommitView.txn)
}),
)
lnutils.SpewLogClosure(newCommitView.txn))
// With the commitment view constructed, if there are any HTLC's, we'll
// need to generate signatures of each of them for the remote party's
@ -5209,10 +5194,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error {
lc.log.Tracef("local chain: our_balance=%v, "+
"their_balance=%v, commit_tx: %v",
localCommitmentView.ourBalance, localCommitmentView.theirBalance,
newLogClosure(func() string {
return spew.Sdump(localCommitmentView.txn)
}),
)
lnutils.SpewLogClosure(localCommitmentView.txn))
// As an optimization, we'll generate a series of jobs for the worker
// pool to verify each of the HTLC signatures presented. Once

@ -36,19 +36,3 @@ func UseLogger(logger btclog.Logger) {
chain.UseLogger(logger)
chainfee.UseLogger(logger)
}
// logClosure is used to provide a closure over expensive logging operations
// so don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

16
log.go

@ -208,19 +208,3 @@ func SetSubLogger(root *build.RotatingLogWriter, subsystem string,
useLogger(logger)
}
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -2156,7 +2156,7 @@ func (p *Brontide) logWireMessage(msg lnwire.Message, read bool) {
summaryPrefix = "Sending"
}
p.log.Debugf("%v", newLogClosure(func() string {
p.log.Debugf("%v", lnutils.NewLogClosure(func() string {
// Debug summary of message.
summary := messageSummary(msg)
if len(summary) > 0 {
@ -2184,9 +2184,7 @@ func (p *Brontide) logWireMessage(msg lnwire.Message, read bool) {
prefix = "writeMessage to peer"
}
p.log.Tracef(prefix+": %v", newLogClosure(func() string {
return spew.Sdump(msg)
}))
p.log.Tracef(prefix+": %v", lnutils.SpewLogClosure(msg))
}
// writeMessage writes and flushes the target lnwire.Message to the remote peer.

@ -22,19 +22,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
peerLog = logger
}
// logClosure is used to provide a closure over expensive logging operations
// so they aren't performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -31,19 +31,3 @@ func UseLogger(logger btclog.Logger) {
log = logger
chainview.UseLogger(logger)
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -13,6 +13,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route"
@ -728,7 +729,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
edge.capacity,
)
log.Trace(newLogClosure(func() string {
log.Trace(lnutils.NewLogClosure(func() string {
return fmt.Sprintf("path finding probability: fromnode=%v,"+
" tonode=%v, amt=%v, cap=%v, probability=%v",
fromVertex, toNodeDist.node, amountToSend,

@ -5,11 +5,11 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btclog"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/graph"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
@ -432,7 +432,7 @@ func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
policy.FeeProportionalMillionths = lnwire.MilliSatoshi(msg.FeeRate)
log.Debugf("New private channel update applied: %v",
newLogClosure(func() string { return spew.Sdump(msg) }))
lnutils.SpewLogClosure(msg))
return true
}

@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
@ -669,10 +670,7 @@ func (r *ChannelRouter) FindRoute(req *RouteRequest) (*route.Route, float64,
}
go log.Tracef("Obtained path to send %v to %x: %v",
req.Amount, req.Target, newLogClosure(func() string {
return spew.Sdump(route)
}),
)
req.Amount, req.Target, lnutils.SpewLogClosure(route))
return route, probability, nil
}
@ -704,7 +702,7 @@ func generateSphinxPacket(rt *route.Route, paymentHash []byte,
}
log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
paymentHash[:], newLogClosure(func() string {
paymentHash, lnutils.NewLogClosure(func() string {
path := make(
[]sphinx.OnionHop, sphinxPath.TrueRouteLength(),
)
@ -734,7 +732,7 @@ func generateSphinxPacket(rt *route.Route, paymentHash []byte,
}
log.Tracef("Generated sphinx packet: %v",
newLogClosure(func() string {
lnutils.NewLogClosure(func() string {
// We make a copy of the ephemeral key and unset the
// internal curve here in order to keep the logs from
// getting noisy.
@ -945,8 +943,8 @@ func (r *ChannelRouter) SendPaymentAsync(ctx context.Context,
// spewPayment returns a log closures that provides a spewed string
// representation of the passed payment.
func spewPayment(payment *LightningPayment) logClosure {
return newLogClosure(func() string {
func spewPayment(payment *LightningPayment) lnutils.LogClosure {
return lnutils.NewLogClosure(func() string {
// Make a copy of the payment with a nilled Curve
// before spewing.
var routeHints [][]zpay32.HopHint
@ -1090,11 +1088,8 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
return nil, err
}
log.Tracef("Dispatching SendToRoute for HTLC hash %v: %v",
htlcHash, newLogClosure(func() string {
return spew.Sdump(rt)
}),
)
log.Tracef("Dispatching SendToRoute for HTLC hash %v: %v", htlcHash,
lnutils.SpewLogClosure(rt))
// Since the HTLC hashes and preimages are specified manually over the
// RPC for SendToRoute requests, we don't have to worry about creating

@ -63,6 +63,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@ -5845,9 +5846,7 @@ func (r *rpcServer) LookupInvoice(ctx context.Context,
}
rpcsLog.Tracef("[lookupinvoice] located invoice %v",
newLogClosure(func() string {
return spew.Sdump(invoice)
}))
lnutils.SpewLogClosure(invoice))
rpcInvoice, err := invoicesrpc.CreateRPCInvoice(
&invoice, r.cfg.ActiveNetParams.Params,

@ -12,7 +12,6 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
@ -313,10 +312,8 @@ func (t *TxPublisher) isNeutrinoBackend() bool {
//
// NOTE: part of the Bumper interface.
func (t *TxPublisher) Broadcast(req *BumpRequest) (<-chan *BumpResult, error) {
log.Tracef("Received broadcast request: %s", newLogClosure(
func() string {
return spew.Sdump(req)
})())
log.Tracef("Received broadcast request: %s", lnutils.SpewLogClosure(
req))
// Attempt an initial broadcast which is guaranteed to comply with the
// RBF rules.

@ -27,19 +27,3 @@ func DisableLog() {
func UseLogger(logger btclog.Logger) {
log = logger
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

@ -13,6 +13,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
@ -1376,10 +1377,7 @@ func (s *UtxoSweeper) handleInputSpent(spend *chainntnfs.SpendDetail) {
log.Debugf("Detected third party spend related to in flight "+
"inputs (is_ours=%v): %v", isOurTx,
newLogClosure(func() string {
return spew.Sdump(spend.SpendingTx)
}),
)
lnutils.SpewLogClosure(spend.SpendingTx))
}
// We now use the spending tx to update the state of the inputs.

@ -43,19 +43,3 @@ func UseLogger(logger btclog.Logger) {
migration7.UseLogger(logger)
migration8.UseLogger(logger)
}
// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string // nolint:unused
// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure { // nolint:unused
return logClosure(c)
}