Repository navigation
lightningd: count the witness stack element when sizing onchain spends - #9662
Open
davelund110 wants to merge 1 commit into
Open
davelund110 wants to merge 1 commit into
davelund110 wants to merge 1 commit into
Conversation
onchaind_tx_unsigned() estimates a spend's weight as the unsigned
transaction plus a worst-case signature and the witness script, but
leaves out the stack element between them: the revocation pubkey of a
penalty for an HTLC output (33 bytes), or the preimage of an HTLC
fulfill (32 bytes). The fee then falls a few percent short of the
feerate it was meant to pay. Usually that only costs a little time, but
when every estimate is at the floor (253 sat/kw, just above a minimum
relay fee of 1 sat/vbyte) the transaction pays less than the minimum
relay fee, and bitcoind refuses it each time it is rebroadcast:
min relay fee not met, 148 < 155
For an HTLC penalty, the cheater's HTLC-timeout or HTLC-success
transaction can then confirm first, and we have to penalise its output
instead. For a fulfill, the peer can time the HTLC out.
test_penalty_htlc_min_feerate is test_penalty_inhtlc with every feerate
at the floor: without this change its HTLC penalty never gets into the
mempool.
Changelog-Fixed: Onchain spends with a revocation pubkey or preimage in their witness (HTLC penalties and fulfills) no longer pay less than the minimum relay fee when feerate estimates are at the floor.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
onchaind_tx_unsigned()sizes an onchain spend before signing it, to pick its fee. The estimate covers the unsigned transaction, a worst-case signature and the witness script, but not the stack element between the signature and the script:The fee then comes out about 5% short of the feerate it was meant to pay. This adds the element's length to the estimate: one line, plus a test.
Why it matters
The estimate is 30 to 31 weight units short (the missing element, less the 2 bytes the 73-byte worst-case signature over-counts, since our signatures are low-R), about 5%. Whenever the feerate chosen is the floor, that puts the transaction below the minimum relay fee, and bitcoind refuses it every time it is rebroadcast:
That happens when estimates sit at the floor, and also by design when the deadline is 101 to 200 blocks away, where
feerate_for_target()usesget_feerate_floor()whatever the estimates say: an HTLC penalty's deadline isto_self_delay(144 by default) after the close, so its first ~44 broadcasts fall in that window. With a full mempool, the same shortfall can also fall undermempoolminfee.This is probably what #2530 ran into in 2019: an HTLC fulfill refused with
min relay fee not met, 136 < 143, about 5% short, closed this year as stale without a cause found. The same line was inonchaind/onchaind.cthen, and moved tolightningd/onchain_control.cin 2023.How it was found
Testing penalties against lnd on a regtest with no fee estimates, so every feerate sat at the floor. CLN's penalty for an HTLC output was refused at every block until the cheater's HTLC-timeout confirmed and CLN penalised that instead. The refused transaction had a witness of 71 + 33 + 136 bytes, 622 weight units against the roughly 590 its fee was computed for.
Tests
test_penalty_htlc_min_feerate(with and without anchors):test_penalty_inhtlcwith every feerate at the floor. Without the change, the HTLC penalty is refused (min relay fee not met, 148 < 155); with it, both penalties are mined.test_coinmoves.py: three expected amounts change with the new fees (the fulfill, the HTLC penalty, and the to-local penalty, whose0x01element is now counted too).check-units; the new test, all oftest_coinmoves.py, and the penalty, onchain and HTLC tests intest_closing.py(58 passed, 1 skipped); andtest_bookkeeper.py, apart from the two report tests that need the Rustcurrencyrateplugin, which wasn't built here.Spends whose element is
0x01were already covered by the slack in the 73-byte signature; they now count that byte too, so their fee rises by a few sat. Spends with no element are unaffected.The same fix is merged in a downstream fork, privkeyio#49.
Checklist
tools/lightning-downgrade(no persistent changes).