lnd: add optional change output index to check reserved wallet balance

This commit is contained in:
Bjarne Magnussen 2021-09-30 13:02:35 +02:00
parent 32fa48df7d
commit a5a477e311
No known key found for this signature in database
GPG key ID: B0A9ADF6B24CE67F
2 changed files with 65 additions and 7 deletions

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
})