ppad-tx-0.1.0: Minimal Bitcoin transaction primitives.
Copyright(c) 2025 Jared Tobin
LicenseMIT
MaintainerJared Tobin <jared@ppad.tech>
Safe HaskellNone
LanguageHaskell2010

Bitcoin.Prim.Tx.Sighash

Description

Sighash computation for legacy, BIP143 segwit, and BIP341 taproot transactions.

Synopsis

Sighash Types

data SighashType Source #

Canonical sighash type flags.

The Bitcoin consensus rules commit the full 32-bit hashType to the signature preimage and only use its low byte for behavioral dispatch (low 5 bits select base type; bit 0x80 selects ANYONECANPAY). SighashType enumerates the six canonical single-byte hashTypes; pass arbitrary 32-bit values directly when reproducing non-canonical hashes.

Instances

Instances details
Generic SighashType Source # 
Instance details

Defined in Bitcoin.Prim.Tx.Sighash

Associated Types

type Rep SighashType 
Instance details

Defined in Bitcoin.Prim.Tx.Sighash

type Rep SighashType = D1 ('MetaData "SighashType" "Bitcoin.Prim.Tx.Sighash" "ppad-tx-0.1.0-21zhDlOjEJY2BsybwwnJrp" 'False) ((C1 ('MetaCons "SIGHASH_ALL" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "SIGHASH_NONE" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "SIGHASH_SINGLE" 'PrefixI 'False) (U1 :: Type -> Type))) :+: (C1 ('MetaCons "SIGHASH_ALL_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "SIGHASH_NONE_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "SIGHASH_SINGLE_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type))))
Show SighashType Source # 
Instance details

Defined in Bitcoin.Prim.Tx.Sighash

Eq SighashType Source # 
Instance details

Defined in Bitcoin.Prim.Tx.Sighash

type Rep SighashType Source # 
Instance details

Defined in Bitcoin.Prim.Tx.Sighash

type Rep SighashType = D1 ('MetaData "SighashType" "Bitcoin.Prim.Tx.Sighash" "ppad-tx-0.1.0-21zhDlOjEJY2BsybwwnJrp" 'False) ((C1 ('MetaCons "SIGHASH_ALL" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "SIGHASH_NONE" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "SIGHASH_SINGLE" 'PrefixI 'False) (U1 :: Type -> Type))) :+: (C1 ('MetaCons "SIGHASH_ALL_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "SIGHASH_NONE_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "SIGHASH_SINGLE_ANYONECANPAY" 'PrefixI 'False) (U1 :: Type -> Type))))

encode_sighash :: SighashType -> Word32 Source #

Encode a canonical SighashType to its 32-bit hashType value.

  encode_sighash SIGHASH_ALL                 == 0x01
  encode_sighash SIGHASH_SINGLE_ANYONECANPAY == 0x83
  

Legacy Sighash

sighash_legacy Source #

Arguments

:: Tx 
-> Int

input index

-> ByteString

scriptPubKey being spent

-> Word32

hashType

-> ByteString

32-byte hash

Compute legacy sighash for P2PKH/P2SH inputs.

Modifies a copy of the transaction based on hashType flags, appends the 4-byte little-endian hashType, and double SHA256s. The hashType is committed to the preimage verbatim; only its low byte determines behavior (see base_type, is_anyonecanpay).

  -- sign input 0 with SIGHASH_ALL
  let hash = sighash_legacy tx 0 scriptPubKey (encode_sighash SIGHASH_ALL)
  -- non-canonical hashType (consensus-valid, committed raw)
  let hash = sighash_legacy tx 0 scriptPubKey 0x6f29291f
  

For base SIGHASH_SINGLE with input index >= output count, returns the special "sighash single bug" value (0x01 followed by 31 zero bytes).

The input index is not validated against the input count; an out-of-range idx produces a deterministic but consensus-undefined hash. Matches Bitcoin Core, which asserts on the same precondition. Contrast sighash_segwit, which validates and returns Nothing.

BIP143 Segwit Sighash

sighash_segwit Source #

Arguments

:: Tx 
-> Int

input index

-> ByteString

scriptCode

-> Word64

value being spent (satoshis)

-> Word32

hashType

-> Maybe ByteString

32-byte hash, or Nothing if index invalid

Compute BIP143 segwit sighash.

Required for signing segwit inputs (P2WPKH, P2WSH). Unlike legacy sighash, this commits to the value being spent, preventing fee manipulation attacks. The hashType is committed to the preimage verbatim; only its low byte determines behavior.

Returns Nothing if the input index is out of range.

  -- sign P2WPKH input 0
  let scriptCode = ...  -- P2WPKH scriptCode
  let hash = sighash_segwit tx 0 scriptCode inputValue
                 (encode_sighash SIGHASH_ALL)
  -- use hash with ECDSA signing (after checking Just)
  

BIP341 Taproot Sighash

sighash_taproot_keypath Source #

Arguments

:: Tx 
-> Int

input index

-> [Word64]

amounts for all inputs (in order)

-> [ByteString]

scriptPubKeys for all inputs (in order)

-> Maybe ByteString

optional annex (including 0x50 prefix)

-> Word8

hash type

-> Maybe ByteString

32-byte hash, or Nothing on invalid input

Compute BIP341 taproot sighash for a key-path spend.

The caller must supply, in input order, the amount and scriptPubKey of every previous output being spent (the entire set is committed to the preimage when not using SIGHASH_ANYONECANPAY).

The annex, if present, must include the mandatory 0x50 prefix byte (as it appears in the witness).

Returns Nothing if any of the following holds:

  • hash_type is not a canonical taproot value
  • the input index is out of range
  • amounts or scriptPubKeys does not match the input count
  • an annex is supplied without the 0x50 prefix or is empty
  • hash_type is SIGHASH_SINGLE (or its ACP variant) and the input index has no corresponding output (such a signature would be consensus-invalid per BIP341)
  sighash_taproot_keypath tx 0 amounts scriptPubKeys Nothing 0x00
  

sighash_taproot_scriptpath Source #

Arguments

:: Tx 
-> Int

input index

-> [Word64]

amounts for all inputs (in order)

-> [ByteString]

scriptPubKeys for all inputs (in order)

-> Maybe ByteString

optional annex (including 0x50 prefix)

-> ByteString

tap leaf hash (32 bytes)

-> Word32

codeseparator position

-> Word8

hash type

-> Maybe ByteString 

Compute BIP341 taproot sighash for a script-path (tapscript) spend.

In addition to the key-path inputs, takes:

  • the 32-byte tap leaf hash (BIP342: tagged hash of leaf_ver || ser_string(script)), computed by the caller
  • the codeseparator position (0xffffffff if none was executed)

Returns Nothing under the same conditions as sighash_taproot_keypath, plus when tap_leaf_hash is not exactly 32 bytes.

Internal

strip_codeseparators :: ByteString -> ByteString Source #

Strip OP_CODESEPARATOR (0xab) opcodes from a script, skipping push-data sections so that data bytes equal to 0xab are preserved.

This is consensus-required preprocessing for the legacy sighash scriptCode (see Bitcoin Core's CTransactionSignatureSerializer). BIP143 segwit sighash does not perform this stripping; for segwit, the caller is responsible for trimming the scriptCode to the portion after the last executed OP_CODESEPARATOR.

On a malformed script (truncated push data), the malformed tail is copied verbatim without further codeseparator processing.