Skip to content

Commit faa3dec

Browse files
fmolettaSDartayetmpaulucci
authored
feat(l1): decouple size of execution batch from header request size during full-sync (#3074)
**Motivation** Allow us to configure the amount of blocks to execute in a single batch during full sync. Currently, the only way to do this is by changing the amount of block headers we ask for in each request. In order to achieve this, this PR proposes adding the enum `BlockSyncState` with variants for Full and Snap sync so we can separate behaviors between each mode and also allow each mode to keep its separate state. This is key as we will need to persist headers and bodies through various fetch requests so we can build custom-sized execution batches. It also replaces the previous configurable env var `BlOCK_HEADER_LIMIT` with `EXECUTE_BLOCK_BATCH` <!-- Why does this pull request exist? What are its goals? --> **Description** * Add `BlockSyncState` enum as a way to differentiate between each sync mode's state during block syncing phase. * Refactor `request_and_validate_block_bodies`: it now receives a slice of headers and returns the requested block bodies instead of the full blocks. This allowed us to completely get rid of header cloning. * `validate_block_body` now receives a reference to the head & body instead of the full block (as a result of refactoring its only user) * `Store::add_block_headers` now only receives the headers (This lets us simplify caller code) * Removed `search_head` variable as having both current & search head serves no purpose. * Abtract current_head selection into `BlockSyncState::get_current_head` * Fix bug in condition used to decide wether to switch from snap to full sync * `start_sync` no longer receives `current_head` <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #2894 --------- Co-authored-by: SDartayet <[email protected]> Co-authored-by: Martin Paulucci <[email protected]>
1 parent 8a568ca commit faa3dec

File tree

14 files changed

+417
-352
lines changed

14 files changed

+417
-352
lines changed

cmd/ethrex/cli.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ pub async fn import_blocks(
394394

395395
for blocks in chains {
396396
let size = blocks.len();
397+
let numbers_and_hashes = blocks
398+
.iter()
399+
.map(|b| (b.header.number, b.hash()))
400+
.collect::<Vec<_>>();
397401
// Execute block by block
398402
for block in &blocks {
399403
let hash = block.hash();
@@ -418,7 +422,7 @@ pub async fn import_blocks(
418422
}
419423

420424
_ = store
421-
.mark_chain_as_canonical(&blocks)
425+
.mark_chain_as_canonical(&numbers_and_hashes)
422426
.await
423427
.inspect_err(|error| warn!("Failed to apply fork choice: {}", error));
424428

crates/common/base64.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub fn encode(bytes: &[u8]) -> Vec<u8> {
3333

3434
let mut bytes_iter = bytes.iter();
3535
while bytes_iter.len() > 0 {
36-
// each block is made of as much as 24 bits (3 bytes)
36+
// each block is made up of as many as 24 bits (3 bytes)
3737
let mut block: Vec<u8> = vec![];
3838

3939
while block.len() < 3 {

crates/common/types/block.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,21 +580,24 @@ pub fn validate_block_header(
580580
}
581581

582582
/// Validates that the body matches with the header
583-
pub fn validate_block_body(block: &Block) -> Result<(), InvalidBlockBodyError> {
583+
pub fn validate_block_body(
584+
block_header: &BlockHeader,
585+
block_body: &BlockBody,
586+
) -> Result<(), InvalidBlockBodyError> {
584587
// Validates that:
585588
// - Transactions root and withdrawals root matches with the header
586589
// - Ommers is empty -> https://eips.ethereum.org/EIPS/eip-3675
587-
let computed_tx_root = compute_transactions_root(&block.body.transactions);
590+
let computed_tx_root = compute_transactions_root(&block_body.transactions);
588591

589-
if block.header.transactions_root != computed_tx_root {
592+
if block_header.transactions_root != computed_tx_root {
590593
return Err(InvalidBlockBodyError::TransactionsRootNotMatch);
591594
}
592595

593-
if !block.body.ommers.is_empty() {
596+
if !block_body.ommers.is_empty() {
594597
return Err(InvalidBlockBodyError::OmmersIsNotEmpty);
595598
}
596599

597-
match (block.header.withdrawals_root, &block.body.withdrawals) {
600+
match (block_header.withdrawals_root, &block_body.withdrawals) {
598601
(Some(withdrawals_root), Some(withdrawals)) => {
599602
let computed_withdrawals_root = compute_withdrawals_root(withdrawals);
600603
if withdrawals_root != computed_withdrawals_root {

crates/networking/p2p/discv4/lookup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl Discv4LookupHandler {
144144
asked_peers: &mut HashSet<H512>,
145145
nodes_to_ask: &Vec<Node>,
146146
) -> (Vec<Node>, u32) {
147-
// send FIND_NODE as much as three times
147+
// send FIND_NODE as many as three times
148148
let alpha = 3;
149149
let mut queries = 0;
150150
let mut nodes = vec![];

crates/networking/p2p/peer_handler.rs

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
use bytes::Bytes;
88
use ethrex_common::{
99
H256, U256,
10-
types::{AccountState, Block, BlockBody, BlockHeader, Receipt, validate_block_body},
10+
types::{AccountState, BlockBody, BlockHeader, Receipt, validate_block_body},
1111
};
1212
use ethrex_rlp::encode::RLPEncode;
1313
use ethrex_trie::Nibbles;
@@ -40,7 +40,7 @@ pub const REQUEST_RETRY_ATTEMPTS: usize = 5;
4040
pub const MAX_RESPONSE_BYTES: u64 = 512 * 1024;
4141
pub const HASH_MAX: H256 = H256([0xFF; 32]);
4242

43-
// Ask as much as 128 block bodies per request
43+
// Request as many as 128 block bodies per request
4444
// this magic number is not part of the protocol and is taken from geth, see:
4545
// https://github.com/ethereum/go-ethereum/blob/2585776aabbd4ae9b00050403b42afb0cee968ec/eth/downloader/downloader.go#L42-L43
4646
//
@@ -268,56 +268,40 @@ impl PeerHandler {
268268
None
269269
}
270270

271-
/// Requests block bodies from any suitable peer given their block hashes and validates them
272-
/// Returns the full block or None if:
271+
/// Requests block bodies from any suitable peer given their block headers and validates them
272+
/// Returns the requested block bodies or None if:
273273
/// - There are no available peers (the node just started up or was rejected by all other nodes)
274274
/// - No peer returned a valid response in the given time and retry limits
275275
/// - The block bodies are invalid given the block headers
276-
pub async fn request_and_validate_block_bodies<'a>(
276+
pub async fn request_and_validate_block_bodies(
277277
&self,
278-
block_hashes: &mut Vec<H256>,
279-
headers_iter: &mut impl Iterator<Item = &BlockHeader>,
280-
) -> Option<Vec<Block>> {
281-
let original_hashes = block_hashes.clone();
282-
let headers_vec: Vec<&BlockHeader> = headers_iter.collect();
278+
block_headers: &[BlockHeader],
279+
) -> Option<Vec<BlockBody>> {
280+
let block_hashes: Vec<H256> = block_headers.iter().map(|h| h.hash()).collect();
283281

284282
for _ in 0..REQUEST_RETRY_ATTEMPTS {
285-
*block_hashes = original_hashes.clone();
286-
let mut headers_iter = headers_vec.iter().copied();
287-
288283
let Some((block_bodies, peer_id)) =
289284
self.request_block_bodies_inner(block_hashes.clone()).await
290285
else {
291286
continue; // Retry on empty response
292287
};
293-
294-
let mut blocks: Vec<Block> = vec![];
295-
let block_bodies_len = block_bodies.len();
296-
297-
// Push blocks
298-
for (_, body) in block_hashes.drain(..block_bodies_len).zip(block_bodies) {
299-
let Some(header) = headers_iter.next() else {
300-
debug!("[SYNCING] Header not found for the block bodies received, skipping...");
301-
break; // Break out of block creation and retry with different peer
302-
};
303-
304-
let block = Block::new(header.clone(), body);
305-
blocks.push(block);
288+
let mut res = Vec::new();
289+
let mut validation_success = true;
290+
for (header, body) in block_headers[..block_bodies.len()].iter().zip(block_bodies) {
291+
if let Err(e) = validate_block_body(header, &body) {
292+
warn!(
293+
"Invalid block body error {e}, discarding peer {peer_id} and retrying..."
294+
);
295+
validation_success = false;
296+
self.record_peer_critical_failure(peer_id).await;
297+
break;
298+
}
299+
res.push(body);
306300
}
307-
308-
// Validate blocks
309-
if let Some(e) = blocks
310-
.iter()
311-
.find_map(|block| validate_block_body(block).err())
312-
{
313-
warn!(
314-
"[SYNCING] Invalid block body error {e}, discarding peer {peer_id} and retrying..."
315-
);
316-
self.record_peer_critical_failure(peer_id).await;
317-
continue; // Retry on validation failure
301+
// Retry on validation failure
302+
if validation_success {
303+
return Some(res);
318304
}
319-
320-
return Some(blocks);
321305
}
322306
None
323307
}

crates/networking/p2p/rlpx/eth/blocks.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use ethrex_rlp::{
1111
structs::{Decoder, Encoder},
1212
};
1313
use ethrex_storage::Store;
14-
#[cfg(feature = "sync-test")]
15-
use std::env;
1614
use tracing::error;
1715

1816
pub const HASH_FIRST_BYTE_DECODER: u8 = 160;
@@ -114,18 +112,6 @@ impl GetBlockHeaders {
114112
(self.skip + 1) as i64
115113
};
116114

117-
#[cfg(feature = "sync-test")]
118-
let limit = if let Ok(env_var_block_limit) = env::var("BLOCK_HEADER_LIMIT") {
119-
env_var_block_limit
120-
.parse()
121-
.expect("Block header limit environmental variable is not a number")
122-
} else if self.limit > BLOCK_HEADER_LIMIT {
123-
BLOCK_HEADER_LIMIT
124-
} else {
125-
self.limit
126-
};
127-
128-
#[cfg(not(feature = "sync-test"))]
129115
let limit = if self.limit > BLOCK_HEADER_LIMIT {
130116
BLOCK_HEADER_LIMIT
131117
} else {

0 commit comments

Comments
 (0)