Repository navigation
lightningd: count the witness stack element when sizing onchain spends - #49
Conversation
kwsantiago
left a comment
There was a problem hiding this comment.
The coinmoves tests hardcode the old fees: fulfill 94534000→94238000, penalty 92908000→92512000 and 49858187000→49858175000 msat; all pass then.
db76cec to
addaee3
Compare
|
Thanks, updated with your values and folded into the commit (addaee3). All 10 coinmoves tests pass. The closing and bookkeeping tests pass too, except the two report tests that need live currency rates. |
|
Could you rebase onto blake2b-unified? #55 fixed the runner OOM that keeps failing ASan/UBSan 2/8 here. |
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.
addaee3 to
3d1200a
Compare
|
Rebased onto blake2b-unified (3d1200a), no conflicts. test_coinmoves, test_penalty_htlc_min_feerate and test_wallet pass locally (74 passed). |
- The cln-* scenarios check that CLN's hsmd really sends independent secrets: lnd keeps any secret, so they'd pass without it. - lnd-cheats-htlc says when CLN only took the HTLC through lnd's HTLC-timeout, as it must without privkeyio/lightning#49, and INTEROP_STRICT_HTLC_PENALTY=1 requires the direct penalty instead. - A scenario whose nodes fail to start stops what did start, instead of leaving bitcoind on the fixed ports for the next one. - lnd no longer gets a REST port it doesn't use, and the README's account of how breaches are made is corrected: CLN's needs its --developer option, lnd's any build. - Results with CLN at d3e988f.
|
Merged, thanks. Worth opening upstream too, since it's the same line there. |
|
Thanks Kyle. Opened upstream as ElementsProject#9662, tested on their master with a standard regtest: the new test fails there without the change and passes with it. |
What
onchaind_tx_unsigned()sizes an onchain spend before signing it, so it can pick the fee. Its estimate covers the unsigned transaction, a worst-case signature and the witness script. It leaves out the stack element that sits between the signature and the script:The fee then comes out about 5% short of the feerate it was meant to pay. This PR adds the element's length to the estimate. It's 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 signatures are ground to low-R), about 5%. Whenever the feerate chosen is bitcoind's own minimum, that 5% puts the transaction below it, and bitcoind refuses it every time it is rebroadcast. That happens:
feerate_for_target()usesget_feerate_floor()by design, whatever the estimates say. An HTLC penalty's deadline isto_self_delay(144 by default) after the close, so its first ~44 broadcasts always fall in this window;mempoolminfee(the error is then "mempool min fee not met").On our regtest:
Upstream Core Lightning has the same line (
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(info->wscript);inlightningd/onchain_control.c), so this is worth sending there too.How it was found
Running Lightning Fork (lnd) against this fork on a Bitcoin Knots BLAKE2b regtest, which has no fee estimates, so everything sits at the floor. lnd broadcast a revoked commitment with an HTLC on it. CLN's penalty for the HTLC was refused at every block, until lnd's HTLC-timeout confirmed and CLN penalised that instead. Decoding the refused transaction showed a witness of 71 + 33 + 136 bytes, 622 weight units against the roughly 590 the fee was computed for.
Tests
test_penalty_htlc_min_feerate(anchors and not):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_penalty_inhtlc,test_penalty_outhtlc,test_penalty_htlc_tx_fulfillandtest_penalty_htlc_tx_timeout(both variants each) still pass.test_coinmoves.py, as the review pointed out: three expected amounts now reflect the new fees (the fulfill, the HTLC penalty, and the to-local penalty, whose0x01element is now counted too). The commit is amended with them, and all 10 tests pass.test_closing.py, andtest_bookkeeper.py, pass with the fix (66 passed), except the twotest_bookkeeper.pyreport tests that need currency rates from the network, which fail the same way without it.blake2b-unifiedat 921d739 (including the runner OOM fix and bitcoin: count the witness script when weighing an anchors to_remote input #57's to_remote anchor weight fix) without conflicts. There,test_coinmoves.py,test_penalty_htlc_min_feerateandtest_wallet.pypass (74 passed).This is independent of
option_independent_secretsand applies on its own to the fork's base. A review checked the arithmetic for every case (penalties on offered and received HTLCs, with and without anchors, and fulfills): all fall 7 to 8 sat short of the minimum at 1 sat/vbyte, and all clear it with the fix. Spends whose element is0x01were 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. We found no existing issue or PR about this upstream.