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
View our public visitor count
Built with 🧡 by the Bitcoin Dev Project
We'd love to hear your feedback on this project?Give Feedback