multi: thread thru the AuxLeafStore everywhere

This commit is contained in:
Oliver Gugger 2024-04-25 19:00:42 +02:00
parent 2510c19024
commit b45d72fe59
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
15 changed files with 194 additions and 47 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify" "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
@ -63,6 +64,10 @@ type Config struct {
// state. // state.
ChanStateDB *channeldb.ChannelStateDB ChanStateDB *channeldb.ChannelStateDB
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
// BlockCache is the main cache for storing block information. // BlockCache is the main cache for storing block information.
BlockCache *blockcache.BlockCache BlockCache *blockcache.BlockCache

View File

@ -105,7 +105,7 @@ type DatabaseBuilder interface {
type WalletConfigBuilder interface { type WalletConfigBuilder interface {
// BuildWalletConfig is responsible for creating or unlocking and then // BuildWalletConfig is responsible for creating or unlocking and then
// fully initializing a wallet. // fully initializing a wallet.
BuildWalletConfig(context.Context, *DatabaseInstances, BuildWalletConfig(context.Context, *DatabaseInstances, *AuxComponents,
*rpcperms.InterceptorChain, *rpcperms.InterceptorChain,
[]*ListenerWithSignal) (*chainreg.PartialChainControl, []*ListenerWithSignal) (*chainreg.PartialChainControl,
*btcwallet.Config, func(), error) *btcwallet.Config, func(), error)
@ -120,14 +120,6 @@ type ChainControlBuilder interface {
*btcwallet.Config) (*chainreg.ChainControl, func(), error) *btcwallet.Config) (*chainreg.ChainControl, func(), error)
} }
// AuxComponents is a set of auxiliary components that can be used by lnd for
// certain custom channel types.
type AuxComponents struct {
// MsgRouter is an optional message router that if set will be used in
// place of a new blank default message router.
MsgRouter fn.Option[msgmux.Router]
}
// ImplementationCfg is a struct that holds all configuration items for // ImplementationCfg is a struct that holds all configuration items for
// components that can be implemented outside lnd itself. // components that can be implemented outside lnd itself.
type ImplementationCfg struct { type ImplementationCfg struct {
@ -160,6 +152,18 @@ type ImplementationCfg struct {
AuxComponents AuxComponents
} }
// AuxComponents is a set of auxiliary components that can be used by lnd for
// certain custom channel types.
type AuxComponents struct {
// AuxLeafStore is an optional data source that can be used by custom
// channels to fetch+store various data.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
// MsgRouter is an optional message router that if set will be used in
// place of a new blank default message router.
MsgRouter fn.Option[msgmux.Router]
}
// DefaultWalletImpl is the default implementation of our normal, btcwallet // DefaultWalletImpl is the default implementation of our normal, btcwallet
// backed configuration. // backed configuration.
type DefaultWalletImpl struct { type DefaultWalletImpl struct {
@ -242,7 +246,8 @@ func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
// //
// NOTE: This is part of the WalletConfigBuilder interface. // NOTE: This is part of the WalletConfigBuilder interface.
func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
dbs *DatabaseInstances, interceptorChain *rpcperms.InterceptorChain, dbs *DatabaseInstances, aux *AuxComponents,
interceptorChain *rpcperms.InterceptorChain,
grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl, grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl,
*btcwallet.Config, func(), error) { *btcwallet.Config, func(), error) {
@ -562,6 +567,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
HeightHintDB: dbs.HeightHintDB, HeightHintDB: dbs.HeightHintDB,
ChanStateDB: dbs.ChanStateDB.ChannelStateDB(), ChanStateDB: dbs.ChanStateDB.ChannelStateDB(),
NeutrinoCS: neutrinoCS, NeutrinoCS: neutrinoCS,
AuxLeafStore: aux.AuxLeafStore,
ActiveNetParams: d.cfg.ActiveNetParams, ActiveNetParams: d.cfg.ActiveNetParams,
FeeURL: d.cfg.FeeURL, FeeURL: d.cfg.FeeURL,
Fee: &lncfg.Fee{ Fee: &lncfg.Fee{
@ -625,8 +631,9 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
// proxyBlockEpoch proxies a block epoch subsections to the underlying neutrino // proxyBlockEpoch proxies a block epoch subsections to the underlying neutrino
// rebroadcaster client. // rebroadcaster client.
func proxyBlockEpoch(notifier chainntnfs.ChainNotifier, func proxyBlockEpoch(
) func() (*blockntfns.Subscription, error) { notifier chainntnfs.ChainNotifier) func() (*blockntfns.Subscription,
error) {
return func() (*blockntfns.Subscription, error) { return func() (*blockntfns.Subscription, error) {
blockEpoch, err := notifier.RegisterBlockEpochNtfn( blockEpoch, err := notifier.RegisterBlockEpochNtfn(
@ -717,6 +724,7 @@ func (d *DefaultWalletImpl) BuildChainControl(
ChainIO: walletController, ChainIO: walletController,
NetParams: *walletConfig.NetParams, NetParams: *walletConfig.NetParams,
CoinSelectionStrategy: walletConfig.CoinSelectionStrategy, CoinSelectionStrategy: walletConfig.CoinSelectionStrategy,
AuxLeafStore: partialChainControl.Cfg.AuxLeafStore,
} }
// The broadcast is already always active for neutrino nodes, so we // The broadcast is already always active for neutrino nodes, so we
@ -899,6 +907,10 @@ type DatabaseInstances struct {
// for native SQL queries for tables that already support it. This may // for native SQL queries for tables that already support it. This may
// be nil if the use-native-sql flag was not set. // be nil if the use-native-sql flag was not set.
NativeSQLStore *sqldb.BaseDB NativeSQLStore *sqldb.BaseDB
// AuxLeafStore is an optional data source that can be used by custom
// channels to fetch+store various data.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
} }
// DefaultDatabaseBuilder is a type that builds the default database backends // DefaultDatabaseBuilder is a type that builds the default database backends

View File

@ -22,6 +22,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lntest/channels"
@ -1590,6 +1591,7 @@ func testBreachSpends(t *testing.T, test breachTest) {
// Notify the breach arbiter about the breach. // Notify the breach arbiter about the breach.
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
alice.State(), height, 1, forceCloseTx, alice.State(), height, 1, forceCloseTx,
fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")
@ -1799,6 +1801,7 @@ func TestBreachDelayedJusticeConfirmation(t *testing.T) {
// Notify the breach arbiter about the breach. // Notify the breach arbiter about the breach.
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
alice.State(), height, uint32(blockHeight), forceCloseTx, alice.State(), height, uint32(blockHeight), forceCloseTx,
fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")

View File

@ -217,6 +217,10 @@ type ChainArbitratorConfig struct {
// meanwhile, turn `PaymentCircuit` into an interface or bring it to a // meanwhile, turn `PaymentCircuit` into an interface or bring it to a
// lower package. // lower package.
QueryIncomingCircuit func(circuit models.CircuitKey) *models.CircuitKey QueryIncomingCircuit func(circuit models.CircuitKey) *models.CircuitKey
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
} }
// ChainArbitrator is a sub-system that oversees the on-chain resolution of all // ChainArbitrator is a sub-system that oversees the on-chain resolution of all
@ -299,8 +303,13 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions,
return nil, err return nil, err
} }
var chanOpts []lnwallet.ChannelOpt
a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) {
chanOpts = append(chanOpts, lnwallet.WithLeafStore(s))
})
chanMachine, err := lnwallet.NewLightningChannel( chanMachine, err := lnwallet.NewLightningChannel(
a.c.cfg.Signer, channel, nil, a.c.cfg.Signer, channel, nil, chanOpts...,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -344,10 +353,15 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error)
return nil, err return nil, err
} }
var chanOpts []lnwallet.ChannelOpt
a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) {
chanOpts = append(chanOpts, lnwallet.WithLeafStore(s))
})
// Finally, we'll force close the channel completing // Finally, we'll force close the channel completing
// the force close workflow. // the force close workflow.
chanMachine, err := lnwallet.NewLightningChannel( chanMachine, err := lnwallet.NewLightningChannel(
a.c.cfg.Signer, channel, nil, a.c.cfg.Signer, channel, nil, chanOpts...,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -193,6 +193,9 @@ type chainWatcherConfig struct {
// obfuscater. This is used by the chain watcher to identify which // obfuscater. This is used by the chain watcher to identify which
// state was broadcast and confirmed on-chain. // state was broadcast and confirmed on-chain.
extractStateNumHint func(*wire.MsgTx, [lnwallet.StateHintSize]byte) uint64 extractStateNumHint func(*wire.MsgTx, [lnwallet.StateHintSize]byte) uint64
// auxLeafStore can be used to fetch information for custom channels.
auxLeafStore fn.Option[lnwallet.AuxLeafStore]
} }
// chainWatcher is a system that's assigned to every active channel. The duty // chainWatcher is a system that's assigned to every active channel. The duty
@ -867,7 +870,7 @@ func (c *chainWatcher) handlePossibleBreach(commitSpend *chainntnfs.SpendDetail,
spendHeight := uint32(commitSpend.SpendingHeight) spendHeight := uint32(commitSpend.SpendingHeight)
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
c.cfg.chanState, broadcastStateNum, spendHeight, c.cfg.chanState, broadcastStateNum, spendHeight,
commitSpend.SpendingTx, commitSpend.SpendingTx, c.cfg.auxLeafStore,
) )
switch { switch {
@ -1117,8 +1120,8 @@ func (c *chainWatcher) dispatchLocalForceClose(
"detected", c.cfg.chanState.FundingOutpoint) "detected", c.cfg.chanState.FundingOutpoint)
forceClose, err := lnwallet.NewLocalForceCloseSummary( forceClose, err := lnwallet.NewLocalForceCloseSummary(
c.cfg.chanState, c.cfg.signer, c.cfg.chanState, c.cfg.signer, commitSpend.SpendingTx, stateNum,
commitSpend.SpendingTx, stateNum, c.cfg.auxLeafStore,
) )
if err != nil { if err != nil {
return err return err
@ -1211,7 +1214,7 @@ func (c *chainWatcher) dispatchRemoteForceClose(
// channel on-chain. // channel on-chain.
uniClose, err := lnwallet.NewUnilateralCloseSummary( uniClose, err := lnwallet.NewUnilateralCloseSummary(
c.cfg.chanState, c.cfg.signer, commitSpend, c.cfg.chanState, c.cfg.signer, commitSpend,
remoteCommit, commitPoint, remoteCommit, commitPoint, c.cfg.auxLeafStore,
) )
if err != nil { if err != nil {
return err return err

View File

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/graph"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
@ -544,6 +545,10 @@ type Config struct {
// backed funding flow to not use utxos still being swept by the sweeper // backed funding flow to not use utxos still being swept by the sweeper
// subsystem. // subsystem.
IsSweeperOutpoint func(wire.OutPoint) bool IsSweeperOutpoint func(wire.OutPoint) bool
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
} }
// Manager acts as an orchestrator/bridge between the wallet's // Manager acts as an orchestrator/bridge between the wallet's
@ -1069,9 +1074,14 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel,
} }
} }
var chanOpts []lnwallet.ChannelOpt
f.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) {
chanOpts = append(chanOpts, lnwallet.WithLeafStore(s))
})
// We create the state-machine object which wraps the database state. // We create the state-machine object which wraps the database state.
lnChannel, err := lnwallet.NewLightningChannel( lnChannel, err := lnwallet.NewLightningChannel(
nil, channel, nil, nil, channel, nil, chanOpts...,
) )
if err != nil { if err != nil {
log.Errorf("Unable to create LightningChannel(%v): %v", log.Errorf("Unable to create LightningChannel(%v): %v",

View File

@ -28,6 +28,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/channelnotifier"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
@ -563,6 +564,9 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
IsSweeperOutpoint: func(wire.OutPoint) bool { IsSweeperOutpoint: func(wire.OutPoint) bool {
return false return false
}, },
AuxLeafStore: fn.Some[lnwallet.AuxLeafStore](
&lnwallet.MockAuxLeafStore{},
),
} }
for _, op := range options { for _, op := range options {
@ -672,6 +676,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
OpenChannelPredicate: chainedAcceptor, OpenChannelPredicate: chainedAcceptor,
DeleteAliasEdge: oldCfg.DeleteAliasEdge, DeleteAliasEdge: oldCfg.DeleteAliasEdge,
AliasManager: oldCfg.AliasManager, AliasManager: oldCfg.AliasManager,
AuxLeafStore: oldCfg.AuxLeafStore,
}) })
require.NoError(t, err, "failed recreating aliceFundingManager") require.NoError(t, err, "failed recreating aliceFundingManager")

3
lnd.go
View File

@ -456,7 +456,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
defer cleanUp() defer cleanUp()
partialChainControl, walletConfig, cleanUp, err := implCfg.BuildWalletConfig( partialChainControl, walletConfig, cleanUp, err := implCfg.BuildWalletConfig(
ctx, dbs, interceptorChain, grpcListeners, ctx, dbs, &implCfg.AuxComponents, interceptorChain,
grpcListeners,
) )
if err != nil { if err != nil {
return mkErr("error creating wallet config: %v", err) return mkErr("error creating wallet config: %v", err)

View File

@ -1965,7 +1965,8 @@ type BreachRetribution struct {
// required to construct the BreachRetribution. If the revocation log is missing // required to construct the BreachRetribution. If the revocation log is missing
// the required fields then ErrRevLogDataMissing will be returned. // the required fields then ErrRevLogDataMissing will be returned.
func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
breachHeight uint32, spendTx *wire.MsgTx) (*BreachRetribution, error) { breachHeight uint32, spendTx *wire.MsgTx,
leafStore fn.Option[AuxLeafStore]) (*BreachRetribution, error) {
// Query the on-disk revocation log for the snapshot which was recorded // Query the on-disk revocation log for the snapshot which was recorded
// at this particular state num. Based on whether a legacy revocation // at this particular state num. Based on whether a legacy revocation
@ -3023,9 +3024,16 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64,
// signature can be submitted to the sigPool to generate all the signatures // signature can be submitted to the sigPool to generate all the signatures
// asynchronously and in parallel. // asynchronously and in parallel.
func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
chanType channeldb.ChannelType, isRemoteInitiator bool, chanState *channeldb.OpenChannel, leaseExpiry uint32,
leaseExpiry uint32, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, remoteCommitView *commitment,
remoteCommitView *commitment) ([]SignJob, chan struct{}, error) { leafStore fn.Option[AuxLeafStore]) ([]SignJob, chan struct{}, error) {
var (
isRemoteInitiator = !chanState.IsInitiator
localChanCfg = chanState.LocalChanCfg
remoteChanCfg = chanState.RemoteChanCfg
chanType = chanState.ChanType
)
txHash := remoteCommitView.txn.TxHash() txHash := remoteCommitView.txn.TxHash()
dustLimit := remoteChanCfg.DustLimit dustLimit := remoteChanCfg.DustLimit
@ -3191,9 +3199,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// validate this new state. This function is called right before sending the // validate this new state. This function is called right before sending the
// new commitment to the remote party. The commit diff returned contains all // new commitment to the remote party. The commit diff returned contains all
// information necessary for retransmission. // information necessary for retransmission.
func (lc *LightningChannel) createCommitDiff( func (lc *LightningChannel) createCommitDiff(newCommit *commitment,
newCommit *commitment, commitSig lnwire.Sig, commitSig lnwire.Sig, htlcSigs []lnwire.Sig) (*channeldb.CommitDiff,
htlcSigs []lnwire.Sig) (*channeldb.CommitDiff, error) { error) {
// First, we need to convert the funding outpoint into the ID that's // First, we need to convert the funding outpoint into the ID that's
// used on the wire to identify this channel. We'll use this shortly // used on the wire to identify this channel. We'll use this shortly
@ -3892,9 +3900,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
leaseExpiry = lc.channelState.ThawHeight leaseExpiry = lc.channelState.ThawHeight
} }
sigBatch, cancelChan, err := genRemoteHtlcSigJobs( sigBatch, cancelChan, err := genRemoteHtlcSigJobs(
keyRing, lc.channelState.ChanType, !lc.channelState.IsInitiator, keyRing, lc.channelState, leaseExpiry, newCommitView,
leaseExpiry, &lc.channelState.LocalChanCfg, lc.leafStore,
&lc.channelState.RemoteChanCfg, newCommitView,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -4465,10 +4472,18 @@ func (lc *LightningChannel) computeView(view *HtlcView,
// meant to verify all the signatures for HTLC's attached to a newly created // meant to verify all the signatures for HTLC's attached to a newly created
// commitment state. The jobs generated are fully populated, and can be sent // commitment state. The jobs generated are fully populated, and can be sent
// directly into the pool of workers. // directly into the pool of workers.
func genHtlcSigValidationJobs(localCommitmentView *commitment, //
keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, //nolint:funlen
chanType channeldb.ChannelType, isLocalInitiator bool, leaseExpiry uint32, func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]VerifyJob, error) { localCommitmentView *commitment, keyRing *CommitmentKeyRing,
htlcSigs []lnwire.Sig, leaseExpiry uint32,
leafStore fn.Option[AuxLeafStore]) ([]VerifyJob, error) {
var (
isLocalInitiator = chanState.IsInitiator
localChanCfg = chanState.LocalChanCfg
chanType = chanState.ChanType
)
txHash := localCommitmentView.txn.TxHash() txHash := localCommitmentView.txn.TxHash()
feePerKw := localCommitmentView.feePerKw feePerKw := localCommitmentView.feePerKw
@ -4858,10 +4873,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error {
leaseExpiry = lc.channelState.ThawHeight leaseExpiry = lc.channelState.ThawHeight
} }
verifyJobs, err := genHtlcSigValidationJobs( verifyJobs, err := genHtlcSigValidationJobs(
localCommitmentView, keyRing, commitSigs.HtlcSigs, lc.channelState, localCommitmentView, keyRing,
lc.channelState.ChanType, lc.channelState.IsInitiator, commitSigs.HtlcSigs, leaseExpiry, lc.leafStore,
leaseExpiry, &lc.channelState.LocalChanCfg,
&lc.channelState.RemoteChanCfg,
) )
if err != nil { if err != nil {
return err return err
@ -6308,10 +6321,10 @@ type UnilateralCloseSummary struct {
// happen in case we have lost state) it should be set to an empty struct, in // happen in case we have lost state) it should be set to an empty struct, in
// which case we will attempt to sweep the non-HTLC output using the passed // which case we will attempt to sweep the non-HTLC output using the passed
// commitPoint. // commitPoint.
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Signer, func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
commitSpend *chainntnfs.SpendDetail, signer input.Signer, commitSpend *chainntnfs.SpendDetail,
remoteCommit channeldb.ChannelCommitment, remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey,
commitPoint *btcec.PublicKey) (*UnilateralCloseSummary, error) { leafStore fn.Option[AuxLeafStore]) (*UnilateralCloseSummary, error) {
// First, we'll generate the commitment point and the revocation point // First, we'll generate the commitment point and the revocation point
// so we can re-construct the HTLC state and also our payment key. // so we can re-construct the HTLC state and also our payment key.
@ -7254,7 +7267,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
localCommitment := lc.channelState.LocalCommitment localCommitment := lc.channelState.LocalCommitment
summary, err := NewLocalForceCloseSummary( summary, err := NewLocalForceCloseSummary(
lc.channelState, lc.Signer, commitTx, lc.channelState, lc.Signer, commitTx,
localCommitment.CommitHeight, localCommitment.CommitHeight, lc.leafStore,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to gen force close "+ return nil, fmt.Errorf("unable to gen force close "+
@ -7271,8 +7284,8 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
// channel state. The passed commitTx must be a fully signed commitment // channel state. The passed commitTx must be a fully signed commitment
// transaction corresponding to localCommit. // transaction corresponding to localCommit.
func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
signer input.Signer, commitTx *wire.MsgTx, stateNum uint64) ( signer input.Signer, commitTx *wire.MsgTx, stateNum uint64,
*LocalForceCloseSummary, error) { leafStore fn.Option[AuxLeafStore]) (*LocalForceCloseSummary, error) {
// Re-derive the original pkScript for to-self output within the // Re-derive the original pkScript for to-self output within the
// commitment transaction. We'll need this to find the corresponding // commitment transaction. We'll need this to find the corresponding

View File

@ -5695,6 +5695,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) {
spendDetail, spendDetail,
aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCommitment,
aliceChannel.channelState.RemoteCurrentRevocation, aliceChannel.channelState.RemoteCurrentRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@ -5844,6 +5845,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
spendDetail, spendDetail,
aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCommitment,
aliceChannel.channelState.RemoteCurrentRevocation, aliceChannel.channelState.RemoteCurrentRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@ -5861,6 +5863,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
spendDetail, spendDetail,
aliceRemoteChainTip.Commitment, aliceRemoteChainTip.Commitment,
aliceChannel.channelState.RemoteNextRevocation, aliceChannel.channelState.RemoteNextRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@ -6741,6 +6744,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) {
breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx
breachRet, err := NewBreachRetribution( breachRet, err := NewBreachRetribution(
aliceChannel.channelState, revokedStateNum, 100, breachTx, aliceChannel.channelState, revokedStateNum, 100, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")
@ -10291,6 +10295,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// error as there are no past delta state saved as revocation logs yet. // error as there are no past delta state saved as revocation logs yet.
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, breachTx, aliceChannel.channelState, stateNum, breachHeight, breachTx,
fn.None[AuxLeafStore](),
) )
require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas)
@ -10298,6 +10303,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// provided. // provided.
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, nil, aliceChannel.channelState, stateNum, breachHeight, nil,
fn.None[AuxLeafStore](),
) )
require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas)
@ -10343,6 +10349,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// successfully. // successfully.
br, err := NewBreachRetribution( br, err := NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, breachTx, aliceChannel.channelState, stateNum, breachHeight, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err) require.NoError(t, err)
@ -10354,6 +10361,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// since the necessary info should now be found in the revocation log. // since the necessary info should now be found in the revocation log.
br, err = NewBreachRetribution( br, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, nil, aliceChannel.channelState, stateNum, breachHeight, nil,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.NoError(t, err) require.NoError(t, err)
assertRetribution(br, 1, 0) assertRetribution(br, 1, 0)
@ -10362,6 +10370,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// error. // error.
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum+1, breachHeight, breachTx, aliceChannel.channelState, stateNum+1, breachHeight, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound)
@ -10369,6 +10378,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
// provided. // provided.
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum+1, breachHeight, nil, aliceChannel.channelState, stateNum+1, breachHeight, nil,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
) )
require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound)
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
@ -62,4 +63,8 @@ type Config struct {
// CoinSelectionStrategy is the strategy that is used for selecting // CoinSelectionStrategy is the strategy that is used for selecting
// coins when funding a transaction. // coins when funding a transaction.
CoinSelectionStrategy wallet.CoinSelectionStrategy CoinSelectionStrategy wallet.CoinSelectionStrategy
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[AuxLeafStore]
} }

View File

@ -17,8 +17,10 @@ import (
"github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/wtxmgr" "github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/tlv"
) )
var ( var (
@ -397,3 +399,45 @@ func (*mockChainIO) GetBlockHeader(
return nil, nil return nil, nil
} }
type MockAuxLeafStore struct{}
// A compile time check to ensure that MockAuxLeafStore implements the
// AuxLeafStore interface.
var _ AuxLeafStore = (*MockAuxLeafStore)(nil)
// FetchLeavesFromView attempts to fetch the auxiliary leaves that
// correspond to the passed aux blob, and pending original (unfiltered)
// HTLC view.
func (*MockAuxLeafStore) FetchLeavesFromView(
_ CommitDiffAuxInput) fn.Result[CommitDiffAuxResult] {
return fn.Ok(CommitDiffAuxResult{})
}
// FetchLeavesFromCommit attempts to fetch the auxiliary leaves that
// correspond to the passed aux blob, and an existing channel
// commitment.
func (*MockAuxLeafStore) FetchLeavesFromCommit(_ AuxChanState,
_ channeldb.ChannelCommitment,
_ CommitmentKeyRing) fn.Result[CommitDiffAuxResult] {
return fn.Ok(CommitDiffAuxResult{})
}
// FetchLeavesFromRevocation attempts to fetch the auxiliary leaves
// from a channel revocation that stores balance + blob information.
func (*MockAuxLeafStore) FetchLeavesFromRevocation(
_ *channeldb.RevocationLog) fn.Result[CommitDiffAuxResult] {
return fn.Ok(CommitDiffAuxResult{})
}
// ApplyHtlcView serves as the state transition function for the custom
// channel's blob. Given the old blob, and an HTLC view, then a new
// blob should be returned that reflects the pending updates.
func (*MockAuxLeafStore) ApplyHtlcView(
_ CommitDiffAuxInput) fn.Result[fn.Option[tlv.Blob]] {
return fn.Ok(fn.None[tlv.Blob]())
}

View File

@ -2496,9 +2496,16 @@ func initStateHints(commit1, commit2 *wire.MsgTx,
func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel,
fundingTx *wire.MsgTx) error { fundingTx *wire.MsgTx) error {
var chanOpts []ChannelOpt
l.Cfg.AuxLeafStore.WhenSome(func(s AuxLeafStore) {
chanOpts = append(chanOpts, WithLeafStore(s))
})
// First, we'll obtain a fully signed commitment transaction so we can // First, we'll obtain a fully signed commitment transaction so we can
// pass into it on the chanvalidate package for verification. // pass into it on the chanvalidate package for verification.
channel, err := NewLightningChannel(l.Cfg.Signer, channelState, nil) channel, err := NewLightningChannel(
l.Cfg.Signer, channelState, nil, chanOpts...,
)
if err != nil { if err != nil {
return err return err
} }

View File

@ -372,6 +372,10 @@ type Config struct {
AddLocalAlias func(alias, base lnwire.ShortChannelID, AddLocalAlias func(alias, base lnwire.ShortChannelID,
gossip bool) error gossip bool) error
// AuxLeafStore is an optional store that can be used to store auxiliary
// leaves for certain custom channel types.
AuxLeafStore fn.Option[lnwallet.AuxLeafStore]
// PongBuf is a slice we'll reuse instead of allocating memory on the // PongBuf is a slice we'll reuse instead of allocating memory on the
// heap. Since only reads will occur and no writes, there is no need // heap. Since only reads will occur and no writes, there is no need
// for any synchronization primitives. As a result, it's safe to share // for any synchronization primitives. As a result, it's safe to share
@ -943,8 +947,12 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
} }
} }
var chanOpts []lnwallet.ChannelOpt
p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) {
chanOpts = append(chanOpts, lnwallet.WithLeafStore(s))
})
lnChan, err := lnwallet.NewLightningChannel( lnChan, err := lnwallet.NewLightningChannel(
p.cfg.Signer, dbChan, p.cfg.SigPool, p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts...,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create channel "+ return nil, fmt.Errorf("unable to create channel "+
@ -4151,6 +4159,10 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error {
chanOpts = append(chanOpts, lnwallet.WithSkipNonceInit()) chanOpts = append(chanOpts, lnwallet.WithSkipNonceInit())
} }
p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) {
chanOpts = append(chanOpts, lnwallet.WithLeafStore(s))
})
// If not already active, we'll add this channel to the set of active // If not already active, we'll add this channel to the set of active
// channels, so we can look it up later easily according to its channel // channels, so we can look it up later easily according to its channel
// ID. // ID.

View File

@ -1273,6 +1273,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
return &pc.Incoming return &pc.Incoming
}, },
AuxLeafStore: implCfg.AuxLeafStore,
}, dbs.ChanStateDB) }, dbs.ChanStateDB)
// Select the configuration and funding parameters for Bitcoin. // Select the configuration and funding parameters for Bitcoin.
@ -1607,6 +1608,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
br, err := lnwallet.NewBreachRetribution( br, err := lnwallet.NewBreachRetribution(
channel, commitHeight, 0, nil, channel, commitHeight, 0, nil,
implCfg.AuxLeafStore,
) )
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -4073,6 +4075,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
DisallowRouteBlinding: s.cfg.ProtocolOptions.NoRouteBlinding(), DisallowRouteBlinding: s.cfg.ProtocolOptions.NoRouteBlinding(),
MaxFeeExposure: thresholdMSats, MaxFeeExposure: thresholdMSats,
Quit: s.quit, Quit: s.quit,
AuxLeafStore: s.implCfg.AuxLeafStore,
MsgRouter: s.implCfg.MsgRouter, MsgRouter: s.implCfg.MsgRouter,
} }