-
Notifications
You must be signed in to change notification settings - Fork 33
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
base: main
Are you sure you want to change the base?
Conversation
1693c48
to
9201e6c
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
0ee58c4
to
566f768
Compare
4ed2d1e
to
71f1cf4
Compare
aebbb79
to
e55b9f7
Compare
There was a problem hiding this 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
cairo/scripts/.DS_Store
Outdated
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
python/mpt/src/mpt/__init__.py
Outdated
if len(decoded) != 4: | ||
raise ValueError(f"Invalid account length: {len(decoded)}") | ||
|
||
# TODO: Fix situation where code is not present in self.codes |
There was a problem hiding this comment.
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]: |
There was a problem hiding this comment.
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?
""" | ||
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 | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
""" | |
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. | |
""" |
""" | ||
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 | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
""" | |
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 nodes ≥ 32 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 | |
""" |
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 anaccessList
field of all addresses and storage keys touched during block, and a state_root.EthereumState
into an EELSState
object