From a63677a381450bc7c871c596cc1b6db0d7610e51 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Fri, 27 Jul 2018 18:27:51 -0700 Subject: [PATCH] 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. --- breacharbiter.go | 10 +++--- contractcourt/contract_resolvers.go | 28 ++++++++--------- lnwallet/wallet.go | 47 +++++++++++++++-------------- utxonursery.go | 10 +++--- 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/breacharbiter.go b/breacharbiter.go index 0862acd8..a5ea1a5f 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -1015,13 +1015,13 @@ func (b *breachArbiter) createJusticeTx( spendableOutputs = append(spendableOutputs, input) } - txVSize := int64(weightEstimate.VSize()) - return b.sweepSpendableOutputsTxn(txVSize, spendableOutputs...) + txWeight := int64(weightEstimate.Weight()) + return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...) } // sweepSpendableOutputsTxn creates a signed transaction from a sequence of // 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) { // 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 // 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 { return nil, err } - txFee := feePerVSize.FeeForVSize(txVSize) + txFee := feePerKw.FeeForWeight(txWeight) // TODO(roasbeef): already start to siphon their funds into fees sweepAmt := int64(totalAmt - txFee) diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 086b8d26..b525b5df 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -451,27 +451,27 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { 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. // // TODO(roasbeef): signal up if fee would be too large // to sweep singly, need to batch - feePerVSize, err := h.FeeEstimator.EstimateFeePerVSize(6) + feePerKw, err := h.FeeEstimator.EstimateFeePerKW(6) if err != nil { 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, - h.payHash[:], int64(feePerVSize)) + h.payHash[:], int64(feePerKw)) // Using a weight estimator, we'll compute the total // fee required, and from that the value we'll end up // with. - totalVSize := (&lnwallet.TxWeightEstimator{}). + totalWeight := (&lnwallet.TxWeightEstimator{}). AddWitnessInput(lnwallet.OfferedHtlcSuccessWitnessSize). - AddP2WKHOutput().VSize() - totalFees := feePerVSize.FeeForVSize(int64(totalVSize)) + AddP2WKHOutput().Weight() + totalFees := feePerKw.FeeForWeight(int64(totalWeight)) sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value - int64(totalFees) @@ -1253,18 +1253,18 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // First, we'll estimate the total weight so we can compute // fees properly. We'll use a lax estimate, as this output is // in no immediate danger. - feePerVSize, err := c.FeeEstimator.EstimateFeePerVSize(6) + feePerKw, err := c.FeeEstimator.EstimateFeePerKW(6) if err != nil { return nil, err } - log.Debugf("%T(%v): using %v sat/vsize for sweep tx", c, - c.chanPoint, int64(feePerVSize)) + log.Debugf("%T(%v): using %v sat/kw for sweep tx", c, + c.chanPoint, int64(feePerKw)) - totalVSize := (&lnwallet.TxWeightEstimator{}). - AddP2PKHInput(). - AddP2WKHOutput().VSize() - totalFees := feePerVSize.FeeForVSize(int64(totalVSize)) + totalWeight := (&lnwallet.TxWeightEstimator{}). + AddP2WKHInput(). + AddP2WKHOutput().Weight() + totalFees := feePerKw.FeeForWeight(int64(totalWeight)) sweepAmt := signDesc.Output.Value - int64(totalFees) c.sweepTx = wire.NewMsgTx(2) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 8a2d9b1f..4f86ba94 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -81,9 +81,9 @@ type initFundingReserveMsg struct { // paying some multiple of the accepted base fee rate of the network. commitFeePerKw SatPerKWeight - // fundingFeePerVSize is the fee rate in sat/vbyte to use for the - // initial funding transaction. - fundingFeePerVSize SatPerVByte + // fundingFeePerKw is the fee rate in sat/kw to use for the initial + // funding transaction. + fundingFeePerKw SatPerKWeight // pushMSat is the number of milli-satoshis that should be pushed over // the responder as part of the initial channel creation. @@ -413,7 +413,7 @@ out: // commitment transaction is valid. func (l *LightningWallet) InitChannelReservation( capacity, ourFundAmt btcutil.Amount, pushMSat lnwire.MilliSatoshi, - commitFeePerKw SatPerKWeight, fundingFeePerVSize SatPerVByte, + commitFeePerKw SatPerKWeight, fundingFeePerKw SatPerKWeight, theirID *btcec.PublicKey, theirAddr net.Addr, chainHash *chainhash.Hash, flags lnwire.FundingFlag) (*ChannelReservation, error) { @@ -421,17 +421,17 @@ func (l *LightningWallet) InitChannelReservation( respChan := make(chan *ChannelReservation, 1) l.msgChan <- &initFundingReserveMsg{ - chainHash: chainHash, - nodeID: theirID, - nodeAddr: theirAddr, - fundingAmount: ourFundAmt, - capacity: capacity, - commitFeePerKw: commitFeePerKw, - fundingFeePerVSize: fundingFeePerVSize, - pushMSat: pushMSat, - flags: flags, - err: errChan, - resp: respChan, + chainHash: chainHash, + nodeID: theirID, + nodeAddr: theirAddr, + fundingAmount: ourFundAmt, + capacity: capacity, + commitFeePerKw: commitFeePerKw, + fundingFeePerKw: fundingFeePerKw, + pushMSat: pushMSat, + flags: flags, + err: errChan, + resp: respChan, } return <-respChan, <-errChan @@ -479,10 +479,10 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // don't need to perform any coin selection. Otherwise, attempt to // obtain enough coins to meet the required funding amount. if req.fundingAmount != 0 { - // Coin selection is done on the basis of sat-per-vbyte, we'll - // use the passed sat/vbyte passed in to perform coin selection. + // 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( - req.fundingFeePerVSize, req.fundingAmount, + req.fundingFeePerKw, req.fundingAmount, reservation.ourContribution, ) 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 // also be generated. // 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 { // 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() 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 // 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 // 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. -func coinSelect(feeRate SatPerVByte, amt btcutil.Amount, +func coinSelect(feeRate SatPerKWeight, amt btcutil.Amount, coins []*Utxo) ([]*Utxo, btcutil.Amount, error) { amtNeeded := amt @@ -1441,7 +1441,8 @@ func coinSelect(feeRate SatPerVByte, amt btcutil.Amount, // amount isn't enough to pay fees, then increase the requested // coin amount by the estimate required fee, performing another // round of coin selection. - requiredFee := feeRate.FeeForVSize(int64(weightEstimate.VSize())) + totalWeight := int64(weightEstimate.Weight()) + requiredFee := feeRate.FeeForWeight(totalWeight) if overShootAmt < requiredFee { amtNeeded = amt + requiredFee continue diff --git a/utxonursery.go b/utxonursery.go index ec2424d3..eba9de75 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -994,15 +994,15 @@ func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput, utxnLog.Infof("Creating sweep transaction for %v CSV inputs, %v CLTV "+ "inputs", len(csvOutputs), len(cltvOutputs)) - txVSize := int64(weightEstimate.VSize()) - return u.populateSweepTx(txVSize, classHeight, csvOutputs, cltvOutputs) + txWeight := int64(weightEstimate.Weight()) + return u.populateSweepTx(txWeight, classHeight, csvOutputs, cltvOutputs) } // populateSweepTx populate the final sweeping transaction with all witnesses // 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 // accounting for the fee estimate. -func (u *utxoNursery) populateSweepTx(txVSize int64, classHeight uint32, +func (u *utxoNursery) populateSweepTx(txWeight int64, classHeight uint32, csvInputs []CsvSpendableOutput, 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. - feePerVSize, err := u.cfg.Estimator.EstimateFeePerVSize(6) + feePerKw, err := u.cfg.Estimator.EstimateFeePerKW(6) if err != nil { return nil, err } - txFee := feePerVSize.FeeForVSize(txVSize) + txFee := feePerKw.FeeForWeight(txWeight) // Sweep as much possible, after subtracting txn fees. sweepAmt := int64(totalSum - txFee)