Implement option_htlcs_claim_tx#1
Open
darosior wants to merge 4 commits into
Open
Conversation
97c81b9 to
d8a6a03
Compare
Implements the experimental `option_htlcs_claim_tx` channel type (feature bits 110/111), which builds on `option_zero_fee_commitments` to close the last on-chain pinning gap. Offered HTLC outputs become P2TR outputs whose preimage (`htlc_success`) spend path commits, via `OP_TEMPLATEHASH`, to a fixed v3 "HTLC claim transaction". - Add the `OptionHTLCsClaimTx` feature, its `ChannelTypeFeatures` accessors, channel-type negotiation, and the `negotiate_htlcs_claim_tx` config flag (depends on `negotiate_anchor_zero_fee_commitments`). - Build the offered HTLC commitment output as a P2TR when the feature applies. - The offered-HTLC `htlc_success` leaf uses the real `OP_TEMPLATEHASH` (0xce). As it pushes the computed hash, the leaf now ends with `<template hash> OP_TEMPLATEHASH OP_EQUAL`. - Resolve such outputs on-chain by broadcasting the fixed claim transaction as an untractable, non-aggregated package, reusing the zero-fee-commitment broadcast path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The `option_htlcs_claim_tx` HTLC claim transaction is template-committed (a single input and a single output) and pays zero fees, so it cannot be modified and can only be relayed and confirmed alongside a fee-paying child (a TRUC 1-parent-1-child package). Wire this up, modelled on how zero-fee commitment transactions are fee-bumped: - Add `EcdsaChannelSigner::sign_htlcs_claim_transaction_input`, which signs the child's input spending the claim transaction's output (a P2WPKH to our payment point). `InMemorySigner` reuses the existing `sign_counterparty_payment_input` path. - `OnchainTxHandler` now reports templated offered HTLC outputs as requiring external funding and yields a new `ClaimEvent::BumpHTLCsClaimTx` carrying the signed claim transaction, surfaced to the user as a new `BumpTransactionEvent::HTLCsClaimTxResolution`. - The default `BumpTransactionEventHandler` constructs a version 3 child spending the claim output plus coin-selected inputs, signs it, and broadcasts `[claim_tx, child]` as a package. Also fix `verify_channel_type_features` to accept `htlcs_claim_tx` as a supported channel type feature, without which a `ChannelMonitor` for such a channel failed to round-trip through serialization. Add a handler unit test and an end-to-end functional test exercising the counterparty-offered-HTLC on-chain resolution path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Test vectors from bolt03/htlcs-claim-tx-test.json, verifying the full
option_htlcs_claim_tx output structure:
1. Commitment tx with a single offered HTLC (P2TR output): checks
the signed commitment tx, htlc_timeout_script, htlc_success_script
(with embedded OP_TEMPLATEHASH digest), template_hash, unsigned
HTLC claim tx, and the claim tx with the tapscript witness.
2. Same but with millisatoshi truncation (25000821 msat → 25000 sat
in the claim tx output); template_hash is identical to case 1
because OP_TEMPLATEHASH commits to the satoshi output value.
3. Mixed commitment: one offered (P2TR) + one received (P2WSH, unchanged).
Run with `RUSTFLAGS="--cfg ldk_test_vectors" cargo test htlcs_claim_tx_test_vectors`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The `option_htlcs_claim_tx` offered-HTLC preimage spend is a P2TR script-path spend whose witness is `[preimage, htlc_success_script, control_block]`, unlike the legacy P2WSH HTLC witnesses that `HTLCClaim::from_witness` knew how to parse. As a result the offering (or forwarding) node's `ChannelMonitor` failed to classify the claim transaction and never extracted the preimage, so the corresponding outbound HTLC could not be resolved on-chain. Teach `HTLCClaim::from_witness` to recognize the templated witness as an `OfferedPreimage` claim (via a control-block size modular check plus a leaf script ending in `OP_TEMPLATEHASH OP_EQUAL`), and read the preimage from the correct witness element during monitor claim processing. This work was done with the assistance of Claude Code (Claude Opus 4.8). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d8a6a03 to
2b4711a
Compare
option_htlcs_claim_tx
This was referenced Jul 1, 2026
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.
This is an unreviewed vibecoded implementation of bip448/bolts#2 against rust-lightning's latest release branch. An end-to-end integration test based on this branch is added to LDK-node here: bip448/ldk-node#1.