commit f535438b3f8df596b234e36ac29a75e875f13e53
parent 8abc27c8bbdffa9ff72b3d6ac3471992b50e08e8
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 14:53:34 +0400
docs: readme and changelog for 0.1.0
Diffstat:
| M | CHANGELOG | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | README.md | | | 128 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- |
2 files changed, 167 insertions(+), 30 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -0,0 +1,69 @@
+# Changelog
+
+- 0.1.0 (UNRELEASED)
+ * A substantial rewrite, with breaking changes throughout:
+
+ * Ports to ppad-bolt1 0.1.0 and ppad-tx 0.2.0. Amounts, points,
+ hashes and per-commitment secrets are bolt1's types (amounts are
+ bounded, with checked arithmetic); bolt3's Pubkey is merged into
+ bolt1's Point, wrapped in role newtypes (RevocationPubkey, etc.).
+
+ * The API is exported from Lightning.Protocol.BOLT3 alone, with
+ snake_case names. Types with invariants (CommitmentNumber,
+ SecretIndex, Seed, Seckey, SecretStore) are abstract, and every
+ type has an NFData instance.
+
+ * ChannelFeatures is replaced by CommitmentFormat (StaticRemotekey
+ or Anchors).
+
+ * CommitmentContext is owner-relative throughout: the payment
+ basepoints are ordered for the obscured commitment number by
+ cc_is_funder. build_commitment_tx fails rather than produce a
+ transaction without outputs.
+
+ * OutputType carries the HTLC an output pays. build_htlc_tx replaces
+ build_htlc_timeout_tx and build_htlc_success_tx, choosing by HTLC
+ direction, and fails if the fee exceeds the amount.
+
+ * Closing transactions have separate builders with spec-shaped
+ inputs: build_legacy_closing_tx applies the signer's dust limit to
+ both outputs and can omit the signer's output; build_closing_tx
+ (option_simple_close) charges the closer, takes an explicit choice
+ of outputs, and gives OP_RETURN outputs amount zero.
+
+ * to_remote_script is split into to_remote_witness_script and
+ to_remote_script_pubkey. Witness functions append the witness
+ script and take typed keys; funding_witness orders signatures by
+ pubkey. Adds htlc_success_witness, htlc_timeout_witness and
+ remote_htlc_sighash.
+
+ * Per-commitment secrets use Seed, SecretIndex and bolt1's
+ PerCommitmentSecret. SecretStore is abstract, with a redacted
+ Show instance and secret_store/un_secret_store for persistence.
+ Adds commitment_secret_index.
+
+ * Adds derive_commitment_keys, derive_privkey and
+ derive_revocationprivkey.
+
+ * Renames encode_tx to encode_commitment_tx, and documents the
+ encoders as producing unsigned, witness-free serializations.
+
+ * Removes the Validate, Decode, Encode and Internal modules, the
+ role-specific key derivation functions, encode_tx_for_signing,
+ encode_witness, encode_funding_witness, has_anchors, sort_outputs,
+ trimmed_htlcs, untrimmed_htlcs, derive_secret, the HTLC output
+ script aliases, the weight constants and unused record fields.
+
+ * Fixes insert_secret, which discarded the entries needed to derive
+ older secrets and accepted invalid secret sequences.
+
+ * Fixes closing transactions, which used each party's dust limit in
+ legacy closes, and charged the funder and paid OP_RETURN outputs in
+ option_simple_close.
+
+ * Rejects key derivations yielding the point at infinity.
+
+ * Tests cover every BOLT #3 Appendix B-F vector.
+
+- 0.0.1 (2026-04-18)
+ * Initial release.
diff --git a/README.md b/README.md
@@ -4,10 +4,15 @@

[](https://docs.ppad.tech/bolt3)
-A pure Haskell implementation of [BOLT #3][bolt3] (Lightning Network
-Bitcoin transaction and script formats), including commitment
-transactions, HTLC transactions, closing transactions, per-commitment
-key derivation, and secret storage.
+A pure Haskell implementation of [BOLT #3][bolt3], the Lightning
+Network's Bitcoin transaction and script formats: commitment, HTLC and
+closing transactions, their output scripts and witnesses,
+per-commitment key derivation, and per-commitment secret generation and
+storage.
+
+Amounts, points and secrets use the types of [ppad-bolt1][bolt1], and
+transactions convert to [ppad-tx][tx] for txids, sighashes and
+serialization.
## Usage
@@ -15,42 +20,102 @@ A sample GHCi session:
```
> :set -XOverloadedStrings
- >
+ > import qualified Bitcoin.Prim.Tx as BT
> import qualified Data.ByteString as BS
- > import qualified Data.ByteString.Base16 as B16
+ > import qualified Lightning.Protocol.BOLT1 as BOLT1
> import Lightning.Protocol.BOLT3
>
- > -- key derivation (BOLT #3 Appendix E test vector)
- > let basepoint = Point (B16.decodeLenient "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2")
- > let perCommitment = PerCommitmentPoint (Point (B16.decodeLenient "025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"))
- > let Just (Pubkey derived) = derive_pubkey basepoint perCommitment
- > B16.encode derived
- "0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"
+ > -- per-commitment secrets come from a seed; secrets aren't shown
+ > let Just sd = seed (BS.replicate 32 0xff)
+ > let Just cn = commitment_number 0
+ > let i = commitment_secret_index cn
+ > let secret = generate_from_seed sd i
+ > secret
+ PerCommitmentSecret <redacted>
>
- > -- secret generation (BOLT #3 Appendix D test vector)
- > let seed = BS.replicate 32 0xFF
- > B16.encode (generate_from_seed seed 281474976710655)
- "7cc854b54e3e0dcdb010d7a3fee464a9687be6e8db3be6854c475621e007a5dc"
+ > -- the peer keeps revealed secrets in compact storage
+ > let Just store = insert_secret secret i empty_secret_store
+ > fmap (== secret) (derive_old_secret i store)
+ Just True
>
- > -- fee calculation
- > let feerate = FeeratePerKw 5000
- > let features = ChannelFeatures { cf_option_anchors = False }
- > commitment_fee feerate features 2
- Satoshi 5340
+ > -- the commitment's per-commitment point, and a private key for it
+ > let Just pcp = derive_per_commitment_point secret
+ > let Just base = seckey (BS.pack [1 .. 32])
+ > derive_privkey base pcp
+ Just (Seckey <redacted>)
>
- > -- HTLC trimming
- > let dust = DustLimit (Satoshi 546)
- > htlc_trim_threshold dust feerate features HTLCOffered
+ > -- fees and HTLC trimming
+ > commitment_fee (FeeratePerKw 5000) StaticRemotekey 2
+ Satoshi 5340
+ > let Just dust = fmap DustLimit (BOLT1.satoshi 546)
+ > htlc_trim_threshold dust (FeeratePerKw 5000) StaticRemotekey HTLCOffered
Satoshi 3861
+ >
+ > -- an option_simple_close transaction: the closer pays the fee, and
+ > -- its OP_RETURN output has amount zero
+ > let Just txid = BT.mkTxId (BS.replicate 32 0x01)
+ > let Just [closer, closee] = traverse BOLT1.milli_satoshi [6000000, 4000000]
+ > let Just fee = BOLT1.satoshi 500
+ > let p2wpkh = Script . BS.append "\x00\x14" . BS.replicate 20
+ :{
+ ghci| let close = ClosingContext {
+ ghci| clc_funding_outpoint = BT.OutPoint txid 0
+ ghci| , clc_closer_msat = closer
+ ghci| , clc_closee_msat = closee
+ ghci| , clc_closer_script = Script "\x6a"
+ ghci| , clc_closee_script = p2wpkh 0x22
+ ghci| , clc_fee = fee
+ ghci| , clc_locktime = Locktime 0
+ ghci| , clc_outputs = CloserAndCloseeOutputs
+ ghci| }
+ ghci| :}
+ > let Just tx = build_closing_tx close
+ > fmap clo_value (cltx_outputs tx)
+ Satoshi 0 :| [Satoshi 4000]
+ > BS.length (encode_closing_tx tx)
+ 92
```
## Documentation
-Haddocks are hosted at [docs.ppad.tech/bolt3][hadoc].
+Haddocks (API documentation, etc.) are hosted at
+[docs.ppad.tech/bolt3](https://docs.ppad.tech/bolt3).
+
+## Specification
+
+This library implements BOLT #3 as of lightning/bolts commit
+[1aadb719][spec] (2026-09-21); a copy lives in `etc/`. Every test
+vector in Appendices B through F is checked byte for byte.
+
+Not implemented:
+
+* `zero_fee_commitments` (version 3 commitment and HTLC transactions,
+ the `shared_anchor` P2A output, and Appendix H). Only the
+ `option_static_remotekey` and `option_anchors` commitment formats are
+ supported.
+* Funding transaction construction (including Appendix G's dual-funded
+ vectors): the library provides the funding output's script and
+ witness only.
+
+The library builds unsigned transactions; signatures are made with
+[ppad-secp256k1][secp] over sighashes from ppad-tx. Spending
+transactions for on-chain resolution belong to BOLT #5.
+
+## Performance
+
+The aim is best-in-class performance for pure Haskell code. Run the
+benchmarks with:
+
+```
+$ cabal bench
+```
## Security
-This is a pre-release version and makes no security guarantees whatsoever.
+This library has received no security audit. Secret material (seeds,
+secret keys, per-commitment secrets and their storage) is kept out of
+`Show` output, and private-key derivation uses constant-time
+arithmetic.
## Development
@@ -64,12 +129,15 @@ $ nix develop
Then do e.g.:
```
-$ cabal build
-$ cabal test
-$ cabal bench
+$ cabal repl ppad-bolt3
```
+to get a REPL for the main library.
+
[bolt3]: https://github.com/lightning/bolts/blob/master/03-transactions.md
+[bolt1]: https://git.ppad.tech/bolt1
+[tx]: https://git.ppad.tech/tx
+[secp]: https://git.ppad.tech/secp256k1
+[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/bolt3