Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(l1): snap sync #1209

Merged
merged 321 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
321 commits
Select commit Hold shift + click to select a range
65f8c77
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Oct 29, 2024
b7c0211
Fix bug in response bytes length calculation
fmoletta Oct 29, 2024
74173d2
Restore Makefile
fmoletta Oct 29, 2024
777732a
Identify storage tries by hashed account address
fmoletta Oct 29, 2024
0e3e276
Merge branch 'trie_iter' into snap-storage-range
fmoletta Oct 29, 2024
abb28cf
Impl logic
fmoletta Oct 29, 2024
fea174d
Connect to main loop
fmoletta Oct 30, 2024
dc6752c
Compute proofs
fmoletta Oct 30, 2024
03d9928
Clippy
fmoletta Oct 30, 2024
c791a89
Chamge type
fmoletta Oct 30, 2024
9fd88d3
Add test
fmoletta Oct 30, 2024
2b9d207
Add messages
fmoletta Oct 30, 2024
37becaa
Add hive test
fmoletta Oct 30, 2024
a6d2f08
fmt
fmoletta Oct 30, 2024
c502f06
Impl messages
fmoletta Oct 31, 2024
242502e
Impl Trie::get_node
fmoletta Oct 31, 2024
3b7d70e
Fix
fmoletta Oct 31, 2024
384eff7
Merge branch 'trie_iter' into snap-storage-range
fmoletta Oct 31, 2024
fa4be1b
Merge branch 'snap-storage-range' into snap-bytecodes
fmoletta Oct 31, 2024
1cf1e9b
implement get_trie_nodes for storage
fmoletta Oct 31, 2024
349d05f
Add byte limit
fmoletta Oct 31, 2024
4fd0ee5
Add request processing
fmoletta Oct 31, 2024
d02e8f2
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Oct 31, 2024
0575a44
Merge branch 'snap-storage-range' of github.com:lambdaclass/lambda_et…
fmoletta Oct 31, 2024
aa29e5f
Fix lingering conflicts
fmoletta Oct 31, 2024
89e144a
Save progress
fmoletta Oct 31, 2024
1d8c3f9
Progress
fmoletta Nov 1, 2024
ea34fbc
Progress
fmoletta Nov 1, 2024
e57f386
remove unused code
fmoletta Nov 1, 2024
0a31fc9
Handle wrong path input lens
fmoletta Nov 1, 2024
2c0e30e
Add restrictions
fmoletta Nov 1, 2024
b3c4816
Fix error handling
fmoletta Nov 1, 2024
80bbc30
clippy+fmt
fmoletta Nov 1, 2024
b7b978a
Add hive test
fmoletta Nov 1, 2024
f02d29a
Cleanup
fmoletta Nov 1, 2024
0e5a005
Draft
fmoletta Nov 1, 2024
003d445
:(
fmoletta Nov 1, 2024
23974bb
Fix code to handle nibbles properly
fmoletta Nov 1, 2024
b159f7d
Simplify code
fmoletta Nov 1, 2024
12b0268
refactor
fmoletta Nov 1, 2024
8e4c884
Remove debug code
fmoletta Nov 1, 2024
b80f19f
Cleanup code
fmoletta Nov 1, 2024
f03ef1e
Merge branch 'snap-bytecodes' into snap-trie-nodes
fmoletta Nov 1, 2024
098a584
Remove debug command
fmoletta Nov 1, 2024
9a57e37
Un-comment noisy info
fmoletta Nov 1, 2024
f54af1d
Update comment
fmoletta Nov 1, 2024
b307261
reorder
fmoletta Nov 1, 2024
9da6ec9
Cite sources
fmoletta Nov 1, 2024
8a0fbbb
Move funcs to encodings module
fmoletta Nov 1, 2024
200d014
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 4, 2024
2332889
Merge branch 'snap-storage-range' into snap-bytecodes
fmoletta Nov 4, 2024
1805ef6
Update workflow
fmoletta Nov 4, 2024
b270580
Update workflow
fmoletta Nov 4, 2024
6af2d6e
Merge branch 'snap-storage-range' into snap-bytecodes
fmoletta Nov 4, 2024
9ec2129
Update workflow
fmoletta Nov 4, 2024
2be03ee
Progress
fmoletta Nov 5, 2024
09f688b
Clippy
fmoletta Nov 5, 2024
f494ad9
Merge branch 'snap-storage-range' into snap-bytecodes
fmoletta Nov 5, 2024
64610c3
Merge branch 'snap-bytecodes' into snap-trie-nodes
fmoletta Nov 5, 2024
d3acbd4
Merge branch 'snap-trie-nodes' into validate-account-range
fmoletta Nov 5, 2024
824afbc
progress
fmoletta Nov 5, 2024
19c0bf0
Remove path field from BranchNode
fmoletta Nov 5, 2024
d32bb4e
Start dumb nibbles impl
fmoletta Nov 5, 2024
7cad9b6
Impl common_prefix
fmoletta Nov 5, 2024
3b69cd7
Update name & doc
fmoletta Nov 5, 2024
16d1432
Impl next
fmoletta Nov 5, 2024
d7c036f
Use DumbNibbles: LeafNode get & insert
fmoletta Nov 5, 2024
a2afdfd
Use DumbNibbles: ExtensionNode get & insert
fmoletta Nov 5, 2024
3a65df9
Make code compile
fmoletta Nov 5, 2024
43ae680
Fix node encoding for LeafNode
fmoletta Nov 5, 2024
e69e954
Fix leaf nibbles
fmoletta Nov 5, 2024
998ed51
Fix leaf partial when inserting to leaf
fmoletta Nov 6, 2024
3cfd7c4
Fixes
fmoletta Nov 6, 2024
ad40f23
Fix
fmoletta Nov 6, 2024
a41cf2b
Fix
fmoletta Nov 6, 2024
68d5f83
Fix
fmoletta Nov 6, 2024
275659a
Fix
fmoletta Nov 6, 2024
6e1b4b7
[DEBUG] Add debug prints
fmoletta Nov 6, 2024
167c591
[DEBUG] Add debug prints
fmoletta Nov 6, 2024
aad0bcf
Fix
fmoletta Nov 6, 2024
71357dd
[DEBUG] Add debug prints
fmoletta Nov 6, 2024
cd847b9
refactor: add next_choice method
fmoletta Nov 6, 2024
37c93d7
Simplify leaf node encoding
fmoletta Nov 7, 2024
92313f0
Simplify encoding of Leaf
fmoletta Nov 7, 2024
3eb5ee9
Simplify encoding of Extension
fmoletta Nov 7, 2024
eb4fd0c
Simplify encoding of Branch
fmoletta Nov 7, 2024
d50e0ff
Remove the NodeEncoder
fmoletta Nov 7, 2024
856224d
Clippy
fmoletta Nov 7, 2024
f713657
Update TrieIterator
fmoletta Nov 7, 2024
30ba82f
Add proptest
fmoletta Nov 7, 2024
21537d4
Remove old nibble representation
fmoletta Nov 7, 2024
28bd344
Rename DumbNibbles -> Nibbles
fmoletta Nov 7, 2024
c56ad04
Update some doc
fmoletta Nov 7, 2024
bebce62
Simplify BranchNode::remove
fmoletta Nov 7, 2024
08c6668
Simplify
fmoletta Nov 7, 2024
e1f032f
Update doc
fmoletta Nov 7, 2024
9dc2752
Fix unit test
fmoletta Nov 7, 2024
bbe367d
Fix test + code
fmoletta Nov 7, 2024
63f1645
Update test values
fmoletta Nov 7, 2024
237f291
Fix potential panick
fmoletta Nov 7, 2024
d5dfa30
Fix
fmoletta Nov 7, 2024
55b0c37
Fix unit tests
fmoletta Nov 7, 2024
01090b4
Remove outdated comment
fmoletta Nov 7, 2024
e1be0c6
[DEBUG] Remove debug prints
fmoletta Nov 7, 2024
bed25f7
Remove funny name test
fmoletta Nov 7, 2024
f2c2eef
doc nibbles module
fmoletta Nov 7, 2024
9050b0c
Remove todo
fmoletta Nov 7, 2024
345ae96
remove debug print
fmoletta Nov 7, 2024
0fa490f
avoid clone
fmoletta Nov 7, 2024
df62fab
Decode leaf & extension nodes
fmoletta Nov 8, 2024
de3695f
Decode branch nodesc
fmoletta Nov 8, 2024
cb92b60
Impl decode_raw for Node directly
fmoletta Nov 8, 2024
87994d3
Update test & remove impls for each node kind
fmoletta Nov 8, 2024
ec31d42
Clippy
fmoletta Nov 8, 2024
439a010
Remove unused fn
fmoletta Nov 8, 2024
69f583c
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 8, 2024
58d7cb0
Add doc comments
fmoletta Nov 8, 2024
797d486
Fix typos
fmoletta Nov 8, 2024
f2ea85f
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 8, 2024
34e2535
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 8, 2024
5b3f4b9
Merge branch 'decode_node' into validate-account-range
fmoletta Nov 11, 2024
b0beea8
Dont write nodes to DB if they are already inlined
fmoletta Nov 11, 2024
52fec40
Merge branch 'dont-store-if-inlined' into validate-account-range
fmoletta Nov 11, 2024
39b9d19
progress
fmoletta Nov 11, 2024
b2375ca
Save current progress
fmoletta Nov 12, 2024
04f94a3
Fix logic
fmoletta Nov 12, 2024
fc3ecc8
Fix logic
fmoletta Nov 12, 2024
774e1d1
Fix logic
fmoletta Nov 12, 2024
827439e
Add extension node logic to remove internal nodes
fmoletta Nov 12, 2024
377b794
Improve test
fmoletta Nov 12, 2024
185b884
Add comment
fmoletta Nov 12, 2024
2c8bb52
Add proptest
fmoletta Nov 12, 2024
76d719a
Add proptest
fmoletta Nov 12, 2024
9254ce8
Add test case
fmoletta Nov 12, 2024
0a27aab
rename func
fmoletta Nov 12, 2024
68661c9
Add doc
fmoletta Nov 12, 2024
fdcbc99
Add doc
fmoletta Nov 13, 2024
6f1e304
Add special case test
fmoletta Nov 13, 2024
925f536
Fix peripherial code
fmoletta Nov 13, 2024
f7f0bdb
Add proptest for special case: no keys
fmoletta Nov 13, 2024
0023b8a
Fix
fmoletta Nov 13, 2024
8e8dba3
Fix logic
fmoletta Nov 13, 2024
1a534cc
Fix logic
fmoletta Nov 13, 2024
6a9db29
Refactor has_right_element
fmoletta Nov 13, 2024
ff88780
Fix logic
fmoletta Nov 13, 2024
7583488
Unify criteria
fmoletta Nov 13, 2024
849a354
Refactor
fmoletta Nov 13, 2024
0908bd5
Clippy
fmoletta Nov 13, 2024
c445edc
Add doc
fmoletta Nov 13, 2024
ec13ce5
Reorder code + add doc
fmoletta Nov 13, 2024
2445f53
Fix logic
fmoletta Nov 13, 2024
16cb1b6
Check result in tests
fmoletta Nov 13, 2024
8151cef
Add tests for unsuccesful cases
fmoletta Nov 13, 2024
a7d5415
Add tests for unsuccesful cases
fmoletta Nov 13, 2024
6c9dd01
Add error handling
fmoletta Nov 13, 2024
87f2445
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 13, 2024
ad0f921
[Postpone] changes not related to the trie
fmoletta Nov 13, 2024
4fc5244
Fix
fmoletta Nov 13, 2024
dab6172
Remove debug code
fmoletta Nov 13, 2024
d02c374
Rename fn and module
fmoletta Nov 13, 2024
07e1f46
clippy
fmoletta Nov 13, 2024
2b36bbb
Update code
fmoletta Nov 13, 2024
2d711b6
Improve interface
fmoletta Nov 13, 2024
56a4ee9
Remove code leftover from debug
fmoletta Nov 13, 2024
c444a01
Consistency
fmoletta Nov 13, 2024
d59b09a
Fix
fmoletta Nov 13, 2024
8dd7c6d
Add test
fmoletta Nov 13, 2024
7e4205c
Remove TODO
fmoletta Nov 13, 2024
c59b8be
Revert "[Postpone] changes not related to the trie"
fmoletta Nov 13, 2024
03b73c8
Fix
fmoletta Nov 13, 2024
e7d3dc4
Add helper methods
fmoletta Nov 13, 2024
5d4ad34
Add syncmode argument
fmoletta Nov 14, 2024
a3820e4
crate
fmoletta Nov 15, 2024
afcdc5a
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 15, 2024
c3d5106
Add SnapSyncManager + send snap replies to them
fmoletta Nov 15, 2024
fa275ec
Add channel between kademlia table and peer connection handler
fmoletta Nov 15, 2024
c88d114
SyncManager first iteration
fmoletta Nov 21, 2024
798fb05
Start syncer in fork_choice handler
fmoletta Nov 21, 2024
a875680
First iteration of state retrieval planning
fmoletta Nov 22, 2024
356eaf0
[REVERT ME] Make V2 endpoints work in order to run hive sync test
fmoletta Nov 22, 2024
a6d64f9
Add parallel state fetching first draft
fmoletta Nov 25, 2024
3f6df84
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 26, 2024
fc9cc3e
Store block bodies and headers
fmoletta Nov 26, 2024
6b33d11
Set latest block
fmoletta Nov 26, 2024
e6c2a08
Start snap fetching
fmoletta Nov 26, 2024
da6cff8
Refactor comunication between backend and peer
fmoletta Nov 26, 2024
64835f3
Avoid holding the mutex for the kademlia table when sending requests …
fmoletta Nov 26, 2024
c798c35
Migrate to full sync
fmoletta Nov 27, 2024
62084e8
Error handling
fmoletta Nov 27, 2024
02a89b2
Fixes
fmoletta Nov 27, 2024
a60948d
revert uneeded changes
fmoletta Nov 27, 2024
cc4452e
revert uneeded changes
fmoletta Nov 27, 2024
c86b553
Updates & fixes
fmoletta Nov 27, 2024
05848a2
Add ethereum/sync test suite to hive (1 test)
fmoletta Nov 27, 2024
9d608f9
Default to full-sync
fmoletta Nov 27, 2024
57f1e29
revert uneeded changes
fmoletta Nov 27, 2024
a7350d8
revert uneeded changes
fmoletta Nov 27, 2024
eb3ff59
Fixes
fmoletta Nov 27, 2024
dc6fdf1
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 27, 2024
0a8d4e8
Clippy
fmoletta Nov 27, 2024
cfce53d
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Nov 27, 2024
493fa91
revert hive workflow change
fmoletta Nov 27, 2024
c6d6767
Revert "[REVERT ME] Make V2 endpoints work in order to run hive sync …
fmoletta Nov 27, 2024
95a9af2
revert hive workflow change
fmoletta Nov 27, 2024
fd90f18
reduce tracing output
fmoletta Nov 27, 2024
3191ddd
fmt
fmoletta Nov 27, 2024
576ee33
Fix
fmoletta Nov 27, 2024
a069f80
Revert "Revert "[REVERT ME] Make V2 endpoints work in order to run hi…
fmoletta Nov 27, 2024
90f9ca5
Snap Sync: Account Range
fmoletta Nov 27, 2024
b00ac70
Update tracing + set correct bytes response limit
fmoletta Nov 28, 2024
024efc9
Fix decoding of GetStorageRange
fmoletta Nov 28, 2024
7821db9
First steps of bytecode fetch queue + improve task spawning and joini…
fmoletta Nov 29, 2024
85ce202
Connect state rebuilding to bytecode fetcher
fmoletta Nov 29, 2024
973f2d9
Fix
fmoletta Nov 29, 2024
cafa197
Impl request storage ranges
fmoletta Nov 29, 2024
cbdeeb6
Storage fetcher skeleton
fmoletta Nov 29, 2024
c76cfeb
Add storage fetcher process
fmoletta Dec 2, 2024
586ec79
Signal end
fmoletta Dec 2, 2024
3f5bb77
Fix
fmoletta Dec 2, 2024
98f3270
Fix
fmoletta Dec 2, 2024
7028663
Fix
fmoletta Dec 2, 2024
711d987
Mute tracing
fmoletta Dec 2, 2024
d5b1ab5
Update
fmoletta Dec 4, 2024
2a59cdc
Update batch size
fmoletta Dec 4, 2024
e21a77a
Add error handling
fmoletta Dec 4, 2024
aec4c48
Add syncmode enum
fmoletta Dec 4, 2024
e1b2c6e
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
fmoletta Dec 4, 2024
317ccb5
Restore store creation
fmoletta Dec 4, 2024
146d767
Uncomment tracing
fmoletta Dec 4, 2024
f53646c
Remove unused fn
fmoletta Dec 4, 2024
ec6a9d4
Fix storage ranges verify logic
fmoletta Dec 4, 2024
1c945f6
Clippy
fmoletta Dec 4, 2024
5b93fbd
Revert "Revert "Revert "[REVERT ME] Make V2 endpoints work in order t…
fmoletta Dec 4, 2024
776ddc8
Clippy
fmoletta Dec 4, 2024
be70833
Fix clippy
fmoletta Dec 4, 2024
f501b02
Revert unstable lib feature
fmoletta Dec 4, 2024
ca622b6
Improve doc
fmoletta Dec 4, 2024
43c9309
Fix diagram linking
fmoletta Dec 4, 2024
ffb9e68
Improve spacing
fmoletta Dec 4, 2024
fa81f67
Improve spacing
fmoletta Dec 4, 2024
b7197a8
Fix typos
fmoletta Dec 4, 2024
eccaf9e
Add doc about bytecode fetcher
fmoletta Dec 4, 2024
559076c
Add doc about storage_fetcher + add improvement idea
fmoletta Dec 4, 2024
a07f35e
revert uneeded change + add doc
fmoletta Dec 4, 2024
81ae7ed
Commit diagram
fmoletta Dec 4, 2024
563d51b
fmt
fmoletta Dec 4, 2024
f10e6fb
Rename doc files
fmoletta Dec 4, 2024
8d70af3
Typo check
fmoletta Dec 4, 2024
907f979
Merge branch 'main' into snap-sync
fmoletta Dec 10, 2024
14599ef
Fix merge
fmoletta Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions cmd/ethrex/ethrex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use ethrex_core::{
H256,
};
use ethrex_net::{
bootnode::BootNode, node_id_from_signing_key, peer_table, sync::SyncManager, types::Node,
bootnode::BootNode,
node_id_from_signing_key, peer_table,
sync::{SyncManager, SyncMode},
types::Node,
};
use ethrex_rlp::decode::RLPDecode;
use ethrex_storage::{EngineType, Store};
Expand Down Expand Up @@ -115,10 +118,7 @@ async fn main() {
.get_one::<String>("datadir")
.map_or(set_datadir(DEFAULT_DATADIR), |datadir| set_datadir(datadir));

let snap_sync = is_snap_sync(&matches);
if snap_sync {
info!("snap-sync not available, defaulting to full-sync");
}
let sync_mode = sync_mode(&matches);

cfg_if::cfg_if! {
if #[cfg(feature = "redb")] {
Expand Down Expand Up @@ -187,7 +187,7 @@ async fn main() {
// Create Kademlia Table here so we can access it from rpc server (for syncing)
let peer_table = peer_table(signer.clone());
// Create SyncManager
let syncer = SyncManager::new(peer_table.clone(), snap_sync);
let syncer = SyncManager::new(peer_table.clone(), sync_mode);

// TODO: Check every module starts properly.
let tracker = TaskTracker::new();
Expand Down Expand Up @@ -299,16 +299,16 @@ fn parse_socket_addr(addr: &str, port: &str) -> io::Result<SocketAddr> {
))
}

fn is_snap_sync(matches: &clap::ArgMatches) -> bool {
fn sync_mode(matches: &clap::ArgMatches) -> SyncMode {
let syncmode = matches.get_one::<String>("syncmode");
if let Some(syncmode) = syncmode {
match &**syncmode {
"full" => false,
"snap" => true,
"full" => SyncMode::Full,
"snap" => SyncMode::Snap,
other => panic!("Invalid syncmode {other} expected either snap or full"),
}
} else {
true
SyncMode::Snap
}
}

Expand Down
1 change: 0 additions & 1 deletion crates/common/types/blobs_bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ pub enum BlobsBundleError {
}

#[cfg(test)]

mod tests {
use super::*;
use crate::{
Expand Down
File renamed without changes.
24 changes: 24 additions & 0 deletions crates/networking/docs/Sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Syncing

## Snap Sync

A snap sync cycle begins by fetching all the block headers (via p2p) between the current head (latest canonical block) and the sync head (block hash sent by a forkChoiceUpdate).
The next two steps are performed in parallel:
On one side, blocks and receipts for all fetched headers are fetched via p2p and stored.

On the other side, the state is reconstructed via p2p snap requests. Our current implementation of this works as follows:
We will spawn two processes, the `bytecode_fetcher` which will remain active and process bytecode requests in batches by requesting bytecode batches from peers and storing them, and the `fetch_snap_state` process, which will iterate over the fetched headers and rebuild the block's state via `rebuild_state_trie`.

`rebuild_state_trie` will spawn a `storage_fetcher` process (which works similarly to the `bytecode_fetcher` and is kept alive for the duration of the rebuild process), it will open a new state trie and will fetch the block's accounts in batches and for each account it will: send the account's code hash to the `bytecode_fetcher` (if not empty), send the account's address and storage root to the `storage_fetcher` (if not empty), and add the account to the state trie. Once all accounts are processed, the final state root will be checked and committed.

(Not implemented yet) When `fetch_snap_state` runs out of available state (aka, the state we need to fetch is older than 128 blocks and peers don't provide it), it will begin the `state_healing` process.
This diagram illustrates the process described above:

![snap_sync](/crates/networking/docs/diagrams/snap_sync.jpg)

The `bytecode_fetcher` has its own channel where it receives code hashes from active `rebuild_state_trie` processes. Once a code hash is received, it is added to a pending queue. When the queue has enough messages for a full batch it will request a batch of bytecodes via snap p2p and store them. If a bytecode could not be fetched by the request (aka, we reached the response limit) it is added back to the pending queue. After the whole state is synced `fetch_snap_state` will send an empty list to the `bytecode_fetcher` to signal the end of the requests so it can request the last (incomplete) bytecode batch and end gracefully.
This diagram illustrates the process described above:

![snap_sync](/crates/networking/docs/diagrams/bytecode_fetcher.jpg)

The `storage_fetcher` works almost alike, but one will be spawned for each `rebuild_state_trie` process as we can't fetch storages from different blocks in the same request.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
225 changes: 217 additions & 8 deletions crates/networking/p2p/peer_channels.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
use std::{sync::Arc, time::Duration};

use bytes::Bytes;
use ethrex_core::{
types::{BlockBody, BlockHeader},
H256,
types::{AccountState, BlockBody, BlockHeader},
H256, U256,
};
use ethrex_rlp::encode::RLPEncode;
use ethrex_trie::verify_range;
use tokio::sync::{mpsc, Mutex};

use crate::{
rlpx::eth::blocks::{
BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders, BLOCK_HEADER_LIMIT,
rlpx::{
eth::blocks::{
BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders, BLOCK_HEADER_LIMIT,
},
snap::{
AccountRange, ByteCodes, GetAccountRange, GetByteCodes, GetStorageRanges, StorageRanges,
},
},
snap::encodable_to_proof,
RLPxMessage,
};

pub const PEER_REPLY_TIMOUT: Duration = Duration::from_secs(45);
pub const MAX_MESSAGES_IN_PEER_CHANNEL: usize = 25;
pub const MAX_RESPONSE_BYTES: u64 = 512 * 1024;
pub const HASH_MAX: H256 = H256([0xFF; 32]);

#[derive(Debug, Clone)]
/// Holds the respective sender and receiver ends of the communication channels bewteen the peer data and its active connection
Expand All @@ -41,8 +52,8 @@ impl PeerChannels {
)
}

/// Requests block headers from the peer
/// Returns the response message or None if:
/// Requests block headers from the peer, starting from the `start` block hash towards newer blocks
/// Returns the block headers or None if:
/// - There are no available peers (the node just started up or was rejected by all other nodes)
/// - The response timed out
/// - The response was empty or not valid
Expand Down Expand Up @@ -76,8 +87,8 @@ impl PeerChannels {
(!block_headers.is_empty()).then_some(block_headers)
}

/// Requests block headers from the peer
/// Returns the response message or None if:
/// Requests block bodies from the peer given their block hashes
/// Returns the block bodies or None if:
/// - There are no available peers (the node just started up or was rejected by all other nodes)
/// - The response timed out
/// - The response was empty or not valid
Expand Down Expand Up @@ -109,4 +120,202 @@ impl PeerChannels {
// Check that the response is not empty and does not contain more bodies than the ones requested
(!block_bodies.is_empty() && block_bodies.len() <= block_hashes_len).then_some(block_bodies)
}

/// Requests an account range from the peer given the state trie's root and the starting hash (the limit hash will be the maximum value of H256)
/// Will also return a boolean indicating if there is more state to be fetched towards the right of the trie
/// Returns the response message or None if:
/// - There are no available peers (the node just started up or was rejected by all other nodes)
/// - The response timed out
/// - The response was not valid
pub async fn request_account_range(
&self,
state_root: H256,
start: H256,
) -> Option<(Vec<H256>, Vec<AccountState>, bool)> {
let request_id = rand::random();
let request = RLPxMessage::GetAccountRange(GetAccountRange {
id: request_id,
root_hash: state_root,
starting_hash: start,
limit_hash: HASH_MAX,
response_bytes: MAX_RESPONSE_BYTES,
});
self.sender.send(request).await.ok()?;
let mut receiver = self.receiver.lock().await;
let (accounts, proof) = tokio::time::timeout(PEER_REPLY_TIMOUT, async move {
loop {
match receiver.recv().await {
Some(RLPxMessage::AccountRange(AccountRange {
id,
accounts,
proof,
})) if id == request_id => return Some((accounts, proof)),
// Ignore replies that don't match the expected id (such as late responses)
Some(_) => continue,
None => return None,
}
}
})
.await
.ok()??;
// Unzip & validate response
let proof = encodable_to_proof(&proof);
let (account_hashes, accounts): (Vec<_>, Vec<_>) = accounts
.into_iter()
.map(|unit| (unit.hash, AccountState::from(unit.account)))
.unzip();
let encoded_accounts = accounts
.iter()
.map(|acc| acc.encode_to_vec())
.collect::<Vec<_>>();
let should_continue = verify_range(
state_root,
&start,
&account_hashes,
&encoded_accounts,
&proof,
)
.ok()?;
Some((account_hashes, accounts, should_continue))
}

/// Requests bytecodes for the given code hashes
/// Returns the bytecodes or None if:
/// - There are no available peers (the node just started up or was rejected by all other nodes)
/// - The response timed out
/// - The response was empty or not valid
pub async fn request_bytecodes(&self, hashes: Vec<H256>) -> Option<Vec<Bytes>> {
let request_id = rand::random();
let hashes_len = hashes.len();
let request = RLPxMessage::GetByteCodes(GetByteCodes {
id: request_id,
hashes,
bytes: MAX_RESPONSE_BYTES,
});
self.sender.send(request).await.ok()?;
let mut receiver = self.receiver.lock().await;
let codes = tokio::time::timeout(PEER_REPLY_TIMOUT, async move {
loop {
match receiver.recv().await {
Some(RLPxMessage::ByteCodes(ByteCodes { id, codes })) if id == request_id => {
return Some(codes)
}
// Ignore replies that don't match the expected id (such as late responses)
Some(_) => continue,
None => return None,
}
}
})
.await
.ok()??;
(!codes.is_empty() && codes.len() <= hashes_len).then_some(codes)
}

/// Requests storage ranges for accounts given their hashed address and storage roots, and the root of their state trie
/// account_hashes & storage_roots must have the same length
/// storage_roots must not contain empty trie hashes, we will treat empty ranges as invalid responses
/// Returns true if the last accoun't storage was not completely fetched by the request
/// Returns the list of hashed storage keys and values for each account's storage or None if:
/// - There are no available peers (the node just started up or was rejected by all other nodes)
/// - The response timed out
/// - The response was empty or not valid
pub async fn request_storage_ranges(
&self,
state_root: H256,
mut storage_roots: Vec<H256>,
account_hashes: Vec<H256>,
start: H256,
) -> Option<(Vec<Vec<H256>>, Vec<Vec<U256>>, bool)> {
let request_id = rand::random();
let request = RLPxMessage::GetStorageRanges(GetStorageRanges {
id: request_id,
root_hash: state_root,
account_hashes,
starting_hash: start,
limit_hash: HASH_MAX,
response_bytes: MAX_RESPONSE_BYTES,
});
self.sender.send(request).await.ok()?;
let mut receiver = self.receiver.lock().await;
let (mut slots, proof) = tokio::time::timeout(PEER_REPLY_TIMOUT, async move {
loop {
match receiver.recv().await {
Some(RLPxMessage::StorageRanges(StorageRanges { id, slots, proof }))
if id == request_id =>
{
return Some((slots, proof))
}
// Ignore replies that don't match the expected id (such as late responses)
Some(_) => continue,
None => return None,
}
}
})
.await
.ok()??;
// Check we got a reasonable amount of storage ranges
if slots.len() > storage_roots.len() || slots.is_empty() {
return None;
}
// Unzip & validate response
let mut proof = encodable_to_proof(&proof);
let mut storage_keys = vec![];
let mut storage_values = vec![];
let mut should_continue = false;
// Validate each storage range
while !slots.is_empty() {
let (hahsed_keys, values): (Vec<_>, Vec<_>) = slots
.remove(0)
.into_iter()
.map(|slot| (slot.hash, slot.data))
.unzip();
// We won't accept empty storage ranges
if hahsed_keys.is_empty() {
return None;
}
let encoded_values = values
.iter()
.map(|val| val.encode_to_vec())
.collect::<Vec<_>>();
let storage_root = storage_roots.remove(0);

// We have 3 cases (as we won't accept empty storage ranges):
// - The range has only 1 element (with key matching the start): We expect one edge proof
// - The range has the full storage: We expect no proofs
// - The range is not the full storage (last range): We expect 2 edge proofs
if hahsed_keys.len() == 1 && hahsed_keys[0] == start {
if proof.is_empty() {
return None;
};
let first_proof = vec![proof.remove(0)];
verify_range(
storage_root,
&start,
&hahsed_keys,
&encoded_values,
&first_proof,
)
.ok()?;
}
// Last element with two edge proofs
if slots.is_empty() && proof.len() >= 2 {
let last_proof = vec![proof.remove(0), proof.remove(0)];
should_continue = verify_range(
storage_root,
&start,
&hahsed_keys,
&encoded_values,
&last_proof,
)
.ok()?;
} else {
// Full range (no proofs)
verify_range(storage_root, &start, &hahsed_keys, &encoded_values, &[]).ok()?;
}

storage_keys.push(hahsed_keys);
storage_values.push(values);
}
Some((storage_keys, storage_values, should_continue))
}
}
12 changes: 10 additions & 2 deletions crates/networking/p2p/rlpx/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,20 @@ pub(crate) enum RLPxError {
BroadcastError(String),
#[error(transparent)]
RecvError(#[from] RecvError),
#[error(transparent)]
Send(#[from] tokio::sync::mpsc::error::SendError<Message>),
#[error("Failed to send msg: {0}")]
SendMessage(String),
#[error("Error when inserting transaction in the mempool: {0}")]
MempoolError(#[from] MempoolError),
}

// tokio::sync::mpsc::error::SendError<Message> is too large to be part of the RLPxError enum directly
// so we will instead save the error's display message
impl From<tokio::sync::mpsc::error::SendError<Message>> for RLPxError {
fn from(value: tokio::sync::mpsc::error::SendError<Message>) -> Self {
Self::SendMessage(value.to_string())
}
}

// Grouping all cryptographic related errors in a single CryptographicError variant
// We can improve this to individual errors if required
impl From<k256::ecdsa::Error> for RLPxError {
Expand Down
10 changes: 8 additions & 2 deletions crates/networking/p2p/rlpx/snap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@ impl RLPxMessage for GetStorageRanges {
let (id, decoder) = decoder.decode_field("request-id")?;
let (root_hash, decoder) = decoder.decode_field("rootHash")?;
let (account_hashes, decoder) = decoder.decode_field("accountHashes")?;
let (starting_hash, decoder) = decoder.decode_field("startingHash")?;
let (limit_hash, decoder) = decoder.decode_field("limitHash")?;
let (starting_hash, decoder): (Bytes, _) = decoder.decode_field("startingHash")?;
let starting_hash = (!starting_hash.is_empty())
.then(|| H256::from_slice(&starting_hash))
.unwrap_or_default();
let (limit_hash, decoder): (Bytes, _) = decoder.decode_field("limitHash")?;
let limit_hash = (!limit_hash.is_empty())
.then(|| H256::from_slice(&limit_hash))
.unwrap_or(H256([0xFF; 32]));
let (response_bytes, decoder) = decoder.decode_field("responseBytes")?;
decoder.finish()?;

Expand Down
Loading
Loading