bolt5

On-chain transaction handling for Lightning (docs.ppad.tech/bolt5).
git clone git://git.ppad.tech/bolt5.git
Log | Files | Refs | README | LICENSE

commit e56a951334d53d70c5f5b3059e6efb995456b757
parent e8e8396aa150a6f8607ab00a7379a744646f2b6c
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 15:30:03 +0400

docs: readme and changelog for 0.1.0

Diffstat:
MCHANGELOG | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MREADME.md | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
2 files changed, 166 insertions(+), 45 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,4 +1,60 @@ # Changelog -- 0.0.1 (UNRELEASED) - * Initial release +- 0.1.0 (UNRELEASED) + * The library's scope is being redesigned, and this version is not + yet ready for release (see the README for what it does and + doesn't cover). + + * Ports to ppad-bolt1 0.1.0, ppad-bolt3 0.1.0 and ppad-tx 0.2.0, with + breaking changes: + + * Amounts, points and hashes are bolt1's types (amounts are + bounded, with checked arithmetic), and ChannelFeatures is + replaced by bolt3's CommitmentFormat. + + * The classifiers read each HTLC output's HTLC from bolt3's + OutputHTLC, and no longer take an HTLC list. + classify_remote_commit_outputs no longer takes the channel + features, and classify_revoked_commit_outputs takes only the + commitment and the revocation pubkey. + + * spend_htlc_tx replaces spend_htlc_timeout and spend_htlc_success, + building the transaction with bolt3's build_htlc_tx, and fails + if bolt3 can't build it. + + * The API is exported from Lightning.Protocol.BOLT5 alone, with + snake_case names (htlc_output_type, htlc_output_type_weight); the + Internal module is removed. Every public type has an NFData + instance. + + * extract_preimage_offered and extract_preimage_htlc_success take + the HTLC's payment hash, and revoked_output_weight and + htlc_output_type_weight the commitment format. + + * spending_fee saturates at 21 million BTC. + + * Fixes preimage extraction, which never matched a real witness (one + ending in the witness script), and checks preimages against the + payment hash. + + * Fixes identify_close, which compared an unsigned serialization with + the signed transaction found on chain; it compares txids. + + * Fixes HTLC output classification, which matched outputs to HTLCs by + script alone, so that HTLCs sharing a payment hash were confused. + + * Fixes the sighash type of the local signature on anchor-channel + HTLC transactions, which is SIGHASH_ALL. + + * Sweeps no longer create outputs below the destination's dust + threshold, and anchor-channel HTLC penalty sweeps use the anchor + scripts' weights. + + * Removes spend_revoked_batch and PenaltyContext, whose transaction + couldn't be signed. + + * Tests check HTLC transactions and preimage extraction against the + BOLT #3 Appendix C and F vectors. + +- 0.0.1 (2026-04-18) + * Initial release. diff --git a/README.md b/README.md @@ -4,9 +4,50 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen) [![](https://img.shields.io/badge/haddock-bolt5-lightblue)](https://docs.ppad.tech/bolt5) -A pure Haskell implementation of [BOLT #5][bolt5] (Lightning Network -on-chain transaction handling), including logic for mutual close, -unilateral close, and revoked transaction close scenarios. +A pure Haskell implementation of parts of [BOLT #5][bolt5], the +Lightning Network's rules for handling channel closes on chain. + +## Status + +**This library's scope is being redesigned, and it is not yet ready for +release.** Its API will change. + +What it does: + +* Identifies our local or the remote's current commitment on chain, by + txid. +* Classifies the outputs of local, remote and revoked commitment + transactions built by [ppad-bolt3][bolt3], reading each HTLC output's + HTLC from bolt3. +* Extracts payment preimages from the witnesses of remote preimage + claims and HTLC-success transactions, checking them against the + payment hash. +* Builds unsigned, single-input spending transactions: to_local sweeps + (delayed and revoked), HTLC-timeout and HTLC-success transactions + (via bolt3), direct HTLC spends from the remote commitment, revoked + HTLC sweeps, second-stage output sweeps and anchor spends. Sweeps + refuse to create outputs below the destination's dust threshold. +* Provides the BOLT #5 Appendix A weights and fee calculation. + +What it doesn't do (known gaps): + +* Per-input signing data. A spending transaction describes one input + and the local signature only; there is no batched penalty + transaction. +* Witness assembly. The caller signs and builds every witness with + bolt3's witness functions. +* Classification of raw on-chain transactions. The classifiers take the + bolt3 commitment transaction for the state in question, which the + caller must rebuild (often impossible for a revoked state). +* Revoked and mutual close detection. Only the current local and + remote commitments are recognised, and the resolutions for a revoked + commitment lack what's needed to build its spends (to_self_delay, + delayed pubkey, payment hashes). +* Fee bumping. Anchor spends and anchor-channel HTLC transactions pay + no fee and can't take fee inputs, and penalty sweeps don't signal + replace-by-fee. +* `zero_fee_commitments` (version 3 transactions and the shared P2A + anchor), and spending the anchor-channel to_remote output. ## Usage @@ -14,56 +55,79 @@ A sample GHCi session: ``` > :set -XOverloadedStrings - > + > import qualified Bitcoin.Prim.Tx as BT + > import qualified Crypto.Hash.SHA256 as SHA256 > import qualified Data.ByteString as BS - > import qualified Lightning.Protocol.BOLT3 as BOLT3 - > import qualified Lightning.Protocol.BOLT5 as BOLT5 + > import qualified Lightning.Protocol.BOLT1 as BOLT1 + > import Lightning.Protocol.BOLT3 + > import Lightning.Protocol.BOLT5 > - > -- fee calculation using BOLT #5 Appendix A weight constants - > let feerate = BOLT3.FeeratePerKw 5000 - > BOLT5.spending_fee feerate BOLT5.to_local_penalty_input_weight - Satoshi 1620 - > BOLT5.spending_fee feerate BOLT5.penalty_tx_base_weight - Satoshi 1070 + > -- fees from the BOLT #5 Appendix A weights + > let feerate = FeeratePerKw 5000 + > let weight = to_local_penalty_input_weight + penalty_tx_base_weight + > spending_fee feerate weight + Satoshi 2690 > - > -- check if an HTLC has timed out at the current block height - > let htlc = BOLT3.HTLC BOLT3.HTLCOffered (BOLT3.MilliSatoshi 50000000) - > (BOLT3.PaymentHash (BS.replicate 32 0xAA)) - > (BOLT3.CltvExpiry 800000) - > BOLT5.htlc_timed_out 799999 htlc + > -- whether an HTLC has timed out at a given block height + > let Just amt = BOLT1.milli_satoshi 50000000 + > let Just hash = BOLT1.payment_hash (BS.replicate 32 0xaa) + > let htlc = HTLC HTLCOffered amt hash (CltvExpiry 800000) + > htlc_timed_out 799999 htlc False - > BOLT5.htlc_timed_out 800000 htlc + > htlc_timed_out 800000 htlc True > - > -- extract a payment preimage from an offered HTLC witness - > let preimage = BS.replicate 32 0xBB - > let sig = BS.replicate 71 0xCC - > let wit = BOLT3.Witness [sig, preimage] - > BOLT5.extract_preimage_offered wit - Just PaymentPreimage <redacted> - > - > -- construct an unsigned spending tx for a to_local output - > let Just txid = BOLT3.mkTxId (BS.replicate 32 0x01) - > let outpoint = BOLT3.OutPoint txid 0 - > let value = BOLT3.Satoshi 1000000 - > let revpk = BOLT3.RevocationPubkey (BOLT3.Pubkey (BS.replicate 33 0x02)) - > let delay = BOLT3.ToSelfDelay 144 - > let delayedpk = BOLT3.LocalDelayedPubkey (BOLT3.Pubkey (BS.replicate 33 0x03)) - > let dest = BOLT3.Script (BS.replicate 34 0x00) - > - > let stx = BOLT5.spend_to_local outpoint value revpk delay - > delayedpk dest feerate - > BOLT5.stx_sighash_type stx + > -- an unsigned sweep of our to_local output, valid after the delay + > let key b = BOLT1.point (BS.cons 0x02 (BS.replicate 32 b)) + > let Just revpk = RevocationPubkey <$> key 0x01 + > let Just delayedpk = LocalDelayedPubkey <$> key 0x02 + > let Just txid = BT.mkTxId (BS.replicate 32 0x01) + > let Just value = BOLT1.satoshi 1000000 + > let dest = Script ("\x00\x14" <> BS.replicate 20 0xcc) + > let delay = ToSelfDelay 144 + > let op = BT.OutPoint txid 0 + > let Just stx = spend_to_local op value revpk delay delayedpk dest feerate + > fmap BT.txout_value (BT.tx_outputs (stx_tx stx)) + 997310 :| [] + > fmap BT.txin_sequence (BT.tx_inputs (stx_tx stx)) + 144 :| [] + > stx_sighash_type stx SIGHASH_ALL + > stx_input_script stx == to_local_script revpk delay delayedpk + True + > + > -- extract the preimage from the remote's claim of an offered HTLC + > let Just preimage = BOLT1.payment_preimage (BS.replicate 32 0xbb) + > let Just ph = BOLT1.payment_hash (SHA256.hash (BS.replicate 32 0xbb)) + > let sig = BS.replicate 71 0xcc + > let wit = offered_htlc_witness_preimage sig preimage (Script "") + > extract_preimage_offered ph wit + Just (PaymentPreimage <redacted>) + > fmap (== preimage) (extract_preimage_offered ph wit) + Just True + > extract_preimage_offered hash wit + Nothing ``` ## Documentation -Haddocks are hosted at [docs.ppad.tech/bolt5][hadoc]. +Haddocks (API documentation, etc.) are hosted at +[docs.ppad.tech/bolt5](https://docs.ppad.tech/bolt5). + +## Specification + +This library tracks BOLT #5 as of lightning/bolts commit +[1aadb719][spec] (2026-09-21); a copy lives in `etc/`. BOLT #5 +publishes no test vectors beyond Appendix A's weights, which are +checked; the tests also check HTLC transactions and preimage extraction +against BOLT #3's Appendix C and F vectors. + +See [Status](#status) for what isn't implemented. ## Security -This is a pre-release and makes no claims about security whatsoever. +This library has received no security audit. It handles no secret +material: it builds unsigned transactions and reads public witnesses. ## Development @@ -77,12 +141,13 @@ $ nix develop Then do e.g.: ``` -$ cabal build -$ cabal test -$ cabal bench +$ cabal repl ppad-bolt5 ``` +to get a REPL for the main library. + [bolt5]: https://github.com/lightning/bolts/blob/master/05-onchain.md +[bolt3]: https://git.ppad.tech/bolt3 +[spec]: https://github.com/lightning/bolts/tree/1aadb719b4007c4cea0ba6e36b08c4fb53788dee [nixos]: https://nixos.org/ [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html -[hadoc]: https://docs.ppad.tech/bolt5