-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
multi: switch to sat/kw fees #1644
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did an initial pass through, and the set of changes reads well to me. We'll need to follow up with an additional few rounds of testing to ensure that we're not introducing some weird fee regression as a side effect of this PR.
I also updated the PR description to include the two issues that this directly affects. Once this goes in, I consider those to issues fixed for future users. However, for existing users that have already run into these issues, we may want to create a follow up patch (a component of the utxo pool really) which will simply re-sign if the fee is too low.
lnwallet/fee_estimator.go
Outdated
b.minFeeRate = SatPerVByte(relayFee / 1000) | ||
// The fee rate is expressed in sat/kb, so we'll manually convert it to | ||
// our desired sat/kw rate. | ||
minRelayFeePerKw := SatPerVByte(relayFee / 1000).FeePerKWeight() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will lose some precision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just convert directly to sat/kw and skip the vbyte hop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
lnwallet/fee_estimator.go
Outdated
// EstimateFeePerVSize takes in a target for the number of blocks until an | ||
// initial confirmation and returns the estimated fee expressed in | ||
// satoshis/vbyte. | ||
// EstimateFeePer takes in a target for the number of blocks until an initial |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
lnwallet/fee_estimator.go
Outdated
satPerByte = b.minFeeRate | ||
// Since we use fee rates in sat/kw internally, we'll convert the | ||
// estimated fee rate from its sat/kb representation to sat/kw. | ||
satPerKw := SatPerVByte(satPerKB / 1000).FeePerKWeight() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably create a SatPeKB->SatPerKw
method to do this conversion without precision loss.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I suggest renaming SatPerVByte
->SatPerKVByte
, then we can do the conversion as is done here (removing the division by 1000 ofc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
contractcourt/contract_resolvers.go
Outdated
lnwallet.OfferedHtlcSuccessWitnessSize, | ||
) | ||
weightEstimator.AddP2WKHOutput() | ||
totalWeight := weightEstimator.Weight() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The chaining previously used read pretty nicely IMO :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
rpcserver.go
Outdated
|
||
paymentMap := map[string]int64{in.Addr: in.Amount} | ||
txid, err := r.sendCoinsOnChain(paymentMap, feeRate) | ||
txid, err := r.sendCoinsOnChain(paymentMap, feePerVByte) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sendCoinsOnChain
should also be modified to take sat/kw
. I think it will be converted to sat/KB
at some point, where which we can do the conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice set of changes! conversion looks solid, my comments are mostly about making the units more explicit
lnwallet/fee_estimator.go
Outdated
// EstimateFee takes in a target for the number of blocks until an | ||
// initial confirmation and returns the estimated fee expressed in | ||
// sat/kw. | ||
EstimateFee(numBlocks uint32) (SatPerKWeight, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change name to EstimateFeePerKW
/EstimateFeePerKWeight
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
lnwallet/fee_estimator.go
Outdated
@@ -56,13 +67,13 @@ type FeeEstimator interface { | |||
type StaticFeeEstimator struct { | |||
// FeeRate is the static fee rate in satoshis-per-vbyte that will be | |||
// returned by this fee estimator. | |||
FeeRate SatPerVByte | |||
FeeRate SatPerKWeight |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while we're changing this, can we set the field name to FeePerKW
, so the units are clear in other parts of the code base? would recommend doing the same for btcd and bitcoind estimators
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
chainregistry.go
Outdated
@@ -37,13 +37,13 @@ const ( | |||
defaultBitcoinBaseFeeMSat = lnwire.MilliSatoshi(1000) | |||
defaultBitcoinFeeRate = lnwire.MilliSatoshi(1) | |||
defaultBitcoinTimeLockDelta = 144 | |||
defaultBitcoinStaticFeeRate = lnwallet.SatPerVByte(50) | |||
defaultBitcoinStaticFeeRate = lnwallet.SatPerKVByte(50 * 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would change these to be explicitly in SatPerKWeight
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but a comment with the feerate in sat/b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
chainregistry.go
Outdated
@@ -37,13 +37,13 @@ const ( | |||
defaultBitcoinBaseFeeMSat = lnwire.MilliSatoshi(1000) | |||
defaultBitcoinFeeRate = lnwire.MilliSatoshi(1) | |||
defaultBitcoinTimeLockDelta = 144 | |||
defaultBitcoinStaticFeeRate = lnwallet.SatPerVByte(50) | |||
defaultBitcoinStaticFeeRate = lnwallet.SatPerKVByte(50 * 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but a comment with the feerate in sat/b.
rpcserver.go
Outdated
case feePerByte != 0: | ||
return lnwallet.SatPerVByte(feePerByte), nil | ||
return lnwallet.SatPerKVByte(feePerByte).FeePerKWeight(), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must be multiplied by 1000.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Don't blindly search and replace 🤫
Needs rebase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🕺
|
||
// By default, we'll use the backend node's minimum relay fee as the | ||
// minimum fee rate we'll propose for transacations. However, if this | ||
// happens to be lower than our fee floor, we'll enforce that instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
contractcourt/contract_resolvers.go
Outdated
AddP2PKHInput(). | ||
AddP2WKHOutput().VSize() | ||
totalFees := feePerVSize.FeeForVSize(int64(totalVSize)) | ||
var weightEstimator lnwallet.TxWeightEstimator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could keep the pattern, only changin to Weight()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
defaultLitecoinDustLimit = btcutil.Amount(54600) | ||
|
||
// defaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
chainregistry.go
Outdated
defaultLitecoinDustLimit = btcutil.Amount(54600) | ||
|
||
// defaultBitcoinStaticFeePerKW is the fee rate of 50 sat/vbyte | ||
// expressed in sat/kw. | ||
//defaultBitcoinStaticFeePerKW = lnwallet.SatPerKVByte(50 * 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
if err != nil { | ||
msg.err <- err | ||
return | ||
} | ||
|
||
// If the converted fee-per-kw is below the current widely used policy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice 😎
htlcswitch/mock.go
Outdated
|
||
quit chan struct{} | ||
} | ||
|
||
func (m *mockFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (lnwallet.SatPerVByte, error) { | ||
func (m *mockFeeEstimator) EstimateFeePerKW(numBlocks uint32) (lnwallet.SatPerKWeight, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wrap line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
lntest/harness.go
Outdated
@@ -172,7 +172,7 @@ func (n *NetworkHarness) SetUp(lndArgs []string) error { | |||
PkScript: addrScript, | |||
Value: btcutil.SatoshiPerBitcoin, | |||
} | |||
if _, err := n.Miner.SendOutputs([]*wire.TxOut{output}, 30); err != nil { | |||
if _, err := n.Miner.SendOutputs([]*wire.TxOut{output}, 7500); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wrap line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
lntest/harness.go
Outdated
@@ -1159,7 +1159,7 @@ func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount, | |||
PkScript: addrScript, | |||
Value: int64(amt), | |||
} | |||
if _, err := n.Miner.SendOutputs([]*wire.TxOut{output}, 30); err != nil { | |||
if _, err := n.Miner.SendOutputs([]*wire.TxOut{output}, 7500); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wrap line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
In this commit, we modify our FeeEstimator interface to return an estimated fee rate in sat/kw. Recently, due to low fees on the network, users have been experiencing failures broadcasting transactions due to not meeting specific fee requirements. This was happening more often than not, as the estimated fee returned by backend nodes (bitcoind and btcd) only takes into account vbytes, rather than weight. The fees returned are also expressed in sat/kb, so we must take care that we do not lose precision while converting to sat/kw. In the event that this happens, a fee floor of 253 sat/kw has been added. This fee rate originates from bitcoind rounding up the conversion from weight to vbytes.
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.
In this commit, we explicitly convert sat/vbyte fee rates input by the user to sat/kw. We do this as users are typically more accustomed to sat/vbyte fee rates, rather than sat/kw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🍡
In this PR, we modify our FeeEstimator interface to return an estimated fee rate in sat/kw. Recently, due to low fees on the network, users have been experiencing failures broadcasting transactions due to not meeting specific fee requirements. This was happening more often than not, as the estimated fee returned by backend nodes (bitcoind and btcd) only takes into account vbytes, rather than weight. The fees returned are also expressed in sat/kb, so we must take care that we do not lose precision while converting to sat/kw. In the event that this happens, a fee floor of 253 sat/kw has been added. This fee rate originates from bitcoind rounding up the conversion from weight to vbytes.
Fixes #1571.
Fixes #1610.