EIP-4881: Deposit Contract Snapshot Interface

Discussion thread for EIP-4881:

https://eips.ethereum.org/EIPS/eip-4881

Standardizing the format and endpoint for transmitting a snapshot of the deposit Merkle tree

Initial discussion on discord

Existing Implementations

In MerkleTree.generate_proof the code references node.left and node.right but these properties don’t actually exist in MerkleTree. For a non-empty tree you would always wind up with a Node instance which does have those properties but for an empty tree the root would a Zero instance and I believe the method would fail.

It would probably be clearer if this duck typing was avoided in any case.

I’m slightly confused about the SSZ format for DepositTreeSnapshot. It’s initial defined as:

DepositTreeSnapshot {
    finalized: Vector[Hash32],
    deposits: uint64,
    execution_block_hash: Hash32,
}

but no length is given for the finalized vector. In the reference implementation finalized is defined as a List but no maximum length is specified. List<Bytes32> seems to be the right data structure but we’d need to specify a maximum length for it to have a complete SSZ object definition.

1 Like

(post deleted by author)

Hi Aj!
Thanks for your feedback. I omitted error/sanity checking to avoid cluttering the code. But I did try to allude to error cases in my comments. Looks like I missed this one. I could simply add:

    def get_proof(self, index: uint) -> Union[Hash32, List[Hash32]]:
+       # omitted check to ensure tree is not empty
        # omitted check to ensure index > finalized deposit index

It would probably be clearer if this duck typing was avoided in any case.

Not sure of a good way to do that here given that left and right are not properties of all classes that extend MerkleTree. Did you have any ideas? It shouldn’t happen for any Leaf as they only occur at depth = 0. As long as the check about index > finalized index is enforced, it wouldn’t happen for any Finalized type either.

Hi Aj!

Thanks for your feedback! The maximum size of the finalized array is the depth of the deposit tree (32). But it’s a variable length array; the length depends on how many deposits are in the snapshot. I see in the ssz specs:

  • vector: ordered fixed-length homogeneous collection, with N values
    notation Vector[type, N], e.g. Vector[uint64, N]
    list : ordered variable-length homogeneous collection, limited to N values
    notation List[type, N]

So perhaps this confusion stems from me defining it initially as a Vector instead of a List? I can change it to a List if that’s more clear.

In regards to the reference implementation, I don’t see mention of using this second parameter to define a maximum length for List[T] in the typing library I imported…

The reference implementation does pass the test cases if I define the snapshot like this:

class DepositTreeSnapshot:
    finalized: List[Hash32, DEPOSIT_CONTRACT_DEPTH]
    deposits: uint64
    execution_block_hash: Hash32

though the tests will also pass if I define as finalized as List[Hash32, 1] which suggests the second parameter is just being ignored…

I can define it that way, though I worry that people might mistake it as a fixed length vector of size 32 instead of specifying a maximum length. Open to suggestions though.

Slow on this, but the List[Hash32, DEPOSIT_CONTRACT_DEPTH] is the right syntax from what I understand. Teku’s implementation of List does enforce the maximum length (and demands to have one specified).

Agreed it should be variable, hence the List instead of Vector. That also affects the SSZ serialisation as a vector doesn’t include any indication of length but a List adds an extra node to the SSZ tree to record the list length.

Congratulations on the EIP reaching Final status.
To learn more, check out EIP-4881: Deposit Contract Snapshot Interface with @ethDreamer