Merge pull request #5665 from LN-Zap/upstream/feat/single-utxo-reserved

lnd: use change output index from authoring TX to check reserved value
This commit is contained in:
Oliver Gugger 2021-10-01 09:51:28 +02:00 committed by GitHub
commit 036041d671
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 7 deletions

View file

@ -405,6 +405,9 @@ you.
[Lnd is updated to use the version of Neutrino containing this
fix](https://github.com/lightningnetwork/lnd/pull/5807).
* [Use the change output index when validating the reserved wallet balance for
SendCoins/SendMany calls](https://github.com/lightningnetwork/lnd/pull/5665)
## Documentation
The [code contribution guidelines have been updated to mention the new

View file

@ -272,6 +272,20 @@ type addSingleFunderSigsMsg struct {
err chan error
}
// CheckReservedValueTxReq is the request struct used to call
// CheckReservedValueTx with. It contains the transaction to check as well as
// an optional explicitly defined index to denote a change output that is not
// watched by the wallet.
type CheckReservedValueTxReq struct {
// Tx is the transaction to check the outputs for.
Tx *wire.MsgTx
// ChangeIndex denotes an optional output index that can be explicitly
// set for a change that is not being watched by the wallet and would
// otherwise not be recognized as a change output.
ChangeIndex *int
}
// LightningWallet is a domain specific, yet general Bitcoin wallet capable of
// executing workflow required to interact with the Lightning Network. It is
// domain specific in the sense that it understands all the fancy scripts used
@ -1036,8 +1050,8 @@ func (l *LightningWallet) CheckReservedValue(in []wire.OutPoint,
// database.
//
// NOTE: This method should only be run with the CoinSelectLock held.
func (l *LightningWallet) CheckReservedValueTx(tx *wire.MsgTx) (btcutil.Amount,
error) {
func (l *LightningWallet) CheckReservedValueTx(req CheckReservedValueTxReq) (
btcutil.Amount, error) {
numAnchors, err := l.currentNumAnchorChans()
if err != nil {
@ -1045,11 +1059,44 @@ func (l *LightningWallet) CheckReservedValueTx(tx *wire.MsgTx) (btcutil.Amount,
}
var inputs []wire.OutPoint
for _, txIn := range tx.TxIn {
for _, txIn := range req.Tx.TxIn {
inputs = append(inputs, txIn.PreviousOutPoint)
}
return l.CheckReservedValue(inputs, tx.TxOut, numAnchors)
reservedVal, err := l.CheckReservedValue(
inputs, req.Tx.TxOut, numAnchors,
)
switch {
// If the error returned from CheckReservedValue is
// ErrReservedValueInvalidated, then it did nonetheless return
// the required reserved value and we check for the optional
// change index.
case errors.Is(err, ErrReservedValueInvalidated):
// Without a change index provided there is nothing more to
// check and the error is returned.
if req.ChangeIndex == nil {
return reservedVal, err
}
// If a change index was provided we make only sure that it
// would leave sufficient funds for the reserved balance value.
//
// Note: This is used if a change output index is explicitly set
// but that may not be watched by the wallet and therefore is
// not picked up by the call to CheckReservedValue above.
chIdx := *req.ChangeIndex
if chIdx < 0 || chIdx >= len(req.Tx.TxOut) ||
req.Tx.TxOut[chIdx].Value < int64(reservedVal) {
return reservedVal, err
}
case err != nil:
return reservedVal, err
}
return reservedVal, nil
}
// initOurContribution initializes the given ChannelReservation with our coins

View file

@ -1009,7 +1009,14 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
return nil, err
}
_, err = r.server.cc.Wallet.CheckReservedValueTx(authoredTx.Tx)
// Check the authored transaction and use the explicitly set change index
// to make sure that the wallet reserved balance is not invalidated.
_, err = r.server.cc.Wallet.CheckReservedValueTx(
lnwallet.CheckReservedValueTxReq{
Tx: authoredTx.Tx,
ChangeIndex: &authoredTx.ChangeIndex,
},
)
if err != nil {
return nil, err
}
@ -1242,7 +1249,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
err = wallet.WithCoinSelectLock(func() error {
var err error
reservedVal, err = wallet.CheckReservedValueTx(
sweepTxPkg.SweepTx,
lnwallet.CheckReservedValueTxReq{
Tx: sweepTxPkg.SweepTx,
},
)
return err
})
@ -1292,7 +1301,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
// Sanity check the new tx by re-doing the check.
err = wallet.WithCoinSelectLock(func() error {
_, err := wallet.CheckReservedValueTx(
sweepTxPkg.SweepTx,
lnwallet.CheckReservedValueTxReq{
Tx: sweepTxPkg.SweepTx,
},
)
return err
})