@@ -1256,19 +1256,28 @@ func (c *ChannelArbitrator) stateStep(
12561256 return StateError , closeTx , err
12571257 }
12581258
1259- // If we can fail an HTLC immediately ( an outgoing HTLC
1260- // with no contract and it was not canceled before),
1261- // then we'll assemble an HTLC fail packet to send .
1259+ // We can fail the upstream HTLC for an outgoing
1260+ // dangling and dust HTLC because we can be sure they
1261+ // will not be resolved onchain .
12621262 getIdx := func (htlc channeldb.HTLC ) uint64 {
12631263 return htlc .HtlcIndex
12641264 }
12651265 remoteDangling := fn .NewSet (fn .Map (
1266- getIdx , htlcActions [HtlcFailNowAction ],
1266+ getIdx , htlcActions [HtlcFailDanglingAction ],
12671267 )... )
12681268 err := c .abandonForwards (remoteDangling )
12691269 if err != nil {
12701270 return StateError , closeTx , err
12711271 }
1272+
1273+ dustHTLCs := fn .NewSet (fn .Map (
1274+ getIdx , htlcActions [HtlcFailDustAction ],
1275+ )... )
1276+
1277+ err = c .abandonForwards (dustHTLCs )
1278+ if err != nil {
1279+ return StateError , closeTx , err
1280+ }
12721281 }
12731282
12741283 // Now that we know we'll need to act, we'll process all the
@@ -1716,10 +1725,11 @@ const (
17161725 // before its timeout period.
17171726 HtlcClaimAction = 2
17181727
1719- // HtlcFailNowAction indicates that we should fail an outgoing HTLC
1720- // immediately by cancelling it backwards as it has no corresponding
1721- // output in our commitment transaction.
1722- HtlcFailNowAction = 3
1728+ // HtlcFailDustAction indicates that we should fail the upstream HTLC
1729+ // for an outgoing dust HTLC immediately (even before the commitment
1730+ // transaction is confirmed) because it has no output on the commitment
1731+ // transaction. This also includes remote pending outgoing dust HTLCs.
1732+ HtlcFailDustAction = 3
17231733
17241734 // HtlcOutgoingWatchAction indicates that we can't yet timeout this
17251735 // HTLC, but we had to go to chain on order to resolve an existing
@@ -1738,6 +1748,13 @@ const (
17381748 // HtlcIncomingDustFinalAction indicates that we should mark an incoming
17391749 // dust htlc as final because it can't be claimed on-chain.
17401750 HtlcIncomingDustFinalAction = 6
1751+
1752+ // HtlcFailDanglingAction indicates that we should fail the upstream
1753+ // HTLC for an outgoing HTLC immediately after the commitment
1754+ // transaction has confirmed because it has no corresponding output on
1755+ // the commitment transaction. This category does NOT include any dust
1756+ // HTLCs which are mapped in the "HtlcFailDustAction" category.
1757+ HtlcFailDanglingAction = 7
17411758)
17421759
17431760// String returns a human readable string describing a chain action.
@@ -1752,8 +1769,8 @@ func (c ChainAction) String() string {
17521769 case HtlcClaimAction :
17531770 return "HtlcClaimAction"
17541771
1755- case HtlcFailNowAction :
1756- return "HtlcFailNowAction "
1772+ case HtlcFailDustAction :
1773+ return "HtlcFailDustAction "
17571774
17581775 case HtlcOutgoingWatchAction :
17591776 return "HtlcOutgoingWatchAction"
@@ -1764,6 +1781,9 @@ func (c ChainAction) String() string {
17641781 case HtlcIncomingDustFinalAction :
17651782 return "HtlcIncomingDustFinalAction"
17661783
1784+ case HtlcFailDanglingAction :
1785+ return "HtlcFailDanglingAction"
1786+
17671787 default :
17681788 return "<unknown action>"
17691789 }
@@ -1944,8 +1964,8 @@ func (c *ChannelArbitrator) checkCommitChainActions(height uint32,
19441964 "failing dust htlc=%x" , c .cfg .ChanPoint ,
19451965 htlc .RHash [:])
19461966
1947- actionMap [HtlcFailNowAction ] = append (
1948- actionMap [HtlcFailNowAction ], htlc ,
1967+ actionMap [HtlcFailDustAction ] = append (
1968+ actionMap [HtlcFailDustAction ], htlc ,
19491969 )
19501970
19511971 // If we don't need to immediately act on this HTLC, then we'll
@@ -2138,12 +2158,30 @@ func (c *ChannelArbitrator) checkRemoteDanglingActions(
21382158 continue
21392159 }
21402160
2161+ // Dust htlcs can be canceled back even before the commitment
2162+ // transaction confirms. Dust htlcs are not enforceable onchain.
2163+ // If another version of the commit tx would confirm we either
2164+ // gain or lose those dust amounts but there is no other way
2165+ // than cancelling the incoming back because we will never learn
2166+ // the preimage.
2167+ if htlc .OutputIndex < 0 {
2168+ log .Infof ("ChannelArbitrator(%v): fail dangling dust " +
2169+ "htlc=%x from local/remote commitments diff" ,
2170+ c .cfg .ChanPoint , htlc .RHash [:])
2171+
2172+ actionMap [HtlcFailDustAction ] = append (
2173+ actionMap [HtlcFailDustAction ], htlc ,
2174+ )
2175+
2176+ continue
2177+ }
2178+
21412179 log .Infof ("ChannelArbitrator(%v): fail dangling htlc=%x from " +
21422180 "local/remote commitments diff" ,
21432181 c .cfg .ChanPoint , htlc .RHash [:])
21442182
2145- actionMap [HtlcFailNowAction ] = append (
2146- actionMap [HtlcFailNowAction ], htlc ,
2183+ actionMap [HtlcFailDanglingAction ] = append (
2184+ actionMap [HtlcFailDanglingAction ], htlc ,
21472185 )
21482186 }
21492187
@@ -2232,8 +2270,21 @@ func (c *ChannelArbitrator) checkRemoteDiffActions(
22322270 continue
22332271 }
22342272
2235- actionMap [HtlcFailNowAction ] = append (
2236- actionMap [HtlcFailNowAction ], htlc ,
2273+ // Dust HTLCs on the remote commitment can be failed back.
2274+ if htlc .OutputIndex < 0 {
2275+ log .Infof ("ChannelArbitrator(%v): fail dangling dust " +
2276+ "htlc=%x from remote commitments diff" ,
2277+ c .cfg .ChanPoint , htlc .RHash [:])
2278+
2279+ actionMap [HtlcFailDustAction ] = append (
2280+ actionMap [HtlcFailDustAction ], htlc ,
2281+ )
2282+
2283+ continue
2284+ }
2285+
2286+ actionMap [HtlcFailDanglingAction ] = append (
2287+ actionMap [HtlcFailDanglingAction ], htlc ,
22372288 )
22382289
22392290 log .Infof ("ChannelArbitrator(%v): fail dangling htlc=%x from " +
0 commit comments