lnwallet: make selectCoinsAndChange return selected coins

This makes the method independent of the ChannelContribution struct.

We also add a function closure to the return of selectCoinsAndChange,
that let is unlock the selected output in case of error.
This commit is contained in:
Johan T. Halseth 2019-07-11 13:14:36 +02:00
parent fcf74debe6
commit 98a3d04ba3
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -477,15 +477,16 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
if req.LocalFundingAmt != 0 {
// Coin selection is done on the basis of sat/kw, so we'll use
// the fee rate passed in to perform coin selection.
err := l.selectCoinsAndChange(
selected, err := l.selectCoinsAndChange(
req.FundingFeePerKw, req.LocalFundingAmt, req.MinConfs,
reservation.ourContribution,
)
if err != nil {
req.err <- err
req.resp <- nil
return
}
reservation.ourContribution.Inputs = selected.coins
reservation.ourContribution.ChangeOutputs = selected.change
}
// Next, we'll grab a series of keys from the wallet which will be used
@ -1273,14 +1274,21 @@ func (l *LightningWallet) WithCoinSelectLock(f func() error) error {
return f()
}
// coinSelection holds the result from selectCoinsAndChange.
type coinSelection struct {
coins []*wire.TxIn
change []*wire.TxOut
unlockCoins func()
}
// selectCoinsAndChange performs coin selection in order to obtain witness
// outputs which sum to at least 'numCoins' amount of satoshis. If coin
// selection is successful/possible, then the selected coins are available
// within the passed contribution's inputs. If necessary, a change address will
// also be generated.
// outputs which sum to at least 'amt' amount of satoshis. If necessary,
// a change address will also be generated. If coin selection is
// successful/possible, then the selected coins and change outputs are
// returned. This method locks the selected outputs, and a function closure to
// unlock them in case of an error is returned.
func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight,
amt btcutil.Amount, minConfs int32,
contribution *ChannelContribution) error {
amt btcutil.Amount, minConfs int32) (*coinSelection, error) {
// We hold the coin select mutex while querying for outputs, and
// performing coin selection in order to avoid inadvertent double
@ -1295,7 +1303,7 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight,
// number of confirmations required.
coins, err := l.ListUnspentWitness(minConfs, math.MaxInt32)
if err != nil {
return err
return nil, err
}
// Perform coin selection over our available, unlocked unspent outputs
@ -1303,13 +1311,34 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight,
// requirements.
selectedCoins, changeAmt, err := coinSelect(feeRate, amt, coins)
if err != nil {
return err
return nil, err
}
// Record any change output(s) generated as a result of the coin
// selection, but only if the addition of the output won't lead to the
// creation of dust.
var changeOutputs []*wire.TxOut
if changeAmt != 0 && changeAmt > DefaultDustLimit() {
changeAddr, err := l.NewAddress(WitnessPubKey, true)
if err != nil {
return nil, err
}
changeScript, err := txscript.PayToAddrScript(changeAddr)
if err != nil {
return nil, err
}
changeOutputs = make([]*wire.TxOut, 1)
changeOutputs[0] = &wire.TxOut{
Value: int64(changeAmt),
PkScript: changeScript,
}
}
// Lock the selected coins. These coins are now "reserved", this
// prevents concurrent funding requests from referring to and this
// double-spending the same set of coins.
contribution.Inputs = make([]*wire.TxIn, len(selectedCoins))
inputs := make([]*wire.TxIn, len(selectedCoins))
for i, coin := range selectedCoins {
outpoint := &coin.OutPoint
l.lockedOutPoints[*outpoint] = struct{}{}
@ -1317,30 +1346,25 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight,
// Empty sig script, we'll actually sign if this reservation is
// queued up to be completed (the other side accepts).
contribution.Inputs[i] = wire.NewTxIn(outpoint, nil, nil)
inputs[i] = wire.NewTxIn(outpoint, nil, nil)
}
// Record any change output(s) generated as a result of the coin
// selection, but only if the addition of the output won't lead to the
// creation of dust.
if changeAmt != 0 && changeAmt > DefaultDustLimit() {
changeAddr, err := l.NewAddress(WitnessPubKey, true)
if err != nil {
return err
}
changeScript, err := txscript.PayToAddrScript(changeAddr)
if err != nil {
return err
}
unlock := func() {
l.coinSelectMtx.Lock()
defer l.coinSelectMtx.Unlock()
contribution.ChangeOutputs = make([]*wire.TxOut, 1)
contribution.ChangeOutputs[0] = &wire.TxOut{
Value: int64(changeAmt),
PkScript: changeScript,
for _, coin := range selectedCoins {
outpoint := &coin.OutPoint
delete(l.lockedOutPoints, *outpoint)
l.UnlockOutpoint(*outpoint)
}
}
return nil
return &coinSelection{
coins: inputs,
change: changeOutputs,
unlockCoins: unlock,
}, nil
}
// DeriveStateHintObfuscator derives the bytes to be used for obfuscating the