cfxcore/sync/state/state_sync_manifest/
snapshot_manifest_manager.rs

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    /// State root verified by blame.
55    pub true_state_root_by_blame_info: StateRootWithAuxInfo,
56    /// Point to the corresponding entry to the snapshot in the blame vectors.
57    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        // new era started
105        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        // validate blame state if requested
122        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    /// request manifest from random peer
227    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            // Safe to unwrap since it's guaranteed to be Some(..)
239            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        // these two header must exist in disk, it's safe to unwrap
295        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        // check snapshot position in `out_state_blame_vec`
309        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        // verify the length of vector.
336        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            // We've jump to another trusted block.
345            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        // verify the length of vector
358        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        // Construct out_state_blame_vec.
367        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            // verify `deferred_state_root`, `deferred_receipts_root` and
396            // `deferred_logs_bloom_hash`
397            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                // Prevent immediate removed by setting 'InfoOnly' to avoid
466                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                    // This field will not be used
487                    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                    // We don't necessarily need to know because
494                    // the execution of the next epoch shifts delta MPT.
495                    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                // We need the extra -1 to get a state root that points to the
503                // snapshot we want.
504                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                    // Invalid block_receipts vector length.
562                    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}