This EIP introduces a new Simple Serialize (SSZ) type, ProgressiveList[T]
, to represent lists of arbitrary length with stable merkleization. Unlike the existing List[T, N]
type, which imposes a fixed capacity N
, ProgressiveList[T]
supports unbounded growth using a recursive tree structure during merkleization to efficiently handle lists of any size while maintaining stable generalized indices (gindices) for individual elements. This enables reduced hash overhead for small lists and avoids arbitrary predefined limits.
Current SSZ List[T, N]
types require a predefined capacity N
, which leads to several issues:
Transaction
), resulting in unnecessary zero-padding and dozens of extra hash computations.N
are often chosen arbitrarily (e.g., MAX_BYTES_PER_TRANSACTION
, MAX_TRANSACTIONS_PER_PAYLOAD
), introducing unnecessary bound checks and restricting design flexibility for future state transitions.N
across forks (e.g., MAX_ATTESTER_SLASHINGS_ELECTRA
, MAX_ATTESTATIONS_ELECTRA
) alters gindices, breaking downstream verifiers.ProgressiveList[T]
addresses these by:
This is particularly valuable for execution-layer (EL) data like receipt logs and calldata, where list sizes vary widely, and for consensus-layer (CL) structures where unbounded growth avoids artificial caps.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
ProgressiveList[T]
ProgressiveList[T]
defines an ordered, homogeneous collection of elements of type T
, where T
is any valid SSZ type (e.g., uint64
, Container
, etc.).
Serialization of ProgressiveList[T]
is identical to List[T, N]
.
ProgressiveList[T]
is represented as a recursive Merkle tree following this process:
pack
or by hash_tree_root
of its elements, depending on whether the element type is basic or composite. (This matches packing behavior of List
)Vector
of chunks, with the next subtree’s root mixed in if present. The last subtree is padded to size with zeros, with a zero mixed in.ProgressiveList[T]
V4 0 (terminator)
\ /
V3 \/
\ /
V2 \/
\ /
V1 \/
\ /
\/ LEN
\ /
\/
ROOT
V1: Vector[T, 1]
V2: Vector[T, 4]
V3: Vector[T, 16]
V4: Vector[T, 64]
mix_in_length(merkleize_progressive_list(pack(value)), len(value))
if value
is an ProgressiveList[T]
of basic objectsmix_in_length(merkleize_progressive_list([hash_tree_root(element) for element in value]), len(value))
if value
is a ProgressiveList[T]
of composite objectsdef merkleize_progressive_list(chunks, base_size=1, scaling_factor=4):
if len(chunks) <= base_size:
return mix_in_aux(merkleize(chunks + [Bytes32()] * (base_size - len(chunks))), Bytes32())
else:
next_size = base_size * scaling_factor
subtree = chunks[:base_size]
successor = chunks[base_size:]
subtree_root = merkleize(subtree)
successor_root = merkleize_progressive_list(successor, next_size, scaling_factor)
return mix_in_aux(subtree_root, successor_root)
# | Layer size (chunks) | Total capacity (chunks) |
---|---|---|
1 | 1 | 1 |
2 | 4 | 5 |
3 | 16 | 21 |
4 | 64 | 85 |
5 | 256 | 341 |
6 | 1'024 | 1'365 |
7 | 4'096 | 5'461 |
8 | 16'384 | 21'845 |
9 | 65'536 | 87'381 |
10 | 262'144 | 349'525 |
11 | 1'048'576 | 1'398'101 |
12 | 4'194'304 | 5'592'405 |
13 | 16'777'216 | 22'369'621 |
14 | 67'108'864 | 89'478'485 |
15 | 268'435'456 | 357'913'941 |
ProgressiveByteList
For convenience ProgressiveByteList
is defined as an alias to ProgressiveList[byte]
.
# | Layer size (bytes) | Total capacity (bytes) |
---|---|---|
1 | 32 | 32 |
2 | 128 | 160 |
3 | 512 | 672 |
4 | 2'048 | 2'720 |
5 | 8'192 | 10'912 |
6 | 32'768 | 43'680 |
7 | 131'072 | 174'752 |
8 | 524'288 | 699'040 |
9 | 2'097'152 | 2'796'192 |
10 | 8'388'608 | 11'184'800 |
11 | 33'554'432 | 44'739'232 |
12 | 134'217'728 | 178'956'960 |
13 | 536'870'912 | 715'827'872 |
14 | 2'147'483'648 | 2'863'311'520 |
15 | 8'589'934'592 | 11'453'246'112 |
List[T, N]
).Dynamic-depth Merkleization destabilizes gindices:
Mixing in successor subtrees ensures predictable gindices and proof sizes.
List[T, N]
:
ProgressiveList[T]
offers a scalable, efficient alternative.
ProgressiveList[T]
is a new SSZ type, coexisting with List[T, N]
and other types without conflict. Its List
-equivalent serialization ensures compatibility with existing serializers.
uint32
length-prefix caps serialization of variable-length subsections at 4GB, but practical limits (e.g., 10MB libp2p messages) apply. Implementations SHOULD enforce context-specific bounds.Copyright and related rights waived via CC0.