tx

Minimal transaction primitives (docs.ppad.tech/tx).
git clone git://git.ppad.tech/tx.git
Log | Files | Refs | README | LICENSE

commit 4845706b1c0fc9a2575500c3bd998c730828b0c0
parent 7119cfdd89057702ee86d2467a4890b33eca0a65
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 18:41:01 +0400

docs, meta: readme, changelog and cabal metadata for 0.2.0

Rewrite the README example against the current sighash API (it no
longer typechecked) and mention taproot; update the synopsis and
description, tested-with format and deepseq bounds; drop unused bench
dependencies.

Diffstat:
MAGENTS.md | 2+-
MCHANGELOG | 9++++++++-
MCLAUDE.md | 2+-
MREADME.md | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mppad-tx.cabal | 16+++++++---------
5 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -4,7 +4,7 @@ Minimal Bitcoin transaction primitives for ppad libraries. ## Project Structure -- `lib/` - library source (Bitcoin.Prim.Tx) +- `lib/` - library source (Bitcoin.Prim.Tx, Bitcoin.Prim.Tx.Sighash) - `test/` - tests (tasty + tasty-hunit) - `bench/` - benchmarks (criterion for timing, weigh for allocations) - `flake.nix` - nix flake for dependency and build management diff --git a/CHANGELOG b/CHANGELOG @@ -9,7 +9,14 @@ * Strips OP_CODESEPARATOR from the script code in legacy sighashes. * Adds BIP341 taproot sighashes (sighash_taproot_keypath, sighash_taproot_scriptpath). - * Adds NFData instances for all transaction types. + * Adds NFData instances for all transaction types, an NFData + instance for SighashType, and Ord instances for TxId and OutPoint. + * Fixes from_bytes, which threw an exception on compactSize lengths + of 2^63 or more, and now rejects counts exceeding the remaining + input. + * Rejects segwit-flagged transactions carrying no witness data, and + serialises transactions whose witnesses are all empty in the legacy + format, as Bitcoin Core does. - 0.1.0 (2026-04-18) * Initial release. diff --git a/CLAUDE.md b/CLAUDE.md @@ -4,7 +4,7 @@ Minimal Bitcoin transaction primitives for ppad libraries. ## Project Structure -- `lib/` - library source (Bitcoin.Prim.Tx) +- `lib/` - library source (Bitcoin.Prim.Tx, Bitcoin.Prim.Tx.Sighash) - `test/` - tests (tasty + tasty-hunit) - `bench/` - benchmarks (criterion for timing, weigh for allocations) - `flake.nix` - nix flake for dependency and build management diff --git a/README.md b/README.md @@ -4,9 +4,10 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen) [![](https://img.shields.io/badge/haddock-tx-lightblue)](https://docs.ppad.tech/tx) -Minimal Bitcoin transaction primitives, including raw transaction -types, serialisation to/from bytes, txid computation, and sighash -calculation (legacy and BIP143 segwit). +Minimal Bitcoin transaction primitives in pure Haskell: raw transaction +types, serialisation to and from bytes (legacy and BIP144 segwit), +txid computation, and sighash calculation (legacy, [BIP143][bp143] +segwit and [BIP341][bp341] taproot). ## Usage @@ -16,42 +17,74 @@ A sample GHCi session: > :set -XOverloadedStrings > import qualified Data.ByteString as BS > import qualified Data.ByteString.Base16 as B16 + > import qualified Data.List.NonEmpty as NE > import Bitcoin.Prim.Tx > import Bitcoin.Prim.Tx.Sighash > - > -- parse a raw transaction from hex - > let raw = "0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000" + > -- the Satoshi -> Hal transaction from block 170 + > :{ + > let raw = mconcat + > [ "0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25" + > , "857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f" + > , "4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd1290" + > , "9d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b0000000043" + > , "4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa2" + > , "8414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6c" + > , "d84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a" + > , "382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b" + > , "64f9d4c03f999b8643f656b412a3ac00000000" + > ] + > :} > let Just tx = from_base16 raw > - > -- compute the txid + > -- txids are displayed byte-reversed > let TxId tid = txid tx > B16.encode (BS.reverse tid) "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16" > - > -- round-trip serialisation > from_bytes (to_bytes tx) == Just tx True > - > -- compute a legacy sighash - > let scriptPubKey = BS.pack [0x76, 0xa9, 0x14] - > let hash = sighash_legacy tx 0 scriptPubKey SIGHASH_ALL - > BS.length hash - 32 + > -- the legacy sighash Satoshi signed: input 0 spends a P2PK output + > -- whose scriptPubKey is the same as this tx's change output + > let spk = txout_script_pubkey (NE.last (tx_outputs tx)) + > B16.encode (sighash_legacy tx 0 spk (encode_sighash SIGHASH_ALL)) + "7a05c6145f10101e9d6325494245adf1297d80f8f38d4d576d57cdba220bcb19" + > + > -- segwit and taproot sighashes validate their arguments + > sighash_segwit tx 1 spk 1000000000 (encode_sighash SIGHASH_ALL) + Nothing ``` ## Documentation -Haddocks are hosted at [docs.ppad.tech/tx][hadoc]. +Haddocks (API documentation, etc.) are hosted at +[docs.ppad.tech/tx][hadoc]. + +## Performance + +The aim is best-in-class performance for pure Haskell code. Run the +benchmarks (criterion for wall time, weigh for allocation) with: + +``` +$ cabal bench +``` ## Security -This is a pre-release library that, at present, claims no security -properties whatsoever. +This library handles no secret material. Parsing is total and +rejects malformed, non-minimal or trailing-byte encodings, matching +Bitcoin Core's serialisation rules; it doesn't validate transactions +against consensus or policy rules (amounts, script validity, etc.). +Sighash computation follows Bitcoin Core, including its legacy quirks +(the SIGHASH_SINGLE "bug" value, OP_CODESEPARATOR removal, raw 32-bit +hashTypes), and is tested against Bitcoin Core's `sighash.json` and +the BIP143 and BIP341 vectors. ## Development -You'll require [Nix][nixos] with [flake][flake] support enabled. -Enter a development shell with: +You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a +development shell with: ``` $ nix develop @@ -60,11 +93,13 @@ $ nix develop Then do e.g.: ``` -$ cabal build -$ cabal test -$ cabal bench +$ cabal repl ppad-tx ``` +to get a REPL for the main library. + +[bp143]: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki +[bp341]: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki [nixos]: https://nixos.org/ [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html [hadoc]: https://docs.ppad.tech/tx diff --git a/ppad-tx.cabal b/ppad-tx.cabal @@ -1,19 +1,19 @@ cabal-version: 3.0 name: ppad-tx version: 0.2.0 -synopsis: Minimal Bitcoin transaction primitives. +synopsis: Minimal Bitcoin transaction primitives license: MIT license-file: LICENSE author: Jared Tobin maintainer: jared@ppad.tech category: Cryptography build-type: Simple -tested-with: GHC == 9.10.3 +tested-with: GHC == { 9.10.3 } extra-doc-files: CHANGELOG description: - Minimal Bitcoin transaction primitives for ppad libraries, including - raw transaction types, serialisation, txid computation, and sighash - calculation. + Minimal Bitcoin transaction primitives: raw transaction types, + serialisation, txid computation, and legacy, BIP143 segwit and BIP341 + taproot sighash calculation. flag llvm description: Use GHC's LLVM backend. @@ -37,7 +37,7 @@ library build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 - , deepseq >= 1.4 && < 2 + , deepseq >= 1.4 && < 1.6 , ppad-base16 >= 0.3 && < 0.4 , ppad-sha256 >= 0.3 && < 0.4 @@ -48,7 +48,7 @@ test-suite tx-tests main-is: Main.hs ghc-options: - -rtsopts -Wall + -rtsopts -Wall -fno-warn-orphans build-depends: base @@ -73,7 +73,6 @@ benchmark tx-bench base , bytestring , criterion - , deepseq , ppad-tx benchmark tx-weigh @@ -88,6 +87,5 @@ benchmark tx-weigh build-depends: base , bytestring - , deepseq , ppad-tx , weigh