multi: switch to transaction weight to calculate transaction fees

Due to a recent change within the codebase to return estimated fee rates
in sat/kw, this commit ensures that we use this fee rate properly by
calculing a transaction's fees using its weight. This includes all of
the different transactions that are created within lnd (funding, sweeps,
etc.). On-chain transactions still rely on a sat/vbyte fee rate since it's
required by btcwallet.
This commit is contained in:
Wilmer Paulino 2018-07-27 18:27:51 -07:00
parent b3d5d7ab9c
commit a63677a381
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
4 changed files with 48 additions and 47 deletions

@ -1015,13 +1015,13 @@ func (b *breachArbiter) createJusticeTx(
spendableOutputs = append(spendableOutputs, input) spendableOutputs = append(spendableOutputs, input)
} }
txVSize := int64(weightEstimate.VSize()) txWeight := int64(weightEstimate.Weight())
return b.sweepSpendableOutputsTxn(txVSize, spendableOutputs...) return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...)
} }
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of // sweepSpendableOutputsTxn creates a signed transaction from a sequence of
// spendable outputs by sweeping the funds into a single p2wkh output. // spendable outputs by sweeping the funds into a single p2wkh output.
func (b *breachArbiter) sweepSpendableOutputsTxn(txVSize int64, func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64,
inputs ...SpendableOutput) (*wire.MsgTx, error) { inputs ...SpendableOutput) (*wire.MsgTx, error) {
// First, we obtain a new public key script from the wallet which we'll // First, we obtain a new public key script from the wallet which we'll
@ -1041,11 +1041,11 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txVSize int64,
// We'll actually attempt to target inclusion within the next two // We'll actually attempt to target inclusion within the next two
// blocks as we'd like to sweep these funds back into our wallet ASAP. // blocks as we'd like to sweep these funds back into our wallet ASAP.
feePerVSize, err := b.cfg.Estimator.EstimateFeePerVSize(2) feePerKw, err := b.cfg.Estimator.EstimateFeePerKW(2)
if err != nil { if err != nil {
return nil, err return nil, err
} }
txFee := feePerVSize.FeeForVSize(txVSize) txFee := feePerKw.FeeForWeight(txWeight)
// TODO(roasbeef): already start to siphon their funds into fees // TODO(roasbeef): already start to siphon their funds into fees
sweepAmt := int64(totalAmt - txFee) sweepAmt := int64(totalAmt - txFee)

@ -451,27 +451,27 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
return nil, err return nil, err
} }
// With out address obtained, we'll query for an // With our address obtained, we'll query for an
// estimate to be confirmed at ease. // estimate to be confirmed at ease.
// //
// TODO(roasbeef): signal up if fee would be too large // TODO(roasbeef): signal up if fee would be too large
// to sweep singly, need to batch // to sweep singly, need to batch
feePerVSize, err := h.FeeEstimator.EstimateFeePerVSize(6) feePerKw, err := h.FeeEstimator.EstimateFeePerKW(6)
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debugf("%T(%x): using %v sat/vbyte to sweep htlc"+ log.Debugf("%T(%x): using %v sat/kw to sweep htlc"+
"incoming+remote htlc confirmed", h, "incoming+remote htlc confirmed", h,
h.payHash[:], int64(feePerVSize)) h.payHash[:], int64(feePerKw))
// Using a weight estimator, we'll compute the total // Using a weight estimator, we'll compute the total
// fee required, and from that the value we'll end up // fee required, and from that the value we'll end up
// with. // with.
totalVSize := (&lnwallet.TxWeightEstimator{}). totalWeight := (&lnwallet.TxWeightEstimator{}).
AddWitnessInput(lnwallet.OfferedHtlcSuccessWitnessSize). AddWitnessInput(lnwallet.OfferedHtlcSuccessWitnessSize).
AddP2WKHOutput().VSize() AddP2WKHOutput().Weight()
totalFees := feePerVSize.FeeForVSize(int64(totalVSize)) totalFees := feePerKw.FeeForWeight(int64(totalWeight))
sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value - sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value -
int64(totalFees) int64(totalFees)
@ -1253,18 +1253,18 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
// First, we'll estimate the total weight so we can compute // First, we'll estimate the total weight so we can compute
// fees properly. We'll use a lax estimate, as this output is // fees properly. We'll use a lax estimate, as this output is
// in no immediate danger. // in no immediate danger.
feePerVSize, err := c.FeeEstimator.EstimateFeePerVSize(6) feePerKw, err := c.FeeEstimator.EstimateFeePerKW(6)
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debugf("%T(%v): using %v sat/vsize for sweep tx", c, log.Debugf("%T(%v): using %v sat/kw for sweep tx", c,
c.chanPoint, int64(feePerVSize)) c.chanPoint, int64(feePerKw))
totalVSize := (&lnwallet.TxWeightEstimator{}). totalWeight := (&lnwallet.TxWeightEstimator{}).
AddP2PKHInput(). AddP2WKHInput().
AddP2WKHOutput().VSize() AddP2WKHOutput().Weight()
totalFees := feePerVSize.FeeForVSize(int64(totalVSize)) totalFees := feePerKw.FeeForWeight(int64(totalWeight))
sweepAmt := signDesc.Output.Value - int64(totalFees) sweepAmt := signDesc.Output.Value - int64(totalFees)
c.sweepTx = wire.NewMsgTx(2) c.sweepTx = wire.NewMsgTx(2)

@ -81,9 +81,9 @@ type initFundingReserveMsg struct {
// paying some multiple of the accepted base fee rate of the network. // paying some multiple of the accepted base fee rate of the network.
commitFeePerKw SatPerKWeight commitFeePerKw SatPerKWeight
// fundingFeePerVSize is the fee rate in sat/vbyte to use for the // fundingFeePerKw is the fee rate in sat/kw to use for the initial
// initial funding transaction. // funding transaction.
fundingFeePerVSize SatPerVByte fundingFeePerKw SatPerKWeight
// pushMSat is the number of milli-satoshis that should be pushed over // pushMSat is the number of milli-satoshis that should be pushed over
// the responder as part of the initial channel creation. // the responder as part of the initial channel creation.
@ -413,7 +413,7 @@ out:
// commitment transaction is valid. // commitment transaction is valid.
func (l *LightningWallet) InitChannelReservation( func (l *LightningWallet) InitChannelReservation(
capacity, ourFundAmt btcutil.Amount, pushMSat lnwire.MilliSatoshi, capacity, ourFundAmt btcutil.Amount, pushMSat lnwire.MilliSatoshi,
commitFeePerKw SatPerKWeight, fundingFeePerVSize SatPerVByte, commitFeePerKw SatPerKWeight, fundingFeePerKw SatPerKWeight,
theirID *btcec.PublicKey, theirAddr net.Addr, theirID *btcec.PublicKey, theirAddr net.Addr,
chainHash *chainhash.Hash, flags lnwire.FundingFlag) (*ChannelReservation, error) { chainHash *chainhash.Hash, flags lnwire.FundingFlag) (*ChannelReservation, error) {
@ -421,17 +421,17 @@ func (l *LightningWallet) InitChannelReservation(
respChan := make(chan *ChannelReservation, 1) respChan := make(chan *ChannelReservation, 1)
l.msgChan <- &initFundingReserveMsg{ l.msgChan <- &initFundingReserveMsg{
chainHash: chainHash, chainHash: chainHash,
nodeID: theirID, nodeID: theirID,
nodeAddr: theirAddr, nodeAddr: theirAddr,
fundingAmount: ourFundAmt, fundingAmount: ourFundAmt,
capacity: capacity, capacity: capacity,
commitFeePerKw: commitFeePerKw, commitFeePerKw: commitFeePerKw,
fundingFeePerVSize: fundingFeePerVSize, fundingFeePerKw: fundingFeePerKw,
pushMSat: pushMSat, pushMSat: pushMSat,
flags: flags, flags: flags,
err: errChan, err: errChan,
resp: respChan, resp: respChan,
} }
return <-respChan, <-errChan return <-respChan, <-errChan
@ -479,10 +479,10 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
// don't need to perform any coin selection. Otherwise, attempt to // don't need to perform any coin selection. Otherwise, attempt to
// obtain enough coins to meet the required funding amount. // obtain enough coins to meet the required funding amount.
if req.fundingAmount != 0 { if req.fundingAmount != 0 {
// Coin selection is done on the basis of sat-per-vbyte, we'll // Coin selection is done on the basis of sat/kw, so we'll use
// use the passed sat/vbyte passed in to perform coin selection. // the fee rate passed in to perform coin selection.
err := l.selectCoinsAndChange( err := l.selectCoinsAndChange(
req.fundingFeePerVSize, req.fundingAmount, req.fundingFeePerKw, req.fundingAmount,
reservation.ourContribution, reservation.ourContribution,
) )
if err != nil { if err != nil {
@ -1276,7 +1276,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
// within the passed contribution's inputs. If necessary, a change address will // within the passed contribution's inputs. If necessary, a change address will
// also be generated. // also be generated.
// TODO(roasbeef): remove hardcoded fees and req'd confs for outputs. // TODO(roasbeef): remove hardcoded fees and req'd confs for outputs.
func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte, func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerKWeight,
amt btcutil.Amount, contribution *ChannelContribution) error { amt btcutil.Amount, contribution *ChannelContribution) error {
// We hold the coin select mutex while querying for outputs, and // We hold the coin select mutex while querying for outputs, and
@ -1286,7 +1286,7 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte,
defer l.coinSelectMtx.Unlock() defer l.coinSelectMtx.Unlock()
walletLog.Infof("Performing funding tx coin selection using %v "+ walletLog.Infof("Performing funding tx coin selection using %v "+
"sat/vbyte as fee rate", int64(feeRate)) "sat/kw as fee rate", int64(feeRate))
// Find all unlocked unspent witness outputs with greater than 1 // Find all unlocked unspent witness outputs with greater than 1
// confirmation. // confirmation.
@ -1395,9 +1395,9 @@ func selectInputs(amt btcutil.Amount, coins []*Utxo) (btcutil.Amount, []*Utxo, e
// coinSelect attempts to select a sufficient amount of coins, including a // coinSelect attempts to select a sufficient amount of coins, including a
// change output to fund amt satoshis, adhering to the specified fee rate. The // change output to fund amt satoshis, adhering to the specified fee rate. The
// specified fee rate should be expressed in sat/vbyte for coin selection to // specified fee rate should be expressed in sat/kw for coin selection to
// function properly. // function properly.
func coinSelect(feeRate SatPerVByte, amt btcutil.Amount, func coinSelect(feeRate SatPerKWeight, amt btcutil.Amount,
coins []*Utxo) ([]*Utxo, btcutil.Amount, error) { coins []*Utxo) ([]*Utxo, btcutil.Amount, error) {
amtNeeded := amt amtNeeded := amt
@ -1441,7 +1441,8 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
// amount isn't enough to pay fees, then increase the requested // amount isn't enough to pay fees, then increase the requested
// coin amount by the estimate required fee, performing another // coin amount by the estimate required fee, performing another
// round of coin selection. // round of coin selection.
requiredFee := feeRate.FeeForVSize(int64(weightEstimate.VSize())) totalWeight := int64(weightEstimate.Weight())
requiredFee := feeRate.FeeForWeight(totalWeight)
if overShootAmt < requiredFee { if overShootAmt < requiredFee {
amtNeeded = amt + requiredFee amtNeeded = amt + requiredFee
continue continue

@ -994,15 +994,15 @@ func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput,
utxnLog.Infof("Creating sweep transaction for %v CSV inputs, %v CLTV "+ utxnLog.Infof("Creating sweep transaction for %v CSV inputs, %v CLTV "+
"inputs", len(csvOutputs), len(cltvOutputs)) "inputs", len(csvOutputs), len(cltvOutputs))
txVSize := int64(weightEstimate.VSize()) txWeight := int64(weightEstimate.Weight())
return u.populateSweepTx(txVSize, classHeight, csvOutputs, cltvOutputs) return u.populateSweepTx(txWeight, classHeight, csvOutputs, cltvOutputs)
} }
// populateSweepTx populate the final sweeping transaction with all witnesses // populateSweepTx populate the final sweeping transaction with all witnesses
// in place for all inputs using the provided txn fee. The created transaction // in place for all inputs using the provided txn fee. The created transaction
// has a single output sending all the funds back to the source wallet, after // has a single output sending all the funds back to the source wallet, after
// accounting for the fee estimate. // accounting for the fee estimate.
func (u *utxoNursery) populateSweepTx(txVSize int64, classHeight uint32, func (u *utxoNursery) populateSweepTx(txWeight int64, classHeight uint32,
csvInputs []CsvSpendableOutput, csvInputs []CsvSpendableOutput,
cltvInputs []SpendableOutput) (*wire.MsgTx, error) { cltvInputs []SpendableOutput) (*wire.MsgTx, error) {
@ -1022,11 +1022,11 @@ func (u *utxoNursery) populateSweepTx(txVSize int64, classHeight uint32,
} }
// Using the txn weight estimate, compute the required txn fee. // Using the txn weight estimate, compute the required txn fee.
feePerVSize, err := u.cfg.Estimator.EstimateFeePerVSize(6) feePerKw, err := u.cfg.Estimator.EstimateFeePerKW(6)
if err != nil { if err != nil {
return nil, err return nil, err
} }
txFee := feePerVSize.FeeForVSize(txVSize) txFee := feePerKw.FeeForWeight(txWeight)
// Sweep as much possible, after subtracting txn fees. // Sweep as much possible, after subtracting txn fees.
sweepAmt := int64(totalSum - txFee) sweepAmt := int64(totalSum - txFee)