lnwallet+funding+rpcserver: check reserved value before
PublishTransaction For a few manual send cases that can be initiated by the user, we check the reserved value.
This commit is contained in:
parent
185ba77f8e
commit
422008e3c0
@ -986,6 +986,27 @@ func (l *LightningWallet) CheckReservedValue(in []wire.OutPoint,
|
||||
return reserved, nil
|
||||
}
|
||||
|
||||
// CheckReservedValueTx calls CheckReservedValue with the inputs and outputs
|
||||
// from the given tx, with the number of anchor channels currently open in the
|
||||
// database.
|
||||
//
|
||||
// NOTE: This method should only be run with the CoinSelectLock held.
|
||||
func (l *LightningWallet) CheckReservedValueTx(tx *wire.MsgTx) (btcutil.Amount,
|
||||
error) {
|
||||
|
||||
numAnchors, err := l.currentNumAnchorChans()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var inputs []wire.OutPoint
|
||||
for _, txIn := range tx.TxIn {
|
||||
inputs = append(inputs, txIn.PreviousOutPoint)
|
||||
}
|
||||
|
||||
return l.CheckReservedValue(inputs, tx.TxOut, numAnchors)
|
||||
}
|
||||
|
||||
// initOurContribution initializes the given ChannelReservation with our coins
|
||||
// and change reserved for the channel, and derives the keys to use for this
|
||||
// channel.
|
||||
|
93
rpcserver.go
93
rpcserver.go
@ -1056,7 +1056,25 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := r.server.cc.Wallet.SendOutputs(outputs, feeRate, minconf, label)
|
||||
// We first do a dry run, to sanity check we won't spend our wallet
|
||||
// balance below the reserved amount.
|
||||
authoredTx, err := r.server.cc.Wallet.CreateSimpleTx(
|
||||
outputs, feeRate, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = r.server.cc.Wallet.CheckReservedValueTx(authoredTx.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If that checks out, we're failry confident that creating sending to
|
||||
// these outputs will keep the wallet balance above the reserve.
|
||||
tx, err := r.server.cc.Wallet.SendOutputs(
|
||||
outputs, feeRate, minconf, label,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1253,7 +1271,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
||||
// With the sweeper instance created, we can now generate a
|
||||
// transaction that will sweep ALL outputs from the wallet in a
|
||||
// single transaction. This will be generated in a concurrent
|
||||
// safe manner, so no need to worry about locking.
|
||||
// safe manner, so no need to worry about locking. The tx will
|
||||
// pay to the change address created above if we needed to
|
||||
// reserve any value, the rest will go to targetAddr.
|
||||
sweepTxPkg, err := sweep.CraftSweepAllTx(
|
||||
feePerKw, lnwallet.DefaultDustLimit(),
|
||||
uint32(bestHeight), nil, targetAddr, wallet,
|
||||
@ -1264,6 +1284,75 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Before we publish the transaction we make sure it won't
|
||||
// violate our reserved wallet value.
|
||||
var reservedVal btcutil.Amount
|
||||
err = wallet.WithCoinSelectLock(func() error {
|
||||
var err error
|
||||
reservedVal, err = wallet.CheckReservedValueTx(
|
||||
sweepTxPkg.SweepTx,
|
||||
)
|
||||
return err
|
||||
})
|
||||
|
||||
// If sending everything to this address would invalidate our
|
||||
// reserved wallet balance, we create a new sweep tx, where
|
||||
// we'll send the reserved value back to our wallet.
|
||||
if err == lnwallet.ErrReservedValueInvalidated {
|
||||
sweepTxPkg.CancelSweepAttempt()
|
||||
|
||||
rpcsLog.Debugf("Reserved value %v not satisfied after "+
|
||||
"send_all, trying with change output",
|
||||
reservedVal)
|
||||
|
||||
// We'll request a change address from the wallet,
|
||||
// where we'll send this reserved value back to. This
|
||||
// ensures this is an address the wallet knows about,
|
||||
// allowing us to pass the reserved value check.
|
||||
changeAddr, err := r.server.cc.Wallet.NewAddress(
|
||||
lnwallet.WitnessPubKey, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send the reserved value to this change address, the
|
||||
// remaining funds will go to the targetAddr.
|
||||
outputs := []sweep.DeliveryAddr{
|
||||
{
|
||||
Addr: changeAddr,
|
||||
Amt: reservedVal,
|
||||
},
|
||||
}
|
||||
|
||||
sweepTxPkg, err = sweep.CraftSweepAllTx(
|
||||
feePerKw, lnwallet.DefaultDustLimit(),
|
||||
uint32(bestHeight), outputs, targetAddr, wallet,
|
||||
wallet.WalletController, wallet.WalletController,
|
||||
r.server.cc.FeeEstimator, r.server.cc.Signer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sanity check the new tx by re-doing the check.
|
||||
err = wallet.WithCoinSelectLock(func() error {
|
||||
_, err := wallet.CheckReservedValueTx(
|
||||
sweepTxPkg.SweepTx,
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
sweepTxPkg.CancelSweepAttempt()
|
||||
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
sweepTxPkg.CancelSweepAttempt()
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpcsLog.Debugf("Sweeping all coins from wallet to addr=%v, "+
|
||||
"with tx=%v", in.Addr, spew.Sdump(sweepTxPkg.SweepTx))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user