1use crate::{
2 block_data_manager::BlockExecutionResult,
3 message::NetworkContext,
4 sync::{
5 error::Error,
6 message::{
7 msgid, Context, SnapshotManifestRequest, SnapshotManifestResponse,
8 },
9 state::storage::SnapshotSyncCandidate,
10 synchronization_state::PeerFilter,
11 SynchronizationProtocolHandler,
12 },
13 verification::compute_receipts_root,
14};
15use cfx_internal_common::{StateRootAuxInfo, StateRootWithAuxInfo};
16use cfx_parameters::{
17 consensus::DEFERRED_STATE_EPOCH_COUNT,
18 consensus_internal::REWARD_EPOCH_COUNT,
19};
20use cfx_storage::{
21 storage_db::{SnapshotInfo, SnapshotKeptToProvideSyncStatus},
22 TrieProof,
23};
24use cfx_types::{option_vec_to_hex, H256};
25use network::node_table::NodeId;
26use primitives::{
27 BlockHeaderBuilder, BlockReceipts, EpochId, EpochNumber, StateRoot,
28 StorageKeyWithSpace, NULL_EPOCH,
29};
30use rand::{rng, seq::IndexedRandom};
31
32use std::{
33 collections::HashSet,
34 fmt::{Debug, Formatter},
35 sync::Arc,
36 time::{Duration, Instant},
37};
38
39pub struct SnapshotManifestManager {
40 manifest_request_status: Option<(Instant, NodeId)>,
41 pub snapshot_candidate: SnapshotSyncCandidate,
42 trusted_blame_block: H256,
43 pub active_peers: HashSet<NodeId>,
44
45 pub chunk_boundaries: Vec<Vec<u8>>,
46 pub chunk_boundary_proofs: Vec<TrieProof>,
47
48 related_data: Option<RelatedData>,
49 config: SnapshotManifestConfig,
50}
51
52#[derive(Clone)]
53pub struct RelatedData {
54 pub true_state_root_by_blame_info: StateRootWithAuxInfo,
56 pub blame_vec_offset: usize,
58 pub receipt_blame_vec: Vec<H256>,
59 pub bloom_blame_vec: Vec<H256>,
60 pub epoch_receipts: Vec<(H256, H256, Arc<BlockReceipts>)>,
61 pub snapshot_info: SnapshotInfo,
62 pub parent_snapshot_info: Option<SnapshotInfo>,
63}
64
65impl SnapshotManifestManager {
66 pub fn new_and_start(
67 snapshot_candidate: SnapshotSyncCandidate, trusted_blame_block: H256,
68 active_peers: HashSet<NodeId>, config: SnapshotManifestConfig,
69 io: &dyn NetworkContext, sync_handler: &SynchronizationProtocolHandler,
70 ) -> Self {
71 let mut manager = Self {
72 manifest_request_status: None,
73 snapshot_candidate,
74 trusted_blame_block,
75 active_peers,
76 chunk_boundaries: vec![],
77 chunk_boundary_proofs: vec![],
78 related_data: None,
79 config,
80 };
81 manager.request_manifest(io, sync_handler, None);
82 manager
83 }
84
85 pub fn handle_snapshot_manifest_response(
86 &mut self, ctx: &Context, response: SnapshotManifestResponse,
87 request: &SnapshotManifestRequest,
88 ) -> Result<Option<RelatedData>, Error> {
89 match self
90 .handle_snapshot_manifest_response_impl(ctx, response, request)
91 {
92 Ok(r) => Ok(r),
93 Err(e) => {
94 self.note_failure(&ctx.node_id);
95 Err(e)
96 }
97 }
98 }
99
100 fn handle_snapshot_manifest_response_impl(
101 &mut self, ctx: &Context, response: SnapshotManifestResponse,
102 request: &SnapshotManifestRequest,
103 ) -> Result<Option<RelatedData>, Error> {
104 if request.snapshot_to_sync != self.snapshot_candidate {
106 info!(
107 "The received snapshot manifest doesn't match the current snapshot_candidate,\
108 current snapshot_candidate = {:?}, requested sync candidate = {:?}",
109 self.snapshot_candidate,
110 request.snapshot_to_sync);
111 return Ok(None);
112 }
113
114 info!(
115 "Snapshot manifest received, checkpoint = {:?}, chunk_boundaries.len()={}, \
116 start={}, next={}",
117 self.snapshot_candidate, response.manifest.chunk_boundaries.len(),
118 option_vec_to_hex(request.start_chunk.as_ref()), option_vec_to_hex(response.manifest.next.as_ref())
119 );
120
121 if request.is_initial_request() {
123 if !self.chunk_boundaries.is_empty() {
124 bail!(Error::InvalidSnapshotManifest(
125 "Initial manifest is not expected".into(),
126 ));
127 }
128 let (
129 blame_vec_offset,
130 state_root_with_aux_info,
131 snapshot_info,
132 parent_snapshot_info,
133 ) = match Self::validate_blame_states(
134 ctx,
135 self.snapshot_candidate.get_snapshot_epoch_id(),
136 &self.trusted_blame_block,
137 &response.state_root_vec,
138 &response.receipt_blame_vec,
139 &response.bloom_blame_vec,
140 ) {
141 Some(info_tuple) => info_tuple,
142 None => {
143 warn!("failed to validate the blame state, re-sync manifest from other peer");
144 self.resync_manifest(ctx);
145 bail!(Error::InvalidSnapshotManifest(
146 "invalid blame state in manifest".into(),
147 ));
148 }
149 };
150
151 let epoch_receipts =
152 match SnapshotManifestManager::validate_epoch_receipts(
153 ctx,
154 blame_vec_offset,
155 self.snapshot_candidate.get_snapshot_epoch_id(),
156 &response.receipt_blame_vec,
157 &response.bloom_blame_vec,
158 &response.block_receipts,
159 ) {
160 Some(epoch_receipts) => epoch_receipts,
161 None => {
162 warn!("failed to validate the epoch receipts, re-sync manifest from other peer");
163 self.resync_manifest(ctx);
164 bail!(Error::InvalidSnapshotManifest(
165 "invalid epoch receipts in manifest".into(),
166 ));
167 }
168 };
169
170 if let Err(e) =
171 response.manifest.validate(&snapshot_info.merkle_root, None)
172 {
173 warn!("failed to validate snapshot manifest, error = {:?}", e);
174 bail!(Error::InvalidSnapshotManifest(
175 "invalid chunk proofs in manifest".into(),
176 ));
177 }
178 self.related_data = Some(RelatedData {
179 true_state_root_by_blame_info: state_root_with_aux_info,
180 blame_vec_offset,
181 receipt_blame_vec: response.receipt_blame_vec,
182 bloom_blame_vec: response.bloom_blame_vec,
183 epoch_receipts,
184 snapshot_info,
185 parent_snapshot_info,
186 });
187 } else {
188 if self.chunk_boundaries.is_empty() {
189 bail!(Error::InvalidSnapshotManifest(
190 "Non-initial manifest is not expected".into()
191 ));
192 }
193 if request.start_chunk.as_ref() != self.chunk_boundaries.last() {
194 bail!(Error::InvalidSnapshotManifest(
195 "manifest start does not match accumulated boundary".into(),
196 ));
197 }
198 let related_data = match &self.related_data {
199 Some(related_data) => related_data,
200 None => bail!(Error::InvalidSnapshotManifest(
201 "missing related data for non-initial manifest".into(),
202 )),
203 };
204 if let Err(e) = response.manifest.validate(
205 &related_data.snapshot_info.merkle_root,
206 self.chunk_boundaries.last().map(|b| b.as_slice()),
207 ) {
208 warn!("failed to validate snapshot manifest, error = {:?}", e);
209 bail!(Error::InvalidSnapshotManifest(
210 "invalid chunk proofs in manifest".into(),
211 ));
212 }
213 }
214 self.chunk_boundaries
215 .extend_from_slice(&response.manifest.chunk_boundaries);
216 self.chunk_boundary_proofs
217 .extend_from_slice(&response.manifest.chunk_boundary_proofs);
218 if response.manifest.next.is_none() {
219 return Ok(self.related_data.clone());
220 } else {
221 self.request_manifest(ctx.io, ctx.manager, response.manifest.next);
222 }
223 Ok(None)
224 }
225
226 pub fn request_manifest(
228 &mut self, io: &dyn NetworkContext,
229 sync_handler: &SynchronizationProtocolHandler,
230 start_chunk: Option<Vec<u8>>,
231 ) {
232 let maybe_trusted_blame_block = if start_chunk.is_none() {
233 Some(self.trusted_blame_block.clone())
234 } else {
235 None
236 };
237 let request = SnapshotManifestRequest::new(
238 self.snapshot_candidate.clone(),
240 maybe_trusted_blame_block,
241 start_chunk,
242 );
243
244 let available_peers = PeerFilter::new(msgid::GET_SNAPSHOT_MANIFEST)
245 .choose_from(&self.active_peers)
246 .select_all(&sync_handler.syn);
247 let maybe_peer = available_peers.choose(&mut rng()).map(|p| *p);
248 if let Some(peer) = maybe_peer {
249 self.manifest_request_status = Some((Instant::now(), peer));
250 sync_handler.request_manager.request_with_delay(
251 io,
252 Box::new(request),
253 Some(peer),
254 None,
255 );
256 }
257 }
258
259 fn resync_manifest(&mut self, ctx: &Context) {
260 self.request_manifest(
261 ctx.io,
262 ctx.manager,
263 self.chunk_boundaries.last().cloned(),
264 );
265 }
266
267 pub fn check_timeout(&mut self, ctx: &Context) {
268 if let Some((manifest_start_time, peer)) = &self.manifest_request_status
269 {
270 if manifest_start_time.elapsed()
271 > self.config.manifest_request_timeout
272 {
273 self.active_peers.remove(peer);
274 self.manifest_request_status = None;
275 self.resync_manifest(ctx);
276 }
277 }
278 }
279
280 pub fn is_inactive(&self) -> bool { self.active_peers.is_empty() }
281
282 pub fn validate_blame_states(
283 ctx: &Context, snapshot_epoch_id: &H256, trusted_blame_block: &H256,
284 state_root_vec: &Vec<StateRoot>, receipt_blame_vec: &Vec<H256>,
285 bloom_blame_vec: &Vec<H256>,
286 ) -> Option<(
287 usize,
288 StateRootWithAuxInfo,
289 SnapshotInfo,
290 Option<SnapshotInfo>,
291 )> {
292 let mut state_blame_vec = vec![];
293
294 let snapshot_block_header = ctx
296 .manager
297 .graph
298 .data_man
299 .block_header_by_hash(snapshot_epoch_id)
300 .expect("block header must exist for snapshot to sync");
301 let trusted_blame_block = ctx
302 .manager
303 .graph
304 .data_man
305 .block_header_by_hash(trusted_blame_block)
306 .expect("trusted_blame_block header must exist");
307
308 let offset = (trusted_blame_block.height()
310 - (snapshot_block_header.height() + DEFERRED_STATE_EPOCH_COUNT))
311 as usize;
312 if offset >= state_root_vec.len() {
313 warn!("validate_blame_states: not enough state_root");
314 return None;
315 }
316
317 let min_vec_len = if snapshot_block_header.height() == 0 {
318 trusted_blame_block.height()
319 - DEFERRED_STATE_EPOCH_COUNT
320 - snapshot_block_header.height()
321 + 1
322 } else {
323 trusted_blame_block.height()
324 - DEFERRED_STATE_EPOCH_COUNT
325 - snapshot_block_header.height()
326 + REWARD_EPOCH_COUNT
327 };
328 let mut trusted_blocks = Vec::new();
329 let mut trusted_block_height = trusted_blame_block.height();
330 let mut blame_count = trusted_blame_block.blame();
331 let mut block_hash = trusted_blame_block.hash();
332 let mut vec_len: usize = 0;
333 trusted_blocks.push(trusted_blame_block);
334
335 loop {
337 vec_len += 1;
338 let block = ctx
339 .manager
340 .graph
341 .data_man
342 .block_header_by_hash(&block_hash)
343 .expect("block header must exist");
344 if block.height() + blame_count as u64 + 1 == trusted_block_height {
346 trusted_block_height = block.height();
347 blame_count = block.blame();
348 trusted_blocks.push(block.clone());
349 }
350 if block.height() + blame_count as u64 == trusted_block_height
351 && vec_len >= min_vec_len as usize
352 {
353 break;
354 }
355 block_hash = *block.parent_hash();
356 }
357 if vec_len != state_root_vec.len() {
359 warn!(
360 "wrong length of state_root_vec, expected {}, but {} found",
361 vec_len,
362 state_root_vec.len()
363 );
364 return None;
365 }
366 state_blame_vec.clear();
368 for state_root in state_root_vec {
369 state_blame_vec.push(state_root.compute_state_root_hash());
370 }
371 let mut slice_begin = 0;
372 for trusted_block in trusted_blocks {
373 let slice_end = slice_begin + trusted_block.blame() as usize + 1;
374 let deferred_state_root = if trusted_block.blame() == 0 {
375 state_blame_vec[slice_begin].clone()
376 } else {
377 BlockHeaderBuilder::compute_blame_state_root_vec_root(
378 state_blame_vec[slice_begin..slice_end].to_vec(),
379 )
380 };
381 let deferred_receipts_root = if trusted_block.blame() == 0 {
382 receipt_blame_vec[slice_begin].clone()
383 } else {
384 BlockHeaderBuilder::compute_blame_state_root_vec_root(
385 receipt_blame_vec[slice_begin..slice_end].to_vec(),
386 )
387 };
388 let deferred_logs_bloom_hash = if trusted_block.blame() == 0 {
389 bloom_blame_vec[slice_begin].clone()
390 } else {
391 BlockHeaderBuilder::compute_blame_state_root_vec_root(
392 bloom_blame_vec[slice_begin..slice_end].to_vec(),
393 )
394 };
395 if deferred_state_root != *trusted_block.deferred_state_root()
398 || deferred_receipts_root
399 != *trusted_block.deferred_receipts_root()
400 || deferred_logs_bloom_hash
401 != *trusted_block.deferred_logs_bloom_hash()
402 {
403 warn!("root mismatch: (state_root, receipts_root, logs_bloom_hash) \
404 should be ({:?} {:?} {:?}), get ({:?} {:?} {:?})",
405 trusted_block.deferred_state_root(),
406 trusted_block.deferred_receipts_root(),
407 trusted_block.deferred_logs_bloom_hash(),
408 deferred_state_root,
409 deferred_receipts_root,
410 deferred_logs_bloom_hash,
411 );
412 return None;
413 }
414 slice_begin = slice_end;
415 }
416
417 let snapshot_epoch_count =
418 ctx.manager.graph.data_man.get_snapshot_epoch_count();
419 let (parent_snapshot_epoch, pivot_chain_parts) =
420 ctx.manager.graph.data_man.get_parent_epochs_for(
421 snapshot_epoch_id.clone(),
422 snapshot_epoch_count as u64,
423 );
424
425 let parent_snapshot_height = if parent_snapshot_epoch == NULL_EPOCH {
426 0
427 } else {
428 ctx.manager
429 .graph
430 .data_man
431 .block_header_by_hash(&parent_snapshot_epoch)
432 .unwrap()
433 .height()
434 };
435 let snapshot_state_root = state_root_vec[offset].clone();
436 let state_root_hash = state_root_vec[offset].compute_state_root_hash();
437
438 let snapshot_before_stable_checkpoint = if snapshot_block_header
439 .height()
440 > snapshot_epoch_count as u64
441 {
442 let (grandparent_snapshot_epoch, grandparent_pivot_chain_parts) =
443 ctx.manager.graph.data_man.get_parent_epochs_for(
444 parent_snapshot_epoch.clone(),
445 snapshot_epoch_count as u64,
446 );
447
448 let grandparent_snapshot_height =
449 if grandparent_snapshot_epoch == NULL_EPOCH {
450 0
451 } else {
452 ctx.manager
453 .graph
454 .data_man
455 .block_header_by_hash(&grandparent_snapshot_epoch)
456 .unwrap()
457 .height()
458 };
459 debug!(
460 "grandparent snapshot epoch {:?}, height {}",
461 grandparent_snapshot_epoch, grandparent_snapshot_height
462 );
463
464 Some(SnapshotInfo {
465 snapshot_info_kept_to_provide_sync:
467 SnapshotKeptToProvideSyncStatus::InfoOnly,
468 serve_one_step_sync: false,
469 merkle_root: state_root_vec[offset - 1].snapshot_root,
470 height: snapshot_block_header.height()
471 - snapshot_epoch_count as u64,
472 parent_snapshot_epoch_id: grandparent_snapshot_epoch,
473 parent_snapshot_height: grandparent_snapshot_height,
474 pivot_chain_parts: grandparent_pivot_chain_parts,
475 })
476 } else {
477 None
478 };
479
480 Some((
481 offset,
482 StateRootWithAuxInfo {
483 state_root: snapshot_state_root,
484 aux_info: StateRootAuxInfo {
485 snapshot_epoch_id: snapshot_epoch_id.clone(),
486 delta_mpt_key_padding:
488 StorageKeyWithSpace::delta_mpt_padding(
489 &state_root_vec[offset].snapshot_root,
490 &state_root_vec[offset].intermediate_delta_root,
491 ),
492 intermediate_epoch_id: parent_snapshot_epoch,
493 maybe_intermediate_mpt_key_padding: None,
496 state_root_hash,
497 },
498 },
499 SnapshotInfo {
500 snapshot_info_kept_to_provide_sync: Default::default(),
501 serve_one_step_sync: false,
502 merkle_root: state_root_vec[offset
505 - ctx
506 .manager
507 .graph
508 .data_man
509 .get_snapshot_blame_plus_depth()]
510 .snapshot_root,
511 height: snapshot_block_header.height(),
512 parent_snapshot_epoch_id: parent_snapshot_epoch,
513 parent_snapshot_height,
514 pivot_chain_parts,
515 },
516 snapshot_before_stable_checkpoint,
517 ))
518 }
519
520 pub fn validate_epoch_receipts(
521 ctx: &Context, blame_vec_offset: usize, snapshot_epoch_id: &EpochId,
522 receipt_blame_vec: &Vec<H256>, bloom_blame_vec: &Vec<H256>,
523 block_receipts: &Vec<BlockExecutionResult>,
524 ) -> Option<Vec<(H256, H256, Arc<BlockReceipts>)>> {
525 let mut epoch_hash = snapshot_epoch_id.clone();
526 let checkpoint = ctx
527 .manager
528 .graph
529 .data_man
530 .block_header_by_hash(snapshot_epoch_id)
531 .expect("checkpoint header must exist");
532 let epoch_receipts_count = if checkpoint.height() == 0 {
533 1
534 } else {
535 REWARD_EPOCH_COUNT
536 } as usize;
537 let mut receipts_vec_offset = 0;
538 let mut result = Vec::new();
539 for idx in 0..epoch_receipts_count {
540 let block_header = ctx
541 .manager
542 .graph
543 .data_man
544 .block_header_by_hash(&epoch_hash)
545 .expect("block header must exist");
546 let ordered_executable_epoch_blocks = ctx
547 .manager
548 .graph
549 .consensus
550 .get_block_hashes_by_epoch(EpochNumber::Number(
551 block_header.height(),
552 ))
553 .expect("ordered executable epoch blocks must exist");
554 let mut epoch_receipts = Vec::new();
555 for i in 0..ordered_executable_epoch_blocks.len() {
556 if let Some(block_receipt) =
557 block_receipts.get(receipts_vec_offset + i)
558 {
559 epoch_receipts.push(block_receipt.block_receipts.clone());
560 } else {
561 return None;
563 }
564 }
565 let receipt_root = compute_receipts_root(&epoch_receipts);
566 let logs_bloom_hash =
567 BlockHeaderBuilder::compute_block_logs_bloom_hash(
568 &epoch_receipts,
569 );
570 if receipt_blame_vec[blame_vec_offset + idx] != receipt_root {
571 debug!(
572 "wrong receipt root, expected={:?}, now={:?}",
573 receipt_blame_vec[blame_vec_offset + idx],
574 receipt_root
575 );
576 return None;
577 }
578 if bloom_blame_vec[blame_vec_offset + idx] != logs_bloom_hash {
579 debug!(
580 "wrong logs bloom hash, expected={:?}, now={:?}",
581 bloom_blame_vec[blame_vec_offset + idx],
582 logs_bloom_hash
583 );
584 return None;
585 }
586 for i in 0..ordered_executable_epoch_blocks.len() {
587 result.push((
588 ordered_executable_epoch_blocks[i],
589 epoch_hash,
590 epoch_receipts[i].clone(),
591 ));
592 }
593 receipts_vec_offset += ordered_executable_epoch_blocks.len();
594 epoch_hash = *block_header.parent_hash();
595 }
596 if receipts_vec_offset == block_receipts.len() {
597 Some(result)
598 } else {
599 None
600 }
601 }
602
603 pub fn on_peer_disconnected(&mut self, peer: &NodeId) {
604 self.active_peers.remove(peer);
605 }
606
607 fn note_failure(&mut self, node_id: &NodeId) {
608 self.active_peers.remove(node_id);
609 }
610}
611
612impl Debug for SnapshotManifestManager {
613 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
614 write!(
615 f,
616 "(request_status = {:?}, candidate={:?} active_peers: {})",
617 self.manifest_request_status,
618 self.snapshot_candidate,
619 self.active_peers.len(),
620 )
621 }
622}
623
624pub struct SnapshotManifestConfig {
625 pub manifest_request_timeout: Duration,
626}