1use crate::{
7 AccountRangeDownload, AccountRangeStep, BlockAccessListCatchUp, BytecodeDownload, BytecodeStep,
8 CatchUpStep, SnapAccountStore, SnapAttemptStore, SnapCatchUpStore, SnapGeneration,
9 SnapPivotPolicy, SnapStateVerifier, SnapSyncError, SnapSyncSession, SnapWrite,
10 StorageRangeDownload, StorageRangeStep, VerifiedRange, DEFAULT_SCAN_CHUNK,
11};
12use alloy_eips::BlockNumHash;
13use reth_db_api::transaction::DbTxMut;
14use reth_network_p2p::{error::RequestError, snap::client::SnapClient};
15use reth_primitives_traits::AlloyBlockHeader;
16use reth_storage_api::{
17 BlockHashReader, DBProvider, DatabaseProviderFactory, HeaderProvider, MetadataProvider,
18 MetadataWriter, StageCheckpointWriter, StateWriter,
19};
20use reth_tasks::Runtime;
21use std::{fmt, future::Future};
22use tokio_util::sync::CancellationToken;
23use tracing::{debug, info};
24
25pub const DEFAULT_RANGES_PER_CHECK: usize = 64;
29
30pub struct SnapBootstrap<C, F, X> {
36 factory: F,
37 runtime: Runtime,
39 context: X,
41 policy: SnapPivotPolicy,
42 session: SnapSyncSession,
44 ranges_per_check: usize,
45 cancel: CancellationToken,
47 shutdown: Option<CancellationToken>,
49 accounts: AccountRangeDownload<C, F>,
50 storage: StorageRangeDownload<C, F>,
51 bytecode: BytecodeDownload<C, F>,
52 catch_up: BlockAccessListCatchUp<C, F>,
53}
54
55impl<C: Clone, F: Clone, X> SnapBootstrap<C, F, X> {
56 pub fn new(client: C, factory: F, runtime: Runtime, context: X) -> Self {
58 let policy = SnapPivotPolicy::default();
59 Self {
60 accounts: AccountRangeDownload::new(client.clone(), factory.clone(), runtime.clone()),
61 storage: StorageRangeDownload::new(client.clone(), factory.clone(), runtime.clone()),
62 bytecode: BytecodeDownload::new(client.clone(), factory.clone(), runtime.clone()),
63 catch_up: BlockAccessListCatchUp::new(client, factory.clone(), runtime.clone()),
64 factory,
65 runtime,
66 context,
67 policy,
68 session: SnapSyncSession::new(policy),
69 ranges_per_check: DEFAULT_RANGES_PER_CHECK,
70 cancel: CancellationToken::new(),
71 shutdown: None,
72 }
73 }
74}
75
76impl<C, F, X> SnapBootstrap<C, F, X> {
77 pub const fn with_policy(mut self, policy: SnapPivotPolicy) -> Self {
79 self.policy = policy;
80 self.session = SnapSyncSession::new(policy);
81 self
82 }
83
84 pub const fn with_ranges_per_check(mut self, ranges: usize) -> Self {
87 self.ranges_per_check = if ranges == 0 { 1 } else { ranges };
88 self
89 }
90
91 pub fn with_cancellation(mut self, cancel: CancellationToken) -> Self {
93 self.cancel = cancel;
94 self
95 }
96
97 pub fn with_shutdown(mut self, shutdown: CancellationToken) -> Self {
101 self.shutdown = Some(shutdown);
102 self
103 }
104}
105
106impl<C, F, X> SnapBootstrap<C, F, X>
107where
108 C: SnapClient + Clone + Unpin,
109 F: DatabaseProviderFactory + Clone + 'static,
110 F::Provider: HeaderProvider + MetadataProvider + BlockHashReader,
111 F::ProviderRW: HeaderProvider
112 + BlockHashReader
113 + MetadataProvider
114 + MetadataWriter
115 + StageCheckpointWriter
116 + StateWriter
117 + DBProvider<Tx: DbTxMut>,
118 X: SnapSyncContext,
119{
120 pub async fn run(&mut self) -> Result<SnapBootstrapOutcome, SnapSyncError> {
125 let mut announced = None;
128 loop {
129 if self.cancel.is_cancelled() {
130 return Ok(SnapBootstrapOutcome::Stopped)
131 }
132 let head = self.context.head()?;
133 let step = match self.resolve(head)? {
134 Resolved::Verified(pivot) => return Ok(SnapBootstrapOutcome::Verified { pivot }),
135 Resolved::BeforeBlockAccessLists => {
136 return Ok(SnapBootstrapOutcome::BeforeBlockAccessLists)
137 }
138 Resolved::Waiting => {
139 if announced.replace(Wait::Pivot) == Some(Wait::Pivot) {
140 debug!(target: "sync::snap", head, "No eligible snap pivot yet, waiting for headers");
141 } else {
142 info!(target: "sync::snap", head, "No eligible snap pivot yet, waiting for headers");
143 }
144 Step::Wait
145 }
146 Resolved::Active(write) => match self.drive(write, head).await {
147 Ok(step) => step,
148 Err(SnapSyncError::Request(RequestError::UnsupportedCapability)) => {
150 if announced.replace(Wait::SnapPeer) == Some(Wait::SnapPeer) {
151 debug!(target: "sync::snap", "No connected peer serves snap/2, waiting for one");
152 } else {
153 info!(target: "sync::snap", "No connected peer serves snap/2, waiting for one");
154 }
155 Step::Wait
156 }
157 Err(error) if error.is_transient() => {
158 debug!(target: "sync::snap", %error, "Waiting for peers or headers");
159 Step::Wait
160 }
161 Err(error) if error.is_reorg() => {
163 debug!(target: "sync::snap", %error, "Snap pivot was reorged");
164 Step::Continue
165 }
166 Err(
168 error @ (SnapSyncError::UnsupportedRecord { .. } |
169 SnapSyncError::UnreadableRecord { .. }),
170 ) => {
171 info!(target: "sync::snap", %error, "Snap progress is unreadable, restarting");
172 Step::Restart
173 }
174 Err(SnapSyncError::Cancelled) => Step::Stop,
175 Err(error) => return Err(error),
176 },
177 };
178 match step {
179 Step::HandedOff(write) => {
180 let pivot = self.pivot()?;
181 info!(target: "sync::snap", ?pivot, "Snap state handed to the trie rebuild");
182 return Ok(SnapBootstrapOutcome::TrieRebuild { write, pivot })
183 }
184 Step::Continue => announced = None,
185 Step::Wait => {
186 if !self.wait(head).await {
187 return Ok(SnapBootstrapOutcome::Stopped)
188 }
189 }
190 Step::Restart => {
191 let provider = self.factory.database_provider_rw()?;
192 provider.abandon_snap_attempt()?;
193 provider.commit()?;
194 }
195 Step::Stop => return Ok(SnapBootstrapOutcome::Stopped),
196 }
197 }
198 }
199
200 fn resolve(&mut self, head: u64) -> Result<Resolved, SnapSyncError> {
204 let provider = self.factory.database_provider_rw()?;
205 let mut session = SnapSyncSession::new(self.policy);
206 if let Some(attempt) = provider.snap_attempt()? {
207 if attempt.is_verified() {
208 return Ok(Resolved::Verified(attempt.pivot()))
209 }
210 if let Some(write) = provider.active_snap_write()? {
211 session.resume(SnapGeneration::new(attempt.pivot(), attempt.state_root()));
213 self.session = session;
214 return Ok(Resolved::Active(write))
215 }
216 }
217
218 session.select(&provider, head, self.context.finalized()?)?;
219 let Some(generation) = session.start() else {
220 return match provider.sealed_header(head)? {
226 Some(header) if header.block_access_list_hash().is_none() => {
227 Ok(Resolved::BeforeBlockAccessLists)
228 }
229 _ => Ok(Resolved::Waiting),
230 }
231 };
232 let write = provider.start_snap_attempt(generation)?;
233 provider.start_account_coverage(write)?;
234 provider.commit()?;
235 info!(
236 target: "sync::snap",
237 pivot = ?generation.target(),
238 state_root = %generation.state_root(),
239 "Started snap attempt"
240 );
241 self.session = session;
242 Ok(Resolved::Active(write))
243 }
244
245 async fn drive(&mut self, write: SnapWrite, head: u64) -> Result<Step, SnapSyncError> {
248 let (applied, complete) = {
249 let provider = self.factory.database_provider_ro()?;
250 let generation = self.session.target().copied().ok_or(SnapSyncError::NoAttempt)?;
251 let handed_off = provider.is_trie_rebuild_started(write)?;
252 if !generation.is_canonical(&provider)? {
253 if handed_off {
255 info!(target: "sync::snap", pivot = ?generation.target(), "Handed-off snap pivot was reorged, restarting");
256 return Ok(Step::Restart)
257 }
258 drop(provider);
259 return self.recover(write, head).await
260 }
261 if handed_off {
262 return Ok(Step::HandedOff(write))
263 }
264 let applied = provider
265 .catch_up_progress(write)?
266 .ok_or(SnapSyncError::NoCatchUpProgress)?
267 .applied();
268 let complete = provider.account_coverage(write)?.is_some_and(|c| c.is_complete()) &&
270 provider.snap_repairs(write)?.is_empty();
271 (applied, complete)
272 };
273 if complete && applied == self.pivot()? {
276 return self.hand_off(write).await
277 }
278 if !self.policy.is_catchable_from(applied.number, head) {
281 info!(target: "sync::snap", ?applied, head, "Snap catch-up outlived the served block access lists, restarting");
282 return Ok(Step::Restart)
283 }
284 let write = self.advance_pivot(write, head)?;
285 if let Some(step) = self.catch_up(write).await? {
288 return Ok(step)
289 }
290 if let Some(step) = self.repair().await? {
293 return Ok(step)
294 }
295 self.download_ranges(write).await
296 }
297
298 async fn recover(&mut self, write: SnapWrite, head: u64) -> Result<Step, SnapSyncError> {
303 let pivot = self.pivot()?;
304 let provider = self.factory.database_provider_ro()?;
305 let Some(reorg) = provider.snap_reorg(write)? else {
306 info!(target: "sync::snap", ?pivot, "Snap pivot was reorged past its kept blocks, restarting");
307 return Ok(Step::Restart)
308 };
309 let ancestor = reorg.ancestor();
310 let committed = provider
313 .sealed_header(ancestor.number)?
314 .is_some_and(|header| header.block_access_list_hash().is_some());
315 if !committed {
316 info!(target: "sync::snap", ?pivot, ?ancestor, "Snap pivot was reorged across block access list activation, restarting");
317 return Ok(Step::Restart)
318 }
319 let resume = provider
320 .catch_up_progress(write)?
321 .ok_or(SnapSyncError::NoCatchUpProgress)?
322 .resume_after(ancestor)
323 .number;
324 if !self.policy.is_catchable_from(resume, head) {
325 info!(target: "sync::snap", ?pivot, ?ancestor, resume, head, "Orphaned block access lists expired, restarting");
326 return Ok(Step::Restart)
327 }
328 let generation = self.policy.select(&provider, head, self.context.finalized()?)?;
329 let Some(generation) = generation.filter(|g| g.target().number >= ancestor.number) else {
332 return Ok(Step::Wait)
333 };
334 drop(provider);
335 let Some(lists) = self.catch_up.orphaned_lists(reorg.orphaned()).await? else {
337 if !self.policy.awaits_orphaned_lists(ancestor.number, head) {
338 info!(target: "sync::snap", ?pivot, ?ancestor, head, "Orphaned block access lists stayed unavailable, restarting");
339 return Ok(Step::Restart)
340 }
341 debug!(target: "sync::snap", ?pivot, "Orphaned block access lists are unavailable");
342 return Ok(Step::Wait)
343 };
344
345 self.commit_blocking(move |provider| {
347 provider.commit_reorg_recovery(write, ancestor, &lists, generation).map(|_| ())
348 })
349 .await?;
350 info!(target: "sync::snap", ?pivot, ?ancestor, to = ?generation.target(), "Recovered snap state from a pivot reorg");
351 Ok(Step::Continue)
352 }
353
354 fn advance_pivot(&mut self, write: SnapWrite, head: u64) -> Result<SnapWrite, SnapSyncError> {
357 let provider = self.factory.database_provider_rw()?;
358 let from = self.pivot()?;
359 let Some(advanced) =
360 self.session.advance(&provider, write, head, self.context.finalized()?)?
361 else {
362 return Ok(write)
363 };
364 provider.commit()?;
366 info!(target: "sync::snap", ?from, to = ?self.pivot()?, "Advanced snap pivot");
367 Ok(advanced)
368 }
369
370 async fn catch_up(&mut self, write: SnapWrite) -> Result<Option<Step>, SnapSyncError> {
372 let pivot = self.pivot()?.number;
373 loop {
374 match self.catch_up.next(write, pivot).await? {
375 CatchUpStep::Complete => return Ok(None),
376 CatchUpStep::Applied { progress, .. } => {
377 debug!(target: "sync::snap", applied = ?progress.applied(), pivot, "Applied block access lists");
378 }
379 CatchUpStep::Unavailable { peer_id, .. } => {
380 debug!(target: "sync::snap", ?peer_id, pivot, "Peer does not serve the pivot's block access lists");
381 return Ok(Some(Step::Wait))
382 }
383 }
384 if self.cancel.is_cancelled() {
385 return Ok(Some(Step::Stop))
386 }
387 }
388 }
389
390 async fn repair(&mut self) -> Result<Option<Step>, SnapSyncError> {
394 for _ in 0..self.ranges_per_check {
395 let range = match self.accounts.next_repair().await? {
396 None => return Ok(None),
397 Some(AccountRangeStep::Verified(range)) => range,
398 Some(AccountRangeStep::Unavailable { .. }) => return Ok(Some(Step::Wait)),
399 };
400 let Some(slots) = self.storage.repair_slots(&range).await? else {
401 return Ok(Some(Step::Wait))
402 };
403 if let Some(step) = self.download_code(&range).await? {
404 return Ok(Some(step))
405 }
406 let hashed_address = range.origin();
407 let remaining = self.accounts.commit_repair(range, slots).await?;
408 debug!(target: "sync::snap", %hashed_address, remaining, "Committed snap repair batch");
409 if self.cancel.is_cancelled() {
410 return Ok(Some(Step::Stop))
411 }
412 }
413 Ok(Some(Step::Continue))
414 }
415
416 async fn download_ranges(&mut self, write: SnapWrite) -> Result<Step, SnapSyncError> {
418 for _ in 0..self.ranges_per_check {
419 if self.cancel.is_cancelled() {
420 return Ok(Step::Stop)
421 }
422 let range = match self.accounts.next().await? {
423 Some(AccountRangeStep::Verified(range)) => range,
424 Some(AccountRangeStep::Unavailable { origin, peer_id }) => {
425 debug!(target: "sync::snap", ?peer_id, %origin, "Peer does not serve the pivot state");
426 return Ok(Step::Wait)
427 }
428 None => return self.hand_off(write).await,
429 };
430 if let Some(step) = self.download_storage_and_code(&range).await? {
431 return Ok(step)
432 }
433 self.accounts.commit(range, Default::default(), Vec::new()).await?;
434 }
435 Ok(Step::Continue)
436 }
437
438 async fn download_storage_and_code(
441 &mut self,
442 range: &VerifiedRange,
443 ) -> Result<Option<Step>, SnapSyncError> {
444 loop {
445 match self.storage.next(range).await? {
446 StorageRangeStep::Complete => break,
447 StorageRangeStep::Committed(_) => {}
448 StorageRangeStep::Unavailable { peer_id, .. } => {
449 debug!(target: "sync::snap", ?peer_id, "Peer does not serve the pivot's storage");
450 return Ok(Some(Step::Wait))
451 }
452 }
453 if self.cancel.is_cancelled() {
456 return Ok(Some(Step::Stop))
457 }
458 }
459 self.download_code(range).await
460 }
461
462 async fn download_code(
464 &mut self,
465 range: &VerifiedRange,
466 ) -> Result<Option<Step>, SnapSyncError> {
467 loop {
468 match self.bytecode.next(range).await? {
469 BytecodeStep::Complete => return Ok(None),
470 BytecodeStep::Committed { .. } => {}
471 BytecodeStep::Unavailable { peer_id, .. } => {
472 debug!(target: "sync::snap", ?peer_id, "Peer does not serve the pivot's code");
473 return Ok(Some(Step::Wait))
474 }
475 }
476 if self.cancel.is_cancelled() {
477 return Ok(Some(Step::Stop))
478 }
479 }
480 }
481
482 async fn hand_off(&self, write: SnapWrite) -> Result<Step, SnapSyncError> {
485 let cancel = self.shutdown.as_ref().unwrap_or(&self.cancel).clone();
486 self.commit_blocking(move |provider| {
487 provider.start_trie_rebuild(write, DEFAULT_SCAN_CHUNK, &cancel)
488 })
489 .await?;
490 Ok(Step::HandedOff(write))
491 }
492
493 async fn commit_blocking(
495 &self,
496 apply: impl FnOnce(&F::ProviderRW) -> Result<(), SnapSyncError> + Send + 'static,
497 ) -> Result<(), SnapSyncError> {
498 let factory = self.factory.clone();
499 self.runtime
500 .spawn_blocking(move || -> Result<(), SnapSyncError> {
501 let provider = factory.database_provider_rw()?;
502 apply(&provider)?;
503 provider.commit()?;
504 Ok(())
505 })
506 .await
507 .map_err(SnapSyncError::blocking_task)?
508 }
509
510 fn pivot(&self) -> Result<BlockNumHash, SnapSyncError> {
512 self.session.target().map(SnapGeneration::target).ok_or(SnapSyncError::NoAttempt)
513 }
514
515 async fn wait(&mut self, head: u64) -> bool {
517 let Self { cancel, context, .. } = self;
518 cancel.run_until_cancelled(context.wait_for_progress(head)).await.unwrap_or(false)
519 }
520}
521
522impl<C, F, X> fmt::Debug for SnapBootstrap<C, F, X> {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 f.debug_struct("SnapBootstrap")
525 .field("policy", &self.policy)
526 .field("session", &self.session)
527 .field("ranges_per_check", &self.ranges_per_check)
528 .finish_non_exhaustive()
529 }
530}
531
532#[derive(Clone, Copy, Debug, Eq, PartialEq)]
534pub enum SnapBootstrapOutcome {
535 TrieRebuild {
538 write: SnapWrite,
540 pivot: BlockNumHash,
542 },
543 Verified {
545 pivot: BlockNumHash,
547 },
548 Stopped,
551 BeforeBlockAccessLists,
553}
554
555pub trait SnapSyncContext: Send {
557 fn head(&self) -> Result<u64, SnapSyncError>;
559
560 fn finalized(&self) -> Result<Option<u64>, SnapSyncError> {
563 Ok(None)
564 }
565
566 fn wait_for_progress(&mut self, head: u64) -> impl Future<Output = bool> + Send;
571}
572
573enum Resolved {
575 Active(SnapWrite),
577 Verified(BlockNumHash),
579 Waiting,
581 BeforeBlockAccessLists,
583}
584
585enum Step {
587 HandedOff(SnapWrite),
588 Continue,
590 Wait,
592 Restart,
594 Stop,
596}
597
598#[derive(Clone, Copy, PartialEq, Eq)]
600enum Wait {
601 Pivot,
602 SnapPeer,
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 use crate::{
609 common::SnapRecord,
610 reorg::StoredAncestry,
611 test_utils::{
612 account, account_range, hashed_factory, header, key, policy, state_root,
613 storage_ranges, storage_root_of, stored_slots, verified_range, ReorgFactoryExt,
614 ScriptedSnapClient,
615 },
616 StateRepairs, VerifiedSnapState,
617 };
618 use alloy_eip7928::{
619 compute_block_access_list_hash, AccountChanges, BalanceChange, BlockAccessIndex,
620 };
621 use alloy_primitives::{keccak256, Address, Bytes, B256, U256};
622 use reth_eth_wire_types::{
623 snap::{AccountRangeMessage, BlockAccessListsMessage},
624 BlockAccessLists,
625 };
626 use reth_network_p2p::{error::PeerRequestResult, snap::client::SnapResponse};
627 use reth_network_peers::{PeerId, WithPeerId};
628 use reth_primitives_traits::{Account, AlloyBlockHeader, Header, SealedHeader};
629 use reth_provider::{
630 test_utils::{insert_headers, MockNodeTypesWithDB},
631 ProviderFactory,
632 };
633 use reth_stages::stages::MerkleStage;
634 use reth_stages_api::{ExecInput, Stage};
635 use reth_stages_types::StageId;
636 use reth_storage_api::{SnapAttemptId, StageCheckpointReader};
637 use reth_trie_common::{HashedPostState, TrieAccount};
638 use std::{cell::RefCell, collections::VecDeque, sync::Arc};
639
640 type Factory = ProviderFactory<MockNodeTypesWithDB>;
641 type Bootstrap = SnapBootstrap<Arc<ScriptedSnapClient>, Factory, TestContext>;
642
643 const FAR: B256 = B256::repeat_byte(0xaa);
644 const STALE: Address = Address::repeat_byte(0x51);
646 const KEPT: Address = Address::repeat_byte(0x52);
648
649 fn accounts() -> Vec<(B256, TrieAccount)> {
650 vec![(key(1), account(1)), (key(2), account(2)), (FAR, account(3))]
651 }
652
653 fn insert_chain(factory: &Factory, tip: u64, root: B256) {
656 insert_headers(factory, &chain(tip, root));
657 }
658
659 fn empty_lists(request_id: u64, blocks: usize) -> PeerRequestResult<SnapResponse> {
661 lists(request_id, &vec![Vec::new(); blocks], true)
662 }
663
664 fn no_lists(request_id: u64) -> PeerRequestResult<SnapResponse> {
666 lists(request_id, &[Vec::new()], false)
667 }
668
669 fn scripted(
670 factory: &Factory,
671 responses: impl IntoIterator<Item = PeerRequestResult<SnapResponse>>,
672 heads: impl IntoIterator<Item = u64>,
673 ) -> (Arc<ScriptedSnapClient>, Bootstrap) {
674 let client = Arc::new(ScriptedSnapClient::new(responses));
675 let context = TestContext { heads: RefCell::new(heads.into_iter().collect()), waits: 0 };
676 let bootstrap =
677 SnapBootstrap::new(Arc::clone(&client), factory.clone(), Runtime::test(), context)
678 .with_policy(policy());
679 (client, bootstrap)
680 }
681
682 fn downloaded(factory: &Factory) -> SnapWrite {
684 let accounts = accounts();
685 insert_chain(factory, 3, state_root(&accounts));
686 let provider = factory.database_provider_rw().unwrap();
687 let pivot = provider.sealed_header(2).unwrap().unwrap().num_hash();
688 let write =
689 provider.start_snap_attempt(SnapGeneration::new(pivot, state_root(&accounts))).unwrap();
690 provider.start_account_coverage(write).unwrap();
691 let range = verified_range(&accounts, 0..accounts.len(), B256::ZERO, &[]);
692 provider.commit_account_range(write, &range, Default::default(), Vec::new()).unwrap();
693 provider.commit().unwrap();
694 write
695 }
696
697 fn attempt_id(factory: &Factory) -> SnapAttemptId {
698 factory.database_provider_ro().unwrap().snap_attempt().unwrap().unwrap().id()
699 }
700
701 struct TestContext {
703 heads: RefCell<VecDeque<u64>>,
704 waits: usize,
705 }
706
707 impl SnapSyncContext for TestContext {
708 fn head(&self) -> Result<u64, SnapSyncError> {
709 let mut heads = self.heads.borrow_mut();
710 let head = if heads.len() > 1 { heads.pop_front() } else { heads.front().copied() };
711 Ok(head.expect("the context is given at least one head"))
712 }
713
714 fn wait_for_progress(&mut self, _head: u64) -> impl Future<Output = bool> + Send {
715 self.waits += 1;
716 std::future::ready(false)
717 }
718 }
719
720 fn chain(tip: u64, root: B256) -> Vec<SealedHeader> {
722 let access_list_hash = compute_block_access_list_hash(&Vec::<AccountChanges>::new());
723 let mut parent = B256::ZERO;
724 (0..=tip)
725 .map(|number| {
726 let mut header = header(number, parent, Some(access_list_hash));
727 header.state_root = root;
728 let sealed = SealedHeader::seal_slow(header);
729 parent = sealed.hash();
730 sealed
731 })
732 .collect()
733 }
734
735 fn branch(
737 parent: &SealedHeader,
738 lists: &[Vec<AccountChanges>],
739 root: B256,
740 ) -> Vec<SealedHeader> {
741 let mut parent = parent.clone();
742 lists
743 .iter()
744 .map(|list| {
745 let access_list_hash = compute_block_access_list_hash(list);
746 let mut header = header(parent.number() + 1, parent.hash(), Some(access_list_hash));
747 header.state_root = root;
748 parent = SealedHeader::seal_slow(header);
749 parent.clone()
750 })
751 .collect()
752 }
753
754 fn lists(
756 request_id: u64,
757 lists: &[Vec<AccountChanges>],
758 served: bool,
759 ) -> PeerRequestResult<SnapResponse> {
760 let block_access_lists =
761 lists.iter().map(|list| served.then(|| alloy_rlp::encode(list).into())).collect();
762 let message = BlockAccessListsMessage {
763 request_id,
764 block_access_lists: BlockAccessLists(block_access_lists),
765 };
766 Ok(WithPeerId::new(PeerId::random(), SnapResponse::BlockAccessLists(message)))
767 }
768
769 fn rebuild_and_verify(factory: &Factory, write: SnapWrite, target: u64) -> VerifiedSnapState {
771 let provider = factory.database_provider_rw().unwrap();
772 let mut stage = MerkleStage::default_execution();
773 loop {
774 let checkpoint = provider.get_stage_checkpoint(StageId::MerkleExecute).unwrap();
775 let output =
776 stage.execute(&provider, ExecInput { target: Some(target), checkpoint }).unwrap();
777 provider.save_stage_checkpoint(StageId::MerkleExecute, output.checkpoint).unwrap();
778 if output.done {
779 break
780 }
781 }
782 provider.verify_state_root(write).unwrap()
783 }
784
785 fn reorg_accounts() -> Vec<(B256, TrieAccount)> {
787 let mut accounts = vec![(keccak256(STALE), account(1)), (keccak256(KEPT), account(2))];
788 accounts.sort_by_key(|(hashed_address, _)| *hashed_address);
789 accounts
790 }
791
792 fn stale_changes() -> AccountChanges {
793 AccountChanges::new(STALE)
794 }
795
796 fn reorged(
800 factory: &Factory,
801 new_lists: [Vec<AccountChanges>; 2],
802 ) -> (SnapAttemptId, Vec<SealedHeader>, Vec<SealedHeader>) {
803 let accounts = reorg_accounts();
804 let root = state_root(&accounts);
805 let shared = chain(2, root);
806 let orphaned = branch(&shared[2], &orphaned_lists(), root);
807 insert_headers(factory, &shared);
808 insert_headers(factory, &orphaned);
809 let provider = factory.database_provider_rw().unwrap();
810 let write =
811 provider.start_snap_attempt(SnapGeneration::new(orphaned[1].num_hash(), root)).unwrap();
812 provider.start_account_coverage(write).unwrap();
813 let range = verified_range(&accounts, 0..2, B256::ZERO, &[]);
814 provider.commit_account_range(write, &range, Default::default(), Vec::new()).unwrap();
815 let mut credited = Account::from(account(1));
817 credited.balance = U256::from(1_000);
818 let stale = HashedPostState::default().with_accounts([(keccak256(STALE), Some(credited))]);
819 provider.write_hashed_state(&stale.into_sorted()).unwrap();
820 provider.commit().unwrap();
821
822 let [first, second] = new_lists;
823 let new = branch(&shared[2], &[first, second, Vec::new()], root);
824 factory.replace_headers_after(2, &new);
825 (attempt_id(factory), orphaned, new)
826 }
827
828 fn orphaned_lists() -> Vec<Vec<AccountChanges>> {
830 [999, 1_000]
831 .map(|balance| {
832 vec![stale_changes().with_balance_change(BalanceChange::new(
833 BlockAccessIndex::new(1),
834 U256::from(balance),
835 ))]
836 })
837 .to_vec()
838 }
839
840 fn unserved_range(request_id: u64) -> PeerRequestResult<SnapResponse> {
842 let message = AccountRangeMessage { request_id, accounts: Vec::new(), proof: Vec::new() };
843 Ok(WithPeerId::new(PeerId::random(), SnapResponse::AccountRange(message)))
844 }
845
846 fn served_account(
848 request_id: u64,
849 accounts: &[(B256, TrieAccount)],
850 hashed_address: B256,
851 ) -> PeerRequestResult<SnapResponse> {
852 let index = accounts.iter().position(|(key, _)| *key == hashed_address).unwrap();
853 account_range(request_id, accounts, index..index + 1, &[hashed_address])
854 }
855
856 fn hashes(headers: &[SealedHeader]) -> Vec<B256> {
857 headers.iter().map(SealedHeader::hash).collect()
858 }
859
860 async fn recover(new_lists: [Vec<AccountChanges>; 2], repaired: bool) -> Vec<B256> {
863 let accounts = reorg_accounts();
864 let factory = hashed_factory();
865 let (attempt, orphaned, new) = reorged(&factory, new_lists.clone());
866 let mut responses = vec![
867 lists(1, &orphaned_lists(), true),
868 lists(2, &new_lists, true),
870 ];
871 if repaired {
872 responses.push(served_account(1, &accounts, keccak256(STALE)));
873 }
874 let (client, mut bootstrap) = scripted(&factory, responses, [5]);
875
876 let outcome = bootstrap.run().await.unwrap();
877
878 let SnapBootstrapOutcome::TrieRebuild { write, pivot } = outcome else {
879 panic!("the state is repaired: {outcome:?}")
880 };
881 assert_eq!(pivot, new[1].num_hash());
882 assert_eq!(attempt_id(&factory), attempt);
883 assert_eq!(*client.block_requests(), [hashes(&orphaned), hashes(&new[..2])]);
884 let origins = client.origins().clone();
885 let verified = rebuild_and_verify(&factory, write, pivot.number);
887 assert_eq!(verified.state_root(), state_root(&accounts));
888 origins
889 }
890
891 #[tokio::test]
892 async fn downloads_the_state_and_hands_it_to_the_trie_rebuild() {
893 let accounts = accounts();
894 let factory = hashed_factory();
895 insert_chain(&factory, 3, state_root(&accounts));
896 let (client, mut bootstrap) =
897 scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
898
899 let outcome = bootstrap.run().await.unwrap();
900
901 let SnapBootstrapOutcome::TrieRebuild { write, pivot } = outcome else {
902 panic!("the state is complete: {outcome:?}")
903 };
904 assert_eq!(pivot.number, 2);
905 assert_eq!(*client.origins(), [B256::ZERO]);
906 let provider = factory.database_provider_ro().unwrap();
907 assert!(provider.is_trie_rebuild_started(write).unwrap());
908 }
909
910 #[tokio::test]
911 async fn pending_repairs_follow_the_pivot_once_the_accounts_are_complete() {
912 let accounts = accounts();
913 let root = state_root(&accounts);
914 let factory = hashed_factory();
915 insert_chain(&factory, 8, root);
916 let provider = factory.database_provider_rw().unwrap();
917 let pivot = provider.sealed_header(2).unwrap().unwrap().num_hash();
918 let write = provider.start_snap_attempt(SnapGeneration::new(pivot, root)).unwrap();
919 provider.start_account_coverage(write).unwrap();
920 let range = verified_range(&accounts, 0..3, B256::ZERO, &[]);
921 provider.commit_account_range(write, &range, Default::default(), Vec::new()).unwrap();
922 let mut repairs = StateRepairs::default();
923 repairs.insert_account(key(2));
924 provider.schedule_snap_repairs(write, repairs).unwrap();
925 provider.commit().unwrap();
926 let (_, mut bootstrap) = scripted(&factory, [unserved_range(1)], [3]);
928 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
929
930 let responses = [empty_lists(1, 5), account_range(1, &accounts, 1..2, &[key(2)])];
932 let (client, mut bootstrap) = scripted(&factory, responses, [8]);
933 let outcome = bootstrap.run().await.unwrap();
934
935 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
936 panic!("the state is complete: {outcome:?}")
937 };
938 assert_eq!(pivot.number, 7);
939 assert_eq!(*client.origins(), [key(2)]);
940 }
941
942 #[tokio::test]
943 async fn scheduled_repairs_are_fetched_at_the_pivot_before_ranges() {
944 let accounts = accounts();
945 let factory = hashed_factory();
946 insert_chain(&factory, 3, state_root(&accounts));
947 let provider = factory.database_provider_rw().unwrap();
948 let pivot = provider.sealed_header(2).unwrap().unwrap().num_hash();
949 let write =
950 provider.start_snap_attempt(SnapGeneration::new(pivot, state_root(&accounts))).unwrap();
951 provider.start_account_coverage(write).unwrap();
952 let mut repairs = StateRepairs::default();
953 repairs.insert_account(key(2));
954 provider.schedule_snap_repairs(write, repairs).unwrap();
955 provider.commit().unwrap();
956 let responses =
958 [account_range(1, &accounts, 1..2, &[key(2)]), account_range(2, &accounts, 0..3, &[])];
959 let (client, mut bootstrap) = scripted(&factory, responses, [3]);
960
961 let outcome = bootstrap.run().await.unwrap();
962
963 assert!(matches!(outcome, SnapBootstrapOutcome::TrieRebuild { .. }), "{outcome:?}");
964 assert_eq!(*client.origins(), [key(2), B256::ZERO]);
965 let provider = factory.database_provider_ro().unwrap();
966 assert!(provider.snap_repairs(write).unwrap().is_empty());
967 }
968
969 #[tokio::test]
970 async fn repairs_yield_to_pivot_checks_between_batches() {
971 let accounts = accounts();
972 let root = state_root(&accounts);
973 let factory = hashed_factory();
974 insert_chain(&factory, 8, root);
975 let provider = factory.database_provider_rw().unwrap();
976 let pivot = provider.sealed_header(2).unwrap().unwrap().num_hash();
977 let write = provider.start_snap_attempt(SnapGeneration::new(pivot, root)).unwrap();
978 provider.start_account_coverage(write).unwrap();
979 let range = verified_range(&accounts, 0..3, B256::ZERO, &[]);
980 provider.commit_account_range(write, &range, Default::default(), Vec::new()).unwrap();
981 let mut repairs = StateRepairs::default();
982 repairs.insert_account(key(1));
983 repairs.insert_account(key(2));
984 provider.schedule_snap_repairs(write, repairs).unwrap();
985 provider.commit().unwrap();
986 let responses = [
987 account_range(1, &accounts, 0..1, &[key(1)]),
988 empty_lists(1, 5),
990 account_range(2, &accounts, 1..2, &[key(2)]),
991 ];
992 let (client, bootstrap) = scripted(&factory, responses, [3, 8]);
993 let mut bootstrap = bootstrap.with_ranges_per_check(1);
994
995 let outcome = bootstrap.run().await.unwrap();
996
997 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
998 panic!("the state is repaired: {outcome:?}")
999 };
1000 assert_eq!(pivot.number, 7);
1001 assert_eq!(*client.origins(), [key(1), key(2)]);
1002 assert_eq!(client.block_requests().len(), 1);
1003 }
1004
1005 #[tokio::test]
1006 async fn a_lagging_pivot_advances_before_more_ranges_download() {
1007 let accounts = accounts();
1008 let root = state_root(&accounts);
1009 let factory = hashed_factory();
1010 insert_chain(&factory, 8, root);
1011 let responses = [
1012 account_range(1, &accounts, 0..1, &[key(1)]),
1013 empty_lists(1, 5),
1015 account_range(2, &accounts, 1..3, &[key(2), FAR]),
1016 ];
1017 let (client, bootstrap) = scripted(&factory, responses, [3, 8]);
1019 let mut bootstrap = bootstrap.with_ranges_per_check(1);
1020
1021 let outcome = bootstrap.run().await.unwrap();
1022
1023 let SnapBootstrapOutcome::TrieRebuild { write, pivot } = outcome else {
1024 panic!("the state is complete: {outcome:?}")
1025 };
1026 assert_eq!(pivot.number, 7);
1027 assert_eq!(*client.origins(), [B256::ZERO, key(2)]);
1028 assert_eq!(client.block_requests().len(), 1);
1029
1030 let verified = rebuild_and_verify(&factory, write, 7);
1032 assert_eq!((verified.target(), verified.state_root()), (pivot, root));
1033 }
1034
1035 #[tokio::test]
1036 async fn peers_without_the_state_wait_and_the_next_run_resumes() {
1037 let accounts = accounts();
1038 let factory = hashed_factory();
1039 insert_chain(&factory, 3, state_root(&accounts));
1040 let responses = [
1041 account_range(1, &accounts, 0..1, &[key(1)]),
1042 Err(RequestError::UnsupportedCapability),
1043 ];
1044 let (_, mut bootstrap) = scripted(&factory, responses, [3]);
1045
1046 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1047 assert_eq!(bootstrap.context.waits, 1);
1048 let attempt = attempt_id(&factory);
1049
1050 let (client, mut resumed) =
1051 scripted(&factory, [account_range(1, &accounts, 1..3, &[key(2), FAR])], [3]);
1052 let outcome = resumed.run().await.unwrap();
1053
1054 assert!(matches!(outcome, SnapBootstrapOutcome::TrieRebuild { .. }));
1055 assert_eq!(attempt_id(&factory), attempt);
1056 assert_eq!(*client.origins(), [key(2)]);
1057 }
1058
1059 #[tokio::test]
1060 async fn no_eligible_pivot_waits_without_starting_an_attempt() {
1061 let factory = hashed_factory();
1062 insert_chain(&factory, 0, state_root(&accounts()));
1063 let (client, mut bootstrap) = scripted(&factory, [], [0]);
1064
1065 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1066
1067 assert_eq!(bootstrap.context.waits, 1);
1068 assert!(client.origins().is_empty());
1069 assert!(factory.database_provider_ro().unwrap().snap_attempt().unwrap().is_none());
1070 }
1071
1072 #[tokio::test]
1073 async fn a_head_before_block_access_lists_falls_back_without_waiting() {
1074 let factory = hashed_factory();
1075 let genesis = header(0, B256::ZERO, None);
1076 insert_headers(&factory, &[SealedHeader::seal_slow(genesis)]);
1077 let (client, mut bootstrap) = scripted(&factory, [], [0]);
1078
1079 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::BeforeBlockAccessLists);
1080
1081 assert_eq!(bootstrap.context.waits, 0);
1082 assert!(client.origins().is_empty());
1083 assert!(factory.database_provider_ro().unwrap().snap_attempt().unwrap().is_none());
1084 }
1085
1086 #[tokio::test]
1087 async fn a_reorged_pivot_without_kept_blocks_restarts_the_attempt() {
1088 let accounts = accounts();
1089 let factory = hashed_factory();
1090 let blocks = chain(3, state_root(&accounts));
1091 insert_headers(&factory, &blocks);
1092 let provider = factory.database_provider_rw().unwrap();
1094 let orphaned = SnapGeneration::new(blocks[2].num_hash(), state_root(&accounts));
1095 provider.start_snap_attempt(orphaned).unwrap();
1096 StoredAncestry::clear(&provider).unwrap();
1097 provider.commit().unwrap();
1098 let mut replaced = chain(3, state_root(&accounts));
1099 replaced[2] = SealedHeader::seal_slow(Header {
1100 extra_data: Bytes::from_static(&[1]),
1101 ..replaced[2].header().clone()
1102 });
1103 replaced[3] = SealedHeader::seal_slow(Header {
1104 parent_hash: replaced[2].hash(),
1105 ..replaced[3].header().clone()
1106 });
1107 factory.replace_headers_after(1, &replaced[2..]);
1108 let orphaned = attempt_id(&factory);
1109 let (_, mut bootstrap) = scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
1110
1111 let outcome = bootstrap.run().await.unwrap();
1112
1113 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1114 panic!("the state is complete: {outcome:?}")
1115 };
1116 assert_ne!(pivot.hash, blocks[2].hash());
1117 assert_ne!(attempt_id(&factory), orphaned);
1118 }
1119
1120 #[tokio::test]
1121 async fn a_handed_off_attempt_is_not_downloaded_again() {
1122 let accounts = accounts();
1123 let factory = hashed_factory();
1124 insert_chain(&factory, 3, state_root(&accounts));
1125 let (_, mut bootstrap) = scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
1126 let first = bootstrap.run().await.unwrap();
1127
1128 let (client, mut resumed) = scripted(&factory, [], [3]);
1129
1130 assert_eq!(resumed.run().await.unwrap(), first);
1131 assert!(client.origins().is_empty());
1132 }
1133
1134 #[tokio::test]
1135 async fn a_catch_up_stalled_past_the_served_lists_restarts() {
1136 let accounts = accounts();
1137 let factory = hashed_factory();
1138 insert_chain(&factory, 11, state_root(&accounts));
1139 let responses = [account_range(1, &accounts, 0..1, &[key(1)]), no_lists(1)];
1141 let (_, stalled) = scripted(&factory, responses, [3, 8]);
1142 let mut stalled = stalled.with_ranges_per_check(1);
1143 assert_eq!(stalled.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1144 let attempt = attempt_id(&factory);
1145
1146 let (client, mut restarted) =
1148 scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [11]);
1149 let outcome = restarted.run().await.unwrap();
1150
1151 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1152 panic!("the state is complete: {outcome:?}")
1153 };
1154 assert_eq!(pivot.number, 10);
1155 assert_ne!(attempt_id(&factory), attempt);
1156 assert!(client.block_requests().is_empty());
1157 }
1158
1159 #[tokio::test]
1160 async fn complete_state_is_handed_off_past_the_served_lists() {
1161 let accounts = accounts();
1162 let factory = hashed_factory();
1163 insert_chain(&factory, 11, state_root(&accounts));
1164 let provider = factory.database_provider_rw().unwrap();
1166 let pivot = provider.sealed_header(2).unwrap().unwrap();
1167 let generation = SnapGeneration::new(pivot.num_hash(), pivot.state_root);
1168 let downloaded = provider.start_snap_attempt(generation).unwrap();
1169 provider.start_account_coverage(downloaded).unwrap();
1170 let range = verified_range(&accounts, 0..3, B256::ZERO, &[]);
1171 provider.commit_account_range(downloaded, &range, Default::default(), Vec::new()).unwrap();
1172 provider.commit().unwrap();
1173
1174 let (client, mut resumed) = scripted(&factory, [], [11]);
1176 let outcome = resumed.run().await.unwrap();
1177
1178 assert_eq!(
1179 outcome,
1180 SnapBootstrapOutcome::TrieRebuild { write: downloaded, pivot: pivot.num_hash() }
1181 );
1182 assert!(client.origins().is_empty());
1183 assert!(client.block_requests().is_empty());
1184 }
1185
1186 #[tokio::test]
1187 async fn unreadable_progress_restarts_the_attempt() {
1188 for record in [br#"{"version":999}"#.as_slice(), b"not json"] {
1190 let accounts = accounts();
1191 let factory = hashed_factory();
1192 insert_chain(&factory, 3, state_root(&accounts));
1193 let (_, mut stopped) =
1194 scripted(&factory, [Err(RequestError::UnsupportedCapability)], [3]);
1195 assert_eq!(stopped.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1196 let provider = factory.database_provider_rw().unwrap();
1198 let stale = provider.active_snap_write().unwrap().unwrap();
1199 provider.write_metadata("snap_account_coverage", record.to_vec()).unwrap();
1200 provider.commit().unwrap();
1201
1202 let (_, mut restarted) =
1203 scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
1204 let outcome = restarted.run().await.unwrap();
1205
1206 let SnapBootstrapOutcome::TrieRebuild { write, .. } = outcome else {
1207 panic!("the state is complete: {outcome:?}")
1208 };
1209 assert_ne!(write.attempt(), stale.attempt());
1210 let provider = factory.database_provider_ro().unwrap();
1211 assert!(matches!(
1212 provider.authorize_snap_write(stale),
1213 Err(SnapSyncError::StaleWrite { .. })
1214 ));
1215 }
1216 }
1217
1218 #[tokio::test]
1219 async fn cancellation_between_storage_responses_stops_the_run() {
1220 let slots = vec![(key(1), U256::from(11)), (key(2), U256::from(12))];
1221 let mut contract = account(1);
1222 contract.storage_root = storage_root_of(&slots);
1223 let accounts = vec![(key(1), contract)];
1224 let factory = hashed_factory();
1225 insert_chain(&factory, 3, state_root(&accounts));
1226 let cancel = CancellationToken::new();
1227 let on_request = cancel.clone();
1228 let client = Arc::new(
1230 ScriptedSnapClient::new([
1231 account_range(1, &accounts, 0..1, &[]),
1232 storage_ranges(1, &[&slots[..1]], &slots, &[B256::ZERO, key(1)]),
1233 ])
1234 .on_storage_request(move || on_request.cancel()),
1235 );
1236 let context = TestContext { heads: RefCell::new(VecDeque::from([3])), waits: 0 };
1237 let mut bootstrap =
1238 SnapBootstrap::new(Arc::clone(&client), factory.clone(), Runtime::test(), context)
1239 .with_policy(policy())
1240 .with_cancellation(cancel);
1241
1242 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1243 assert_eq!(client.storage_requests().len(), 1);
1244 }
1245
1246 #[tokio::test]
1247 async fn a_new_branch_too_short_for_a_pivot_waits_before_fetching_lists() {
1248 let factory = hashed_factory();
1249 let (attempt, _, _) = reorged(&factory, [Vec::new(), Vec::new()]);
1250 let (client, mut bootstrap) = scripted(&factory, [], [2]);
1252
1253 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1254
1255 assert!(client.block_requests().is_empty());
1256 assert_eq!(attempt_id(&factory), attempt);
1257 }
1258
1259 #[tokio::test]
1260 async fn a_handed_off_pivot_reorged_before_verification_restarts_the_attempt() {
1261 let accounts = accounts();
1262 let root = state_root(&accounts);
1263 let factory = hashed_factory();
1264 let shared = chain(3, root);
1265 insert_headers(&factory, &shared);
1266 let (_, mut bootstrap) = scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
1267 let SnapBootstrapOutcome::TrieRebuild { pivot: orphaned, .. } =
1268 bootstrap.run().await.unwrap()
1269 else {
1270 panic!("the state is complete")
1271 };
1272 let attempt = attempt_id(&factory);
1273 let new = branch(&shared[1], &[vec![stale_changes()], Vec::new()], root);
1275 factory.replace_headers_after(1, &new);
1276 let (_, mut resumed) = scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [3]);
1277
1278 let outcome = resumed.run().await.unwrap();
1279
1280 let SnapBootstrapOutcome::TrieRebuild { write, pivot } = outcome else {
1281 panic!("the new branch's state is complete: {outcome:?}")
1282 };
1283 assert_ne!(pivot, orphaned);
1284 assert_eq!(pivot, new[0].num_hash());
1285 assert_ne!(attempt_id(&factory), attempt);
1286 let verified = rebuild_and_verify(&factory, write, pivot.number);
1287 assert_eq!(verified.state_root(), root);
1288 }
1289
1290 #[tokio::test]
1291 async fn a_field_only_the_orphaned_branch_changed_is_fetched_again() {
1292 let origins = recover([Vec::new(), Vec::new()], true).await;
1293
1294 assert_eq!(origins, [keccak256(STALE)]);
1296 }
1297
1298 #[tokio::test]
1299 async fn a_field_the_new_branch_changes_too_needs_no_repair() {
1300 let balance = stale_changes()
1301 .with_balance_change(BalanceChange::new(BlockAccessIndex::new(1), U256::ONE));
1302
1303 let origins = recover([vec![balance], Vec::new()], false).await;
1304
1305 assert!(origins.is_empty());
1306 }
1307
1308 #[tokio::test]
1309 async fn a_second_reorg_repairs_what_the_first_new_branch_changed() {
1310 let accounts = reorg_accounts();
1311 let factory = hashed_factory();
1312 let kept = AccountChanges::new(KEPT)
1313 .with_balance_change(BalanceChange::new(BlockAccessIndex::new(1), U256::from(7)));
1314 let first_lists = [vec![kept], Vec::new()];
1315 let (attempt, _, first) = reorged(&factory, first_lists.clone());
1316 let responses =
1318 [lists(1, &orphaned_lists(), true), lists(2, &first_lists, true), unserved_range(1)];
1319 let (_, mut bootstrap) = scripted(&factory, responses, [5]);
1320 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1321
1322 let shared = factory.database_provider_ro().unwrap().sealed_header(2).unwrap().unwrap();
1324 let second = branch(&shared, &[Vec::new(), Vec::new(), Vec::new()], state_root(&accounts));
1325 factory.replace_headers_after(2, &second);
1326 let mut responses =
1327 vec![lists(1, &first_lists, true), lists(2, &[Vec::new(), Vec::new()], true)];
1328 let mut repaired = [keccak256(STALE), keccak256(KEPT)];
1330 repaired.sort();
1331 for (id, hashed_address) in (1..).zip(repaired) {
1332 responses.push(served_account(id, &accounts, hashed_address));
1333 }
1334 let (client, mut bootstrap) = scripted(&factory, responses, [5]);
1335
1336 let outcome = bootstrap.run().await.unwrap();
1337
1338 let SnapBootstrapOutcome::TrieRebuild { write, pivot } = outcome else {
1339 panic!("the state is repaired: {outcome:?}")
1340 };
1341 assert_eq!(pivot, second[1].num_hash());
1342 assert_eq!(attempt_id(&factory), attempt);
1343 assert_eq!(client.block_requests()[0], hashes(&first[..2]));
1344 assert_eq!(*client.origins(), repaired);
1345 let verified = rebuild_and_verify(&factory, write, pivot.number);
1346 assert_eq!(verified.state_root(), state_root(&accounts));
1347 }
1348
1349 #[tokio::test]
1350 async fn unserved_orphaned_lists_are_waited_for() {
1351 let accounts = reorg_accounts();
1352 let factory = hashed_factory();
1353 let (attempt, orphaned, new) = reorged(&factory, [Vec::new(), Vec::new()]);
1354 let (_, mut bootstrap) = scripted(&factory, [lists(1, &orphaned_lists(), false)], [5]);
1355 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1356 assert_eq!(attempt_id(&factory), attempt);
1357
1358 let responses = [
1360 lists(1, &orphaned_lists(), true),
1361 lists(2, &[Vec::new(), Vec::new()], true),
1362 served_account(1, &accounts, keccak256(STALE)),
1363 ];
1364 let (client, mut bootstrap) = scripted(&factory, responses, [5]);
1365 let outcome = bootstrap.run().await.unwrap();
1366
1367 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1368 panic!("the state is repaired: {outcome:?}")
1369 };
1370 assert_eq!(pivot, new[1].num_hash());
1371 assert_eq!(attempt_id(&factory), attempt);
1372 assert_eq!(client.block_requests()[0], hashes(&orphaned));
1373 }
1374
1375 #[tokio::test]
1376 async fn a_reorg_across_block_access_list_activation_restarts_the_attempt() {
1377 let accounts = accounts();
1378 let root = state_root(&accounts);
1379 let factory = hashed_factory();
1380 let mut shared = chain(1, root);
1381 let mut ancestor = header(2, shared[1].hash(), None);
1383 ancestor.state_root = root;
1384 shared.push(SealedHeader::seal_slow(ancestor));
1385 let orphaned = branch(&shared[2], &orphaned_lists(), root);
1386 insert_headers(&factory, &shared);
1387 insert_headers(&factory, &orphaned);
1388 let provider = factory.database_provider_rw().unwrap();
1389 provider.start_snap_attempt(SnapGeneration::new(orphaned[1].num_hash(), root)).unwrap();
1390 provider.commit().unwrap();
1391 let attempt = attempt_id(&factory);
1392 let new = branch(&shared[2], &[vec![stale_changes()], Vec::new(), Vec::new()], root);
1393 factory.replace_headers_after(2, &new);
1394 let (client, mut bootstrap) =
1395 scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [5]);
1396
1397 let outcome = bootstrap.run().await.unwrap();
1398
1399 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1400 panic!("the new branch's state is complete: {outcome:?}")
1401 };
1402 assert_eq!(pivot, new[1].num_hash());
1403 assert_ne!(attempt_id(&factory), attempt);
1404 assert!(client.block_requests().is_empty());
1405 }
1406
1407 #[tokio::test]
1408 async fn a_catch_up_stalled_below_the_ancestor_restarts_before_fetching_lists() {
1409 let accounts = accounts();
1410 let root = state_root(&accounts);
1411 let factory = hashed_factory();
1412 let shared = chain(2, root);
1413 let orphaned = branch(&shared[2], &orphaned_lists(), root);
1414 insert_headers(&factory, &shared);
1415 insert_headers(&factory, &orphaned);
1416 let provider = factory.database_provider_rw().unwrap();
1418 let write =
1419 provider.start_snap_attempt(SnapGeneration::new(shared[1].num_hash(), root)).unwrap();
1420 provider
1421 .advance_snap_pivot(write, SnapGeneration::new(orphaned[1].num_hash(), root))
1422 .unwrap();
1423 provider.commit().unwrap();
1424 let attempt = attempt_id(&factory);
1425 let mut lists = vec![vec![stale_changes()]];
1426 lists.resize(8, Vec::new());
1427 let new = branch(&shared[2], &lists, root);
1428 factory.replace_headers_after(2, &new);
1429 let (client, mut bootstrap) =
1431 scripted(&factory, [account_range(1, &accounts, 0..3, &[])], [10]);
1432
1433 let outcome = bootstrap.run().await.unwrap();
1434
1435 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1436 panic!("the new branch's state is complete: {outcome:?}")
1437 };
1438 assert_eq!(pivot, new[6].num_hash());
1439 assert_ne!(attempt_id(&factory), attempt);
1440 assert!(client.block_requests().is_empty());
1441 }
1442
1443 #[tokio::test]
1444 async fn orphaned_lists_unserved_past_the_state_window_restart_the_attempt() {
1445 let accounts = accounts();
1446 let root = state_root(&accounts);
1447 let factory = hashed_factory();
1448 let shared = chain(2, root);
1449 let orphaned = branch(&shared[2], &orphaned_lists(), root);
1450 insert_headers(&factory, &shared);
1451 insert_headers(&factory, &orphaned);
1452 let provider = factory.database_provider_rw().unwrap();
1453 provider.start_snap_attempt(SnapGeneration::new(orphaned[1].num_hash(), root)).unwrap();
1454 provider.commit().unwrap();
1455 let attempt = attempt_id(&factory);
1456 let mut new_lists = vec![vec![stale_changes()]];
1457 new_lists.resize(129, Vec::new());
1458 let new = branch(&shared[2], &new_lists, root);
1459 factory.replace_headers_after(2, &new);
1460 let responses =
1463 [lists(1, &orphaned_lists(), false), account_range(1, &accounts, 0..3, &[])];
1464 let (_, bootstrap) = scripted(&factory, responses, [131]);
1465 let mut bootstrap = bootstrap.with_policy(policy().with_history(256));
1466
1467 let outcome = bootstrap.run().await.unwrap();
1468
1469 let SnapBootstrapOutcome::TrieRebuild { pivot, .. } = outcome else {
1470 panic!("the new branch's state is complete: {outcome:?}")
1471 };
1472 assert_eq!(pivot, new[127].num_hash());
1473 assert_ne!(attempt_id(&factory), attempt);
1474 }
1475
1476 #[tokio::test]
1477 async fn a_cancelled_repair_resumes_at_its_pending_slots() {
1478 let slots = vec![(key(1), U256::from(11)), (key(2), U256::from(12))];
1479 let mut contract = account(1);
1480 contract.storage_root = storage_root_of(&slots);
1481 let accounts = vec![(key(1), contract)];
1482 let factory = hashed_factory();
1483 insert_chain(&factory, 3, state_root(&accounts));
1484 let provider = factory.database_provider_rw().unwrap();
1485 let pivot = provider.sealed_header(2).unwrap().unwrap().num_hash();
1486 let write =
1487 provider.start_snap_attempt(SnapGeneration::new(pivot, state_root(&accounts))).unwrap();
1488 provider.start_account_coverage(write).unwrap();
1489 let mut repairs = StateRepairs::default();
1490 repairs.insert_slot(key(1), key(1));
1491 repairs.insert_slot(key(1), key(2));
1492 provider.schedule_snap_repairs(write, repairs).unwrap();
1493 provider.commit().unwrap();
1494 let cancel = CancellationToken::new();
1495 let on_request = cancel.clone();
1496 let client = Arc::new(
1499 ScriptedSnapClient::new([
1500 account_range(1, &accounts, 0..1, &[key(1)]),
1501 storage_ranges(1, &[&slots[..1]], &slots, &[key(1)]),
1502 storage_ranges(2, &[], &[], &[]),
1503 ])
1504 .on_storage_request(move || on_request.cancel()),
1505 );
1506 let context = TestContext { heads: RefCell::new(VecDeque::from([3])), waits: 0 };
1507 let mut bootstrap =
1508 SnapBootstrap::new(Arc::clone(&client), factory.clone(), Runtime::test(), context)
1509 .with_policy(policy())
1510 .with_cancellation(cancel);
1511 assert_eq!(bootstrap.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1512 assert_eq!(client.storage_requests().len(), 2);
1513 let provider = factory.database_provider_ro().unwrap();
1514 let pending = provider.snap_repairs(write).unwrap().slots(key(1)).collect::<Vec<_>>();
1515 assert_eq!(pending, [key(2)]);
1516 assert_eq!(stored_slots(&provider, key(1)), slots[..1]);
1517 drop(provider);
1518
1519 let (client, mut resumed) = scripted(
1520 &factory,
1521 [
1522 account_range(1, &accounts, 0..1, &[key(1)]),
1523 storage_ranges(1, &[&slots[1..]], &slots, &[key(2)]),
1524 Err(RequestError::UnsupportedCapability),
1525 ],
1526 [3],
1527 );
1528 assert_eq!(resumed.run().await.unwrap(), SnapBootstrapOutcome::Stopped);
1529 assert_eq!(*client.storage_requests(), [(vec![key(1)], key(2))]);
1530 let provider = factory.database_provider_ro().unwrap();
1531 assert!(provider.snap_repairs(write).unwrap().is_empty());
1532 assert_eq!(stored_slots(&provider, key(1)), slots);
1533 }
1534
1535 #[tokio::test]
1536 async fn a_cancelled_run_still_finishes_the_hand_off_scan() {
1537 let factory = hashed_factory();
1538 let write = downloaded(&factory);
1539 let cancel = CancellationToken::new();
1540 cancel.cancel();
1541 let (_, bootstrap) = scripted(&factory, [], [3]);
1542 let bootstrap = bootstrap.with_cancellation(cancel).with_shutdown(CancellationToken::new());
1543
1544 assert!(matches!(bootstrap.hand_off(write).await, Ok(Step::HandedOff(_))));
1546
1547 let provider = factory.database_provider_ro().unwrap();
1548 assert!(provider.is_trie_rebuild_started(write).unwrap());
1549 }
1550
1551 #[tokio::test]
1552 async fn shutdown_stops_the_hand_off_scan_without_committing() {
1553 let factory = hashed_factory();
1554 let write = downloaded(&factory);
1555 let shutdown = CancellationToken::new();
1556 shutdown.cancel();
1557 let (_, bootstrap) = scripted(&factory, [], [3]);
1558 let bootstrap = bootstrap.with_shutdown(shutdown);
1559
1560 assert!(matches!(bootstrap.hand_off(write).await, Err(SnapSyncError::Cancelled)));
1561
1562 let provider = factory.database_provider_ro().unwrap();
1563 assert!(!provider.is_trie_rebuild_started(write).unwrap());
1564 }
1565}