Step 7: Add Witness Field
Our transaction now has all its fields populated except for the witness data.
For a P2WPKH input, the witness structure is:
1
2 witness = [num_items] [signature_item] [pubkey_item]
3
4 where:
5 - num_items = 0x02 (always 2 items for P2WPKH)
6 - signature_item = [length][DER_signature + SIGHASH_ALL]
7 - pubkey_item = [length][compressed_pubkey]
Our test transaction's witness data:
witness = 02 # number of witness items
47 # signature length (71 bytes)
304402203609e17b84f6a7d30c80bfa...01 # DER signature + SIGHASH_ALL
21 # pubkey length (33 bytes)
025476c2e83188368da1ff3e292e7aca...57 # compressed public key
Code Implementation
def get_p2wpkh_witness(priv: bytes, msg: bytes) -> bytes:
"""
Create witness stack for P2WPKH input with format:
[num_items][sig_len][signature][pubkey_len][pubkey]
"""
# Get signature with sighash byte
signature_with_sighash = sign(priv, msg)
# Get compressed public key
compressed_public_key = get_pub_from_priv(priv)
# Number of witness items (always 2 for P2WPKH)
num_witness_items = bytes([2])
# Serialize signature with its length
sig_len = bytes([len(signature_with_sighash)])
serialized_sig = sig_len + signature_with_sighash
# Serialize public key with its length
pk_len = bytes([len(compressed_public_key)])
serialized_pk = pk_len + compressed_public_key
# Combine all parts
serialized_witness = num_witness_items + serialized_sig + serialized_pk
return serialized_witness
BIP143 Test Vector
def test_witness_stack():
# Private key from BIP143
privkey = bytes.fromhex(
'619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9'
)
# Commitment hash from previous step
commitment = bytes.fromhex(
'c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670'
)
# Generate witness stack
witness = get_p2wpkh_witness(privkey, commitment)
# Expected witness data from BIP143 (broken down for clarity)
expected_witness = (
b'\x02' + # Number of witness items
b'\x47' + # Signature length (71 bytes)
bytes.fromhex( # DER signature + SIGHASH_ALL
'304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a' +
'0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01'
) +
b'\x21' + # Public key length (33 bytes)
bytes.fromhex( # Compressed public key
'025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357'
)
)
print("Generated witness:", witness.hex())
print("Expected witness:", expected_witness.hex())
assert witness == expected_witness, "Witness stack does not match BIP143 test vector"
print("Success! Witness stack matches BIP143 test vector")
When you run this code locally, it will generate the witness stack that matches the BIP143 test vector:
# Expected witness stack from BIP143
witness = "02" + # Number of witness items
"47" + # Signature length (71 bytes)
"304402203609e17b84f6a7d30c80bfa...01 # DER signature + SIGHASH_ALL
21 # pubkey length (33 bytes)
025476c2e83188368da1ff3e292e7aca...57 # compressed public key
Note about Browser Execution
This code cannot be run directly in the browser as Trinket doesn't support the ECDSA module. Please run the code locally to verify the witness stack generation. Here's what you should see when running locally:
