tx

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

PPAD-TX.md (6060B)


      1 # ppad-tx
      2 
      3 Minimal Bitcoin transaction primitives for ppad libraries.
      4 
      5 ## Motivation
      6 
      7 Multiple ppad-bolt implementations duplicate core tx-related types:
      8 
      9 | Type             | bolt2 | bolt3 | bolt7 |
     10 |------------------|-------|-------|-------|
     11 | TxId             | yes   | yes   | -     |
     12 | Outpoint         | yes   | yes   | -     |
     13 | Sequence         | -     | yes   | -     |
     14 | Locktime         | -     | yes   | -     |
     15 | Script           | -     | yes   | -     |
     16 | Witness          | -     | yes   | -     |
     17 | Satoshi(s)       | yes   | yes   | -     |
     18 | MilliSatoshi(s)  | yes   | yes   | -     |
     19 | Point/Pubkey     | yes   | yes   | yes   |
     20 | Signature        | yes   | -     | yes   |
     21 | ChainHash        | yes   | -     | yes   |
     22 | ShortChannelId   | yes   | -     | yes   |
     23 
     24 Common gap across all: no raw Tx structure, no serialisation, no txid
     25 computation from raw bytes.
     26 
     27 ppad-tx will provide canonical definitions and allow bolt impls to depend
     28 on a single source.
     29 
     30 ## Scope
     31 
     32 ### In scope (v1)
     33 
     34 - Raw transaction types (Tx, TxIn, TxOut)
     35 - Outpoint, Sequence, Locktime
     36 - Serialisation to/from bytes (legacy and segwit formats)
     37 - TxId computation (double SHA256 of non-witness serialisation)
     38 - Sighash computation (legacy and BIP143 segwit)
     39 - Basic amount types (Satoshi)
     40 
     41 ### Out of scope (v1)
     42 
     43 - Script execution or validation
     44 - Signature creation/verification (use ppad-secp256k1)
     45 - Transaction building DSL
     46 - PSBT support
     47 - Taproot/BIP341 sighash (defer to v2)
     48 
     49 ## Module layout
     50 
     51 ```
     52 lib/
     53   Bitcoin/
     54     Prim/
     55       Tx.hs           -- core types, serialisation, txid
     56       Tx/
     57         Sighash.hs    -- sighash computation
     58 ```
     59 
     60 Follow ppad-script conventions:
     61 - `Bitcoin.Prim.*` namespace
     62 - ByteArray for internal representation where appropriate
     63 - base16 conversion utilities
     64 - OPTIONS_HADDOCK prune
     65 
     66 ## Core types
     67 
     68 ```haskell
     69 -- | Transaction ID (32 bytes, little-endian double-SHA256).
     70 newtype TxId = TxId BS.ByteString
     71 
     72 -- | Transaction outpoint.
     73 data OutPoint = OutPoint
     74   { op_txid  :: {-# UNPACK #-} !TxId
     75   , op_vout  :: {-# UNPACK #-} !Word32
     76   }
     77 
     78 -- | Transaction input.
     79 data TxIn = TxIn
     80   { txin_prevout    :: {-# UNPACK #-} !OutPoint
     81   , txin_script_sig :: !BS.ByteString
     82   , txin_sequence   :: {-# UNPACK #-} !Word32
     83   }
     84 
     85 -- | Transaction output.
     86 data TxOut = TxOut
     87   { txout_value         :: {-# UNPACK #-} !Word64  -- satoshis
     88   , txout_script_pubkey :: !BS.ByteString
     89   }
     90 
     91 -- | Witness stack for a single input.
     92 newtype Witness = Witness [BS.ByteString]
     93 
     94 -- | Complete transaction.
     95 data Tx = Tx
     96   { tx_version  :: {-# UNPACK #-} !Word32
     97   , tx_inputs   :: ![TxIn]
     98   , tx_outputs  :: ![TxOut]
     99   , tx_witnesses :: ![Witness]  -- empty list for legacy tx
    100   , tx_locktime :: {-# UNPACK #-} !Word32
    101   }
    102 ```
    103 
    104 ## Serialisation
    105 
    106 ### Format detection
    107 
    108 - Legacy: version || inputs || outputs || locktime
    109 - Segwit: version || 0x00 || 0x01 || inputs || outputs || witnesses || locktime
    110 
    111 Detect segwit by marker byte (0x00) after version. If present and followed
    112 by flag (0x01), parse as segwit.
    113 
    114 ### Public API
    115 
    116 ```haskell
    117 -- Serialisation
    118 to_bytes   :: Tx -> BS.ByteString      -- segwit format if witnesses present
    119 from_bytes :: BS.ByteString -> Maybe Tx
    120 
    121 to_base16   :: Tx -> BS.ByteString
    122 from_base16 :: BS.ByteString -> Maybe Tx
    123 
    124 -- Legacy serialisation (for txid computation)
    125 to_bytes_legacy :: Tx -> BS.ByteString
    126 
    127 -- TxId
    128 txid :: Tx -> TxId  -- double SHA256 of legacy serialisation
    129 ```
    130 
    131 ### Encoding details
    132 
    133 All integers little-endian. Variable-length integers (compactSize):
    134 - 0x00-0xfc: 1 byte
    135 - 0xfd: 0xfd || 2 bytes (little-endian)
    136 - 0xfe: 0xfe || 4 bytes
    137 - 0xff: 0xff || 8 bytes
    138 
    139 ## Sighash
    140 
    141 ### Flags
    142 
    143 ```haskell
    144 data SighashType
    145   = SIGHASH_ALL
    146   | SIGHASH_NONE
    147   | SIGHASH_SINGLE
    148   | SIGHASH_ALL_ANYONECANPAY
    149   | SIGHASH_NONE_ANYONECANPAY
    150   | SIGHASH_SINGLE_ANYONECANPAY
    151 ```
    152 
    153 ### Legacy sighash
    154 
    155 Per BIP-143 predecessor. Modify tx copy based on flags, append sighash
    156 type as 4-byte LE, double SHA256.
    157 
    158 ### BIP143 segwit sighash
    159 
    160 Required for signing segwit inputs. Precomputed:
    161 - hashPrevouts: SHA256(SHA256(all input outpoints))
    162 - hashSequence: SHA256(SHA256(all input sequences))
    163 - hashOutputs: SHA256(SHA256(all outputs))
    164 
    165 Then:
    166 ```
    167 version || hashPrevouts || hashSequence || outpoint || scriptCode ||
    168 value || sequence || hashOutputs || locktime || sighashType
    169 ```
    170 
    171 ### Public API
    172 
    173 ```haskell
    174 -- Legacy
    175 sighash_legacy
    176   :: Tx
    177   -> Int              -- input index
    178   -> BS.ByteString    -- scriptPubKey being spent
    179   -> SighashType
    180   -> BS.ByteString    -- 32-byte hash
    181 
    182 -- BIP143 segwit
    183 sighash_segwit
    184   :: Tx
    185   -> Int              -- input index
    186   -> BS.ByteString    -- scriptCode
    187   -> Word64           -- value being spent
    188   -> SighashType
    189   -> BS.ByteString    -- 32-byte hash
    190 ```
    191 
    192 ## Dependencies
    193 
    194 Minimal:
    195 - base
    196 - bytestring
    197 - primitive (for ByteArray if needed)
    198 - ppad-sha256 (for txid and sighash)
    199 - ppad-base16 (for hex conversion)
    200 
    201 ## Testing
    202 
    203 - Known tx vectors from Bitcoin Core / BIPs
    204 - Round-trip: from_bytes . to_bytes == id
    205 - TxId computation against known txids
    206 - Sighash vectors from BIP143
    207 
    208 Sources:
    209 - BIP143 test vectors
    210 - Bitcoin Core's tx_valid.json / tx_invalid.json
    211 - Manually constructed edge cases (empty witness, max inputs, etc.)
    212 
    213 ## Implementation steps
    214 
    215 ### Step 1: Core types + serialisation (independent)
    216 
    217 - Define Tx, TxIn, TxOut, OutPoint, Witness
    218 - Implement compactSize encoding/decoding
    219 - Implement to_bytes / from_bytes (both formats)
    220 - Implement txid computation
    221 
    222 ### Step 2: Sighash (depends on Step 1)
    223 
    224 - Define SighashType
    225 - Implement legacy sighash
    226 - Implement BIP143 segwit sighash
    227 
    228 ### Step 3: Tests + benchmarks
    229 
    230 - Add tx serialisation round-trip tests
    231 - Add known vector tests for txid
    232 - Add BIP143 sighash vectors
    233 - Criterion benchmarks for serialisation
    234 - Weigh benchmarks for allocations
    235 
    236 ### Step 4: Polish
    237 
    238 - Haddock documentation with examples
    239 - Ensure line length < 80
    240 - Module headers, OPTIONS_HADDOCK prune
    241 
    242 ## Future (v2)
    243 
    244 - BIP341 taproot sighash
    245 - BIP340 schnorr signature integration
    246 - Witness program version detection
    247 - Transaction weight/vsize calculation