Skip to content

Commit b7885db

Browse files
committed
lnwallet+size: select HTLC fees based on channel type
1 parent ea2a58e commit b7885db

File tree

4 files changed

+125
-64
lines changed

4 files changed

+125
-64
lines changed

input/size.go

+17
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,23 @@ const (
202202
// which will transition an incoming HTLC to the delay-and-claim state.
203203
HtlcSuccessWeight = 703
204204

205+
// HtlcConfirmedScriptOverhead is the extra length of an HTLC script
206+
// that requires confirmation before it can be spent. These extra bytes
207+
// is a result of the extra CSV check.
208+
HtlcConfirmedScriptOverhead = 3
209+
210+
// HtlcTimeoutWeightConfirmed is the weight of the HTLC timeout
211+
// transaction which will transition an outgoing HTLC to the
212+
// delay-and-claim state, for the confirmed HTLC outputs. It is 3 bytes
213+
// larger because of the additional CSV check in the input script.
214+
HtlcTimeoutWeightConfirmed = HtlcTimeoutWeight + HtlcConfirmedScriptOverhead
215+
216+
// HtlcSuccessWeightCOnfirmed is the weight of the HTLC success
217+
// transaction which will transition an incoming HTLC to the
218+
// delay-and-claim state, for the confirmed HTLC outputs. It is 3 bytes
219+
// larger because of the cdditional CSV check in the input script.
220+
HtlcSuccessWeightConfirmed = HtlcSuccessWeight + HtlcConfirmedScriptOverhead
221+
205222
// MaxHTLCNumber is the maximum number HTLCs which can be included in a
206223
// commitment transaction. This limit was chosen such that, in the case
207224
// of a contract breach, the punishment transaction is able to sweep

lnwallet/channel.go

+55-47
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ func locateOutputIndex(p *PaymentDescriptor, tx *wire.MsgTx, ourCommit bool,
596596
// we need to keep track of the indexes of each HTLC in order to properly write
597597
// the current state to disk, and also to locate the PaymentDescriptor
598598
// corresponding to HTLC outputs in the commitment transaction.
599-
func (c *commitment) populateHtlcIndexes() error {
599+
func (c *commitment) populateHtlcIndexes(chanType channeldb.ChannelType) error {
600600
// First, we'll set up some state to allow us to locate the output
601601
// index of the all the HTLC's within the commitment transaction. We
602602
// must keep this index so we can validate the HTLC signatures sent to
@@ -608,8 +608,10 @@ func (c *commitment) populateHtlcIndexes() error {
608608
// populateIndex is a helper function that populates the necessary
609609
// indexes within the commitment view for a particular HTLC.
610610
populateIndex := func(htlc *PaymentDescriptor, incoming bool) error {
611-
isDust := htlcIsDust(incoming, c.isOurs, c.feePerKw,
612-
htlc.Amount.ToSatoshis(), c.dustLimit)
611+
isDust := htlcIsDust(
612+
chanType, incoming, c.isOurs, c.feePerKw,
613+
htlc.Amount.ToSatoshis(), c.dustLimit,
614+
)
613615

614616
var err error
615617
switch {
@@ -782,8 +784,10 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
782784
// generate them in order to locate the outputs within the commitment
783785
// transaction. As we'll mark dust with a special output index in the
784786
// on-disk state snapshot.
785-
isDustLocal := htlcIsDust(htlc.Incoming, true, feeRate,
786-
htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit)
787+
isDustLocal := htlcIsDust(
788+
chanType, htlc.Incoming, true, feeRate,
789+
htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit,
790+
)
787791
if !isDustLocal && localCommitKeys != nil {
788792
ourP2WSH, ourWitnessScript, err = genHtlcScript(
789793
chanType, htlc.Incoming, true, htlc.RefundTimeout,
@@ -793,8 +797,10 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
793797
return pd, err
794798
}
795799
}
796-
isDustRemote := htlcIsDust(htlc.Incoming, false, feeRate,
797-
htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit)
800+
isDustRemote := htlcIsDust(
801+
chanType, htlc.Incoming, false, feeRate,
802+
htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit,
803+
)
798804
if !isDustRemote && remoteCommitKeys != nil {
799805
theirP2WSH, theirWitnessScript, err = genHtlcScript(
800806
chanType, htlc.Incoming, false, htlc.RefundTimeout,
@@ -930,7 +936,8 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
930936

931937
// Finally, we'll re-populate the HTLC index for this state so we can
932938
// properly locate each HTLC within the commitment transaction.
933-
if err := commit.populateHtlcIndexes(); err != nil {
939+
err = commit.populateHtlcIndexes(lc.channelState.ChanType)
940+
if err != nil {
934941
return nil, err
935942
}
936943

@@ -1410,8 +1417,10 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate,
14101417
pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob))
14111418
copy(pd.OnionBlob[:], wireMsg.OnionBlob[:])
14121419

1413-
isDustRemote := htlcIsDust(false, false, feeRate,
1414-
wireMsg.Amount.ToSatoshis(), remoteDustLimit)
1420+
isDustRemote := htlcIsDust(
1421+
lc.channelState.ChanType, false, false, feeRate,
1422+
wireMsg.Amount.ToSatoshis(), remoteDustLimit,
1423+
)
14151424
if !isDustRemote {
14161425
theirP2WSH, theirWitnessScript, err := genHtlcScript(
14171426
lc.channelState.ChanType, false, false,
@@ -2168,7 +2177,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
21682177
// If the HTLC is dust, then we'll skip it as it doesn't have
21692178
// an output on the commitment transaction.
21702179
if htlcIsDust(
2171-
htlc.Incoming, false,
2180+
chanState.ChanType, htlc.Incoming, false,
21722181
chainfee.SatPerKWeight(revokedSnapshot.FeePerKw),
21732182
htlc.Amt.ToSatoshis(), chanState.RemoteChanCfg.DustLimit,
21742183
) {
@@ -2239,25 +2248,14 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
22392248
}, nil
22402249
}
22412250

2242-
// htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
2243-
// transaction based on the current fee rate.
2244-
func htlcTimeoutFee(feePerKw chainfee.SatPerKWeight) btcutil.Amount {
2245-
return feePerKw.FeeForWeight(input.HtlcTimeoutWeight)
2246-
}
2247-
2248-
// htlcSuccessFee returns the fee in satoshis required for an HTLC success
2249-
// transaction based on the current fee rate.
2250-
func htlcSuccessFee(feePerKw chainfee.SatPerKWeight) btcutil.Amount {
2251-
return feePerKw.FeeForWeight(input.HtlcSuccessWeight)
2252-
}
2253-
22542251
// htlcIsDust determines if an HTLC output is dust or not depending on two
22552252
// bits: if the HTLC is incoming and if the HTLC will be placed on our
22562253
// commitment transaction, or theirs. These two pieces of information are
22572254
// require as we currently used second-level HTLC transactions as off-chain
22582255
// covenants. Depending on the two bits, we'll either be using a timeout or
22592256
// success transaction which have different weights.
2260-
func htlcIsDust(incoming, ourCommit bool, feePerKw chainfee.SatPerKWeight,
2257+
func htlcIsDust(chanType channeldb.ChannelType,
2258+
incoming, ourCommit bool, feePerKw chainfee.SatPerKWeight,
22612259
htlcAmt, dustLimit btcutil.Amount) bool {
22622260

22632261
// First we'll determine the fee required for this HTLC based on if this is
@@ -2269,25 +2267,25 @@ func htlcIsDust(incoming, ourCommit bool, feePerKw chainfee.SatPerKWeight,
22692267
// If this is an incoming HTLC on our commitment transaction, then the
22702268
// second-level transaction will be a success transaction.
22712269
case incoming && ourCommit:
2272-
htlcFee = htlcSuccessFee(feePerKw)
2270+
htlcFee = HtlcSuccessFee(chanType, feePerKw)
22732271

22742272
// If this is an incoming HTLC on their commitment transaction, then
22752273
// we'll be using a second-level timeout transaction as they've added
22762274
// this HTLC.
22772275
case incoming && !ourCommit:
2278-
htlcFee = htlcTimeoutFee(feePerKw)
2276+
htlcFee = HtlcTimeoutFee(chanType, feePerKw)
22792277

22802278
// If this is an outgoing HTLC on our commitment transaction, then
22812279
// we'll be using a timeout transaction as we're the sender of the
22822280
// HTLC.
22832281
case !incoming && ourCommit:
2284-
htlcFee = htlcTimeoutFee(feePerKw)
2282+
htlcFee = HtlcTimeoutFee(chanType, feePerKw)
22852283

22862284
// If this is an outgoing HTLC on their commitment transaction, then
22872285
// we'll be using an HTLC success transaction as they're the receiver
22882286
// of this HTLC.
22892287
case !incoming && !ourCommit:
2290-
htlcFee = htlcSuccessFee(feePerKw)
2288+
htlcFee = HtlcSuccessFee(chanType, feePerKw)
22912289
}
22922290

22932291
return (htlcAmt - htlcFee) < dustLimit
@@ -2431,7 +2429,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
24312429

24322430
// Finally, we'll populate all the HTLC indexes so we can track the
24332431
// locations of each HTLC in the commitment state.
2434-
if err := c.populateHtlcIndexes(); err != nil {
2432+
if err := c.populateHtlcIndexes(lc.channelState.ChanType); err != nil {
24352433
return nil, err
24362434
}
24372435

@@ -2769,8 +2767,10 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
27692767
// dust output after taking into account second-level HTLC fees, then a
27702768
// sigJob will be generated and appended to the current batch.
27712769
for _, htlc := range remoteCommitView.incomingHTLCs {
2772-
if htlcIsDust(true, false, feePerKw, htlc.Amount.ToSatoshis(),
2773-
dustLimit) {
2770+
if htlcIsDust(
2771+
chanType, true, false, feePerKw,
2772+
htlc.Amount.ToSatoshis(), dustLimit,
2773+
) {
27742774
continue
27752775
}
27762776

@@ -2785,7 +2785,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
27852785
// HTLC timeout transaction for them. The output of the timeout
27862786
// transaction needs to account for fees, so we'll compute the
27872787
// required fee and output now.
2788-
htlcFee := htlcTimeoutFee(feePerKw)
2788+
htlcFee := HtlcTimeoutFee(chanType, feePerKw)
27892789
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
27902790

27912791
// With the fee calculate, we can properly create the HTLC
@@ -2822,8 +2822,10 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
28222822
sigBatch = append(sigBatch, sigJob)
28232823
}
28242824
for _, htlc := range remoteCommitView.outgoingHTLCs {
2825-
if htlcIsDust(false, false, feePerKw, htlc.Amount.ToSatoshis(),
2826-
dustLimit) {
2825+
if htlcIsDust(
2826+
chanType, false, false, feePerKw,
2827+
htlc.Amount.ToSatoshis(), dustLimit,
2828+
) {
28272829
continue
28282830
}
28292831

@@ -2836,7 +2838,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
28362838
// HTLC success transaction for them. The output of the timeout
28372839
// transaction needs to account for fees, so we'll compute the
28382840
// required fee and output now.
2839-
htlcFee := htlcSuccessFee(feePerKw)
2841+
htlcFee := HtlcSuccessFee(chanType, feePerKw)
28402842
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
28412843

28422844
// With the proper output amount calculated, we can now
@@ -3785,16 +3787,20 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
37853787
// weight, needed to calculate the transaction fee.
37863788
var totalHtlcWeight int64
37873789
for _, htlc := range filteredHTLCView.ourUpdates {
3788-
if htlcIsDust(remoteChain, !remoteChain, feePerKw,
3789-
htlc.Amount.ToSatoshis(), dustLimit) {
3790+
if htlcIsDust(
3791+
lc.channelState.ChanType, remoteChain, !remoteChain,
3792+
feePerKw, htlc.Amount.ToSatoshis(), dustLimit,
3793+
) {
37903794
continue
37913795
}
37923796

37933797
totalHtlcWeight += input.HTLCWeight
37943798
}
37953799
for _, htlc := range filteredHTLCView.theirUpdates {
3796-
if htlcIsDust(!remoteChain, !remoteChain, feePerKw,
3797-
htlc.Amount.ToSatoshis(), dustLimit) {
3800+
if htlcIsDust(
3801+
lc.channelState.ChanType, !remoteChain, !remoteChain,
3802+
feePerKw, htlc.Amount.ToSatoshis(), dustLimit,
3803+
) {
37983804
continue
37993805
}
38003806

@@ -3857,7 +3863,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
38573863
Index: uint32(htlc.localOutputIndex),
38583864
}
38593865

3860-
htlcFee := htlcSuccessFee(feePerKw)
3866+
htlcFee := HtlcSuccessFee(chanType, feePerKw)
38613867
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
38623868

38633869
successTx, err := createHtlcSuccessTx(
@@ -3911,7 +3917,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
39113917
Index: uint32(htlc.localOutputIndex),
39123918
}
39133919

3914-
htlcFee := htlcTimeoutFee(feePerKw)
3920+
htlcFee := HtlcTimeoutFee(chanType, feePerKw)
39153921
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
39163922

39173923
timeoutTx, err := createHtlcTimeoutTx(
@@ -5383,7 +5389,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
53835389
// In order to properly reconstruct the HTLC transaction, we'll need to
53845390
// re-calculate the fee required at this state, so we can add the
53855391
// correct output value amount to the transaction.
5386-
htlcFee := htlcTimeoutFee(feePerKw)
5392+
htlcFee := HtlcTimeoutFee(chanType, feePerKw)
53875393
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
53885394

53895395
// With the fee calculated, re-construct the second level timeout
@@ -5513,7 +5519,7 @@ func newIncomingHtlcResolution(signer input.Signer,
55135519

55145520
// First, we'll reconstruct the original HTLC success transaction,
55155521
// taking into account the fee rate used.
5516-
htlcFee := htlcSuccessFee(feePerKw)
5522+
htlcFee := HtlcSuccessFee(chanType, feePerKw)
55175523
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
55185524
successTx, err := createHtlcSuccessTx(
55195525
chanType, op, secondLevelOutputAmt, csvDelay,
@@ -5637,8 +5643,10 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool,
56375643
// We'll skip any HTLC's which were dust on the commitment
56385644
// transaction, as these don't have a corresponding output
56395645
// within the commitment transaction.
5640-
if htlcIsDust(htlc.Incoming, ourCommit, feePerKw,
5641-
htlc.Amt.ToSatoshis(), dustLimit) {
5646+
if htlcIsDust(
5647+
chanType, htlc.Incoming, ourCommit, feePerKw,
5648+
htlc.Amt.ToSatoshis(), dustLimit,
5649+
) {
56425650
continue
56435651
}
56445652

@@ -6141,7 +6149,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView,
61416149
// For an extra HTLC fee to be paid on our commitment, the HTLC must be
61426150
// large enough to make a non-dust HTLC timeout transaction.
61436151
htlcFee := lnwire.NewMSatFromSatoshis(
6144-
htlcTimeoutFee(feePerKw),
6152+
HtlcTimeoutFee(lc.channelState.ChanType, feePerKw),
61456153
)
61466154

61476155
// If we are looking at the remote commitment, we must use the remote
@@ -6151,7 +6159,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView,
61516159
lc.channelState.RemoteChanCfg.DustLimit,
61526160
)
61536161
htlcFee = lnwire.NewMSatFromSatoshis(
6154-
htlcSuccessFee(feePerKw),
6162+
HtlcSuccessFee(lc.channelState.ChanType, feePerKw),
61556163
)
61566164
}
61576165

lnwallet/channel_test.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,8 @@ func TestHTLCDustLimit(t *testing.T) {
10111011

10121012
// The amount of the HTLC should be above Alice's dust limit and below
10131013
// Bob's dust limit.
1014-
htlcSat := (btcutil.Amount(500) + htlcTimeoutFee(
1014+
htlcSat := (btcutil.Amount(500) + HtlcTimeoutFee(
1015+
aliceChannel.channelState.ChanType,
10151016
chainfee.SatPerKWeight(
10161017
aliceChannel.channelState.LocalCommitment.FeePerKw,
10171018
),
@@ -1119,8 +1120,12 @@ func TestHTLCSigNumber(t *testing.T) {
11191120
t.Fatalf("unable to get fee: %v", err)
11201121
}
11211122

1122-
belowDust := btcutil.Amount(500) + htlcTimeoutFee(feePerKw)
1123-
aboveDust := btcutil.Amount(1400) + htlcSuccessFee(feePerKw)
1123+
belowDust := btcutil.Amount(500) + HtlcTimeoutFee(
1124+
channeldb.SingleFunderTweaklessBit, feePerKw,
1125+
)
1126+
aboveDust := btcutil.Amount(1400) + HtlcSuccessFee(
1127+
channeldb.SingleFunderTweaklessBit, feePerKw,
1128+
)
11241129

11251130
// ===================================================================
11261131
// Test that Bob will reject a commitment if Alice doesn't send enough
@@ -1278,7 +1283,8 @@ func TestChannelBalanceDustLimit(t *testing.T) {
12781283
defaultFee := calcStaticFee(1)
12791284
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis()
12801285
htlcSat := aliceBalance - defaultFee
1281-
htlcSat += htlcSuccessFee(
1286+
htlcSat += HtlcSuccessFee(
1287+
aliceChannel.channelState.ChanType,
12821288
chainfee.SatPerKWeight(
12831289
aliceChannel.channelState.LocalCommitment.FeePerKw,
12841290
),
@@ -4759,10 +4765,10 @@ func TestChanAvailableBalanceNearHtlcFee(t *testing.T) {
47594765
aliceChannel.channelState.LocalCommitment.CommitFee,
47604766
)
47614767
htlcTimeoutFee := lnwire.NewMSatFromSatoshis(
4762-
htlcTimeoutFee(feeRate),
4768+
HtlcTimeoutFee(aliceChannel.channelState.ChanType, feeRate),
47634769
)
47644770
htlcSuccessFee := lnwire.NewMSatFromSatoshis(
4765-
htlcSuccessFee(feeRate),
4771+
HtlcSuccessFee(aliceChannel.channelState.ChanType, feeRate),
47664772
)
47674773

47684774
// Helper method to check the current reported balance.
@@ -6273,7 +6279,8 @@ func TestChanReserveLocalInitiatorDustHtlc(t *testing.T) {
62736279
// limit (1300 sat). It is considered dust if the amount remaining
62746280
// after paying the HTLC fee is below the dustlimit, so we choose a
62756281
// size of 500+htlcFee.
6276-
htlcSat := btcutil.Amount(500) + htlcTimeoutFee(
6282+
htlcSat := btcutil.Amount(500) + HtlcTimeoutFee(
6283+
aliceChannel.channelState.ChanType,
62776284
chainfee.SatPerKWeight(
62786285
aliceChannel.channelState.LocalCommitment.FeePerKw,
62796286
),

0 commit comments

Comments
 (0)