lnwallet: introduce mutex around coin selection logic

This commit is contained in:
Olaoluwa Osuntokun 2015-12-28 14:12:18 -06:00
parent 7874357384
commit 36e7c38812

@ -136,6 +136,10 @@ type LightningWallet struct {
lmtx sync.RWMutex lmtx sync.RWMutex
DB walletdb.DB DB walletdb.DB
// This mutex MUST be held when performing coin selection in order to
// avoid inadvertently creating multiple funding transaction which
// double spend inputs accross each other.
coinSelectMtx sync.RWMutex
// A wrapper around a namespace within boltdb reserved for ln-based // A wrapper around a namespace within boltdb reserved for ln-based
// wallet meta-data. // wallet meta-data.
@ -327,12 +331,19 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
reservation.partialState.TheirLNID = req.nodeID reservation.partialState.TheirLNID = req.nodeID
ourContribution := reservation.ourContribution ourContribution := reservation.ourContribution
// We hold the coin select mutex while querying for outputs, and
// performing coin selection in order to avoid inadvertent double spends
// accross funding transactions.
// NOTE: we don't use defer her so we can properly release the lock
// when we encounter an error condition.
l.coinSelectMtx.Lock()
// Find all unlocked unspent outputs with greater than 6 confirmations. // Find all unlocked unspent outputs with greater than 6 confirmations.
// TODO(roasbeef): make 6 a config paramter?
maxConfs := int32(math.MaxInt32) maxConfs := int32(math.MaxInt32)
// TODO(roasbeef): make 6 a config paramter?
unspentOutputs, err := l.wallet.ListUnspent(6, maxConfs, nil) unspentOutputs, err := l.wallet.ListUnspent(6, maxConfs, nil)
if err != nil { if err != nil {
l.coinSelectMtx.Unlock()
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
@ -341,6 +352,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
// Convert the outputs to coins for coin selection below. // Convert the outputs to coins for coin selection below.
coins, err := outputsToCoins(unspentOutputs) coins, err := outputsToCoins(unspentOutputs)
if err != nil { if err != nil {
l.coinSelectMtx.Unlock()
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
@ -361,6 +373,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
} }
selectedCoins, err := selector.CoinSelect(req.fundingAmount, coins) selectedCoins, err := selector.CoinSelect(req.fundingAmount, coins)
if err != nil { if err != nil {
l.coinSelectMtx.Unlock()
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
@ -380,6 +393,8 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
ourContribution.Inputs[i] = wire.NewTxIn(outPoint, nil) ourContribution.Inputs[i] = wire.NewTxIn(outPoint, nil)
} }
l.coinSelectMtx.Unlock()
// Create some possibly neccessary change outputs. // Create some possibly neccessary change outputs.
selectedTotalValue := coinset.NewCoinSet(selectedCoins.Coins()).TotalValue() selectedTotalValue := coinset.NewCoinSet(selectedCoins.Coins()).TotalValue()
if selectedTotalValue > req.fundingAmount { if selectedTotalValue > req.fundingAmount {