09 - scriptLen and scriptPubKey
Last updated
Last updated
Part of the transaction pre-image is the scriptPubKey held in the UTXO being spent in the input. This is broken down as two fields as follows:
scriptLen - the locking script length, a VarInt (1, 3, 5 or 9 bytes depending on script length)
scriptPubKey - the locking script for the UTXO being spent (Length as defined by previous parameter)
To extract the locking script, we must first extract the length. There are 4 possible sizes for the VarInt, depending on the length of the script. The size of this field can be inferred from the value in its first byte:
If the value is equal to or less than 0xFC, the varInt is a 1 byte integer value containing the integer value of scriptLen.
If the first byte is 0xFD, the varInt is 3 bytes long, with the last 2 bytes containing the integer value of scriptLen.
If the first byte is 0xFE, the varInt is 5 bytes long, with the last 4 bytes containing the integer value of scriptLen.
If the first byte is 0xFF, the varInt is 9 bytes long, with the last 8 bytes containing the integer value of scriptLen.
Typically, we can know roughly how big a script might be. A 2 byte length is valid from 253B up to 64kB so we can assume for our purposes this is what we are expecting.
<r_tx_preimg>
...
Version, hash_prevouts, hash_nSequence and hash_outpoints have been removed
<r_tx_preimg>
0P_3
VarInt is 3 bytes long
<r_tx_preimg> 0x03
OP_SPLIT
Split length
<varint> <rr_tx_preimg>
OP_SWAP
Move to top of stack
<rr_tx_preimg> <varint>
OP_1
First byte is VarInt length field
<rr_tx_preimg> <varint> 0x01
OP_SPLIT
Calculate txid using OP_HASH160
<rr_tx_preimg> <varint_id> <length_be>
OP_NIP
Nip varint ID
<rr_tx_preimg> <length_be>
OP_SPLIT
Split script from pre-image
<rrr_tx_preimg> <lock_script>
...
Rest of script
Now that the script is on the stack, it is possible for it to utilise data stored in itself to enforce the conditions of the next output state. We will get into this shortly.
This example splits a script of unknown length from the stack. It first splits off the 1-byte VarInt type identifier, checks whether the varInt is 1 byte, 3 bytes or 5 bytes long, and then where needed splits the length value from r_tx_preimg before separating the script from the pre-image.
<r_tx_preimg>
...
Version, hash_prevouts, hash_nSequence and hash_outpoints have been removed
<r_tx_preimg>
0P_1
VarInt type ID is 1 byte
<r_tx_preimg> 0x01
OP_SPLIT
Split type
<type> <rr_tx_preimg>
OP_SWAP
Move to top of stack
<rr_tx_preimg> <type>
OP_2
<rr_tx_preimg> <type> 0x02
OP_NUM2BIN
<rr_tx_preimg> <type>
OP_DUP
Duplicate type
<rr_tx_preimg> <type> <type>
0xFC
0xFC or less is 1 byte varInt
<rr_tx_preimg> <type> <type+00> 0xFC00
OP_GREATERTHANOREQUALTO
Is type >= 0xFE? - Use GREATERTHANOREQUAL because 0xFE is a NEGATIVE integer
<rr_tx_preimg> <type> <result>
OP_NOTIF
If NOT, enter loop. Otherwise <type> is length
<rr_tx_preimg> <type>
OP_DUP
Duplicate type
<rr_tx_preimg> <type> <type>
0xFD
Is length 2 bytes?
<rr_tx_preimg> <type> <type> 0xFD
OP_EQUAL
Test
<rr_tx_preimg> <type> <result>
OP_IF
If 2 bytes then...
<rr_tx_preimg> <type>
OP_DROP
Drop type
<rr_tx_preimg>
OP_2
2 byte length
<rr_tx_preimg> 0x02
OP_SPLIT
Split
<length> <rrr_tx_preimg>
OP_SWAP
Move length_bigendian to top of stack
<rr_tx_preimg> <type>
OP_ELSE
If not 2-byte
<rr_tx_preimg> <type>
0xFE
Is length 4 bytes?
<rr_tx_preimg> <type> 0xFE
OP_EQUAL
Check equality
<rr_tx_preimg> <result>
OP_IF
Enter if statement
<rr_tx_preimg>
OP_4
Length is 4 bytes
<rr_tx_preimg> 0x04
OP_SPLIT
Split length
<length> <rrr_tx_preimg>
OP_SWAP
Swap it to front
<rr_tx_preimg>
OP_ELSE
If not 4, must be 8
<rr_tx_preimg>
OP_8
Length is 8 bytes
<rr_tx_preimg> 0x08
OP_SPLIT
Split length
<length> <rrr_tx_preimg>
OP_SWAP
Swap to front
<rrr_tx_preimg> <length>
OP_ENDIF
Exit IF loop
<rrr_tx_preimg> <length>
OP_ENDIF
Exit IF loop
<rrr_tx_preimg> <length>
0x00
Add 00 to stack (cannot use OP_FALSE)
<rrr_tx_preimg> <length> 0x00
OP_CAT
Add zeroes to the length to ensure it will be interpreted as a positive integer
<rrr_tx_preimg> <length+00>
OP_BIN2NUM
Optimally encode the nubmer
<r_tx_preimg> <length>
OP_SPLIT
split the script from the pre-image remainder
<lock_script> <rrrr_tx_preimg>
OP_SWAP
Swap to the front
<rrrr_tx_preimg> <lock_script>
...
Rest of script
These script elements can easily be customised to your requirements as you define your OP_PUSH_TX script. Simple checks may require scripts smaller than 253 bytes allowing these checks to be optimised as needed. Understanding the processing of the scriptLen value is an important aspect of this process. Much care must be taken to handle integer values so that they are interpreted correctly.