rpc: acquire global coin select lock in related RPCs
This ensures proper coin selection synchronization between lnd and users of the RPC to avoid potential double spend errors.
This commit is contained in:
parent
9d9e54f83e
commit
c2f1fe26c1
@ -38,6 +38,13 @@ type Config struct {
|
||||
// any relevant requests to.
|
||||
Wallet lnwallet.WalletController
|
||||
|
||||
// CoinSelectionLocker allows the caller to perform an operation, which
|
||||
// is synchronized with all coin selection attempts. This can be used
|
||||
// when an operation requires that all coin selection operations cease
|
||||
// forward progress. Think of this as an exclusive lock on coin
|
||||
// selection operations.
|
||||
CoinSelectionLocker sweep.CoinSelectionLocker
|
||||
|
||||
// KeyRing is an interface that the WalletKit will use to derive any
|
||||
// keys due to incoming client requests.
|
||||
KeyRing keychain.KeyRing
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
@ -258,7 +259,15 @@ func (w *WalletKit) ListUnspent(ctx context.Context,
|
||||
|
||||
// With our arguments validated, we'll query the internal wallet for
|
||||
// the set of UTXOs that match our query.
|
||||
utxos, err := w.cfg.Wallet.ListUnspentWitness(minConfs, maxConfs)
|
||||
//
|
||||
// We'll acquire the global coin selection lock to ensure there aren't
|
||||
// any other concurrent processes attempting to lock any UTXOs which may
|
||||
// be shown available to us.
|
||||
var utxos []*lnwallet.Utxo
|
||||
err = w.cfg.CoinSelectionLocker.WithCoinSelectLock(func() error {
|
||||
utxos, err = w.cfg.Wallet.ListUnspentWitness(minConfs, maxConfs)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -301,7 +310,13 @@ func (w *WalletKit) LeaseOutput(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expiration, err := w.cfg.Wallet.LeaseOutput(lockID, *op)
|
||||
// Acquire the global coin selection lock to ensure there aren't any
|
||||
// other concurrent processes attempting to lease the same UTXO.
|
||||
var expiration time.Time
|
||||
err = w.cfg.CoinSelectionLocker.WithCoinSelectLock(func() error {
|
||||
expiration, err = w.cfg.Wallet.LeaseOutput(lockID, *op)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -328,7 +343,12 @@ func (w *WalletKit) ReleaseOutput(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := w.cfg.Wallet.ReleaseOutput(lockID, *op); err != nil {
|
||||
// Acquire the global coin selection lock to maintain consistency as
|
||||
// it's acquired when we initially leased the output.
|
||||
err = w.cfg.CoinSelectionLocker.WithCoinSelectLock(func() error {
|
||||
return w.cfg.Wallet.ReleaseOutput(lockID, *op)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
12
rpcserver.go
12
rpcserver.go
@ -950,7 +950,17 @@ func (r *rpcServer) ListUnspent(ctx context.Context,
|
||||
|
||||
// With our arguments validated, we'll query the internal wallet for
|
||||
// the set of UTXOs that match our query.
|
||||
utxos, err := r.server.cc.wallet.ListUnspentWitness(minConfs, maxConfs)
|
||||
//
|
||||
// We'll acquire the global coin selection lock to ensure there aren't
|
||||
// any other concurrent processes attempting to lock any UTXOs which may
|
||||
// be shown available to us.
|
||||
var utxos []*lnwallet.Utxo
|
||||
err = r.server.cc.wallet.WithCoinSelectLock(func() error {
|
||||
utxos, err = r.server.cc.wallet.ListUnspentWitness(
|
||||
minConfs, maxConfs,
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -153,6 +153,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, cc *chainControl
|
||||
subCfgValue.FieldByName("Wallet").Set(
|
||||
reflect.ValueOf(cc.wallet),
|
||||
)
|
||||
subCfgValue.FieldByName("CoinSelectionLocker").Set(
|
||||
reflect.ValueOf(cc.wallet),
|
||||
)
|
||||
subCfgValue.FieldByName("KeyRing").Set(
|
||||
reflect.ValueOf(cc.keyRing),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user