Skip to main content

reth_db_api/models/
snap.rs

1//! Snap synchronization models.
2
3use alloy_eips::BlockNumHash;
4use alloy_primitives::B256;
5use core::fmt;
6use serde::{Deserialize, Serialize};
7
8/// Encoding version of [`SnapAttempt`] written by this build.
9pub const SNAP_ATTEMPT_VERSION: u32 = 1;
10
11/// The snap synchronization attempt that owns the downloaded state.
12///
13/// Snap writes land in the canonical hashed state tables, so this record is what separates state a
14/// live attempt is filling in from what an abandoned one left behind. It is never deleted: an
15/// abandoned attempt keeps its identity, so a later attempt can never take it.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub struct SnapAttempt {
18    // Encoding version of this record.
19    version: u32,
20    // Identity of this attempt.
21    id: SnapAttemptId,
22    // Pivot block the downloaded state is anchored to.
23    pivot: BlockNumHash,
24    // Root downloaded ranges authenticate against.
25    state_root: B256,
26    // Bumped whenever the pivot moves, so writes proved against a superseded root are refused.
27    state_version: u64,
28    // Whether the downloaded state has been verified.
29    status: SnapBootstrapStatus,
30}
31
32impl SnapAttempt {
33    /// Creates the record for an attempt superseding `previous`.
34    pub const fn start(previous: Option<Self>, pivot: BlockNumHash, state_root: B256) -> Self {
35        let id = match previous {
36            Some(previous) => previous.id.next(),
37            None => SnapAttemptId::FIRST,
38        };
39        Self {
40            version: SNAP_ATTEMPT_VERSION,
41            id,
42            pivot,
43            state_root,
44            state_version: 0,
45            status: SnapBootstrapStatus::Unfinished,
46        }
47    }
48
49    /// Identity of this attempt.
50    pub const fn id(&self) -> SnapAttemptId {
51        self.id
52    }
53
54    /// Pivot block the downloaded state is anchored to.
55    pub const fn pivot(&self) -> BlockNumHash {
56        self.pivot
57    }
58
59    /// Root downloaded ranges authenticate against.
60    pub const fn state_root(&self) -> B256 {
61        self.state_root
62    }
63
64    /// Generation of the pivot this attempt is anchored to.
65    pub const fn state_version(&self) -> u64 {
66        self.state_version
67    }
68
69    /// Returns whether the downloaded state is still incomplete.
70    pub const fn is_unfinished(&self) -> bool {
71        matches!(self.status, SnapBootstrapStatus::Unfinished)
72    }
73
74    /// Returns whether the reconstructed trie root matched the target header.
75    pub const fn is_verified(&self) -> bool {
76        matches!(self.status, SnapBootstrapStatus::Verified)
77    }
78
79    /// Re-anchors this attempt, superseding writes proved against the previous root.
80    pub const fn re_anchor(&mut self, pivot: BlockNumHash, state_root: B256) {
81        self.pivot = pivot;
82        self.state_root = state_root;
83        self.state_version = self.state_version.saturating_add(1);
84    }
85
86    /// Marks the downloaded state verified.
87    pub const fn verify(&mut self) {
88        self.status = SnapBootstrapStatus::Verified;
89    }
90
91    /// Gives up on the downloaded state, keeping this identity taken.
92    pub const fn abandon(&mut self) {
93        self.status = SnapBootstrapStatus::Abandoned;
94    }
95}
96
97/// Identity of one snap synchronization attempt.
98///
99/// Only ever handed out once, so an abandoned attempt's leftover state is never mistaken for the
100/// current attempt's work.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
102pub struct SnapAttemptId(u64);
103
104impl SnapAttemptId {
105    /// Identity of a node's first attempt.
106    pub const FIRST: Self = Self(0);
107
108    /// Returns the identity superseding this one.
109    pub const fn next(self) -> Self {
110        Self(self.0.saturating_add(1))
111    }
112}
113
114impl From<SnapAttemptId> for u64 {
115    fn from(id: SnapAttemptId) -> Self {
116        id.0
117    }
118}
119
120impl fmt::Display for SnapAttemptId {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        write!(f, "{}", self.0)
123    }
124}
125
126// Whether a snap attempt's downloaded state has been verified.
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
128enum SnapBootstrapStatus {
129    // Downloads are outstanding, so the state is incomplete and is not canonical yet.
130    Unfinished,
131    // The reconstructed trie root matched the target header.
132    Verified,
133    // Downloads were given up, leaving incomplete state behind for cleanup.
134    Abandoned,
135}