commit baa2031ec129e3fbb3038195cdc241d190585b09
parent a468ba5a0daee4cf6719cba659ea5272806f694b
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 17:44:02 +0400
plans: add
Diffstat:
| A | plans/PPAD-TX.md | | | 247 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 247 insertions(+), 0 deletions(-)
diff --git a/plans/PPAD-TX.md b/plans/PPAD-TX.md
@@ -0,0 +1,247 @@
+# ppad-tx
+
+Minimal Bitcoin transaction primitives for ppad libraries.
+
+## Motivation
+
+Multiple ppad-bolt implementations duplicate core tx-related types:
+
+| Type | bolt2 | bolt3 | bolt7 |
+|------------------|-------|-------|-------|
+| TxId | yes | yes | - |
+| Outpoint | yes | yes | - |
+| Sequence | - | yes | - |
+| Locktime | - | yes | - |
+| Script | - | yes | - |
+| Witness | - | yes | - |
+| Satoshi(s) | yes | yes | - |
+| MilliSatoshi(s) | yes | yes | - |
+| Point/Pubkey | yes | yes | yes |
+| Signature | yes | - | yes |
+| ChainHash | yes | - | yes |
+| ShortChannelId | yes | - | yes |
+
+Common gap across all: no raw Tx structure, no serialisation, no txid
+computation from raw bytes.
+
+ppad-tx will provide canonical definitions and allow bolt impls to depend
+on a single source.
+
+## Scope
+
+### In scope (v1)
+
+- Raw transaction types (Tx, TxIn, TxOut)
+- Outpoint, Sequence, Locktime
+- Serialisation to/from bytes (legacy and segwit formats)
+- TxId computation (double SHA256 of non-witness serialisation)
+- Sighash computation (legacy and BIP143 segwit)
+- Basic amount types (Satoshi)
+
+### Out of scope (v1)
+
+- Script execution or validation
+- Signature creation/verification (use ppad-secp256k1)
+- Transaction building DSL
+- PSBT support
+- Taproot/BIP341 sighash (defer to v2)
+
+## Module layout
+
+```
+lib/
+ Bitcoin/
+ Prim/
+ Tx.hs -- core types, serialisation, txid
+ Tx/
+ Sighash.hs -- sighash computation
+```
+
+Follow ppad-script conventions:
+- `Bitcoin.Prim.*` namespace
+- ByteArray for internal representation where appropriate
+- base16 conversion utilities
+- OPTIONS_HADDOCK prune
+
+## Core types
+
+```haskell
+-- | Transaction ID (32 bytes, little-endian double-SHA256).
+newtype TxId = TxId BS.ByteString
+
+-- | Transaction outpoint.
+data OutPoint = OutPoint
+ { op_txid :: {-# UNPACK #-} !TxId
+ , op_vout :: {-# UNPACK #-} !Word32
+ }
+
+-- | Transaction input.
+data TxIn = TxIn
+ { txin_prevout :: {-# UNPACK #-} !OutPoint
+ , txin_script_sig :: !BS.ByteString
+ , txin_sequence :: {-# UNPACK #-} !Word32
+ }
+
+-- | Transaction output.
+data TxOut = TxOut
+ { txout_value :: {-# UNPACK #-} !Word64 -- satoshis
+ , txout_script_pubkey :: !BS.ByteString
+ }
+
+-- | Witness stack for a single input.
+newtype Witness = Witness [BS.ByteString]
+
+-- | Complete transaction.
+data Tx = Tx
+ { tx_version :: {-# UNPACK #-} !Word32
+ , tx_inputs :: ![TxIn]
+ , tx_outputs :: ![TxOut]
+ , tx_witnesses :: ![Witness] -- empty list for legacy tx
+ , tx_locktime :: {-# UNPACK #-} !Word32
+ }
+```
+
+## Serialisation
+
+### Format detection
+
+- Legacy: version || inputs || outputs || locktime
+- Segwit: version || 0x00 || 0x01 || inputs || outputs || witnesses || locktime
+
+Detect segwit by marker byte (0x00) after version. If present and followed
+by flag (0x01), parse as segwit.
+
+### Public API
+
+```haskell
+-- Serialisation
+to_bytes :: Tx -> BS.ByteString -- segwit format if witnesses present
+from_bytes :: BS.ByteString -> Maybe Tx
+
+to_base16 :: Tx -> BS.ByteString
+from_base16 :: BS.ByteString -> Maybe Tx
+
+-- Legacy serialisation (for txid computation)
+to_bytes_legacy :: Tx -> BS.ByteString
+
+-- TxId
+txid :: Tx -> TxId -- double SHA256 of legacy serialisation
+```
+
+### Encoding details
+
+All integers little-endian. Variable-length integers (compactSize):
+- 0x00-0xfc: 1 byte
+- 0xfd: 0xfd || 2 bytes (little-endian)
+- 0xfe: 0xfe || 4 bytes
+- 0xff: 0xff || 8 bytes
+
+## Sighash
+
+### Flags
+
+```haskell
+data SighashType
+ = SIGHASH_ALL
+ | SIGHASH_NONE
+ | SIGHASH_SINGLE
+ | SIGHASH_ALL_ANYONECANPAY
+ | SIGHASH_NONE_ANYONECANPAY
+ | SIGHASH_SINGLE_ANYONECANPAY
+```
+
+### Legacy sighash
+
+Per BIP-143 predecessor. Modify tx copy based on flags, append sighash
+type as 4-byte LE, double SHA256.
+
+### BIP143 segwit sighash
+
+Required for signing segwit inputs. Precomputed:
+- hashPrevouts: SHA256(SHA256(all input outpoints))
+- hashSequence: SHA256(SHA256(all input sequences))
+- hashOutputs: SHA256(SHA256(all outputs))
+
+Then:
+```
+version || hashPrevouts || hashSequence || outpoint || scriptCode ||
+value || sequence || hashOutputs || locktime || sighashType
+```
+
+### Public API
+
+```haskell
+-- Legacy
+sighash_legacy
+ :: Tx
+ -> Int -- input index
+ -> BS.ByteString -- scriptPubKey being spent
+ -> SighashType
+ -> BS.ByteString -- 32-byte hash
+
+-- BIP143 segwit
+sighash_segwit
+ :: Tx
+ -> Int -- input index
+ -> BS.ByteString -- scriptCode
+ -> Word64 -- value being spent
+ -> SighashType
+ -> BS.ByteString -- 32-byte hash
+```
+
+## Dependencies
+
+Minimal:
+- base
+- bytestring
+- primitive (for ByteArray if needed)
+- ppad-sha256 (for txid and sighash)
+- ppad-base16 (for hex conversion)
+
+## Testing
+
+- Known tx vectors from Bitcoin Core / BIPs
+- Round-trip: from_bytes . to_bytes == id
+- TxId computation against known txids
+- Sighash vectors from BIP143
+
+Sources:
+- BIP143 test vectors
+- Bitcoin Core's tx_valid.json / tx_invalid.json
+- Manually constructed edge cases (empty witness, max inputs, etc.)
+
+## Implementation steps
+
+### Step 1: Core types + serialisation (independent)
+
+- Define Tx, TxIn, TxOut, OutPoint, Witness
+- Implement compactSize encoding/decoding
+- Implement to_bytes / from_bytes (both formats)
+- Implement txid computation
+
+### Step 2: Sighash (depends on Step 1)
+
+- Define SighashType
+- Implement legacy sighash
+- Implement BIP143 segwit sighash
+
+### Step 3: Tests + benchmarks
+
+- Add tx serialisation round-trip tests
+- Add known vector tests for txid
+- Add BIP143 sighash vectors
+- Criterion benchmarks for serialisation
+- Weigh benchmarks for allocations
+
+### Step 4: Polish
+
+- Haddock documentation with examples
+- Ensure line length < 80
+- Module headers, OPTIONS_HADDOCK prune
+
+## Future (v2)
+
+- BIP341 taproot sighash
+- BIP340 schnorr signature integration
+- Witness program version detection
+- Transaction weight/vsize calculation