multi: Utxo restriction single funding case.
Restrict the utxo selection when opening a single internal wallet funded backed channel.
This commit is contained in:
6 files changed
+87
-12
No files matched your search
+26
-4
@@ -537,6 +537,12 @@ type Config struct {
|
|||||||
// AliasManager is an implementation of the aliasHandler interface that
|
// AliasManager is an implementation of the aliasHandler interface that
|
||||||
// abstracts away the handling of many alias functions.
|
// abstracts away the handling of many alias functions.
|
||||||
AliasManager aliasHandler
|
AliasManager aliasHandler
|
||||||
|
|
||||||
|
// IsSweeperOutpoint queries the sweeper store for successfully
|
||||||
|
// published sweeps. This is useful to decide for the internal wallet
|
||||||
|
// backed funding flow to not use utxos still being swept by the sweeper
|
||||||
|
// subsystem.
|
||||||
|
IsSweeperOutpoint func(wire.OutPoint) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager acts as an orchestrator/bridge between the wallet's
|
// Manager acts as an orchestrator/bridge between the wallet's
|
||||||
@@ -4600,10 +4606,26 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||||||
MinConfs: msg.MinConfs,
|
MinConfs: msg.MinConfs,
|
||||||
CommitType: commitType,
|
CommitType: commitType,
|
||||||
ChanFunder: msg.ChanFunder,
|
ChanFunder: msg.ChanFunder,
|
||||||
ZeroConf: zeroConf,
|
// Unconfirmed Utxos which are marked by the sweeper subsystem
|
||||||
OptionScidAlias: scid,
|
// are excluded from the coin selection because they are not
|
||||||
ScidAliasFeature: scidFeatureVal,
|
// final and can be RBFed by the sweeper subsystem.
|
||||||
Memo: msg.Memo,
|
AllowUtxoForFunding: func(u lnwallet.Utxo) bool {
|
||||||
|
// Utxos with at least 1 confirmation are safe to use
|
||||||
|
// for channel openings because they don't bare the risk
|
||||||
|
// of being replaced (BIP 125 RBF).
|
||||||
|
if u.Confirmations > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the sweeper storage to make sure we don't use
|
||||||
|
// an unconfirmed utxo still in use by the sweeper
|
||||||
|
// subsystem.
|
||||||
|
return !f.cfg.IsSweeperOutpoint(u.OutPoint)
|
||||||
|
},
|
||||||
|
ZeroConf: zeroConf,
|
||||||
|
OptionScidAlias: scid,
|
||||||
|
ScidAliasFeature: scidFeatureVal,
|
||||||
|
Memo: msg.Memo,
|
||||||
}
|
}
|
||||||
|
|
||||||
reservation, err := f.cfg.Wallet.InitChannelReservation(req)
|
reservation, err := f.cfg.Wallet.InitChannelReservation(req)
|
||||||
|
|||||||
@@ -334,7 +334,8 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) {
|
|||||||
}
|
}
|
||||||
for _, coin := range manuallySelectedCoins {
|
for _, coin := range manuallySelectedCoins {
|
||||||
if _, ok := unspent[coin.OutPoint]; !ok {
|
if _, ok := unspent[coin.OutPoint]; !ok {
|
||||||
return fmt.Errorf("outpoint already spent: %v",
|
return fmt.Errorf("outpoint already spent or "+
|
||||||
|
"locked by another subsystem: %v",
|
||||||
coin.OutPoint)
|
coin.OutPoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2963,7 +2963,9 @@ func testSingleFunderExternalFundingTx(miner *rpctest.Harness,
|
|||||||
// we'll create a new chanfunding.Assembler hacked by Alice's wallet.
|
// we'll create a new chanfunding.Assembler hacked by Alice's wallet.
|
||||||
aliceChanFunder := chanfunding.NewWalletAssembler(
|
aliceChanFunder := chanfunding.NewWalletAssembler(
|
||||||
chanfunding.WalletConfig{
|
chanfunding.WalletConfig{
|
||||||
CoinSource: lnwallet.NewCoinSource(alice),
|
CoinSource: lnwallet.NewCoinSource(
|
||||||
|
alice, nil,
|
||||||
|
),
|
||||||
CoinSelectLocker: alice,
|
CoinSelectLocker: alice,
|
||||||
CoinLeaser: alice,
|
CoinLeaser: alice,
|
||||||
Signer: alice.Cfg.Signer,
|
Signer: alice.Cfg.Signer,
|
||||||
|
|||||||
+30
-4
@@ -184,6 +184,15 @@ type InitFundingReserveMsg struct {
|
|||||||
// used.
|
// used.
|
||||||
ChanFunder chanfunding.Assembler
|
ChanFunder chanfunding.Assembler
|
||||||
|
|
||||||
|
// AllowUtxoForFunding enables the channel funding workflow to restrict
|
||||||
|
// the selection of utxos when selecting the inputs for the channel
|
||||||
|
// opening. This does ONLY apply for the internal wallet backed channel
|
||||||
|
// opening case.
|
||||||
|
//
|
||||||
|
// NOTE: This is very useful when opening channels with unconfirmed
|
||||||
|
// inputs to make sure stable non-replaceable inputs are used.
|
||||||
|
AllowUtxoForFunding func(Utxo) bool
|
||||||
|
|
||||||
// ZeroConf is a boolean that is true if a zero-conf channel was
|
// ZeroConf is a boolean that is true if a zero-conf channel was
|
||||||
// negotiated.
|
// negotiated.
|
||||||
ZeroConf bool
|
ZeroConf bool
|
||||||
@@ -849,7 +858,9 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
|||||||
// P2WPKH dust limit and to avoid threading through two
|
// P2WPKH dust limit and to avoid threading through two
|
||||||
// different dust limits.
|
// different dust limits.
|
||||||
cfg := chanfunding.WalletConfig{
|
cfg := chanfunding.WalletConfig{
|
||||||
CoinSource: &CoinSource{l},
|
CoinSource: NewCoinSource(
|
||||||
|
l, req.AllowUtxoForFunding,
|
||||||
|
),
|
||||||
CoinSelectLocker: l,
|
CoinSelectLocker: l,
|
||||||
CoinLeaser: l,
|
CoinLeaser: l,
|
||||||
Signer: l.Cfg.Signer,
|
Signer: l.Cfg.Signer,
|
||||||
@@ -2525,12 +2536,16 @@ func (l *LightningWallet) CancelRebroadcast(txid chainhash.Hash) {
|
|||||||
// CoinSource is a wrapper around the wallet that implements the
|
// CoinSource is a wrapper around the wallet that implements the
|
||||||
// chanfunding.CoinSource interface.
|
// chanfunding.CoinSource interface.
|
||||||
type CoinSource struct {
|
type CoinSource struct {
|
||||||
wallet *LightningWallet
|
wallet *LightningWallet
|
||||||
|
allowUtxo func(Utxo) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCoinSource creates a new instance of the CoinSource wrapper struct.
|
// NewCoinSource creates a new instance of the CoinSource wrapper struct.
|
||||||
func NewCoinSource(w *LightningWallet) *CoinSource {
|
func NewCoinSource(w *LightningWallet, allowUtxo func(Utxo) bool) *CoinSource {
|
||||||
return &CoinSource{wallet: w}
|
return &CoinSource{
|
||||||
|
wallet: w,
|
||||||
|
allowUtxo: allowUtxo,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCoins returns all UTXOs from the source that have between
|
// ListCoins returns all UTXOs from the source that have between
|
||||||
@@ -2546,7 +2561,18 @@ func (c *CoinSource) ListCoins(minConfs int32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var coins []wallet.Coin
|
var coins []wallet.Coin
|
||||||
|
|
||||||
for _, utxo := range utxos {
|
for _, utxo := range utxos {
|
||||||
|
// If there is a filter function supplied all utxos not adhering
|
||||||
|
// to these conditions will be discared.
|
||||||
|
if c.allowUtxo != nil && !c.allowUtxo(*utxo) {
|
||||||
|
walletLog.Infof("Cannot use unconfirmed "+
|
||||||
|
"utxo=%v because it is unstable and could be "+
|
||||||
|
"replaced", utxo.OutPoint)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
coins = append(coins, wallet.Coin{
|
coins = append(coins, wallet.Coin{
|
||||||
TxOut: wire.TxOut{
|
TxOut: wire.TxOut{
|
||||||
Value: int64(utxo.Value),
|
Value: int64(utxo.Value),
|
||||||
|
|||||||
@@ -1489,8 +1489,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
EnableUpfrontShutdown: cfg.EnableUpfrontShutdown,
|
EnableUpfrontShutdown: cfg.EnableUpfrontShutdown,
|
||||||
MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte(
|
MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte(
|
||||||
s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(),
|
s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(),
|
||||||
DeleteAliasEdge: deleteAliasEdge,
|
DeleteAliasEdge: deleteAliasEdge,
|
||||||
AliasManager: s.aliasMgr,
|
AliasManager: s.aliasMgr,
|
||||||
|
IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1735,3 +1735,26 @@ func (s *UtxoSweeper) handleBumpEvent(r *BumpResult) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSweeperOutpoint determines whether the outpoint was created by the sweeper.
|
||||||
|
//
|
||||||
|
// NOTE: It is enough to check the txid because the sweeper will create
|
||||||
|
// outpoints which solely belong to the internal LND wallet.
|
||||||
|
func (s *UtxoSweeper) IsSweeperOutpoint(op wire.OutPoint) bool {
|
||||||
|
found, err := s.cfg.Store.IsOurTx(op.Hash)
|
||||||
|
// In case there is an error fetching the transaction details from the
|
||||||
|
// sweeper store we assume the outpoint is still used by the sweeper
|
||||||
|
// (worst case scenario).
|
||||||
|
//
|
||||||
|
// TODO(ziggie): Ensure that confirmed outpoints are deleted from the
|
||||||
|
// bucket.
|
||||||
|
if err != nil && !errors.Is(err, errNoTxHashesBucket) {
|
||||||
|
log.Errorf("failed to fetch info for outpoint(%v:%d) "+
|
||||||
|
"with: %v, we assume it is still in use by the sweeper",
|
||||||
|
op.Hash, op.Index, err)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
Reference in new issue
Block a user