commit a468ba5a0daee4cf6719cba659ea5272806f694b
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 17:43:35 +0400
Initial project skeleton for ppad-tx
Minimal Bitcoin transaction primitives library, following ppad
conventions (Bitcoin.Prim.* namespace, nix flake, LLVM support).
Modules:
- Bitcoin.Prim.Tx: core types (Tx, TxIn, TxOut, OutPoint, Witness, TxId)
and serialisation stubs
- Bitcoin.Prim.Tx.Sighash: sighash types and computation stubs
Dependencies: ppad-sha256, ppad-base16.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
| A | .gitignore | | | 1 | + |
| A | AGENTS.md | | | 141 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | CHANGELOG | | | 4 | ++++ |
| A | CLAUDE.md | | | 141 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | LICENSE | | | 20 | ++++++++++++++++++++ |
| A | bench/Main.hs | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
| A | bench/Weight.hs | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| A | flake.nix | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/Bitcoin/Prim/Tx.hs | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lib/Bitcoin/Prim/Tx/Sighash.hs | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | ppad-tx.cabal | | | 91 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | test/Main.hs | | | 27 | +++++++++++++++++++++++++++ |
12 files changed, 773 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+dist-newstyle/
diff --git a/AGENTS.md b/AGENTS.md
@@ -0,0 +1,141 @@
+# ppad-tx
+
+Minimal Bitcoin transaction primitives for ppad libraries.
+
+## Project Structure
+
+- `lib/` - library source (Bitcoin.Prim.Tx)
+- `test/` - tests (tasty + tasty-hunit)
+- `bench/` - benchmarks (criterion for timing, weigh for allocations)
+- `flake.nix` - nix flake for dependency and build management
+- `ppad-tx.cabal` - cabal package definition
+- `CLAUDE.md` / `AGENTS.md` - keep these in sync
+
+## Build and Test
+
+Enter devshell and use cabal:
+
+```
+nix develop
+cabal build
+cabal test
+cabal bench
+```
+
+Do not use stack. All dependency and build management via nix.
+
+## Dependencies
+
+### ppad libraries (use freely)
+
+Use ppad libraries (github.com/ppad-tech, git.ppad.tech) liberally.
+Current dependencies: ppad-sha256, ppad-base16.
+
+### External libraries
+
+Use only minimal external dependencies. Prefer GHC's core/boot libraries
+(base, bytestring, primitive, etc.).
+
+**Ask for explicit confirmation before adding any library outside of:**
+- GHC boot/core libraries
+- ppad-* libraries
+- Test dependencies (tasty, QuickCheck, etc. for test-suite only)
+- Benchmark dependencies (criterion, weigh for benchmark only)
+
+## Code Style
+
+### Performance
+
+- Use strictness annotations (BangPatterns) liberally
+- Prefer UNPACK for strict record fields
+- Use MagicHash, UnboxedTuples, GHC.Exts for hot paths
+- Do not rely on UNBOX pragmas; implement primitives directly with
+ MagicHash and GHC.Exts when needed
+- Use INLINE pragmas for small functions
+- Refer to ppad-sha256 and ppad-fixed for low-level patterns
+
+### Type safety
+
+- Encode invariants into the type system
+- Use newtypes liberally (e.g., TxId, Satoshi)
+- Use ADTs to make illegal states unrepresentable
+- Prefer smart constructors that validate inputs
+
+### Safety
+
+- Never use partial Prelude functions (head, tail, !!, etc.)
+- Avoid brittle partials in tests too (e.g., unchecked indexing). Prefer
+ bounds checks or total helpers even in test code.
+- Avoid non-exhaustive pattern matches and unsafe behavior; use total
+ helpers and make all constructors explicit.
+- Use Maybe/Either for fallible operations
+- Validate all inputs at system boundaries
+
+### Formatting
+
+- Keep lines under 80 characters
+- Use Haskell2010
+- Module header with copyright, license, maintainer
+- OPTIONS_HADDOCK prune for public modules
+- Haddock examples for exported functions
+
+## Testing
+
+Use tasty to wrap all tests:
+- tasty-hunit for unit tests with known vectors
+- tasty-quickcheck for property-based tests
+- Source test vectors from BIPs (BIP143), Bitcoin Core tx_valid.json
+
+Property tests should enforce invariants that can't be encoded in types.
+
+## Benchmarking
+
+Always maintain benchmark suites:
+- `bench/Main.hs` - criterion for wall-time benchmarks
+- `bench/Weight.hs` - weigh for allocation tracking
+
+Define NFData instances for types that need benchmarking.
+
+## Git Workflow
+
+- Feature branches for development; commit freely there
+- Logical, atomic commits on feature branches
+- Master should be mostly merge commits
+- Merge to master with `--no-ff` after validation
+- Always build and test before creating a merge commit
+- Write detailed merge commit messages summarising changes
+
+### Worktree flow (for planned work)
+
+When starting work on an implementation plan:
+
+```
+git worktree add ./impl-<desc> -b impl/<desc> master
+# work in that worktree
+# merge to master when complete
+git worktree remove ./impl-<desc>
+```
+
+### Commits
+
+- Higher-level descriptions in merge commits
+- Never update git config
+- Never use destructive git commands (push --force, hard reset) without
+ explicit request
+- Never skip hooks unless explicitly requested
+
+## Planning
+
+When planning work:
+- Highlight which steps can be done independently
+- Consider forking subagents for concurrent work on independent steps
+- Write implementation plans to `plans/IMPL<n>.md` if the project uses
+ this convention
+
+## Flake Structure
+
+The flake.nix follows ppad conventions:
+- Uses ppad-nixpkgs as base
+- Follows references to avoid duplication
+- Supports LLVM backend via cabal flag
+- Provides devShell with ghc, cabal, cc, llvm
diff --git a/CHANGELOG b/CHANGELOG
@@ -0,0 +1,4 @@
+# Changelog
+
+- 0.1.0 (UNRELEASED)
+ * Initial release.
diff --git a/CLAUDE.md b/CLAUDE.md
@@ -0,0 +1,141 @@
+# ppad-tx
+
+Minimal Bitcoin transaction primitives for ppad libraries.
+
+## Project Structure
+
+- `lib/` - library source (Bitcoin.Prim.Tx)
+- `test/` - tests (tasty + tasty-hunit)
+- `bench/` - benchmarks (criterion for timing, weigh for allocations)
+- `flake.nix` - nix flake for dependency and build management
+- `ppad-tx.cabal` - cabal package definition
+- `CLAUDE.md` / `AGENTS.md` - keep these in sync
+
+## Build and Test
+
+Enter devshell and use cabal:
+
+```
+nix develop
+cabal build
+cabal test
+cabal bench
+```
+
+Do not use stack. All dependency and build management via nix.
+
+## Dependencies
+
+### ppad libraries (use freely)
+
+Use ppad libraries (github.com/ppad-tech, git.ppad.tech) liberally.
+Current dependencies: ppad-sha256, ppad-base16.
+
+### External libraries
+
+Use only minimal external dependencies. Prefer GHC's core/boot libraries
+(base, bytestring, primitive, etc.).
+
+**Ask for explicit confirmation before adding any library outside of:**
+- GHC boot/core libraries
+- ppad-* libraries
+- Test dependencies (tasty, QuickCheck, etc. for test-suite only)
+- Benchmark dependencies (criterion, weigh for benchmark only)
+
+## Code Style
+
+### Performance
+
+- Use strictness annotations (BangPatterns) liberally
+- Prefer UNPACK for strict record fields
+- Use MagicHash, UnboxedTuples, GHC.Exts for hot paths
+- Do not rely on UNBOX pragmas; implement primitives directly with
+ MagicHash and GHC.Exts when needed
+- Use INLINE pragmas for small functions
+- Refer to ppad-sha256 and ppad-fixed for low-level patterns
+
+### Type safety
+
+- Encode invariants into the type system
+- Use newtypes liberally (e.g., TxId, Satoshi)
+- Use ADTs to make illegal states unrepresentable
+- Prefer smart constructors that validate inputs
+
+### Safety
+
+- Never use partial Prelude functions (head, tail, !!, etc.)
+- Avoid brittle partials in tests too (e.g., unchecked indexing). Prefer
+ bounds checks or total helpers even in test code.
+- Avoid non-exhaustive pattern matches and unsafe behavior; use total
+ helpers and make all constructors explicit.
+- Use Maybe/Either for fallible operations
+- Validate all inputs at system boundaries
+
+### Formatting
+
+- Keep lines under 80 characters
+- Use Haskell2010
+- Module header with copyright, license, maintainer
+- OPTIONS_HADDOCK prune for public modules
+- Haddock examples for exported functions
+
+## Testing
+
+Use tasty to wrap all tests:
+- tasty-hunit for unit tests with known vectors
+- tasty-quickcheck for property-based tests
+- Source test vectors from BIPs (BIP143), Bitcoin Core tx_valid.json
+
+Property tests should enforce invariants that can't be encoded in types.
+
+## Benchmarking
+
+Always maintain benchmark suites:
+- `bench/Main.hs` - criterion for wall-time benchmarks
+- `bench/Weight.hs` - weigh for allocation tracking
+
+Define NFData instances for types that need benchmarking.
+
+## Git Workflow
+
+- Feature branches for development; commit freely there
+- Logical, atomic commits on feature branches
+- Master should be mostly merge commits
+- Merge to master with `--no-ff` after validation
+- Always build and test before creating a merge commit
+- Write detailed merge commit messages summarising changes
+
+### Worktree flow (for planned work)
+
+When starting work on an implementation plan:
+
+```
+git worktree add ./impl-<desc> -b impl/<desc> master
+# work in that worktree
+# merge to master when complete
+git worktree remove ./impl-<desc>
+```
+
+### Commits
+
+- Higher-level descriptions in merge commits
+- Never update git config
+- Never use destructive git commands (push --force, hard reset) without
+ explicit request
+- Never skip hooks unless explicitly requested
+
+## Planning
+
+When planning work:
+- Highlight which steps can be done independently
+- Consider forking subagents for concurrent work on independent steps
+- Write implementation plans to `plans/IMPL<n>.md` if the project uses
+ this convention
+
+## Flake Structure
+
+The flake.nix follows ppad conventions:
+- Uses ppad-nixpkgs as base
+- Follows references to avoid duplication
+- Supports LLVM backend via cabal flag
+- Provides devShell with ghc, cabal, cc, llvm
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2025 Jared Tobin
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/bench/Main.hs b/bench/Main.hs
@@ -0,0 +1,44 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module Main where
+
+import Control.DeepSeq
+import Criterion.Main
+import GHC.Generics
+
+import Bitcoin.Prim.Tx
+import Bitcoin.Prim.Tx.Sighash
+
+-- NFData instances ------------------------------------------------------------
+
+deriving stock instance Generic TxId
+instance NFData TxId
+
+deriving stock instance Generic OutPoint
+instance NFData OutPoint
+
+deriving stock instance Generic TxIn
+instance NFData TxIn
+
+deriving stock instance Generic TxOut
+instance NFData TxOut
+
+deriving stock instance Generic Witness
+instance NFData Witness
+
+deriving stock instance Generic Tx
+instance NFData Tx
+
+deriving stock instance Generic SighashType
+instance NFData SighashType
+
+-- benchmarks ------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain [
+ -- add benchmarks here
+ ]
diff --git a/bench/Weight.hs b/bench/Weight.hs
@@ -0,0 +1,43 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE StandaloneDeriving #-}
+
+module Main where
+
+import Control.DeepSeq
+import GHC.Generics
+import qualified Weigh as W
+
+import Bitcoin.Prim.Tx
+import Bitcoin.Prim.Tx.Sighash
+
+-- NFData instances ------------------------------------------------------------
+
+deriving stock instance Generic TxId
+instance NFData TxId
+
+deriving stock instance Generic OutPoint
+instance NFData OutPoint
+
+deriving stock instance Generic TxIn
+instance NFData TxIn
+
+deriving stock instance Generic TxOut
+instance NFData TxOut
+
+deriving stock instance Generic Witness
+instance NFData Witness
+
+deriving stock instance Generic Tx
+instance NFData Tx
+
+deriving stock instance Generic SighashType
+instance NFData SighashType
+
+-- allocation benchmarks -------------------------------------------------------
+
+main :: IO ()
+main = W.mainWith $ do
+ -- add allocation benchmarks here
+ pure ()
diff --git a/flake.nix b/flake.nix
@@ -0,0 +1,92 @@
+{
+ description = "Minimal Bitcoin transaction primitives for Haskell.";
+
+ inputs = {
+ ppad-nixpkgs = {
+ type = "git";
+ url = "git://git.ppad.tech/nixpkgs.git";
+ ref = "master";
+ };
+ ppad-base16 = {
+ type = "git";
+ url = "git://git.ppad.tech/base16.git";
+ ref = "master";
+ inputs.ppad-nixpkgs.follows = "ppad-nixpkgs";
+ };
+ ppad-sha256 = {
+ type = "git";
+ url = "git://git.ppad.tech/sha256.git";
+ ref = "master";
+ inputs.ppad-nixpkgs.follows = "ppad-nixpkgs";
+ };
+ flake-utils.follows = "ppad-nixpkgs/flake-utils";
+ nixpkgs.follows = "ppad-nixpkgs/nixpkgs";
+ };
+
+ outputs = { self, nixpkgs, flake-utils, ppad-nixpkgs
+ , ppad-base16, ppad-sha256
+ }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ lib = "ppad-tx";
+
+ pkgs = import nixpkgs { inherit system; };
+ hlib = pkgs.haskell.lib;
+ llvm = pkgs.llvmPackages_19.llvm;
+ clang = pkgs.llvmPackages_19.clang;
+
+ base16 = ppad-base16.packages.${system}.default;
+ base16-llvm =
+ hlib.addBuildTools
+ (hlib.enableCabalFlag base16 "llvm")
+ [ llvm clang ];
+
+ sha256 = ppad-sha256.packages.${system}.default;
+ sha256-llvm =
+ hlib.addBuildTools
+ (hlib.enableCabalFlag sha256 "llvm")
+ [ llvm clang ];
+
+ hpkgs = pkgs.haskell.packages.ghc910.extend (new: old: {
+ ppad-base16 = base16-llvm;
+ ppad-sha256 = sha256-llvm;
+ ${lib} = new.callCabal2nix lib ./. {
+ ppad-base16 = new.ppad-base16;
+ ppad-sha256 = new.ppad-sha256;
+ };
+ });
+
+ cc = pkgs.stdenv.cc;
+ ghc = hpkgs.ghc;
+ cabal = hpkgs.cabal-install;
+ in
+ {
+ packages.default = hpkgs.${lib};
+
+ packages.haddock = hpkgs.${lib}.doc;
+
+ devShells.default = hpkgs.shellFor {
+ packages = p: [
+ (hlib.doBenchmark p.${lib})
+ ];
+
+ buildInputs = [
+ cabal
+ cc
+ llvm
+ ];
+
+ doBenchmark = true;
+
+ shellHook = ''
+ PS1="[${lib}] \w$ "
+ echo "entering ${system} shell, using"
+ echo "cc: $(${cc}/bin/cc --version)"
+ echo "ghc: $(${ghc}/bin/ghc --version)"
+ echo "cabal: $(${cabal}/bin/cabal --version)"
+ echo "llc: $(${llvm}/bin/llc --version | head -2 | tail -1)"
+ '';
+ };
+ }
+ );
+}
diff --git a/lib/Bitcoin/Prim/Tx.hs b/lib/Bitcoin/Prim/Tx.hs
@@ -0,0 +1,108 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-- |
+-- Module: Bitcoin.Prim.Tx
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Minimal Bitcoin transaction primitives, including raw transaction
+-- types, serialisation to/from bytes, and txid computation.
+
+module Bitcoin.Prim.Tx (
+ -- * Transaction Types
+ Tx(..)
+ , TxIn(..)
+ , TxOut(..)
+ , OutPoint(..)
+ , Witness(..)
+ , TxId(..)
+
+ -- * Serialisation
+ , to_bytes
+ , from_bytes
+ , to_bytes_legacy
+ , to_base16
+ , from_base16
+
+ -- * TxId
+ , txid
+ ) where
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Base16 as B16
+import Data.Word (Word32, Word64)
+import GHC.Generics (Generic)
+
+-- | Transaction ID (32 bytes, little-endian double-SHA256).
+newtype TxId = TxId BS.ByteString
+ deriving (Eq, Show, Generic)
+
+-- | Transaction outpoint (txid + output index).
+data OutPoint = OutPoint
+ { op_txid :: {-# UNPACK #-} !TxId
+ , op_vout :: {-# UNPACK #-} !Word32
+ } deriving (Eq, Show, Generic)
+
+-- | Transaction input.
+data TxIn = TxIn
+ { txin_prevout :: {-# UNPACK #-} !OutPoint
+ , txin_script_sig :: !BS.ByteString
+ , txin_sequence :: {-# UNPACK #-} !Word32
+ } deriving (Eq, Show, Generic)
+
+-- | Transaction output.
+data TxOut = TxOut
+ { txout_value :: {-# UNPACK #-} !Word64 -- ^ satoshis
+ , txout_script_pubkey :: !BS.ByteString
+ } deriving (Eq, Show, Generic)
+
+-- | Witness stack for a single input.
+newtype Witness = Witness [BS.ByteString]
+ deriving (Eq, Show, Generic)
+
+-- | 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
+ } deriving (Eq, Show, Generic)
+
+-- serialisation ---------------------------------------------------------------
+
+-- | Serialise a transaction to bytes.
+--
+-- Uses segwit format if witnesses are present, legacy otherwise.
+to_bytes :: Tx -> BS.ByteString
+to_bytes = error "Bitcoin.Prim.Tx.to_bytes: not yet implemented"
+
+-- | Parse a transaction from bytes.
+from_bytes :: BS.ByteString -> Maybe Tx
+from_bytes = error "Bitcoin.Prim.Tx.from_bytes: not yet implemented"
+
+-- | Serialise a transaction to legacy format (no witness data).
+--
+-- Used for txid computation.
+to_bytes_legacy :: Tx -> BS.ByteString
+to_bytes_legacy = error "Bitcoin.Prim.Tx.to_bytes_legacy: not yet implemented"
+
+-- | Serialise a transaction to base16.
+to_base16 :: Tx -> BS.ByteString
+to_base16 tx = B16.encode (to_bytes tx)
+
+-- | Parse a transaction from base16.
+from_base16 :: BS.ByteString -> Maybe Tx
+from_base16 b16 = do
+ bs <- B16.decode b16
+ from_bytes bs
+
+-- txid ------------------------------------------------------------------------
+
+-- | Compute the transaction ID (double SHA256 of legacy serialisation).
+txid :: Tx -> TxId
+txid = error "Bitcoin.Prim.Tx.txid: not yet implemented"
diff --git a/lib/Bitcoin/Prim/Tx/Sighash.hs b/lib/Bitcoin/Prim/Tx/Sighash.hs
@@ -0,0 +1,61 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveGeneric #-}
+
+-- |
+-- Module: Bitcoin.Prim.Tx.Sighash
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Sighash computation for legacy and BIP143 segwit transactions.
+
+module Bitcoin.Prim.Tx.Sighash (
+ -- * Sighash Types
+ SighashType(..)
+
+ -- * Legacy Sighash
+ , sighash_legacy
+
+ -- * BIP143 Segwit Sighash
+ , sighash_segwit
+ ) where
+
+import Bitcoin.Prim.Tx (Tx)
+import qualified Data.ByteString as BS
+import Data.Word (Word64)
+import GHC.Generics (Generic)
+
+-- | Sighash type flags.
+data SighashType
+ = SIGHASH_ALL
+ | SIGHASH_NONE
+ | SIGHASH_SINGLE
+ | SIGHASH_ALL_ANYONECANPAY
+ | SIGHASH_NONE_ANYONECANPAY
+ | SIGHASH_SINGLE_ANYONECANPAY
+ deriving (Eq, Show, Generic)
+
+-- | Compute legacy sighash.
+--
+-- Modifies a copy of the transaction based on sighash flags, appends
+-- the sighash type as 4-byte little-endian, and double SHA256s.
+sighash_legacy
+ :: Tx
+ -> Int -- ^ input index
+ -> BS.ByteString -- ^ scriptPubKey being spent
+ -> SighashType
+ -> BS.ByteString -- ^ 32-byte hash
+sighash_legacy = error "Bitcoin.Prim.Tx.Sighash.sighash_legacy: not yet implemented"
+
+-- | Compute BIP143 segwit sighash.
+--
+-- Required for signing segwit inputs.
+sighash_segwit
+ :: Tx
+ -> Int -- ^ input index
+ -> BS.ByteString -- ^ scriptCode
+ -> Word64 -- ^ value being spent (satoshis)
+ -> SighashType
+ -> BS.ByteString -- ^ 32-byte hash
+sighash_segwit = error "Bitcoin.Prim.Tx.Sighash.sighash_segwit: not yet implemented"
diff --git a/ppad-tx.cabal b/ppad-tx.cabal
@@ -0,0 +1,91 @@
+cabal-version: 3.0
+name: ppad-tx
+version: 0.1.0
+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
+extra-doc-files: CHANGELOG
+description:
+ Minimal Bitcoin transaction primitives for ppad libraries, including
+ raw transaction types, serialisation, txid computation, and sighash
+ calculation.
+
+flag llvm
+ description: Use GHC's LLVM backend.
+ default: False
+ manual: True
+
+source-repository head
+ type: git
+ location: git.ppad.tech/tx.git
+
+library
+ default-language: Haskell2010
+ hs-source-dirs: lib
+ ghc-options:
+ -Wall
+ if flag(llvm)
+ ghc-options: -fllvm -O2
+ exposed-modules:
+ Bitcoin.Prim.Tx
+ Bitcoin.Prim.Tx.Sighash
+ build-depends:
+ base >= 4.9 && < 5
+ , bytestring >= 0.9 && < 0.13
+ , ppad-base16 >= 0.2.1 && < 0.3
+ , ppad-sha256 >= 0.3 && < 0.4
+
+test-suite tx-tests
+ type: exitcode-stdio-1.0
+ default-language: Haskell2010
+ hs-source-dirs: test
+ main-is: Main.hs
+
+ ghc-options:
+ -rtsopts -Wall
+
+ build-depends:
+ base
+ , bytestring
+ , ppad-base16
+ , ppad-tx
+ , tasty
+ , tasty-hunit
+ , tasty-quickcheck
+
+benchmark tx-bench
+ type: exitcode-stdio-1.0
+ default-language: Haskell2010
+ hs-source-dirs: bench
+ main-is: Main.hs
+
+ ghc-options:
+ -rtsopts -O2 -Wall
+
+ build-depends:
+ base
+ , bytestring
+ , criterion
+ , deepseq
+ , ppad-tx
+
+benchmark tx-weigh
+ type: exitcode-stdio-1.0
+ default-language: Haskell2010
+ hs-source-dirs: bench
+ main-is: Weight.hs
+
+ ghc-options:
+ -rtsopts -O2 -Wall
+
+ build-depends:
+ base
+ , bytestring
+ , deepseq
+ , ppad-tx
+ , weigh
diff --git a/test/Main.hs b/test/Main.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Test.Tasty
+import qualified Test.Tasty.HUnit as H
+
+-- main ------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain $
+ testGroup "ppad-tx" [
+ testGroup "serialisation" [
+ testGroup "round-trip" [
+ ]
+ , testGroup "known vectors" [
+ ]
+ ]
+ , testGroup "txid" [
+ ]
+ , testGroup "sighash" [
+ testGroup "legacy" [
+ ]
+ , testGroup "BIP143 segwit" [
+ ]
+ ]
+ ]