Start your career in bitcoin open source — ₿OSS

APPLY FOR THE ₿OSS CHALLENGE TODAY

Step 5: Calculate Preimage and Sighash

Now that we have our transaction digest and scriptCode, we're ready for the signing process.

Before we create our signature, we need to understand two key concepts:

  • Preimage
  • Sighash (also called commitment hash)

The signing process follows these equations:

text
1   preimage = version + hashPrevouts + hashSequence + outpoint + scriptCode + value + sequence + hashOutputs + locktime + sighash_type
2 sigHash = sha256(sha256(preimage))

About SIGHASH_TYPE

The sighash_type determines which parts of the transaction are committed to in the signature:

The preimage is what we actually sign, it's a commitment to all the transaction data we want to protect.

By double-hashing (SHA256d) this preimage, we get our sighash, which is what gets signed with our private key.

Code Implementation

python
def get_commitment_hash(
  outpoint: bytes,           # The input being signed (txid + vout)
  scriptcode: bytes,         # From step 2
  value: int,                # Value of the output being spent
  outputs: List[bytes],      # Transaction outputs
  all_inputs: List[bytes]    # All transaction inputs (required for BIP143)
) -> bytes:
  """Calculate BIP143 sighash"""
  
  # Version (4 bytes little-endian)
  version = int_to_little_endian(1, 4)
  
  # Get transaction digest components
  hash_prevouts, hash_sequence, hash_outputs = get_transaction_digest(
      all_inputs,  # Uses ALL transaction inputs
      outputs
  )
  
  # Value of spent output (8 bytes little-endian)
  value_bytes = int_to_little_endian(value, 8)
  
  # Sequence for THIS input (4 bytes)
  sequence = b'\xff\xff\xff\xff'  # From test vector
  
  # Locktime (4 bytes little-endian)
  locktime = int_to_little_endian(0x11, 4)  # 17 in hex
  
  # SIGHASH_ALL (4 bytes little-endian)
  sighash_type = int_to_little_endian(1, 4)
  
  # Build preimage according to BIP143 structure
  preimage = (
      version +
      hash_prevouts +
      hash_sequence +
      outpoint +
      scriptcode +
      value_bytes +
      sequence +
      hash_outputs +
      locktime +
      sighash_type
  )
  
  # Double SHA256 to produce the final sighash
  return hashlib.sha256(hashlib.sha256(preimage).digest()).digest()

Test Vector from BIP143

Let's verify again our implementation against the official BIP143 test vectors.

You can verify that the values we generated in the previous step match these test vectors from BIP143.

python
   # Test vector values from BIP143
 preimage = "0100000096b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd3752b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3bef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a010000001976a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac0046c32300000000ffffffff863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e51100000001000000"
 
 # Individual components
 nVersion     = "01000000"
 hashPrevouts = "96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37"
 hashSequence = "52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b"
 outpoint     = "ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a01000000"
 scriptCode   = "1976a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac"
 amount       = "0046c32300000000"
 nSequence    = "ffffffff"
 hashOutputs  = "863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5"
 nLockTime    = "11000000"
 nHashType    = "01000000"
 
 # Final signature hash
 sigHash      = "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670"

Next Step

With our sighash ready, we'll move on to sign it.

Suggest Edits