From 36e7c38812e96e58b83d3a5368f27076e97b2a3f Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 28 Dec 2015 14:12:18 -0600 Subject: [PATCH] lnwallet: introduce mutex around coin selection logic --- lnwallet/wallet.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 980a2478a..d0dce59a5 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -136,6 +136,10 @@ type LightningWallet struct { lmtx sync.RWMutex 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 // wallet meta-data. @@ -327,12 +331,19 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg reservation.partialState.TheirLNID = req.nodeID 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. - // TODO(roasbeef): make 6 a config paramter? maxConfs := int32(math.MaxInt32) + // TODO(roasbeef): make 6 a config paramter? unspentOutputs, err := l.wallet.ListUnspent(6, maxConfs, nil) if err != nil { + l.coinSelectMtx.Unlock() req.err <- err req.resp <- nil return @@ -341,6 +352,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // Convert the outputs to coins for coin selection below. coins, err := outputsToCoins(unspentOutputs) if err != nil { + l.coinSelectMtx.Unlock() req.err <- err req.resp <- nil return @@ -361,6 +373,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg } selectedCoins, err := selector.CoinSelect(req.fundingAmount, coins) if err != nil { + l.coinSelectMtx.Unlock() req.err <- err req.resp <- nil return @@ -380,6 +393,8 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg ourContribution.Inputs[i] = wire.NewTxIn(outPoint, nil) } + l.coinSelectMtx.Unlock() + // Create some possibly neccessary change outputs. selectedTotalValue := coinset.NewCoinSet(selectedCoins.Coins()).TotalValue() if selectedTotalValue > req.fundingAmount {