SSZ hash_tree_root¶
Consensus-layer merkleization (SHA-256). The standard specification follows the recursive SSZ definition directly: a subtree is either its leaf, the precomputed zero root at that depth, or the hash of its two children. Source references are indexed only when recursion reaches a live leaf, so no leaf list or explicit Merkle frontier is materialized.
type htr_depth¶
The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)type htr_leaf_count¶
A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)Constants¶
The limits and byte offsets below are the SSZ dimensions of execution-layer requests and their constituent fields.
let HTR_BYTE_LIST_LIMIT¶
let HTR_BYTE_LIST_LIMIT : int(1073741824) = 1073741824let HTR_DEPOSIT_LENGTH¶
let HTR_DEPOSIT_LENGTH : int(192) = 192let HTR_DEPOSIT_PUBKEY¶
let HTR_DEPOSIT_PUBKEY : int(0) = 0let HTR_DEPOSIT_PUBKEY_LENGTH¶
let HTR_DEPOSIT_PUBKEY_LENGTH : int(48) = 48let HTR_DEPOSIT_WITHDRAWAL_CREDENTIALS¶
let HTR_DEPOSIT_WITHDRAWAL_CREDENTIALS : int(48) = 48let HTR_DEPOSIT_AMOUNT¶
let HTR_DEPOSIT_AMOUNT : int(80) = 80let HTR_DEPOSIT_SIGNATURE¶
let HTR_DEPOSIT_SIGNATURE : int(88) = 88let HTR_DEPOSIT_SIGNATURE_LENGTH¶
let HTR_DEPOSIT_SIGNATURE_LENGTH : int(96) = 96let HTR_DEPOSIT_INDEX¶
let HTR_DEPOSIT_INDEX : int(184) = 184let HTR_WITHDRAWAL_REQUEST_LENGTH¶
let HTR_WITHDRAWAL_REQUEST_LENGTH : int(76) = 76let HTR_REQUEST_SOURCE_ADDRESS¶
let HTR_REQUEST_SOURCE_ADDRESS : int(0) = 0let HTR_REQUEST_SOURCE_PUBKEY¶
let HTR_REQUEST_SOURCE_PUBKEY : int(20) = 20let HTR_REQUEST_PUBKEY_LENGTH¶
let HTR_REQUEST_PUBKEY_LENGTH : int(48) = 48let HTR_WITHDRAWAL_REQUEST_AMOUNT¶
let HTR_WITHDRAWAL_REQUEST_AMOUNT : int(68) = 68let HTR_CONSOLIDATION_REQUEST_LENGTH¶
let HTR_CONSOLIDATION_REQUEST_LENGTH : int(116) = 116let HTR_CONSOLIDATION_REQUEST_TARGET_PUBKEY¶
let HTR_CONSOLIDATION_REQUEST_TARGET_PUBKEY : int(68) = 68let HTR_BUILDER_DEPOSIT_LENGTH¶
let HTR_BUILDER_DEPOSIT_LENGTH : int(184) = 184let HTR_BUILDER_DEPOSIT_PUBKEY¶
let HTR_BUILDER_DEPOSIT_PUBKEY : int(0) = 0let HTR_BUILDER_DEPOSIT_WITHDRAWAL_CREDENTIALS¶
let HTR_BUILDER_DEPOSIT_WITHDRAWAL_CREDENTIALS : int(48) = 48let HTR_BUILDER_DEPOSIT_AMOUNT¶
let HTR_BUILDER_DEPOSIT_AMOUNT : int(80) = 80let HTR_BUILDER_DEPOSIT_SIGNATURE¶
let HTR_BUILDER_DEPOSIT_SIGNATURE : int(88) = 88let HTR_BUILDER_EXIT_LENGTH¶
let HTR_BUILDER_EXIT_LENGTH : int(68) = 68let HTR_BUILDER_EXIT_SOURCE_ADDRESS¶
let HTR_BUILDER_EXIT_SOURCE_ADDRESS : int(0) = 0let HTR_BUILDER_EXIT_PUBKEY¶
let HTR_BUILDER_EXIT_PUBKEY : int(20) = 20let SSZ_ZERO_HASH_01¶
let SSZ_ZERO_HASH_01 : hash = hash_from_bits(0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_02¶
let SSZ_ZERO_HASH_02 : hash = hash_from_bits(0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_03¶
let SSZ_ZERO_HASH_03 : hash = hash_from_bits(0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_04¶
let SSZ_ZERO_HASH_04 : hash = hash_from_bits(0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_05¶
let SSZ_ZERO_HASH_05 : hash = hash_from_bits(0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_06¶
let SSZ_ZERO_HASH_06 : hash = hash_from_bits(0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_07¶
let SSZ_ZERO_HASH_07 : hash = hash_from_bits(0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_08¶
let SSZ_ZERO_HASH_08 : hash = hash_from_bits(0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_09¶
let SSZ_ZERO_HASH_09 : hash = hash_from_bits(0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_10¶
let SSZ_ZERO_HASH_10 : hash = hash_from_bits(0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_11¶
let SSZ_ZERO_HASH_11 : hash = hash_from_bits(0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_12¶
let SSZ_ZERO_HASH_12 : hash = hash_from_bits(0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_13¶
let SSZ_ZERO_HASH_13 : hash = hash_from_bits(0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_14¶
let SSZ_ZERO_HASH_14 : hash = hash_from_bits(0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_15¶
let SSZ_ZERO_HASH_15 : hash = hash_from_bits(0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_16¶
let SSZ_ZERO_HASH_16 : hash = hash_from_bits(0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_17¶
let SSZ_ZERO_HASH_17 : hash = hash_from_bits(0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_18¶
let SSZ_ZERO_HASH_18 : hash = hash_from_bits(0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_19¶
let SSZ_ZERO_HASH_19 : hash = hash_from_bits(0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_20¶
let SSZ_ZERO_HASH_20 : hash = hash_from_bits(0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_21¶
let SSZ_ZERO_HASH_21 : hash = hash_from_bits(0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_22¶
let SSZ_ZERO_HASH_22 : hash = hash_from_bits(0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_23¶
let SSZ_ZERO_HASH_23 : hash = hash_from_bits(0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_24¶
let SSZ_ZERO_HASH_24 : hash = hash_from_bits(0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256let SSZ_ZERO_HASH_25¶
let SSZ_ZERO_HASH_25 : hash = hash_from_bits(0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544)Interprets canonical full-width bits directly as a digest.
function hash_from_bits(value : bits(256)) -> hash = {
let word_value = word_from_bits(value);
word_to_hash(word_value)
}The common digest type used by trie, code, and block hashes.
type hash = b256function ssz_zero_hash¶
The root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}let SSZ_ZERO_HASH_01 : hash = hash_from_bits(0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b)let SSZ_ZERO_HASH_02 : hash = hash_from_bits(0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71)let SSZ_ZERO_HASH_03 : hash = hash_from_bits(0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c)let SSZ_ZERO_HASH_04 : hash = hash_from_bits(0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c)let SSZ_ZERO_HASH_05 : hash = hash_from_bits(0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30)let SSZ_ZERO_HASH_06 : hash = hash_from_bits(0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1)let SSZ_ZERO_HASH_07 : hash = hash_from_bits(0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c)let SSZ_ZERO_HASH_08 : hash = hash_from_bits(0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193)let SSZ_ZERO_HASH_09 : hash = hash_from_bits(0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1)let SSZ_ZERO_HASH_10 : hash = hash_from_bits(0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b)let SSZ_ZERO_HASH_11 : hash = hash_from_bits(0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220)let SSZ_ZERO_HASH_12 : hash = hash_from_bits(0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f)let SSZ_ZERO_HASH_13 : hash = hash_from_bits(0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e)let SSZ_ZERO_HASH_14 : hash = hash_from_bits(0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784)let SSZ_ZERO_HASH_15 : hash = hash_from_bits(0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb)let SSZ_ZERO_HASH_16 : hash = hash_from_bits(0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb)let SSZ_ZERO_HASH_17 : hash = hash_from_bits(0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab)let SSZ_ZERO_HASH_18 : hash = hash_from_bits(0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4)let SSZ_ZERO_HASH_19 : hash = hash_from_bits(0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f)let SSZ_ZERO_HASH_20 : hash = hash_from_bits(0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa)let SSZ_ZERO_HASH_21 : hash = hash_from_bits(0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c)let SSZ_ZERO_HASH_22 : hash = hash_from_bits(0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167)let SSZ_ZERO_HASH_23 : hash = hash_from_bits(0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7)let SSZ_ZERO_HASH_24 : hash = hash_from_bits(0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0)let SSZ_ZERO_HASH_25 : hash = hash_from_bits(0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544)let ZERO_HASH : hash = hash_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The common digest type used by trie, code, and block hashes.
type hash = b256The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)function htr_leaf_capacity¶
Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function merkleize_vector_subtree¶
function merkleize_vector_subtree(leaves, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
let leaves_length = length(leaves);
if (count == 1) & (start < leaves_length) then {
leaves[leaves_length - 1 - start]
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = merkleize_vector_subtree(leaves, start, left_count, child_depth);
let right_root = merkleize_vector_subtree(leaves, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}function merkleize_vector_subtree(leaves, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
let leaves_length = length(leaves);
if (count == 1) & (start < leaves_length) then {
leaves[leaves_length - 1 - start]
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = merkleize_vector_subtree(leaves, start, left_count, child_depth);
let right_root = merkleize_vector_subtree(leaves, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashThe root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function merkleize¶
function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}function fatal_error(_reason) = exit(())Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}function merkleize_vector_subtree(leaves, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
let leaves_length = length(leaves);
if (count == 1) & (start < leaves_length) then {
leaves[leaves_length - 1 - start]
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = merkleize_vector_subtree(leaves, start, left_count, child_depth);
let right_root = merkleize_vector_subtree(leaves, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_uint¶
Encodes a protocol quantity as an SSZ little-endian basic-value chunk.
function htr_uint(v : ssz_uint) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = get_slice_int(8, v, 0);
bytes[1] = get_slice_int(8, v, 8);
bytes[2] = get_slice_int(8, v, 16);
bytes[3] = get_slice_int(8, v, 24);
bytes[4] = get_slice_int(8, v, 32);
bytes[5] = get_slice_int(8, v, 40);
bytes[6] = get_slice_int(8, v, 48);
bytes[7] = get_slice_int(8, v, 56);
B256(bytes)
}function B256(bytes : vector(32, inc, byte)) -> b256 = bytesval get_slice_int = pure {_: "get_slice_int"}: forall ('w : Int). (int('w), int, int) -> bits('w)let ZERO_HASH : hash = hash_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The common digest type used by trie, code, and block hashes.
type hash = b256An eight-byte unsigned integer read from the stateless-input SSZ schema. This is a transport type; decoders widen or validate it into the semantic field type at the container boundary.
type ssz_uint = range(0, 2 ^ 64 - 1)function htr_u256¶
uint256 as a 32-byte little-endian chunk.
function htr_u256(value : word) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = get_slice_int(8, value, 0);
bytes[1] = get_slice_int(8, value, 8);
bytes[2] = get_slice_int(8, value, 16);
bytes[3] = get_slice_int(8, value, 24);
bytes[4] = get_slice_int(8, value, 32);
bytes[5] = get_slice_int(8, value, 40);
bytes[6] = get_slice_int(8, value, 48);
bytes[7] = get_slice_int(8, value, 56);
bytes[8] = get_slice_int(8, value, 64);
bytes[9] = get_slice_int(8, value, 72);
bytes[10] = get_slice_int(8, value, 80);
bytes[11] = get_slice_int(8, value, 88);
bytes[12] = get_slice_int(8, value, 96);
bytes[13] = get_slice_int(8, value, 104);
bytes[14] = get_slice_int(8, value, 112);
bytes[15] = get_slice_int(8, value, 120);
bytes[16] = get_slice_int(8, value, 128);
bytes[17] = get_slice_int(8, value, 136);
bytes[18] = get_slice_int(8, value, 144);
bytes[19] = get_slice_int(8, value, 152);
bytes[20] = get_slice_int(8, value, 160);
bytes[21] = get_slice_int(8, value, 168);
bytes[22] = get_slice_int(8, value, 176);
bytes[23] = get_slice_int(8, value, 184);
bytes[24] = get_slice_int(8, value, 192);
bytes[25] = get_slice_int(8, value, 200);
bytes[26] = get_slice_int(8, value, 208);
bytes[27] = get_slice_int(8, value, 216);
bytes[28] = get_slice_int(8, value, 224);
bytes[29] = get_slice_int(8, value, 232);
bytes[30] = get_slice_int(8, value, 240);
bytes[31] = get_slice_int(8, value, 248);
B256(bytes)
}function B256(bytes : vector(32, inc, byte)) -> b256 = bytesval get_slice_int = pure {_: "get_slice_int"}: forall ('w : Int). (int('w), int, int) -> bits('w)let ZERO_HASH : hash = hash_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)The common digest type used by trie, code, and block hashes.
type hash = b256The EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function htr_bytes32¶
Bytes32 chunks are themselves.
The common digest type used by trie, code, and block hashes.
type hash = b256function htr_addr¶
A 20-byte address, left-aligned in its chunk.
function htr_addr(address_bytes : address) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = address_bytes[0];
bytes[1] = address_bytes[1];
bytes[2] = address_bytes[2];
bytes[3] = address_bytes[3];
bytes[4] = address_bytes[4];
bytes[5] = address_bytes[5];
bytes[6] = address_bytes[6];
bytes[7] = address_bytes[7];
bytes[8] = address_bytes[8];
bytes[9] = address_bytes[9];
bytes[10] = address_bytes[10];
bytes[11] = address_bytes[11];
bytes[12] = address_bytes[12];
bytes[13] = address_bytes[13];
bytes[14] = address_bytes[14];
bytes[15] = address_bytes[15];
bytes[16] = address_bytes[16];
bytes[17] = address_bytes[17];
bytes[18] = address_bytes[18];
bytes[19] = address_bytes[19];
B256(bytes)
}function B256(bytes : vector(32, inc, byte)) -> b256 = byteslet ZERO_HASH : hash = hash_from_bits(0x0000000000000000000000000000000000000000000000000000000000000000)A 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)The common digest type used by trie, code, and block hashes.
type hash = b256function htr_length_chunk¶
function htr_length_chunk(len) =
let length_word = word_of_byte_count(len) in
htr_u256(length_word)function htr_length_chunk(len) =
let length_word = word_of_byte_count(len) in
htr_u256(length_word)uint256 as a 32-byte little-endian chunk.
function htr_u256(value : word) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = get_slice_int(8, value, 0);
bytes[1] = get_slice_int(8, value, 8);
bytes[2] = get_slice_int(8, value, 16);
bytes[3] = get_slice_int(8, value, 24);
bytes[4] = get_slice_int(8, value, 32);
bytes[5] = get_slice_int(8, value, 40);
bytes[6] = get_slice_int(8, value, 48);
bytes[7] = get_slice_int(8, value, 56);
bytes[8] = get_slice_int(8, value, 64);
bytes[9] = get_slice_int(8, value, 72);
bytes[10] = get_slice_int(8, value, 80);
bytes[11] = get_slice_int(8, value, 88);
bytes[12] = get_slice_int(8, value, 96);
bytes[13] = get_slice_int(8, value, 104);
bytes[14] = get_slice_int(8, value, 112);
bytes[15] = get_slice_int(8, value, 120);
bytes[16] = get_slice_int(8, value, 128);
bytes[17] = get_slice_int(8, value, 136);
bytes[18] = get_slice_int(8, value, 144);
bytes[19] = get_slice_int(8, value, 152);
bytes[20] = get_slice_int(8, value, 160);
bytes[21] = get_slice_int(8, value, 168);
bytes[22] = get_slice_int(8, value, 176);
bytes[23] = get_slice_int(8, value, 184);
bytes[24] = get_slice_int(8, value, 192);
bytes[25] = get_slice_int(8, value, 200);
bytes[26] = get_slice_int(8, value, 208);
bytes[27] = get_slice_int(8, value, 216);
bytes[28] = get_slice_int(8, value, 224);
bytes[29] = get_slice_int(8, value, 232);
bytes[30] = get_slice_int(8, value, 240);
bytes[31] = get_slice_int(8, value, 248);
B256(bytes)
}function mix_in_length¶
function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)function htr_length_chunk(len) =
let length_word = word_of_byte_count(len) in
htr_u256(length_word)function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashfunction clog2¶
The ceiling of log2 — the Merkle depth of an n-chunk capacity.
function clog2(n : htr_leaf_count) -> htr_depth = {
var remaining : htr_leaf_count =
if n == 0 then 0 else n - 1;
var depth : htr_depth = 0;
foreach (_step from 0 to 24) {
if remaining != 0 then {
let current_depth = depth;
depth =
if current_depth < 25 then {
current_depth + 1
} else {
fatal_error(WitnessDeficient)
};
remaining = tdiv_nat(remaining, 2)
}
};
depth
}function fatal_error(_reason) = exit(())Truncating division specialized to a non-negative dividend and positive divisor. Singleton operands determine the exact natural-number result.
val tdiv_nat = pure {smt: "div", ocaml: "quotient", interpreter: "quotient", lem: "integerDiv", c: "tdiv_int", cpp: "tdiv_int", systemverilog: "tdiv_int", coq: "Z.quot", lean: "Nat.div", _: "tdiv_int"}: forall ('n : Int) ('m : Int), ('n >= 0 & 'm >= 1).
(int('n), int('m)) -> int(div('n, 'm))The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_chunk¶
function htr_chunk(bytes, chunk_index) =
if chunk_index <= 33554432 then {
let offset : source_pointer = chunk_index * WORD_BYTE_LENGTH;
let chunk_word = slice_load(bytes, offset);
word_to_hash(chunk_word)
} else {
fatal_error(WitnessDeficient)
}function fatal_error(_reason) = exit(())function htr_chunk(bytes, chunk_index) =
if chunk_index <= 33554432 then {
let offset : source_pointer = chunk_index * WORD_BYTE_LENGTH;
let chunk_word = slice_load(bytes, offset);
word_to_hash(chunk_word)
} else {
fatal_error(WitnessDeficient)
}Serializes an EVM word as a 32-byte big-endian digest.
function word_to_hash(value : word) -> hash = {
let zero_bytes = vector_init(32, 0x00);
var result : hash = B256(zero_bytes);
result[0] = get_slice_int(8, value, 248);
result[1] = get_slice_int(8, value, 240);
result[2] = get_slice_int(8, value, 232);
result[3] = get_slice_int(8, value, 224);
result[4] = get_slice_int(8, value, 216);
result[5] = get_slice_int(8, value, 208);
result[6] = get_slice_int(8, value, 200);
result[7] = get_slice_int(8, value, 192);
result[8] = get_slice_int(8, value, 184);
result[9] = get_slice_int(8, value, 176);
result[10] = get_slice_int(8, value, 168);
result[11] = get_slice_int(8, value, 160);
result[12] = get_slice_int(8, value, 152);
result[13] = get_slice_int(8, value, 144);
result[14] = get_slice_int(8, value, 136);
result[15] = get_slice_int(8, value, 128);
result[16] = get_slice_int(8, value, 120);
result[17] = get_slice_int(8, value, 112);
result[18] = get_slice_int(8, value, 104);
result[19] = get_slice_int(8, value, 96);
result[20] = get_slice_int(8, value, 88);
result[21] = get_slice_int(8, value, 80);
result[22] = get_slice_int(8, value, 72);
result[23] = get_slice_int(8, value, 64);
result[24] = get_slice_int(8, value, 56);
result[25] = get_slice_int(8, value, 48);
result[26] = get_slice_int(8, value, 40);
result[27] = get_slice_int(8, value, 32);
result[28] = get_slice_int(8, value, 24);
result[29] = get_slice_int(8, value, 16);
result[30] = get_slice_int(8, value, 8);
result[31] = get_slice_int(8, value, 0);
result
}let WORD_BYTE_LENGTH : int(32) = 32The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}An absolute byte position in a named source region.
type source_pointer = range(0, default_host_region_bound)function htr_chunk_count¶
function htr_chunk_count(byte_len) =
if byte_len <= HTR_BYTE_LIST_LIMIT then {
(byte_len + 31) / 32
} else {
fatal_error(WitnessDeficient)
}function fatal_error(_reason) = exit(())function htr_chunk_count(byte_len) =
if byte_len <= HTR_BYTE_LIST_LIMIT then {
(byte_len + 31) / 32
} else {
fatal_error(WitnessDeficient)
}let HTR_BYTE_LIST_LIMIT : int(1073741824) = 1073741824The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}function htr_bytes_subtree¶
function htr_bytes_subtree(bytes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(bytes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_bytes_subtree(bytes, start, left_count, child_depth);
let right_root = htr_bytes_subtree(bytes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())function htr_bytes_subtree(bytes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(bytes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_bytes_subtree(bytes, start, left_count, child_depth);
let right_root = htr_bytes_subtree(bytes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function htr_chunk(bytes, chunk_index) =
if chunk_index <= 33554432 then {
let offset : source_pointer = chunk_index * WORD_BYTE_LENGTH;
let chunk_word = slice_load(bytes, offset);
word_to_hash(chunk_word)
} else {
fatal_error(WitnessDeficient)
}Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashThe root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_bytes_root¶
The Merkle root of a byte span's chunks at depth.
function htr_bytes_root(bytes : StatelessInputSlice, depth : htr_depth) -> hash = {
let count = htr_chunk_count(bytes.len);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
htr_bytes_subtree(bytes, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}function fatal_error(_reason) = exit(())function htr_bytes_subtree(bytes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(bytes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_bytes_subtree(bytes, start, left_count, child_depth);
let right_root = htr_bytes_subtree(bytes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function htr_chunk_count(byte_len) =
if byte_len <= HTR_BYTE_LIST_LIMIT then {
(byte_len + 31) / 32
} else {
fatal_error(WitnessDeficient)
}Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)Helpers¶
function htr_bytevector¶
hash_tree_root of a fixed-size ByteVector.
function htr_bytevector(bytes : StatelessInputSlice) -> hash = {
let chunks = htr_chunk_count(bytes.len);
let depth = clog2(chunks);
htr_bytes_root(bytes, depth)
}The ceiling of log2 — the Merkle depth of an n-chunk capacity.
function clog2(n : htr_leaf_count) -> htr_depth = {
var remaining : htr_leaf_count =
if n == 0 then 0 else n - 1;
var depth : htr_depth = 0;
foreach (_step from 0 to 24) {
if remaining != 0 then {
let current_depth = depth;
depth =
if current_depth < 25 then {
current_depth + 1
} else {
fatal_error(WitnessDeficient)
};
remaining = tdiv_nat(remaining, 2)
}
};
depth
}The Merkle root of a byte span's chunks at depth.
function htr_bytes_root(bytes : StatelessInputSlice, depth : htr_depth) -> hash = {
let count = htr_chunk_count(bytes.len);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
htr_bytes_subtree(bytes, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}function htr_chunk_count(byte_len) =
if byte_len <= HTR_BYTE_LIST_LIMIT then {
(byte_len + 31) / 32
} else {
fatal_error(WitnessDeficient)
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_ssz_uint¶
function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)function decode_ssz_uint(input, offset) = {
let byte0 = slice_byte(input, offset);
let offset1 = ssz_field_offset(offset, 1);
let byte1 = slice_byte(input, offset1);
let offset2 = ssz_field_offset(offset, 2);
let byte2 = slice_byte(input, offset2);
let offset3 = ssz_field_offset(offset, 3);
let byte3 = slice_byte(input, offset3);
let offset4 = ssz_field_offset(offset, 4);
let byte4 = slice_byte(input, offset4);
let offset5 = ssz_field_offset(offset, 5);
let byte5 = slice_byte(input, offset5);
let offset6 = ssz_field_offset(offset, 6);
let byte6 = slice_byte(input, offset6);
let offset7 = ssz_field_offset(offset, 7);
let byte7 = slice_byte(input, offset7);
unsigned(byte0)
+ unsigned(byte1)
* 2 ^ 8
+ unsigned(byte2)
* 2 ^ 16
+ unsigned(byte3)
* 2 ^ 24
+ unsigned(byte4)
* 2 ^ 32
+ unsigned(byte5)
* 2 ^ 40
+ unsigned(byte6)
* 2 ^ 48
+ unsigned(byte7)
* 2 ^ 56
}function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)Encodes a protocol quantity as an SSZ little-endian basic-value chunk.
function htr_uint(v : ssz_uint) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = get_slice_int(8, v, 0);
bytes[1] = get_slice_int(8, v, 8);
bytes[2] = get_slice_int(8, v, 16);
bytes[3] = get_slice_int(8, v, 24);
bytes[4] = get_slice_int(8, v, 32);
bytes[5] = get_slice_int(8, v, 40);
bytes[6] = get_slice_int(8, v, 48);
bytes[7] = get_slice_int(8, v, 56);
B256(bytes)
}function htr_ssz_u256¶
function htr_ssz_u256(bytes, offset) =
let value = ssz_u256(bytes, offset) in
htr_u256(value)function htr_ssz_u256(bytes, offset) =
let value = ssz_u256(bytes, offset) in
htr_u256(value)uint256 as a 32-byte little-endian chunk.
function htr_u256(value : word) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = get_slice_int(8, value, 0);
bytes[1] = get_slice_int(8, value, 8);
bytes[2] = get_slice_int(8, value, 16);
bytes[3] = get_slice_int(8, value, 24);
bytes[4] = get_slice_int(8, value, 32);
bytes[5] = get_slice_int(8, value, 40);
bytes[6] = get_slice_int(8, value, 48);
bytes[7] = get_slice_int(8, value, 56);
bytes[8] = get_slice_int(8, value, 64);
bytes[9] = get_slice_int(8, value, 72);
bytes[10] = get_slice_int(8, value, 80);
bytes[11] = get_slice_int(8, value, 88);
bytes[12] = get_slice_int(8, value, 96);
bytes[13] = get_slice_int(8, value, 104);
bytes[14] = get_slice_int(8, value, 112);
bytes[15] = get_slice_int(8, value, 120);
bytes[16] = get_slice_int(8, value, 128);
bytes[17] = get_slice_int(8, value, 136);
bytes[18] = get_slice_int(8, value, 144);
bytes[19] = get_slice_int(8, value, 152);
bytes[20] = get_slice_int(8, value, 160);
bytes[21] = get_slice_int(8, value, 168);
bytes[22] = get_slice_int(8, value, 176);
bytes[23] = get_slice_int(8, value, 184);
bytes[24] = get_slice_int(8, value, 192);
bytes[25] = get_slice_int(8, value, 200);
bytes[26] = get_slice_int(8, value, 208);
bytes[27] = get_slice_int(8, value, 216);
bytes[28] = get_slice_int(8, value, 224);
bytes[29] = get_slice_int(8, value, 232);
bytes[30] = get_slice_int(8, value, 240);
bytes[31] = get_slice_int(8, value, 248);
B256(bytes)
}Decodes a 32-byte little-endian SSZ integer into an EVM word.
function ssz_u256(input, offset) = {
var result : word = WORD_ZERO;
foreach (k from 0 to 31) {
let source_index = ssz_u256_index(k);
let source_offset = ssz_field_offset(offset, source_index);
let source_byte = slice_byte(input, source_offset);
let byte_value = unsigned(source_byte);
let shifted_result = word_mul(result, 256);
result = word_add(shifted_result, byte_value)
};
result
}function htr_ssz_bytes32¶
function htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)Bytes32 chunks are themselves.
function htr_bytes32(b : hash) -> hash = bfunction htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)function ssz_bytes32(input, offset) = {
let value = slice_load(input, offset);
word_to_hash(value)
}function htr_ssz_addr¶
function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)A 20-byte address, left-aligned in its chunk.
function htr_addr(address_bytes : address) -> hash = {
var bytes = ZERO_HASH;
bytes[0] = address_bytes[0];
bytes[1] = address_bytes[1];
bytes[2] = address_bytes[2];
bytes[3] = address_bytes[3];
bytes[4] = address_bytes[4];
bytes[5] = address_bytes[5];
bytes[6] = address_bytes[6];
bytes[7] = address_bytes[7];
bytes[8] = address_bytes[8];
bytes[9] = address_bytes[9];
bytes[10] = address_bytes[10];
bytes[11] = address_bytes[11];
bytes[12] = address_bytes[12];
bytes[13] = address_bytes[13];
bytes[14] = address_bytes[14];
bytes[15] = address_bytes[15];
bytes[16] = address_bytes[16];
bytes[17] = address_bytes[17];
bytes[18] = address_bytes[18];
bytes[19] = address_bytes[19];
B256(bytes)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function ssz_addr(input, offset) = {
let value = slice_load_n(input, offset, ADDRESS_BYTE_LENGTH);
word_to_address(value)
}function htr_ssz_bytevector¶
function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function fatal_error(_reason) = exit(())hash_tree_root of a fixed-size ByteVector.
function htr_bytevector(bytes : StatelessInputSlice) -> hash = {
let chunks = htr_chunk_count(bytes.len);
let depth = clog2(chunks);
htr_bytes_root(bytes, depth)
}function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}function htr_bytelist¶
function htr_bytelist(bytes, limit_bytes) = {
let capacity = htr_chunk_count(limit_bytes);
let depth = clog2(capacity);
let root = htr_bytes_root(bytes, depth);
mix_in_length(root, bytes.len)
}The ceiling of log2 — the Merkle depth of an n-chunk capacity.
function clog2(n : htr_leaf_count) -> htr_depth = {
var remaining : htr_leaf_count =
if n == 0 then 0 else n - 1;
var depth : htr_depth = 0;
foreach (_step from 0 to 24) {
if remaining != 0 then {
let current_depth = depth;
depth =
if current_depth < 25 then {
current_depth + 1
} else {
fatal_error(WitnessDeficient)
};
remaining = tdiv_nat(remaining, 2)
}
};
depth
}function htr_bytelist(bytes, limit_bytes) = {
let capacity = htr_chunk_count(limit_bytes);
let depth = clog2(capacity);
let root = htr_bytes_root(bytes, depth);
mix_in_length(root, bytes.len)
}The Merkle root of a byte span's chunks at depth.
function htr_bytes_root(bytes : StatelessInputSlice, depth : htr_depth) -> hash = {
let count = htr_chunk_count(bytes.len);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
htr_bytes_subtree(bytes, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}function htr_chunk_count(byte_len) =
if byte_len <= HTR_BYTE_LIST_LIMIT then {
(byte_len + 31) / 32
} else {
fatal_error(WitnessDeficient)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)The execution payload¶
function htr_withdrawal¶
hash_tree_root of one SSZ withdrawal (4 fields, depth 2).
function htr_withdrawal(withdrawal : StatelessInputSliceLength(44)) -> hash = {
let index_root = htr_ssz_uint(withdrawal, WD_INDEX);
let validator_root = htr_ssz_uint(withdrawal, WD_VALIDATOR_INDEX);
let address_root = htr_ssz_addr(withdrawal, WD_ADDRESS);
let amount_root = htr_ssz_uint(withdrawal, WD_AMOUNT);
merkleize([index_root, validator_root, address_root, amount_root], 2)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let WD_ADDRESS : int(16) = 16let WD_AMOUNT : int(36) = 36let WD_INDEX : int(0) = 0let WD_VALIDATOR_INDEX : int(8) = 8A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_transactions_subtree¶
function htr_transactions_subtree(transactions, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let transaction = ssz_list_at(transactions, start);
htr_bytelist(transaction, HTR_BYTE_LIST_LIMIT)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_transactions_subtree(transactions, start, left_count, child_depth);
let right_root = htr_transactions_subtree(transactions, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())function htr_bytelist(bytes, limit_bytes) = {
let capacity = htr_chunk_count(limit_bytes);
let depth = clog2(capacity);
let root = htr_bytes_root(bytes, depth);
mix_in_length(root, bytes.len)
}Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}function htr_transactions_subtree(transactions, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let transaction = ssz_list_at(transactions, start);
htr_bytelist(transaction, HTR_BYTE_LIST_LIMIT)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_transactions_subtree(transactions, start, left_count, child_depth);
let right_root = htr_transactions_subtree(transactions, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashReturns a variable-width list item by resolving its adjacent offsets.
function ssz_list_at forall ('maximum : Int) ('index : Int),
source_valid_length('maximum) & 'maximum <= 2 ^ 30 - 1 & 0 <= 'index. (
items : BoundedSszListRef('maximum),
index : int('index),
) -> (
StatelessInputSlice
) = {
let bytes = items.bytes;
let count = items.count;
let item_index : range(0, 'maximum - 1) =
if index < count then index else fatal_error(InvalidConfig);
let next_index : range(0, 'maximum) = item_index + 1;
let start_position = ssz_offset_table_position(item_index);
let start_offset = ssz_u32_in_slice(bytes, start_position);
let start = ssz_offset_to_source_pointer(start_offset);
let stop : source_pointer =
if next_index < items.count then {
let stop_position = ssz_offset_table_position(next_index);
let stop_offset = ssz_u32_in_slice(bytes, stop_position);
ssz_offset_to_source_pointer(stop_offset)
} else {
bytes.len
};
let start_value = start;
let stop_value = stop;
let items_length = bytes.len;
if (start_value <= stop_value) & (stop_value <= items_length) then {
let item_length = stop_value - start_value;
if (items.max_item_length != 0) & (items.max_item_length < item_length) then {
fatal_error(InvalidConfig)
};
sub_slice(bytes, start, item_length)
} else {
fatal_error(InvalidConfig)
}
}The root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}let HTR_BYTE_LIST_LIMIT : int(1073741824) = 1073741824The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_transactions¶
hash_tree_root of the transactions list
(List[ByteList[2^30], 2^20]).
function htr_transactions(transactions : TransactionListRef) -> hash = {
let count : htr_leaf_count = transactions.count;
let root = htr_transactions_subtree(transactions, 0, count, 20);
mix_in_length(root, transactions.count)
}function htr_transactions_subtree(transactions, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let transaction = ssz_list_at(transactions, start);
htr_bytelist(transaction, HTR_BYTE_LIST_LIMIT)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_transactions_subtree(transactions, start, left_count, child_depth);
let right_root = htr_transactions_subtree(transactions, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)A schema-bounded source reference to encoded transaction envelopes.
type TransactionListRef = BoundedSszListRef(transaction_count_bound)The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_withdrawals_subtree¶
function htr_withdrawals_subtree(withdrawals, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let withdrawal = ssz_fixed_list_at(withdrawals, start, WD_SIZE);
htr_withdrawal(withdrawal)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_withdrawals_subtree(withdrawals, start, left_count, child_depth);
let right_root = htr_withdrawals_subtree(withdrawals, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}hash_tree_root of one SSZ withdrawal (4 fields, depth 2).
function htr_withdrawal(withdrawal : StatelessInputSliceLength(44)) -> hash = {
let index_root = htr_ssz_uint(withdrawal, WD_INDEX);
let validator_root = htr_ssz_uint(withdrawal, WD_VALIDATOR_INDEX);
let address_root = htr_ssz_addr(withdrawal, WD_ADDRESS);
let amount_root = htr_ssz_uint(withdrawal, WD_AMOUNT);
merkleize([index_root, validator_root, address_root, amount_root], 2)
}function htr_withdrawals_subtree(withdrawals, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let withdrawal = ssz_fixed_list_at(withdrawals, start, WD_SIZE);
htr_withdrawal(withdrawal)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_withdrawals_subtree(withdrawals, start, left_count, child_depth);
let right_root = htr_withdrawals_subtree(withdrawals, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashfunction ssz_fixed_list_at(items, index, item_size) = {
let bytes = items.bytes;
let width_value = item_size;
let offset_value = index * width_value;
let items_length = bytes.len;
if (index < items.count) & (offset_value + width_value <= items_length) then {
sub_slice(bytes, offset_value, width_value)
} else {
fatal_error(InvalidConfig)
}
}The root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}let WD_SIZE : int(44) = 44The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_withdrawals¶
hash_tree_root of the withdrawals list (List[SszWithdrawal, 16]).
function htr_withdrawals(withdrawals : WithdrawalListRef) -> hash = {
let count : htr_leaf_count = withdrawals.count;
let root = htr_withdrawals_subtree(withdrawals, 0, count, 4);
mix_in_length(root, withdrawals.count)
}function htr_withdrawals_subtree(withdrawals, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
let withdrawal = ssz_fixed_list_at(withdrawals, start, WD_SIZE);
htr_withdrawal(withdrawal)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_withdrawals_subtree(withdrawals, start, left_count, child_depth);
let right_root = htr_withdrawals_subtree(withdrawals, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)A schema-bounded source reference to withdrawals.
type WithdrawalListRef = BoundedSszListRef(withdrawal_count_bound)The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_execution_payload¶
hash_tree_root of the SszExecutionPayload (19 fields, depth 5).
function htr_execution_payload(input_ref : StatelessInputRef) -> hash = {
let payload = input_ref.execution_payload;
let parent_hash_root = htr_ssz_bytes32(payload, 0);
let fee_recipient_root = htr_ssz_addr(payload, PL_FEE_RECIPIENT);
let state_root = htr_ssz_bytes32(payload, PL_STATE_ROOT);
let receipts_root = htr_ssz_bytes32(payload, PL_RECEIPTS_ROOT);
let logs_bloom_root = htr_ssz_bytevector(payload, PL_LOGS_BLOOM, LOGS_BLOOM_BYTE_LENGTH);
let prev_randao_root = htr_ssz_bytes32(payload, PL_PREV_RANDAO);
let block_number_root = htr_ssz_uint(payload, PL_BLOCK_NUMBER);
let gas_limit_root = htr_ssz_uint(payload, PL_GAS_LIMIT);
let gas_used_root = htr_ssz_uint(payload, PL_GAS_USED);
let timestamp_root = htr_ssz_uint(payload, PL_TIMESTAMP);
let extra_data_root = htr_bytelist(input_ref.extra_data, WORD_BYTE_LENGTH);
let base_fee_root = htr_ssz_u256(payload, PL_BASE_FEE);
let block_hash_root = htr_ssz_bytes32(payload, PL_BLOCK_HASH);
let transactions_root = htr_transactions(input_ref.transactions);
let withdrawals_root = htr_withdrawals(input_ref.withdrawals);
let blob_gas_used_root = htr_ssz_uint(payload, PL_BLOB_GAS_USED);
let excess_blob_gas_root = htr_ssz_uint(payload, PL_EXCESS_BLOB_GAS);
let block_access_list_root = htr_bytelist(input_ref.block_access_list, HTR_BYTE_LIST_LIMIT);
let slot_number_root = htr_ssz_uint(payload, PL_SLOT_NUMBER);
merkleize(
[
parent_hash_root,
fee_recipient_root,
state_root,
receipts_root,
logs_bloom_root,
prev_randao_root,
block_number_root,
gas_limit_root,
gas_used_root,
timestamp_root,
extra_data_root,
base_fee_root,
block_hash_root,
transactions_root,
withdrawals_root,
blob_gas_used_root,
excess_blob_gas_root,
block_access_list_root,
slot_number_root,
],
5,
)
}function htr_bytelist(bytes, limit_bytes) = {
let capacity = htr_chunk_count(limit_bytes);
let depth = clog2(capacity);
let root = htr_bytes_root(bytes, depth);
mix_in_length(root, bytes.len)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function htr_ssz_u256(bytes, offset) =
let value = ssz_u256(bytes, offset) in
htr_u256(value)function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)hash_tree_root of the transactions list
(List[ByteList[2^30], 2^20]).
function htr_transactions(transactions : TransactionListRef) -> hash = {
let count : htr_leaf_count = transactions.count;
let root = htr_transactions_subtree(transactions, 0, count, 20);
mix_in_length(root, transactions.count)
}hash_tree_root of the withdrawals list (List[SszWithdrawal, 16]).
function htr_withdrawals(withdrawals : WithdrawalListRef) -> hash = {
let count : htr_leaf_count = withdrawals.count;
let root = htr_withdrawals_subtree(withdrawals, 0, count, 4);
mix_in_length(root, withdrawals.count)
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_BYTE_LIST_LIMIT : int(1073741824) = 1073741824let LOGS_BLOOM_BYTE_LENGTH : int(256) = 256let PL_BASE_FEE : int(440) = 440let PL_BLOB_GAS_USED : int(512) = 512let PL_BLOCK_HASH : int(472) = 472let PL_BLOCK_NUMBER : int(404) = 404let PL_EXCESS_BLOB_GAS : int(520) = 520let PL_FEE_RECIPIENT : int(32) = 32let PL_GAS_LIMIT : int(412) = 412let PL_GAS_USED : int(420) = 420let PL_LOGS_BLOOM : int(116) = 116let PL_PREV_RANDAO : int(372) = 372let PL_RECEIPTS_ROOT : int(84) = 84let PL_SLOT_NUMBER : int(532) = 532let PL_STATE_ROOT : int(52) = 52let PL_TIMESTAMP : int(428) = 428let WORD_BYTE_LENGTH : int(32) = 32Every variable region of the input, resolved once before decoding. Consumers receive explicit source spans instead of re-reading nested SSZ offset tables.
struct StatelessInputRef = {
protocol : ProtocolProfile,
new_payload_request : StatelessInputSlice,
execution_payload : StatelessInputSliceAtLeast(540),
versioned_hashes : StatelessInputSlice,
deposits : StatelessInputSlice,
withdrawal_requests : StatelessInputSlice,
consolidation_requests : StatelessInputSlice,
builder_deposit_requests : StatelessInputSlice,
builder_exit_requests : StatelessInputSlice,
extra_data : StatelessInputSliceAtMost(extra_data_length_bound),
transactions : TransactionListRef,
withdrawals : WithdrawalListRef,
block_access_list : StatelessInputSliceAtMost(block_access_list_length_bound),
witness_state : WitnessNodeListRef,
witness_codes : WitnessCodeListRef,
witness_headers : WitnessHeaderListRef,
chain_config : StatelessInputSlice,
public_keys : StatelessInputSlice,
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_versioned_hashes_subtree¶
function htr_versioned_hashes_subtree(versioned_hashes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(versioned_hashes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_versioned_hashes_subtree(versioned_hashes, start, left_count, child_depth);
let right_root = htr_versioned_hashes_subtree(versioned_hashes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())function htr_chunk(bytes, chunk_index) =
if chunk_index <= 33554432 then {
let offset : source_pointer = chunk_index * WORD_BYTE_LENGTH;
let chunk_word = slice_load(bytes, offset);
word_to_hash(chunk_word)
} else {
fatal_error(WitnessDeficient)
}Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}function htr_versioned_hashes_subtree(versioned_hashes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(versioned_hashes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_versioned_hashes_subtree(versioned_hashes, start, left_count, child_depth);
let right_root = htr_versioned_hashes_subtree(versioned_hashes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashThe root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_versioned_hashes¶
hash_tree_root of the versioned hashes (List[Bytes32, 4096]).
function htr_versioned_hashes(versioned_hashes : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(versioned_hashes, WORD_BYTE_LENGTH, sizeof(blob_commitment_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_versioned_hashes_subtree(versioned_hashes, 0, count, 12);
mix_in_length(root, items.count)
}function htr_versioned_hashes_subtree(versioned_hashes, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_chunk(versioned_hashes, start)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_versioned_hashes_subtree(versioned_hashes, start, left_count, child_depth);
let right_root = htr_versioned_hashes_subtree(versioned_hashes, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let WORD_BYTE_LENGTH : int(32) = 32A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}Maximum blob commitments in the stateless-input SSZ list.
type blob_commitment_count_bound : Int = 2 ^ 12The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)The execution requests¶
The EIP-7685 execution-request commitments.
function htr_deposit¶
hash_tree_root of one deposit request (5 fields, depth 3).
function htr_deposit(deposit : StatelessInputSliceLength(192)) -> hash = {
let pubkey = htr_ssz_bytevector(deposit, HTR_DEPOSIT_PUBKEY, HTR_DEPOSIT_PUBKEY_LENGTH);
let withdrawal_credentials = htr_ssz_bytes32(deposit, HTR_DEPOSIT_WITHDRAWAL_CREDENTIALS);
let amount = htr_ssz_uint(deposit, HTR_DEPOSIT_AMOUNT);
let signature = htr_ssz_bytevector(deposit, HTR_DEPOSIT_SIGNATURE, HTR_DEPOSIT_SIGNATURE_LENGTH);
let index = htr_ssz_uint(deposit, HTR_DEPOSIT_INDEX);
merkleize([pubkey, withdrawal_credentials, amount, signature, index], 3)
}function htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_DEPOSIT_AMOUNT : int(80) = 80let HTR_DEPOSIT_INDEX : int(184) = 184let HTR_DEPOSIT_PUBKEY : int(0) = 0let HTR_DEPOSIT_PUBKEY_LENGTH : int(48) = 48let HTR_DEPOSIT_SIGNATURE : int(88) = 88let HTR_DEPOSIT_SIGNATURE_LENGTH : int(96) = 96let HTR_DEPOSIT_WITHDRAWAL_CREDENTIALS : int(48) = 48A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_withdrawal_request¶
hash_tree_root of one withdrawal request (3 fields, depth 2).
function htr_withdrawal_request(request : StatelessInputSliceLength(76)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_REQUEST_SOURCE_ADDRESS);
let source_pubkey = htr_ssz_bytevector(request, HTR_REQUEST_SOURCE_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let amount = htr_ssz_uint(request, HTR_WITHDRAWAL_REQUEST_AMOUNT);
merkleize([source_address, source_pubkey, amount], 2)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_REQUEST_PUBKEY_LENGTH : int(48) = 48let HTR_REQUEST_SOURCE_ADDRESS : int(0) = 0let HTR_REQUEST_SOURCE_PUBKEY : int(20) = 20let HTR_WITHDRAWAL_REQUEST_AMOUNT : int(68) = 68A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_consolidation_request¶
hash_tree_root of one consolidation request (3 fields, depth 2).
function htr_consolidation_request(request : StatelessInputSliceLength(116)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_REQUEST_SOURCE_ADDRESS);
let source_pubkey = htr_ssz_bytevector(request, HTR_REQUEST_SOURCE_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let target_pubkey = htr_ssz_bytevector(request, HTR_CONSOLIDATION_REQUEST_TARGET_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
merkleize([source_address, source_pubkey, target_pubkey], 2)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_CONSOLIDATION_REQUEST_TARGET_PUBKEY : int(68) = 68let HTR_REQUEST_PUBKEY_LENGTH : int(48) = 48let HTR_REQUEST_SOURCE_ADDRESS : int(0) = 0let HTR_REQUEST_SOURCE_PUBKEY : int(20) = 20A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_builder_deposit_request¶
hash_tree_root of one builder deposit request (4 fields, depth 2).
function htr_builder_deposit_request(request : StatelessInputSliceLength(184)) -> hash = {
let pubkey = htr_ssz_bytevector(request, HTR_BUILDER_DEPOSIT_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let withdrawal_credentials = htr_ssz_bytes32(request, HTR_BUILDER_DEPOSIT_WITHDRAWAL_CREDENTIALS);
let amount = htr_ssz_uint(request, HTR_BUILDER_DEPOSIT_AMOUNT);
let signature = htr_ssz_bytevector(request, HTR_BUILDER_DEPOSIT_SIGNATURE, HTR_DEPOSIT_SIGNATURE_LENGTH);
merkleize([pubkey, withdrawal_credentials, amount, signature], 2)
}function htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function htr_ssz_uint(bytes, offset) =
let value = decode_ssz_uint(bytes, offset) in
htr_uint(value)function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_BUILDER_DEPOSIT_AMOUNT : int(80) = 80let HTR_BUILDER_DEPOSIT_PUBKEY : int(0) = 0let HTR_BUILDER_DEPOSIT_SIGNATURE : int(88) = 88let HTR_BUILDER_DEPOSIT_WITHDRAWAL_CREDENTIALS : int(48) = 48let HTR_DEPOSIT_SIGNATURE_LENGTH : int(96) = 96let HTR_REQUEST_PUBKEY_LENGTH : int(48) = 48A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_builder_exit_request¶
hash_tree_root of one builder exit request (2 fields, depth 1).
function htr_builder_exit_request(request : StatelessInputSliceLength(68)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_BUILDER_EXIT_SOURCE_ADDRESS);
let pubkey = htr_ssz_bytevector(request, HTR_BUILDER_EXIT_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
merkleize([source_address, pubkey], 1)
}function htr_ssz_addr(bytes, offset) =
let value = ssz_addr(bytes, offset) in
htr_addr(value)function htr_ssz_bytevector(bytes, offset, len) = {
if offset + len <= bytes.len then {
let field = sub_slice(bytes, offset, len);
htr_bytevector(field)
} else {
fatal_error(InvalidConfig)
}
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let HTR_BUILDER_EXIT_PUBKEY : int(20) = 20let HTR_BUILDER_EXIT_SOURCE_ADDRESS : int(0) = 0let HTR_REQUEST_PUBKEY_LENGTH : int(48) = 48A stateless-input range of exactly 'required bytes.
type StatelessInputSliceLength('required : Int) = {
'off 'len,
stateless_input_valid_range('off, 'len) & 'len == 'required.
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256type HtrRequestKind¶
The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}function htr_request_leaf¶
function htr_request_leaf(requests, index, kind) =
match kind {
HtrDeposit => {
let offset = index * HTR_DEPOSIT_LENGTH;
if offset + HTR_DEPOSIT_LENGTH <= requests.len then {
let deposit = sub_slice(requests, offset, HTR_DEPOSIT_LENGTH);
htr_deposit(deposit)
} else {
fatal_error(InvalidConfig)
}
},
HtrWithdrawalRequest => {
let offset = index * HTR_WITHDRAWAL_REQUEST_LENGTH;
if offset + HTR_WITHDRAWAL_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_WITHDRAWAL_REQUEST_LENGTH);
htr_withdrawal_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrConsolidationRequest => {
let offset = index * HTR_CONSOLIDATION_REQUEST_LENGTH;
if offset + HTR_CONSOLIDATION_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_CONSOLIDATION_REQUEST_LENGTH);
htr_consolidation_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderDepositRequest => {
let offset = index * HTR_BUILDER_DEPOSIT_LENGTH;
if offset + HTR_BUILDER_DEPOSIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_DEPOSIT_LENGTH);
htr_builder_deposit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderExitRequest => {
let offset = index * HTR_BUILDER_EXIT_LENGTH;
if offset + HTR_BUILDER_EXIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_EXIT_LENGTH);
htr_builder_exit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
}function fatal_error(_reason) = exit(())hash_tree_root of one builder deposit request (4 fields, depth 2).
function htr_builder_deposit_request(request : StatelessInputSliceLength(184)) -> hash = {
let pubkey = htr_ssz_bytevector(request, HTR_BUILDER_DEPOSIT_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let withdrawal_credentials = htr_ssz_bytes32(request, HTR_BUILDER_DEPOSIT_WITHDRAWAL_CREDENTIALS);
let amount = htr_ssz_uint(request, HTR_BUILDER_DEPOSIT_AMOUNT);
let signature = htr_ssz_bytevector(request, HTR_BUILDER_DEPOSIT_SIGNATURE, HTR_DEPOSIT_SIGNATURE_LENGTH);
merkleize([pubkey, withdrawal_credentials, amount, signature], 2)
}hash_tree_root of one builder exit request (2 fields, depth 1).
function htr_builder_exit_request(request : StatelessInputSliceLength(68)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_BUILDER_EXIT_SOURCE_ADDRESS);
let pubkey = htr_ssz_bytevector(request, HTR_BUILDER_EXIT_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
merkleize([source_address, pubkey], 1)
}hash_tree_root of one consolidation request (3 fields, depth 2).
function htr_consolidation_request(request : StatelessInputSliceLength(116)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_REQUEST_SOURCE_ADDRESS);
let source_pubkey = htr_ssz_bytevector(request, HTR_REQUEST_SOURCE_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let target_pubkey = htr_ssz_bytevector(request, HTR_CONSOLIDATION_REQUEST_TARGET_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
merkleize([source_address, source_pubkey, target_pubkey], 2)
}hash_tree_root of one deposit request (5 fields, depth 3).
function htr_deposit(deposit : StatelessInputSliceLength(192)) -> hash = {
let pubkey = htr_ssz_bytevector(deposit, HTR_DEPOSIT_PUBKEY, HTR_DEPOSIT_PUBKEY_LENGTH);
let withdrawal_credentials = htr_ssz_bytes32(deposit, HTR_DEPOSIT_WITHDRAWAL_CREDENTIALS);
let amount = htr_ssz_uint(deposit, HTR_DEPOSIT_AMOUNT);
let signature = htr_ssz_bytevector(deposit, HTR_DEPOSIT_SIGNATURE, HTR_DEPOSIT_SIGNATURE_LENGTH);
let index = htr_ssz_uint(deposit, HTR_DEPOSIT_INDEX);
merkleize([pubkey, withdrawal_credentials, amount, signature, index], 3)
}function htr_request_leaf(requests, index, kind) =
match kind {
HtrDeposit => {
let offset = index * HTR_DEPOSIT_LENGTH;
if offset + HTR_DEPOSIT_LENGTH <= requests.len then {
let deposit = sub_slice(requests, offset, HTR_DEPOSIT_LENGTH);
htr_deposit(deposit)
} else {
fatal_error(InvalidConfig)
}
},
HtrWithdrawalRequest => {
let offset = index * HTR_WITHDRAWAL_REQUEST_LENGTH;
if offset + HTR_WITHDRAWAL_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_WITHDRAWAL_REQUEST_LENGTH);
htr_withdrawal_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrConsolidationRequest => {
let offset = index * HTR_CONSOLIDATION_REQUEST_LENGTH;
if offset + HTR_CONSOLIDATION_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_CONSOLIDATION_REQUEST_LENGTH);
htr_consolidation_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderDepositRequest => {
let offset = index * HTR_BUILDER_DEPOSIT_LENGTH;
if offset + HTR_BUILDER_DEPOSIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_DEPOSIT_LENGTH);
htr_builder_deposit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderExitRequest => {
let offset = index * HTR_BUILDER_EXIT_LENGTH;
if offset + HTR_BUILDER_EXIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_EXIT_LENGTH);
htr_builder_exit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
}hash_tree_root of one withdrawal request (3 fields, depth 2).
function htr_withdrawal_request(request : StatelessInputSliceLength(76)) -> hash = {
let source_address = htr_ssz_addr(request, HTR_REQUEST_SOURCE_ADDRESS);
let source_pubkey = htr_ssz_bytevector(request, HTR_REQUEST_SOURCE_PUBKEY, HTR_REQUEST_PUBKEY_LENGTH);
let amount = htr_ssz_uint(request, HTR_WITHDRAWAL_REQUEST_AMOUNT);
merkleize([source_address, source_pubkey, amount], 2)
}let HTR_BUILDER_DEPOSIT_LENGTH : int(184) = 184let HTR_BUILDER_EXIT_LENGTH : int(68) = 68let HTR_CONSOLIDATION_REQUEST_LENGTH : int(116) = 116let HTR_DEPOSIT_LENGTH : int(192) = 192let HTR_WITHDRAWAL_REQUEST_LENGTH : int(76) = 76The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}function htr_requests_subtree¶
function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function fatal_error(_reason) = exit(())Number of leaves in a complete supported SSZ tree at depth.
function htr_leaf_capacity(depth : htr_depth) -> htr_leaf_count = match depth {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 128,
8 => 256,
9 => 512,
10 => 1024,
11 => 2048,
12 => 4096,
13 => 8192,
14 => 16384,
15 => 32768,
16 => 65536,
17 => 131072,
18 => 262144,
19 => 524288,
20 => 1048576,
21 => 2097152,
22 => 4194304,
23 => 8388608,
24 => 16777216,
25 => 33554432,
}function htr_request_leaf(requests, index, kind) =
match kind {
HtrDeposit => {
let offset = index * HTR_DEPOSIT_LENGTH;
if offset + HTR_DEPOSIT_LENGTH <= requests.len then {
let deposit = sub_slice(requests, offset, HTR_DEPOSIT_LENGTH);
htr_deposit(deposit)
} else {
fatal_error(InvalidConfig)
}
},
HtrWithdrawalRequest => {
let offset = index * HTR_WITHDRAWAL_REQUEST_LENGTH;
if offset + HTR_WITHDRAWAL_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_WITHDRAWAL_REQUEST_LENGTH);
htr_withdrawal_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrConsolidationRequest => {
let offset = index * HTR_CONSOLIDATION_REQUEST_LENGTH;
if offset + HTR_CONSOLIDATION_REQUEST_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_CONSOLIDATION_REQUEST_LENGTH);
htr_consolidation_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderDepositRequest => {
let offset = index * HTR_BUILDER_DEPOSIT_LENGTH;
if offset + HTR_BUILDER_DEPOSIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_DEPOSIT_LENGTH);
htr_builder_deposit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
HtrBuilderExitRequest => {
let offset = index * HTR_BUILDER_EXIT_LENGTH;
if offset + HTR_BUILDER_EXIT_LENGTH <= requests.len then {
let request = sub_slice(requests, offset, HTR_BUILDER_EXIT_LENGTH);
htr_builder_exit_request(request)
} else {
fatal_error(InvalidConfig)
}
},
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}The SSZ Merkle parent: SHA-256(left ++ right) over two 32-byte
chunks.
val sha256_pair = impure { c: "host_sha256_pair" } : (hash, hash) -> hashThe root of an all-zero subtree at level.
function ssz_zero_hash(level : htr_depth) -> hash = match level {
0 => ZERO_HASH,
1 => SSZ_ZERO_HASH_01,
2 => SSZ_ZERO_HASH_02,
3 => SSZ_ZERO_HASH_03,
4 => SSZ_ZERO_HASH_04,
5 => SSZ_ZERO_HASH_05,
6 => SSZ_ZERO_HASH_06,
7 => SSZ_ZERO_HASH_07,
8 => SSZ_ZERO_HASH_08,
9 => SSZ_ZERO_HASH_09,
10 => SSZ_ZERO_HASH_10,
11 => SSZ_ZERO_HASH_11,
12 => SSZ_ZERO_HASH_12,
13 => SSZ_ZERO_HASH_13,
14 => SSZ_ZERO_HASH_14,
15 => SSZ_ZERO_HASH_15,
16 => SSZ_ZERO_HASH_16,
17 => SSZ_ZERO_HASH_17,
18 => SSZ_ZERO_HASH_18,
19 => SSZ_ZERO_HASH_19,
20 => SSZ_ZERO_HASH_20,
21 => SSZ_ZERO_HASH_21,
22 => SSZ_ZERO_HASH_22,
23 => SSZ_ZERO_HASH_23,
24 => SSZ_ZERO_HASH_24,
25 => SSZ_ZERO_HASH_25,
}The reason a block fails validation; one variant per violated block-validity rule.
enum FatalError = {
/* chain config: wrong fork / inactive activation */
InvalidConfig,
/* witness ancestor headers not contiguous */
HeaderChainBroken,
/* a transaction failed to RLP-decode */
RlpDecode,
/* a tx signature did not authenticate its sender */
InvalidSignature,
/* header.gas_limit is outside the consensus domain */
InvalidGasLimit,
/* EIP-7778: a tx exceeds the block's remaining gas */
GasUsedExceedsLimit,
/* a tx exceeds the block's remaining blob gas */
BlobGasLimitExceeded,
/* an invalid tx or a failed block-end system call */
ExecutionInvalid,
/* recomputed cumulative gas != header.gas_used */
InvalidGasUsed,
/* recomputed blob gas != header.blob_gas_used */
InvalidBlobGasUsed,
/* header.excess_blob_gas != expected */
InvalidExcessBlobGas,
/* recomputed post-state root != header.state_root */
InvalidStateRoot,
/* recomputed receipts root != header.receipts_root */
InvalidReceiptsRoot,
/* recomputed logs bloom != header.logs_bloom */
InvalidLogsBloom,
/* recomputed block hash != payload expected hash */
InvalidBlockHash,
/* header.parent_hash != authenticated parent */
InvalidParentHash,
/* EIP-7928: BAL item count > gas_limit / 2000 */
BlockAccessListTooLarge,
/* reconstructed EIP-7928 BAL bytes mismatch */
InvalidBlockAccessList,
/* reconstructed EIP-7685 request bytes mismatch */
InvalidExecutionRequests,
/* a missing/inconsistent proof node (thrown at use) */
WitnessDeficient,
/* an exact protocol integer exceeds its bounded execution representation */
NumericOverflow,
}The largest Merkle depth required by the supported execution-layer SSZ schemas. ByteList[2^30] is the widest one, with 2^25 chunks.
type htr_depth = range(0, 25)A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_deposits¶
hash_tree_root of the deposit-request list (depth 13).
function htr_deposits(deposits : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(deposits, HTR_DEPOSIT_LENGTH, sizeof(deposit_request_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(deposits, HtrDeposit, 0, count, 13);
mix_in_length(root, items.count)
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let HTR_DEPOSIT_LENGTH : int(192) = 192The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}Maximum deposit requests in their stateless-input SSZ list.
type deposit_request_count_bound : Int = 2 ^ 13The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_withdrawal_requests¶
hash_tree_root of the withdrawal-request list (depth 4).
function htr_withdrawal_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_WITHDRAWAL_REQUEST_LENGTH,
sizeof(withdrawal_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrWithdrawalRequest, 0, count, 4);
mix_in_length(root, items.count)
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let HTR_WITHDRAWAL_REQUEST_LENGTH : int(76) = 76The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)Maximum withdrawal requests in their stateless-input SSZ list.
type withdrawal_request_count_bound : Int = 2 ^ 4function htr_consolidation_requests¶
hash_tree_root of the consolidation-request list (depth 1).
function htr_consolidation_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_CONSOLIDATION_REQUEST_LENGTH,
sizeof(consolidation_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrConsolidationRequest, 0, count, 1);
mix_in_length(root, items.count)
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let HTR_CONSOLIDATION_REQUEST_LENGTH : int(116) = 116The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}Maximum consolidation requests in their stateless-input SSZ list.
type consolidation_request_count_bound : Int = 2 ^ 1The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_builder_deposit_requests¶
hash_tree_root of the builder-deposit-request list (depth 6).
function htr_builder_deposit_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_BUILDER_DEPOSIT_LENGTH,
sizeof(builder_deposit_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrBuilderDepositRequest, 0, count, 6);
mix_in_length(root, items.count)
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let HTR_BUILDER_DEPOSIT_LENGTH : int(184) = 184The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}Maximum builder deposit requests in their stateless-input SSZ list.
type builder_deposit_request_count_bound : Int = 2 ^ 6The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_builder_exit_requests¶
hash_tree_root of the builder-exit-request list (depth 4).
function htr_builder_exit_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(requests, HTR_BUILDER_EXIT_LENGTH, sizeof(builder_exit_request_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrBuilderExitRequest, 0, count, 4);
mix_in_length(root, items.count)
}function htr_requests_subtree(requests, kind, start, count, depth) =
if count == 0 then {
ssz_zero_hash(depth)
} else if depth == 0 then {
if count == 1 then {
htr_request_leaf(requests, start, kind)
} else {
fatal_error(WitnessDeficient)
}
} else {
let child_depth : htr_depth = depth - 1;
let half = htr_leaf_capacity(child_depth);
let left_count : htr_leaf_count =
if count < half then count else half;
let right_count : htr_leaf_count =
if count < half then 0 else count - half;
let left_root = htr_requests_subtree(requests, kind, start, left_count, child_depth);
let right_root = htr_requests_subtree(requests, kind, start + half, right_count, child_depth);
sha256_pair(left_root, right_root)
}function mix_in_length(root, len) =
let length_chunk = htr_length_chunk(len) in
sha256_pair(root, length_chunk)Constructs a schema-bounded fixed-item SSZ list reference.
function ssz_bounded_fixed_list_ref forall ('maximum : Int) ('item_size : Int),
source_valid_length('maximum) & source_valid_length('item_size). (
bytes : StatelessInputSlice,
item_size : int('item_size),
maximum_count : int('maximum),
) -> (
BoundedSszListRef('maximum)
) = {
let width : range(1, 'item_size) =
if 0 < item_size then item_size else fatal_error(InvalidConfig);
let span = bytes.len;
let raw_count = span / width;
if span != raw_count * width then {
fatal_error(InvalidConfig)
};
let count : range(0, 'maximum) =
if raw_count <= maximum_count then raw_count else fatal_error(InvalidConfig);
struct { bytes = bytes, count = count, max_item_length = item_size }
}let HTR_BUILDER_EXIT_LENGTH : int(68) = 68The closed fixed-width leaf-operation algebra carried through the generic request-list recursion. htr_request_leaf interprets it at depth zero, replacing a higher-order leaf function without duplicating merkleization.
enum HtrRequestKind = {
/* a validator deposit request (EIP-6110) */
HtrDeposit,
/* a validator withdrawal request (EIP-7002) */
HtrWithdrawalRequest,
/* a validator consolidation request (EIP-7251) */
HtrConsolidationRequest,
/* a builder deposit request */
HtrBuilderDepositRequest,
/* a builder exit request */
HtrBuilderExitRequest,
}A stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}Maximum builder exit requests in their stateless-input SSZ list.
type builder_exit_request_count_bound : Int = 2 ^ 4The common digest type used by trie, code, and block hashes.
type hash = b256A leaf count in any supported execution-layer SSZ Merkle tree.
type htr_leaf_count = range(0, 33554432)function htr_execution_requests¶
hash_tree_root of the SszExecutionRequests container.
function htr_execution_requests(input_ref : StatelessInputRef) -> hash = {
let deposits = htr_deposits(input_ref.deposits);
let withdrawal_requests = htr_withdrawal_requests(input_ref.withdrawal_requests);
let consolidation_requests = htr_consolidation_requests(input_ref.consolidation_requests);
let builder_deposit_requests = htr_builder_deposit_requests(input_ref.builder_deposit_requests);
let builder_exit_requests = htr_builder_exit_requests(input_ref.builder_exit_requests);
merkleize(
[deposits, withdrawal_requests, consolidation_requests, builder_deposit_requests, builder_exit_requests],
3,
)
}hash_tree_root of the builder-deposit-request list (depth 6).
function htr_builder_deposit_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_BUILDER_DEPOSIT_LENGTH,
sizeof(builder_deposit_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrBuilderDepositRequest, 0, count, 6);
mix_in_length(root, items.count)
}hash_tree_root of the builder-exit-request list (depth 4).
function htr_builder_exit_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(requests, HTR_BUILDER_EXIT_LENGTH, sizeof(builder_exit_request_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrBuilderExitRequest, 0, count, 4);
mix_in_length(root, items.count)
}hash_tree_root of the consolidation-request list (depth 1).
function htr_consolidation_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_CONSOLIDATION_REQUEST_LENGTH,
sizeof(consolidation_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrConsolidationRequest, 0, count, 1);
mix_in_length(root, items.count)
}hash_tree_root of the deposit-request list (depth 13).
function htr_deposits(deposits : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(deposits, HTR_DEPOSIT_LENGTH, sizeof(deposit_request_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(deposits, HtrDeposit, 0, count, 13);
mix_in_length(root, items.count)
}hash_tree_root of the withdrawal-request list (depth 4).
function htr_withdrawal_requests(requests : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(
requests,
HTR_WITHDRAWAL_REQUEST_LENGTH,
sizeof(withdrawal_request_count_bound),
);
let count : htr_leaf_count = items.count;
let root = htr_requests_subtree(requests, HtrWithdrawalRequest, 0, count, 4);
mix_in_length(root, items.count)
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}Every variable region of the input, resolved once before decoding. Consumers receive explicit source spans instead of re-reading nested SSZ offset tables.
struct StatelessInputRef = {
protocol : ProtocolProfile,
new_payload_request : StatelessInputSlice,
execution_payload : StatelessInputSliceAtLeast(540),
versioned_hashes : StatelessInputSlice,
deposits : StatelessInputSlice,
withdrawal_requests : StatelessInputSlice,
consolidation_requests : StatelessInputSlice,
builder_deposit_requests : StatelessInputSlice,
builder_exit_requests : StatelessInputSlice,
extra_data : StatelessInputSliceAtMost(extra_data_length_bound),
transactions : TransactionListRef,
withdrawals : WithdrawalListRef,
block_access_list : StatelessInputSliceAtMost(block_access_list_length_bound),
witness_state : WitnessNodeListRef,
witness_codes : WitnessCodeListRef,
witness_headers : WitnessHeaderListRef,
chain_config : StatelessInputSlice,
public_keys : StatelessInputSlice,
}The common digest type used by trie, code, and block hashes.
type hash = b256function htr_new_payload_request¶
hash_tree_root of the SszNewPayloadRequest (4 fields, depth 2) —
the commitment the guest proves its input against.
function htr_new_payload_request(input_ref : StatelessInputRef) -> hash = {
let payload_root = htr_execution_payload(input_ref);
let versioned_hashes_root = htr_versioned_hashes(input_ref.versioned_hashes);
let beacon_root = htr_ssz_bytes32(input_ref.new_payload_request, NPR_BEACON_ROOT);
let requests_root = htr_execution_requests(input_ref);
merkleize([payload_root, versioned_hashes_root, beacon_root, requests_root], 2)
}hash_tree_root of the SszExecutionPayload (19 fields, depth 5).
function htr_execution_payload(input_ref : StatelessInputRef) -> hash = {
let payload = input_ref.execution_payload;
let parent_hash_root = htr_ssz_bytes32(payload, 0);
let fee_recipient_root = htr_ssz_addr(payload, PL_FEE_RECIPIENT);
let state_root = htr_ssz_bytes32(payload, PL_STATE_ROOT);
let receipts_root = htr_ssz_bytes32(payload, PL_RECEIPTS_ROOT);
let logs_bloom_root = htr_ssz_bytevector(payload, PL_LOGS_BLOOM, LOGS_BLOOM_BYTE_LENGTH);
let prev_randao_root = htr_ssz_bytes32(payload, PL_PREV_RANDAO);
let block_number_root = htr_ssz_uint(payload, PL_BLOCK_NUMBER);
let gas_limit_root = htr_ssz_uint(payload, PL_GAS_LIMIT);
let gas_used_root = htr_ssz_uint(payload, PL_GAS_USED);
let timestamp_root = htr_ssz_uint(payload, PL_TIMESTAMP);
let extra_data_root = htr_bytelist(input_ref.extra_data, WORD_BYTE_LENGTH);
let base_fee_root = htr_ssz_u256(payload, PL_BASE_FEE);
let block_hash_root = htr_ssz_bytes32(payload, PL_BLOCK_HASH);
let transactions_root = htr_transactions(input_ref.transactions);
let withdrawals_root = htr_withdrawals(input_ref.withdrawals);
let blob_gas_used_root = htr_ssz_uint(payload, PL_BLOB_GAS_USED);
let excess_blob_gas_root = htr_ssz_uint(payload, PL_EXCESS_BLOB_GAS);
let block_access_list_root = htr_bytelist(input_ref.block_access_list, HTR_BYTE_LIST_LIMIT);
let slot_number_root = htr_ssz_uint(payload, PL_SLOT_NUMBER);
merkleize(
[
parent_hash_root,
fee_recipient_root,
state_root,
receipts_root,
logs_bloom_root,
prev_randao_root,
block_number_root,
gas_limit_root,
gas_used_root,
timestamp_root,
extra_data_root,
base_fee_root,
block_hash_root,
transactions_root,
withdrawals_root,
blob_gas_used_root,
excess_blob_gas_root,
block_access_list_root,
slot_number_root,
],
5,
)
}hash_tree_root of the SszExecutionRequests container.
function htr_execution_requests(input_ref : StatelessInputRef) -> hash = {
let deposits = htr_deposits(input_ref.deposits);
let withdrawal_requests = htr_withdrawal_requests(input_ref.withdrawal_requests);
let consolidation_requests = htr_consolidation_requests(input_ref.consolidation_requests);
let builder_deposit_requests = htr_builder_deposit_requests(input_ref.builder_deposit_requests);
let builder_exit_requests = htr_builder_exit_requests(input_ref.builder_exit_requests);
merkleize(
[deposits, withdrawal_requests, consolidation_requests, builder_deposit_requests, builder_exit_requests],
3,
)
}function htr_ssz_bytes32(bytes, offset) =
let value = ssz_bytes32(bytes, offset) in
htr_bytes32(value)hash_tree_root of the versioned hashes (List[Bytes32, 4096]).
function htr_versioned_hashes(versioned_hashes : StatelessInputSlice) -> hash = {
let items = ssz_bounded_fixed_list_ref(versioned_hashes, WORD_BYTE_LENGTH, sizeof(blob_commitment_count_bound));
let count : htr_leaf_count = items.count;
let root = htr_versioned_hashes_subtree(versioned_hashes, 0, count, 12);
mix_in_length(root, items.count)
}function merkleize(leaves, depth) = {
let count : htr_leaf_count = length(leaves);
let capacity = htr_leaf_capacity(depth);
if count <= capacity then {
merkleize_vector_subtree(leaves, 0, count, depth)
} else {
fatal_error(WitnessDeficient)
}
}let NPR_BEACON_ROOT : int(8) = 8Every variable region of the input, resolved once before decoding. Consumers receive explicit source spans instead of re-reading nested SSZ offset tables.
struct StatelessInputRef = {
protocol : ProtocolProfile,
new_payload_request : StatelessInputSlice,
execution_payload : StatelessInputSliceAtLeast(540),
versioned_hashes : StatelessInputSlice,
deposits : StatelessInputSlice,
withdrawal_requests : StatelessInputSlice,
consolidation_requests : StatelessInputSlice,
builder_deposit_requests : StatelessInputSlice,
builder_exit_requests : StatelessInputSlice,
extra_data : StatelessInputSliceAtMost(extra_data_length_bound),
transactions : TransactionListRef,
withdrawals : WithdrawalListRef,
block_access_list : StatelessInputSliceAtMost(block_access_list_length_bound),
witness_state : WitnessNodeListRef,
witness_codes : WitnessCodeListRef,
witness_headers : WitnessHeaderListRef,
chain_config : StatelessInputSlice,
public_keys : StatelessInputSlice,
}The common digest type used by trie, code, and block hashes.
type hash = b256