Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

dev: MPT library for EELS #962

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft

dev: MPT library for EELS #962

wants to merge 13 commits into from

Conversation

Eikix
Copy link
Member

@Eikix Eikix commented Mar 10, 2025

  • load an EthereumState object from ZKPI which is a flattened view of all MPT nodes (State Trie and Storage Tries) and Codes touched during block, as well as an accessList field of all addresses and storage keys touched during block, and a state_root.
  • entrypoint to transform EthereumState into an EELS State object
  • entrypoints to get/set/delete nodes in the EthereumState

@Eikix Eikix force-pushed the dev/zkpi_to_partial_mpt branch 2 times, most recently from 1693c48 to 9201e6c Compare March 10, 2025 17:51
Copy link

codecov bot commented Mar 10, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 79.89%. Comparing base (708f65e) to head (9fa107b).
Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #962      +/-   ##
==========================================
- Coverage   83.46%   79.89%   -3.57%     
==========================================
  Files          65       63       -2     
  Lines       17032    16629     -403     
==========================================
- Hits        14215    13285     -930     
- Misses       2817     3344     +527     
Flag Coverage Δ
ci-ef-tests 79.89% <ø> (?)
nightly-ef-tests ?
nightly-unit ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Eikix Eikix force-pushed the dev/zkpi_to_partial_mpt branch from 0ee58c4 to 566f768 Compare March 11, 2025 08:57
@Eikix Eikix force-pushed the dev/zkpi_to_partial_mpt branch from 4ed2d1e to 71f1cf4 Compare March 12, 2025 12:31
@Eikix Eikix force-pushed the dev/zkpi_to_partial_mpt branch from aebbb79 to e55b9f7 Compare March 12, 2025 15:02
Copy link
Collaborator

@enitrat enitrat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm overall, although i would need to spend more time on the deletion / upsertion process

.DS_Store Outdated
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can add to gitignore 🤣

EMPTY_BYTES_RLP = b"\x80"


def nibble_path_to_hex(nibble_path: Bytes) -> Bytes:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def nibble_path_to_hex(nibble_path: Bytes) -> Bytes:
def nibble_path_to_hex(nibble_path: Bytes) -> str:

codes: Mapping[Bytes32, Bytes]
access_list: Mapping[Address, Optional[List[Bytes32]]]
state_root: Hash32

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nodes: Mapping[Bytes32, Bytes] # Stores MPT nodes by their hash
codes: Mapping[Bytes32, Bytes] # Stores contract code by code hash
access_list: Mapping[Address, Optional[List[Bytes32]]] # Tracks accessed addresses and storage
state_root: Hash32 # Root hash of the state trie

)


class EthereumState:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PartialState ? Confusing with EELS' "State" imo

account = self.get(keccak256(address))
if account is None:
return EMPTY_TRIE_ROOT_HASH
decoded = rlp.decode(account)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(echoing to a previous comment with a class for decoded accounts, we could do this validation when creating the class instance), as I see it's used in a lot of places

if len(decoded) != 4:
raise ValueError(f"Invalid account length: {len(decoded)}")

# TODO: Fix situation where code is not present in self.codes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done, as we fetch the code from the node if it's not in the zkpi right?

node = self._decode_node(node_data)
return self._process_node(node, nibble_path)

def _process_node(self, node: InternalNode, nibble_path: Bytes) -> Optional[Bytes]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is recursive until we reach a leaf node right?

Comment on lines +468 to +482
"""
Delete a value from the trie at the given path.

Parameters
----------
path : Bytes
The path to delete
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root

Returns
-------
Optional[Hash32]
The new root hash after deletion, or None if the path wasn't found
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
Delete a value from the trie at the given path.
Parameters
----------
path : Bytes
The path to delete
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root
Returns
-------
Optional[Hash32]
The new root hash after deletion, or None if the path wasn't found
"""
"""
Delete a value from the trie at the given path.
The deletion process in a Merkle Patricia Trie follows these steps:
1. Navigate to the target node using the path:
- At each branch node, follow the appropriate nibble
- At each extension node, verify the shared prefix matches
- Continue until reaching a leaf node
2. When the target node is found:
- If it's a leaf node and the path matches exactly, remove it
- If it's not found, return without changes
3. After deletion, restructure the trie to maintain invariants:
- If a branch node is left with only one child, convert it to an extension node
- If an extension node's child is deleted, remove the extension node
- Merge extension nodes with their child extension nodes when possible
- Update all affected node hashes up to the root
For example, if deleting 'abc' from a trie:
```
Before: After:
[Root] [Root]
| |
[Branch] [Branch]
/ \ / \
'ab' 'd' 'ab' 'd'
| |
'c':123 'x':456
'x':456
```
Parameters
----------
path : Bytes
The path to delete
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root
Returns
-------
Optional[Hash32]
The new root hash after deletion, or None if the path wasn't found.
Returns the original root_hash if no changes were made.
"""

Comment on lines +784 to +800
"""
Insert or update a value in the trie at the given path.

Parameters
----------
path : Bytes
The path where the value should be stored
value : Bytes
The RLP-encoded value to store
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root

Returns
-------
Hash32
The new root hash after insertion/update
"""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
Insert or update a value in the trie at the given path.
Parameters
----------
path : Bytes
The path where the value should be stored
value : Bytes
The RLP-encoded value to store
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root
Returns
-------
Hash32
The new root hash after insertion/update
"""
"""
Insert or update a value in the trie at the given path.
The upsert process in a Merkle Patricia Trie follows these steps:
1. Special case - Empty trie:
- If the trie is empty (root_hash == EMPTY_TRIE_ROOT_HASH)
- Create a new leaf node with the entire path and value
- No need for complex path splitting or node creation
2. Path traversal and node creation:
a) At a Branch node:
- If path is empty: (not supported in this implementation)
- Otherwise: Follow the next nibble
- If that branch is empty: Create new leaf node
- If branch exists: Recursively upsert into that branch
b) At an Extension node:
- Find common prefix between path and node's key_segment
- If paths diverge: Create a branch node at divergence point
- If extension is prefix of path: Recursively upsert remaining path
- Handle path compression by merging extension nodes when possible
c) At a Leaf node:
- If paths match exactly: Update value
- If paths differ: Create branch node at first different nibble
- Add both paths (existing and new) to the branch
- Create extension node if common prefix exists
3. Node encoding and storage:
- Encode modified nodes using RLP
- For nodes32 bytes: Store separately and use hash as reference
- For nodes < 32 bytes: Store directly in parent node
- Update node hashes up to root
Example of inserting 'abc':123 into a trie:
```
Before: After:
[Root] [Root]
| |
[Branch] [Branch]
| / \
'd' 'ab' 'd'
| | |
456 'c':123 456
```
Parameters
----------
path : Bytes
The path where the value should be stored
value : Bytes
The RLP-encoded value to store
root_hash : Hash32, optional
The root hash to start from, defaults to the trie's state_root
Returns
-------
Hash32
The new root hash after insertion/update
"""

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants