cfx_storage/impls/snapshot_sync/restoration/
full_sync_verifier.rs1pub struct FullSyncVerifier<SnapshotDbManager: SnapshotDbManagerTrait> {
6 number_chunks: usize,
7 merkle_root: MerkleHash,
8 chunk_boundaries: Vec<Vec<u8>>,
9 chunk_boundary_proofs: Vec<TrieProof>,
10 chunk_verified: Vec<bool>,
11 number_incomplete_chunk: usize,
12
13 pending_boundary_nodes: HashMap<CompressedPathRaw, SnapshotMptNode>,
14 boundary_subtree_total_size: HashMap<BoundarySubtreeIndex, u64>,
15 chunk_index_by_upper_key: HashMap<Vec<u8>, usize>,
16
17 temp_snapshot_db: SnapshotDbManager::SnapshotDbWrite,
18}
19
20impl<SnapshotDbManager: SnapshotDbManagerTrait>
21 FullSyncVerifier<SnapshotDbManager>
22{
23 pub fn new(
24 number_chunks: usize, chunk_boundaries: Vec<Vec<u8>>,
25 chunk_boundary_proofs: Vec<TrieProof>, merkle_root: MerkleHash,
26 snapshot_db_manager: &SnapshotDbManager, epoch_id: &EpochId,
27 epoch_height: u64,
28 ) -> Result<Self> {
29 if number_chunks != chunk_boundaries.len() + 1 {
30 bail!(Error::InvalidSnapshotSyncProof)
31 }
32 if number_chunks != chunk_boundary_proofs.len() + 1 {
33 bail!(Error::InvalidSnapshotSyncProof)
34 }
35 let mut chunk_index_by_upper_key = HashMap::new();
36 for (chunk_index, (chunk_boundary, proof)) in chunk_boundaries
37 .iter()
38 .zip(chunk_boundary_proofs.iter())
39 .enumerate()
40 {
41 if chunk_boundary.len() > CompressedPathRaw::MAX_PATH_BYTES {
44 bail!(Error::InvalidSnapshotSyncProof)
45 }
46 if merkle_root.ne(proof.get_merkle_root()) {
47 bail!(Error::InvalidSnapshotSyncProof)
48 }
49 if proof.number_leaf_nodes() != 1 {
51 bail!(Error::InvalidSnapshotSyncProof)
52 }
53 if proof.if_proves_key(&*chunk_boundary)
54 != (true, proof.get_proof_nodes().last())
55 {
56 bail!(Error::InvalidSnapshotSyncProof)
57 }
58 chunk_index_by_upper_key
59 .insert(chunk_boundary.clone(), chunk_index);
60 }
61
62 Ok(Self {
63 number_chunks,
64 merkle_root,
65 chunk_boundaries,
66 chunk_boundary_proofs,
67 chunk_verified: vec![false; number_chunks],
68 number_incomplete_chunk: number_chunks,
69 pending_boundary_nodes: Default::default(),
70 boundary_subtree_total_size: Default::default(),
71 chunk_index_by_upper_key,
72 temp_snapshot_db: snapshot_db_manager
73 .new_temp_snapshot_for_full_sync(
74 epoch_id,
75 &merkle_root,
76 epoch_height,
77 )?,
78 })
79 }
80
81 pub fn is_completed(&self) -> bool { self.number_incomplete_chunk == 0 }
82
83 pub fn restore_chunk<Key: Borrow<[u8]> + Debug>(
85 &mut self, chunk_upper_key: &Option<Vec<u8>>, keys: &Vec<Key>,
86 values: Vec<Vec<u8>>,
87 ) -> Result<bool> {
88 let chunk_index = match chunk_upper_key {
89 None => self.number_chunks - 1,
90 Some(upper_key) => {
91 match self.chunk_index_by_upper_key.get(upper_key) {
92 Some(index) => *index,
93 None => {
94 warn!("chunk key {:?} does not match boundaries in manifest", upper_key);
95 return Ok(false);
96 }
97 }
98 }
99 };
100 for key in keys {
103 if key.borrow().len() > CompressedPathRaw::MAX_PATH_BYTES {
104 warn!("chunk contains an over-long key, rejecting chunk");
105 return Ok(false);
106 }
107 }
108
109 if !keys.is_empty() {
111 let mut previous = keys.first().unwrap();
112 for key in &keys[1..] {
113 if key.borrow().le(previous.borrow()) {
114 warn!("chunk key not in order");
115 return Ok(false);
116 }
117 previous = key;
118 }
119 }
120
121 let key_range_left;
122 let maybe_key_range_right_excl;
123 let maybe_left_proof;
124 let maybe_right_proof;
125 if chunk_index == 0 {
126 key_range_left = vec![];
127 maybe_left_proof = None;
128 } else {
129 key_range_left = self.chunk_boundaries[chunk_index - 1].clone();
130 maybe_left_proof = self.chunk_boundary_proofs.get(chunk_index - 1);
131
132 if let Some(first_key) = keys.first() {
134 if first_key.borrow().lt(&*key_range_left) {
135 warn!(
136 "first chunk key {:?} less than left range {:?}",
137 first_key, key_range_left
138 );
139 return Ok(false);
140 }
141 }
142 };
143 if chunk_index == self.number_chunks - 1 {
144 maybe_key_range_right_excl = None;
145 maybe_right_proof = None;
146 } else {
147 let key_range_right_excl =
148 self.chunk_boundaries[chunk_index].clone();
149 maybe_right_proof = self.chunk_boundary_proofs.get(chunk_index);
150
151 if let Some(last_key) = keys.last() {
153 if last_key.borrow().ge(&*key_range_right_excl) {
154 warn!(
155 "last chunk key {:?} larger than left range {:?}",
156 last_key, key_range_right_excl,
157 );
158 return Ok(false);
159 }
160 }
161
162 maybe_key_range_right_excl = Some(key_range_right_excl);
163 }
164
165 let chunk_verifier = MptSliceVerifier::new(
168 maybe_left_proof,
169 &*key_range_left,
170 maybe_right_proof,
171 maybe_key_range_right_excl.as_ref().map(|v| &**v),
172 self.merkle_root.clone(),
173 );
174
175 let chunk_rebuilder = chunk_verifier.restore(keys, &values)?;
176 if chunk_rebuilder.is_valid {
177 self.chunk_verified[chunk_index] = true;
178 self.number_incomplete_chunk -= 1;
179
180 self.temp_snapshot_db.start_transaction()?;
181 for (key, value) in keys.into_iter().zip(values.into_iter()) {
183 self.temp_snapshot_db.put_kv(key.borrow(), &*value)?;
184 }
185
186 let mut snapshot_mpt =
188 self.temp_snapshot_db.open_snapshot_mpt_owned()?;
189 for (path, node) in chunk_rebuilder.inner_nodes_to_write {
190 snapshot_mpt.write_node(&path, &node)?;
191 }
192 drop(snapshot_mpt);
193 self.temp_snapshot_db.commit_transaction()?;
194
195 for (path, node) in chunk_rebuilder.boundary_nodes {
197 let mut children_table = VanillaChildrenTable::default();
198 unsafe {
199 for (child_index, merkle_ref) in
200 node.get_children_table_ref().iter()
201 {
202 *children_table.get_child_mut_unchecked(child_index) =
203 SubtreeMerkleWithSize {
204 merkle: *merkle_ref,
205 subtree_size: 0,
206 delta_subtree_size: 0,
207 }
208 }
209 *children_table.get_children_count_mut() =
210 node.get_children_count();
211 }
212 self.pending_boundary_nodes.insert(
213 path,
214 SnapshotMptNode(VanillaTrieNode::new(
215 node.get_merkle().clone(),
216 children_table,
217 node.value_as_slice()
218 .into_option()
219 .map(|ref_v| ref_v.into()),
220 node.compressed_path_ref().into(),
221 )),
222 );
223 }
224 for (subtree_index, subtree_size) in
225 chunk_rebuilder.boundary_subtree_total_size
226 {
227 *self
228 .boundary_subtree_total_size
229 .entry(subtree_index)
230 .or_default() += subtree_size;
231 }
232 }
233
234 if self.is_completed() {
235 self.finalize()?
236 }
237
238 Ok(chunk_rebuilder.is_valid)
239 }
240
241 pub fn finalize(&mut self) -> Result<()> {
245 self.temp_snapshot_db.start_transaction()?;
246 let mut snapshot_mpt =
247 self.temp_snapshot_db.open_snapshot_mpt_owned()?;
248
249 for (path, mut node) in self.pending_boundary_nodes.drain() {
250 let mut subtree_index = BoundarySubtreeIndex {
251 parent_node: node.get_merkle().clone(),
252 child_index: 0,
253 };
254 for child_index in 0..CHILDREN_COUNT as u8 {
255 subtree_index.child_index = child_index;
256 if let Some(subtree_size) =
257 self.boundary_subtree_total_size.get(&subtree_index)
258 {
259 unsafe {
261 node.get_child_mut_unchecked(child_index)
262 .subtree_size = *subtree_size;
263 }
264 }
265 }
266
267 snapshot_mpt.write_node(&path, &node)?;
268 }
269
270 drop(snapshot_mpt);
271 self.temp_snapshot_db.commit_transaction()?;
272 Ok(())
273 }
274}
275
276use crate::{
277 impls::{
278 errors::*,
279 merkle_patricia_trie::{
280 trie_node::TrieNodeTrait, CompressedPathRaw, VanillaChildrenTable,
281 VanillaTrieNode, CHILDREN_COUNT,
282 },
283 snapshot_sync::restoration::mpt_slice_verifier::{
284 BoundarySubtreeIndex, MptSliceVerifier,
285 },
286 },
287 storage_db::{
288 SnapshotDbManagerTrait, SnapshotDbWriteableTrait, SnapshotMptNode,
289 SnapshotMptTraitRw, SubtreeMerkleWithSize,
290 },
291 TrieProof,
292};
293use primitives::{EpochId, MerkleHash};
294use std::{borrow::Borrow, collections::HashMap, fmt::Debug};