lnwallet/channel: enforce absolute fee floor of 250 sat/kw

This enforces the _actualized_ fee rate of the  commitment transaction,
rather than the fee floor used for estimation. The new value of 250
sat/kw corresponds to 1 sat/byte, rather than 253 which is only rounded
up during estimation to account for the fact that BOLT 3 rounds down to
the nearest satoshi and that the vbyte fee estimation is lossy.

Previously we would incorrectly fail to sign the next commitment even
though the fee was technically high enough. Restarting with this commit
should solve the issue as long as the channel hasn't already gone to
chain.
This commit is contained in:
Conner Fromknecht 2020-04-20 23:37:34 -07:00
parent 3ab5899853
commit 89bd58786e
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
3 changed files with 45 additions and 2 deletions

View File

@ -9,8 +9,13 @@ import (
const (
// FeePerKwFloor is the lowest fee rate in sat/kw that we should use for
// determining transaction fees.
// estimating transaction fees before signing.
FeePerKwFloor SatPerKWeight = 253
// AbsoluteFeePerKwFloor is the lowest fee rate in sat/kw of a
// transaction that we should ever _create_. This is the the equivalent
// of 1 sat/byte in sat/kw.
AbsoluteFeePerKwFloor SatPerKWeight = 250
)
// SatPerKVByte represents a fee rate in sat/kb.

View File

@ -2412,7 +2412,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
effFeeRate := chainfee.SatPerKWeight(fee) * 1000 /
chainfee.SatPerKWeight(weight)
if effFeeRate < chainfee.FeePerKwFloor {
if effFeeRate < chainfee.AbsoluteFeePerKwFloor {
return nil, fmt.Errorf("height=%v, for ChannelPoint(%v) "+
"attempts to create commitment with feerate %v: %v",
nextHeight, lc.channelState.FundingOutpoint,

View File

@ -7638,3 +7638,41 @@ func TestChannelMaxFeeRate(t *testing.T) {
assertMaxFeeRate(0.000001, 690)
assertMaxFeeRate(0.0000001, chainfee.FeePerKwFloor)
}
// TestChannelFeeRateFloor asserts that valid commitments can be proposed and
// received using chainfee.FeePerKwFloor as the initiator's fee rate.
func TestChannelFeeRateFloor(t *testing.T) {
t.Parallel()
alice, bob, cleanUp, err := CreateTestChannels(
channeldb.SingleFunderTweaklessBit,
)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
// Set the fee rate to the proposing fee rate floor.
minFee := chainfee.FeePerKwFloor
// Alice is the initiator, so only she can propose fee updates.
if err := alice.UpdateFee(minFee); err != nil {
t.Fatalf("unable to send fee update")
}
if err := bob.ReceiveUpdateFee(minFee); err != nil {
t.Fatalf("unable to receive fee update")
}
// Check that alice can still sign commitments.
sig, htlcSigs, _, err := alice.SignNextCommitment()
if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err)
}
// Check that bob can still receive commitments.
err = bob.ReceiveNewCommitment(sig, htlcSigs)
if err != nil {
t.Fatalf("bob unable to process alice's new commitment: %v",
err)
}
}