cfxcore/sync/
synchronization_graph.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use std::{
6    cmp::max,
7    collections::{BinaryHeap, HashMap, HashSet, VecDeque},
8    mem, panic,
9    sync::{
10        atomic::{AtomicUsize, Ordering},
11        Arc,
12    },
13    thread,
14    time::{Duration, SystemTime, UNIX_EPOCH},
15};
16
17use cfx_parameters::consensus_internal::ELASTICITY_MULTIPLIER;
18use parking_lot::{Mutex, RwLock};
19use slab::Slab;
20use tokio::sync::mpsc::error::TryRecvError;
21use unexpected::{Mismatch, OutOfBounds};
22
23use cfx_executor::machine::Machine;
24use cfx_types::{H256, U256};
25use cfxcore_errors::ProviderBlockError;
26use dag::{Graph, RichDAG, RichTreeGraph, TreeGraph, DAG};
27use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
28use malloc_size_of_derive::MallocSizeOf as DeriveMallocSizeOf;
29use metrics::{
30    register_meter_with_group, register_queue, Meter, MeterTimer, Queue,
31};
32use primitives::{
33    pos::PosBlockId, transaction::SignedTransaction, Block, BlockHeader,
34    EpochNumber,
35};
36
37use crate::{
38    block_data_manager::{BlockDataManager, BlockStatus},
39    channel::Channel,
40    consensus::{pos_handler::PosVerifier, SharedConsensusGraph},
41    core_error::{BlockError, CoreError as Error},
42    pow::{PowComputer, ProofOfWorkConfig},
43    state_exposer::{SyncGraphBlockState, STATE_EXPOSER},
44    statistics::SharedStatistics,
45    sync::synchronization_protocol_handler::FutureBlockContainer,
46    verification::*,
47    Notifications,
48};
49
50lazy_static! {
51    static ref SYNC_INSERT_HEADER: Arc<dyn Meter> =
52        register_meter_with_group("timer", "sync::insert_block_header");
53    static ref SYNC_INSERT_BLOCK: Arc<dyn Meter> =
54        register_meter_with_group("timer", "sync::insert_block");
55    static ref CONSENSUS_WORKER_QUEUE: Arc<dyn Queue> =
56        register_queue("consensus_worker_queue");
57}
58
59const NULL: usize = !0;
60const BLOCK_INVALID: u8 = 0;
61const BLOCK_HEADER_ONLY: u8 = 1;
62const BLOCK_HEADER_GRAPH_READY: u8 = 2;
63const BLOCK_GRAPH_READY: u8 = 3;
64
65#[derive(Copy, Clone)]
66pub struct SyncGraphConfig {
67    pub future_block_buffer_capacity: usize,
68    pub enable_state_expose: bool,
69    pub is_consortium: bool,
70}
71
72#[derive(Debug)]
73pub struct SyncGraphStatistics {
74    pub inserted_block_count: usize,
75    pub inserted_header_count: usize,
76}
77
78impl SyncGraphStatistics {
79    pub fn new() -> SyncGraphStatistics {
80        SyncGraphStatistics {
81            // Already counted genesis block
82            inserted_header_count: 1,
83            inserted_block_count: 1,
84        }
85    }
86
87    pub fn clear(&mut self) {
88        self.inserted_header_count = 1;
89        self.inserted_block_count = 1;
90    }
91}
92
93#[derive(DeriveMallocSizeOf)]
94pub struct SynchronizationGraphNode {
95    pub block_header: Arc<BlockHeader>,
96    /// The status of graph connectivity in the current block view.
97    pub graph_status: u8,
98    /// Whether the block body is ready.
99    pub block_ready: bool,
100    /// Whether parent is in old era and already reclaimed
101    pub parent_reclaimed: bool,
102    /// The index of the parent of the block.
103    pub parent: usize,
104    /// The indices of the children of the block.
105    pub children: Vec<usize>,
106    /// The indices of the blocks referenced by the block.
107    pub referees: Vec<usize>,
108    /// The number of blocks referenced by the block but
109    /// haven't been inserted in synchronization graph.
110    pub pending_referee_count: usize,
111    /// The indices of the blocks referencing the block.
112    pub referrers: Vec<usize>,
113    /// the timestamp in seconds when graph_status updated
114    pub last_update_timestamp: u64,
115}
116
117#[derive(DeriveMallocSizeOf)]
118pub struct UnreadyBlockFrontier {
119    frontier: HashSet<usize>,
120    updated: bool,
121}
122
123impl UnreadyBlockFrontier {
124    fn new() -> Self {
125        UnreadyBlockFrontier {
126            frontier: HashSet::new(),
127            updated: false,
128        }
129    }
130
131    pub fn reset_update_state(&mut self) { self.updated = false; }
132
133    pub fn updated(&self) -> bool { self.updated }
134
135    pub fn get_frontier(&self) -> &HashSet<usize> { &self.frontier }
136
137    pub fn remove(&mut self, index: &usize) -> bool {
138        self.updated = true;
139        self.frontier.remove(index)
140    }
141
142    pub fn contains(&self, index: &usize) -> bool {
143        self.frontier.contains(index)
144    }
145
146    pub fn insert(&mut self, index: usize) -> bool {
147        self.updated = true;
148        self.frontier.insert(index)
149    }
150
151    pub fn len(&self) -> usize { self.frontier.len() }
152}
153
154pub struct SynchronizationGraphInner {
155    pub arena: Slab<SynchronizationGraphNode>,
156    pub hash_to_arena_indices: HashMap<H256, usize>,
157    pub data_man: Arc<BlockDataManager>,
158    children_by_hash: HashMap<H256, Vec<usize>>,
159    referrers_by_hash: HashMap<H256, Vec<usize>>,
160    pub pow_config: ProofOfWorkConfig,
161    pub pow: Arc<PowComputer>,
162    pub config: SyncGraphConfig,
163    /// The indices of blocks whose graph_status is not GRAPH_READY.
164    /// It may consider not header-graph-ready in phases
165    /// `CatchUpRecoverBlockHeaderFromDB` and `CatchUpSyncBlockHeader`.
166    /// Or, it may consider not block-graph-ready in phases
167    /// `CatchUpRecoverBlockFromDB`, `CatchUpSyncBlock`, and `Normal`.
168    pub not_ready_blocks_frontier: UnreadyBlockFrontier,
169
170    /// This includes the blocks whose parent and referees are all ready, and
171    /// only pos_reference has not been ready (pos_reference not committed
172    /// or its pivot decision is not ready).
173    pub pos_not_ready_blocks_frontier: HashSet<usize>,
174    pub old_era_blocks_frontier: VecDeque<usize>,
175    pub old_era_blocks_frontier_set: HashSet<usize>,
176
177    /// Set to `true` in `CatchUpCheckpointPhase` and
178    /// `CatchUpFillBlockBodyPhase` so that sync graph and consensus graph
179    /// remain unchanged for consistency.
180    pub locked_for_catchup: bool,
181    /// The set of blocks that we need to download block bodies in
182    /// `CatchUpFillBlockBodyPhase`.
183    pub block_to_fill_set: HashSet<H256>,
184    machine: Arc<Machine>,
185    pub pos_verifier: Arc<PosVerifier>,
186}
187
188impl MallocSizeOf for SynchronizationGraphInner {
189    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
190        self.arena.size_of(ops)
191            + self.hash_to_arena_indices.size_of(ops)
192            + self.data_man.size_of(ops)
193            + self.children_by_hash.size_of(ops)
194            + self.referrers_by_hash.size_of(ops)
195            + self.pow_config.size_of(ops)
196            + self.not_ready_blocks_frontier.size_of(ops)
197            + self.old_era_blocks_frontier.size_of(ops)
198            + self.old_era_blocks_frontier_set.size_of(ops)
199        // Does not count size_of machine.
200    }
201}
202
203impl SynchronizationGraphInner {
204    pub fn with_genesis_block(
205        genesis_header: Arc<BlockHeader>, pow_config: ProofOfWorkConfig,
206        pow: Arc<PowComputer>, config: SyncGraphConfig,
207        data_man: Arc<BlockDataManager>, machine: Arc<Machine>,
208        pos_verifier: Arc<PosVerifier>,
209    ) -> Self {
210        let mut inner = SynchronizationGraphInner {
211            arena: Slab::new(),
212            hash_to_arena_indices: HashMap::new(),
213            data_man,
214            children_by_hash: HashMap::new(),
215            referrers_by_hash: HashMap::new(),
216            pow_config,
217            pow,
218            config,
219            not_ready_blocks_frontier: UnreadyBlockFrontier::new(),
220            pos_not_ready_blocks_frontier: Default::default(),
221            old_era_blocks_frontier: Default::default(),
222            old_era_blocks_frontier_set: Default::default(),
223            block_to_fill_set: Default::default(),
224            locked_for_catchup: false,
225            machine,
226            pos_verifier,
227        };
228        let genesis_hash = genesis_header.hash();
229        let genesis_block_index = inner.insert(genesis_header);
230        debug!(
231            "genesis block {:?} has index {} in sync graph",
232            genesis_hash, genesis_block_index
233        );
234
235        inner.old_era_blocks_frontier.push_back(genesis_block_index);
236        inner
237            .old_era_blocks_frontier_set
238            .insert(genesis_block_index);
239
240        inner
241    }
242
243    fn get_genesis_in_current_era(&self) -> usize {
244        let genesis_hash = self.data_man.get_cur_consensus_era_genesis_hash();
245        *self.hash_to_arena_indices.get(&genesis_hash).unwrap()
246    }
247
248    pub fn get_genesis_hash_and_height_in_current_era(&self) -> (H256, u64) {
249        let era_genesis = self.get_genesis_in_current_era();
250        (
251            self.arena[era_genesis].block_header.hash(),
252            self.arena[era_genesis].block_header.height(),
253        )
254    }
255
256    pub fn get_stable_hash_and_height_in_current_era(&self) -> (H256, u64) {
257        let stable_hash = self.data_man.get_cur_consensus_era_stable_hash();
258        // The stable block may not be in the sync-graph when this function is
259        // invoked during the synchronization phase, let's query the
260        // data from data manager
261        let height = self
262            .data_man
263            .block_header_by_hash(&stable_hash)
264            .expect("stable block must exist in data manager")
265            .height();
266        (stable_hash, height)
267    }
268
269    fn try_clear_old_era_blocks(&mut self) {
270        let max_num_of_cleared_blocks = 2;
271        let mut num_cleared = 0;
272        let era_genesis = self.get_genesis_in_current_era();
273        let genesis_seq_num = self
274            .data_man
275            .local_block_info_by_hash(
276                &self.data_man.get_cur_consensus_era_genesis_hash(),
277            )
278            .expect("local_block_info for genesis must exist")
279            .get_seq_num();
280        let mut era_genesis_in_frontier = false;
281
282        while let Some(index) = self.old_era_blocks_frontier.pop_front() {
283            if index == era_genesis {
284                era_genesis_in_frontier = true;
285                continue;
286            }
287
288            // Remove node with index
289            if !self.old_era_blocks_frontier_set.contains(&index) {
290                continue;
291            }
292
293            let hash = self.arena[index].block_header.hash();
294            assert!(self.arena[index].parent == NULL);
295
296            if !self.is_graph_ready_in_db(&hash, genesis_seq_num) {
297                // This block has not been processed in consensus. Clearing it
298                // now may make its referrers not block-graph-ready.
299                // See https://github.com/Conflux-Chain/conflux-rust/issues/1426.
300                //
301                // The blocks pushed to `old_era_blocks_frontier` are all
302                // BlockGraphReady, so it's ensured that they will be inserted
303                // into consensus and their local block infos will be persisted.
304                continue;
305            }
306
307            let referees: Vec<usize> =
308                self.arena[index].referees.iter().map(|x| *x).collect();
309            for referee in referees {
310                self.arena[referee].referrers.retain(|&x| x != index);
311            }
312            let referee_hashes: Vec<H256> = self.arena[index]
313                .block_header
314                .referee_hashes()
315                .iter()
316                .map(|x| *x)
317                .collect();
318            for referee_hash in referee_hashes {
319                let mut remove_referee_hash: bool = false;
320                if let Some(referrers) =
321                    self.referrers_by_hash.get_mut(&referee_hash)
322                {
323                    referrers.retain(|&x| x != index);
324                    remove_referee_hash = referrers.len() == 0;
325                }
326                // clean empty key
327                if remove_referee_hash {
328                    self.referrers_by_hash.remove(&referee_hash);
329                }
330            }
331
332            let children: Vec<usize> =
333                self.arena[index].children.iter().map(|x| *x).collect();
334            for child in children {
335                self.arena[child].parent = NULL;
336                self.arena[child].parent_reclaimed = true;
337                // We will check `is_graph_ready_in_db` before garbage
338                // collecting the blocks in `old_era_blocks_frontier`,
339                // so we do not need to check graph-ready-related status here
340                // before inserting them.
341                self.old_era_blocks_frontier.push_back(child);
342                assert!(!self.old_era_blocks_frontier_set.contains(&child));
343                self.old_era_blocks_frontier_set.insert(child);
344            }
345
346            let referrers: Vec<usize> =
347                self.arena[index].referrers.iter().map(|x| *x).collect();
348            for referrer in referrers {
349                self.arena[referrer].referees.retain(|&x| x != index);
350            }
351
352            self.old_era_blocks_frontier_set.remove(&index);
353            self.arena.remove(index);
354            self.hash_to_arena_indices.remove(&hash);
355            // only remove block header in memory cache
356            self.data_man
357                .remove_block_header(&hash, false /* remove_db */);
358
359            num_cleared += 1;
360            if num_cleared == max_num_of_cleared_blocks {
361                break;
362            }
363        }
364
365        if era_genesis_in_frontier {
366            self.old_era_blocks_frontier.push_front(era_genesis);
367        }
368    }
369
370    pub fn insert_invalid(&mut self, header: Arc<BlockHeader>) -> usize {
371        let hash = header.hash();
372        let me = self.arena.insert(SynchronizationGraphNode {
373            graph_status: BLOCK_INVALID,
374            block_ready: false,
375            parent_reclaimed: false,
376            parent: NULL,
377            children: Vec::new(),
378            referees: Vec::new(),
379            pending_referee_count: 0,
380            referrers: Vec::new(),
381            block_header: header,
382            last_update_timestamp: SystemTime::now()
383                .duration_since(UNIX_EPOCH)
384                .unwrap()
385                .as_secs(),
386        });
387        self.hash_to_arena_indices.insert(hash, me);
388
389        if let Some(children) = self.children_by_hash.remove(&hash) {
390            for child in &children {
391                self.arena[*child].parent = me;
392            }
393            self.arena[me].children = children;
394        }
395        if let Some(referrers) = self.referrers_by_hash.remove(&hash) {
396            for referrer in &referrers {
397                let ref mut node_referrer = self.arena[*referrer];
398                node_referrer.referees.push(me);
399                debug_assert!(node_referrer.pending_referee_count > 0);
400                if node_referrer.pending_referee_count > 0 {
401                    node_referrer.pending_referee_count =
402                        node_referrer.pending_referee_count - 1;
403                }
404            }
405            self.arena[me].referrers = referrers;
406        }
407
408        me
409    }
410
411    /// Return the index of the inserted block.
412    pub fn insert(&mut self, header: Arc<BlockHeader>) -> usize {
413        let hash = header.hash();
414        let is_genesis =
415            hash == self.data_man.get_cur_consensus_era_genesis_hash();
416
417        let me = self.arena.insert(SynchronizationGraphNode {
418            graph_status: if is_genesis {
419                BLOCK_GRAPH_READY
420            } else {
421                BLOCK_HEADER_ONLY
422            },
423            block_ready: is_genesis,
424            parent_reclaimed: false,
425            parent: NULL,
426            children: Vec::new(),
427            referees: Vec::new(),
428            pending_referee_count: 0,
429            referrers: Vec::new(),
430            block_header: header.clone(),
431            last_update_timestamp: SystemTime::now()
432                .duration_since(UNIX_EPOCH)
433                .unwrap()
434                .as_secs(),
435        });
436        self.hash_to_arena_indices.insert(hash, me);
437
438        if !is_genesis {
439            let parent_hash = header.parent_hash().clone();
440            if let Some(parent) =
441                self.hash_to_arena_indices.get(&parent_hash).cloned()
442            {
443                self.arena[me].parent = parent;
444                self.arena[parent].children.push(me);
445            } else {
446                self.children_by_hash
447                    .entry(parent_hash)
448                    .or_insert(Vec::new())
449                    .push(me);
450            }
451        }
452        for referee_hash in header.referee_hashes() {
453            if let Some(referee) =
454                self.hash_to_arena_indices.get(referee_hash).cloned()
455            {
456                self.arena[me].referees.push(referee);
457                self.arena[referee].referrers.push(me);
458            } else {
459                self.arena[me].pending_referee_count =
460                    self.arena[me].pending_referee_count + 1;
461                self.referrers_by_hash
462                    .entry(*referee_hash)
463                    .or_insert(Vec::new())
464                    .push(me);
465            }
466        }
467
468        if let Some(children) = self.children_by_hash.remove(&hash) {
469            for child in &children {
470                self.arena[*child].parent = me;
471            }
472            self.arena[me].children = children;
473        }
474        if let Some(referrers) = self.referrers_by_hash.remove(&hash) {
475            for referrer in &referrers {
476                let ref mut node_referrer = self.arena[*referrer];
477                node_referrer.referees.push(me);
478                debug_assert!(node_referrer.pending_referee_count > 0);
479                if node_referrer.pending_referee_count > 0 {
480                    node_referrer.pending_referee_count =
481                        node_referrer.pending_referee_count - 1;
482                }
483            }
484            self.arena[me].referrers = referrers;
485        }
486
487        me
488    }
489
490    // TODO local_block_info is also loaded for invalid check, so maybe we can
491    // refactor code to avoid loading it twice.
492    fn is_graph_ready_in_db(
493        &self, parent_or_referee_hash: &H256, genesis_seq_num: u64,
494    ) -> bool {
495        if let Some(info) = self
496            .data_man
497            .local_block_info_by_hash(parent_or_referee_hash)
498        {
499            if info.get_status() == BlockStatus::Invalid {
500                false
501            } else {
502                info.get_seq_num() < genesis_seq_num
503                    || info.get_instance_id() == self.data_man.get_instance_id()
504            }
505        } else {
506            false
507        }
508    }
509
510    fn new_to_be_graph_ready(
511        &mut self, index: usize, minimal_status: u8,
512    ) -> bool {
513        let ref node_me = self.arena[index];
514        // If a block has become graph-ready before and reclaimed,
515        // it will be marked as `already_processed`
516        // in `insert_block_header`, so we do not need to handle this case here.
517        // And thus we also won't propagate graph-ready to already processed
518        // blocks.
519        if node_me.graph_status >= minimal_status {
520            return false;
521        }
522
523        let genesis_hash = self.data_man.get_cur_consensus_era_genesis_hash();
524        let genesis_seq_num = self
525            .data_man
526            .local_block_info_by_hash(&genesis_hash)
527            .expect("local_block_info for genesis must exist")
528            .get_seq_num();
529        let parent = self.arena[index].parent;
530        let parent_graph_ready = if parent == NULL {
531            self.arena[index].parent_reclaimed
532                || self.is_graph_ready_in_db(
533                    self.arena[index].block_header.parent_hash(),
534                    genesis_seq_num,
535                )
536        } else {
537            self.arena[parent].graph_status >= minimal_status
538        };
539
540        if !parent_graph_ready {
541            return false;
542        } else if parent == NULL {
543            self.arena[index].parent_reclaimed = true;
544        }
545
546        // check whether referees are `BLOCK_HEADER_GRAPH_READY`
547        // 1. referees which are in
548        // memory and status is BLOCK_HEADER_GRAPH_READY.
549        // 2. referees
550        // which are not in memory and not invalid in disk
551        // (assume these blocks are BLOCK_GRAPH_READY)
552        let mut referee_hash_in_mem = HashSet::new();
553        for referee in self.arena[index].referees.iter() {
554            if self.arena[*referee].graph_status < minimal_status {
555                return false;
556            } else {
557                referee_hash_in_mem
558                    .insert(self.arena[*referee].block_header.hash());
559            }
560        }
561
562        for referee_hash in self.arena[index].block_header.referee_hashes() {
563            if !referee_hash_in_mem.contains(referee_hash) {
564                if !self.is_graph_ready_in_db(referee_hash, genesis_seq_num) {
565                    return false;
566                }
567            }
568        }
569
570        if !self.is_pos_reference_graph_ready(
571            index,
572            genesis_seq_num,
573            minimal_status,
574        ) {
575            debug!(
576                "Block {:?} not ready for its pos_reference: {:?}",
577                self.arena[index].block_header.hash(),
578                self.pos_verifier.get_pivot_decision(
579                    self.arena[index]
580                        .block_header
581                        .pos_reference()
582                        .as_ref()
583                        .unwrap()
584                )
585            );
586            // All its future will remain not ready.
587            self.pos_not_ready_blocks_frontier.insert(index);
588            return false;
589        }
590
591        // parent and referees are all header graph ready.
592        true
593    }
594
595    fn new_to_be_header_graph_ready(&mut self, index: usize) -> bool {
596        self.new_to_be_graph_ready(index, BLOCK_HEADER_GRAPH_READY)
597    }
598
599    fn new_to_be_block_graph_ready(&mut self, index: usize) -> bool {
600        self.new_to_be_graph_ready(index, BLOCK_GRAPH_READY)
601            && self.arena[index].block_ready
602    }
603
604    fn is_pos_reference_graph_ready(
605        &self, index: usize, genesis_seq_num: u64, minimal_status: u8,
606    ) -> bool {
607        // Check if the pos reference is committed.
608        match self.arena[index].block_header.pos_reference() {
609            // TODO(lpl): Should we check if the pos reference will never be
610            // committed?
611            Some(pos_reference) => {
612                match self.pos_verifier.get_pivot_decision(pos_reference) {
613                    // The pos reference has not been committed.
614                    None => false,
615                    Some(pivot_decision) => {
616                        // Check if this pivot_decision is graph_ready.
617                        match self.hash_to_arena_indices.get(&pivot_decision) {
618                            None => self.is_graph_ready_in_db(
619                                &pivot_decision,
620                                genesis_seq_num,
621                            ),
622                            Some(index) => {
623                                self.arena[*index].graph_status
624                                    >= minimal_status
625                            }
626                        }
627                    }
628                }
629            }
630            None => true,
631        }
632    }
633
634    // Get parent (height, timestamp, gas_limit, difficulty,
635    // parent_and_referee_pos_references) This function assumes that the
636    // parent and referee information MUST exist in memory or in disk.
637    fn get_parent_and_referee_info(
638        &self, index: usize,
639    ) -> (u64, u64, U256, U256, Vec<Option<PosBlockId>>) {
640        let parent_height;
641        let parent_timestamp;
642        let parent_gas_limit;
643        let parent_difficulty;
644        // Since eventually all blocks should have pos_references, we do not
645        // try to avoid loading them here before PoS is enabled.
646        let mut pos_references = Vec::new();
647        let parent = self.arena[index].parent;
648
649        // Get info for parent.
650        if parent != NULL {
651            parent_height = self.arena[parent].block_header.height();
652            parent_timestamp = self.arena[parent].block_header.timestamp();
653            parent_gas_limit = *self.arena[parent].block_header.gas_limit();
654            parent_difficulty = *self.arena[parent].block_header.difficulty();
655            pos_references
656                .push(self.arena[parent].block_header.pos_reference().clone())
657        } else {
658            let parent_hash = self.arena[index].block_header.parent_hash();
659            let parent_header = self
660                .data_man
661                .block_header_by_hash(parent_hash)
662                .unwrap()
663                .clone();
664            parent_height = parent_header.height();
665            parent_timestamp = parent_header.timestamp();
666            parent_gas_limit = *parent_header.gas_limit();
667            parent_difficulty = *parent_header.difficulty();
668            pos_references.push(parent_header.pos_reference().clone());
669        }
670
671        // Get pos references for referees.
672        let mut referee_hash_in_mem = HashSet::new();
673        for referee in self.arena[index].referees.iter() {
674            pos_references.push(
675                self.arena[*referee].block_header.pos_reference().clone(),
676            );
677            referee_hash_in_mem
678                .insert(self.arena[*referee].block_header.hash());
679        }
680
681        for referee_hash in self.arena[index].block_header.referee_hashes() {
682            if !referee_hash_in_mem.contains(referee_hash) {
683                let referee_header = self
684                    .data_man
685                    .block_header_by_hash(referee_hash)
686                    .unwrap()
687                    .clone();
688                pos_references.push(referee_header.pos_reference().clone());
689            }
690        }
691
692        (
693            parent_height,
694            parent_timestamp,
695            parent_gas_limit,
696            parent_difficulty,
697            pos_references,
698        )
699    }
700
701    fn verify_header_graph_ready_block(
702        &self, index: usize,
703    ) -> Result<(), Error> {
704        let epoch = self.arena[index].block_header.height();
705        let (
706            parent_height,
707            parent_timestamp,
708            parent_gas_limit,
709            parent_difficulty,
710            predecessor_pos_references,
711        ) = self.get_parent_and_referee_info(index);
712
713        // Verify the height and epoch numbers are correct
714        if parent_height + 1 != epoch {
715            warn!("Invalid height. mine {}, parent {}", epoch, parent_height);
716            return Err(From::from(BlockError::InvalidHeight(Mismatch {
717                expected: parent_height + 1,
718                found: epoch,
719            })));
720        }
721
722        // Verify the timestamp being correctly set.
723        // Conflux tries to maintain the timestamp drift among blocks
724        // in the graph, which probably being generated at the same time,
725        // within a small bound (specified by ACCEPTABLE_TIME_DRIFT).
726        // This is achieved through the following mechanism. Anytime
727        // when receiving a new block from the peer, if the timestamp of
728        // the block is more than ACCEPTABLE_TIME_DRIFT later than the
729        // current timestamp of the node, the block is postponed to be
730        // added into the graph until the current timestamp passes the
731        // the timestamp of the block. Otherwise, this block can be added
732        // into the graph.
733        // Meanwhile, Conflux also requires that the timestamp of each
734        // block must be later than or equal to its parent's timestamp.
735        // This is achieved through adjusting the timestamp of a newly
736        // generated block to the one later than its parent's timestamp.
737        // This is also enough for difficulty adjustment computation where
738        // the timespan in the adjustment period is only computed based on
739        // timestamps of pivot chain blocks.
740        let my_timestamp = self.arena[index].block_header.timestamp();
741        if parent_timestamp > my_timestamp {
742            let my_timestamp = UNIX_EPOCH + Duration::from_secs(my_timestamp);
743            let pa_timestamp =
744                UNIX_EPOCH + Duration::from_secs(parent_timestamp);
745
746            warn!("Invalid timestamp: parent {:?} timestamp {}, me {:?} timestamp {}",
747                  self.arena[index].block_header.parent_hash().clone(),
748                  parent_timestamp,
749                  self.arena[index].block_header.hash(),
750                  self.arena[index].block_header.timestamp());
751            return Err(From::from(BlockError::InvalidTimestamp(
752                OutOfBounds {
753                    max: Some(my_timestamp),
754                    min: Some(pa_timestamp),
755                    found: my_timestamp,
756                },
757            )));
758        }
759
760        let parent_gas_limit = parent_gas_limit
761            * if epoch == self.machine.params().transition_heights.cip1559 {
762                ELASTICITY_MULTIPLIER
763            } else {
764                1
765            };
766
767        // Verify the gas limit is respected
768        let self_gas_limit = *self.arena[index].block_header.gas_limit();
769        let gas_limit_divisor = self.machine.params().gas_limit_bound_divisor;
770        let min_gas_limit = self.machine.params().min_gas_limit;
771        let gas_upper =
772            parent_gas_limit + parent_gas_limit / gas_limit_divisor - 1;
773        let gas_lower = max(
774            parent_gas_limit - parent_gas_limit / gas_limit_divisor + 1,
775            min_gas_limit,
776        );
777
778        if self_gas_limit < gas_lower || self_gas_limit > gas_upper {
779            return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
780                min: Some(gas_lower),
781                max: Some(gas_upper),
782                found: self_gas_limit,
783            })));
784        }
785
786        if !self.config.is_consortium {
787            // Verify difficulty being correctly set
788            let mut difficulty_invalid = false;
789            let my_diff = *self.arena[index].block_header.difficulty();
790            let mut min_diff = my_diff;
791            let mut max_diff = my_diff;
792            let initial_difficulty: U256 =
793                self.pow_config.initial_difficulty.into();
794
795            if parent_height
796                < self
797                    .pow_config
798                    .difficulty_adjustment_epoch_period(parent_height)
799            {
800                if my_diff != initial_difficulty {
801                    difficulty_invalid = true;
802                    min_diff = initial_difficulty;
803                    max_diff = initial_difficulty;
804                }
805            } else {
806                let last_period_upper = (parent_height
807                    / self
808                        .pow_config
809                        .difficulty_adjustment_epoch_period(parent_height))
810                    * self
811                        .pow_config
812                        .difficulty_adjustment_epoch_period(parent_height);
813                if last_period_upper != parent_height {
814                    // parent_epoch should not trigger difficulty adjustment
815                    if my_diff != parent_difficulty {
816                        difficulty_invalid = true;
817                        min_diff = parent_difficulty;
818                        max_diff = parent_difficulty;
819                    }
820                } else {
821                    let (lower, upper) =
822                        self.pow_config.get_adjustment_bound(parent_difficulty);
823                    min_diff = lower;
824                    max_diff = upper;
825                    if my_diff < min_diff || my_diff > max_diff {
826                        difficulty_invalid = true;
827                    }
828                }
829            }
830
831            if difficulty_invalid {
832                return Err(From::from(BlockError::InvalidDifficulty(
833                    OutOfBounds {
834                        min: Some(min_diff),
835                        max: Some(max_diff),
836                        found: my_diff,
837                    },
838                )));
839            }
840        }
841
842        if let Some(pos_reference) =
843            self.arena[index].block_header.pos_reference()
844        {
845            let mut pred_pos_ref_list = Vec::new();
846            for maybe_pos_ref in predecessor_pos_references {
847                if let Some(pos_ref) = maybe_pos_ref {
848                    pred_pos_ref_list.push(pos_ref);
849                }
850            }
851            if !self
852                .pos_verifier
853                .verify_against_predecessors(pos_reference, &pred_pos_ref_list)
854            {
855                bail!(BlockError::InvalidPosReference);
856            }
857        }
858
859        Ok(())
860    }
861
862    fn verify_graph_ready_block(
863        &self, index: usize, verification_config: &VerificationConfig,
864    ) -> Result<(), Error> {
865        let block_header = &self.arena[index].block_header;
866        // TODO: Take the parent from the arena like
867        // `get_parent_and_referee_info` does.
868        let parent = self
869            .data_man
870            .block_header_by_hash(block_header.parent_hash())
871            .expect("headers will not be deleted");
872        let block = self
873            .data_man
874            .block_by_hash(&block_header.hash(), true)
875            .expect("received");
876        verification_config.verify_sync_graph_ready_block(&block, &parent)
877    }
878
879    fn process_invalid_blocks(&mut self, invalid_set: &HashSet<usize>) {
880        for index in invalid_set {
881            let hash = self.arena[*index].block_header.hash();
882            // Mark this block as invalid, so we don't need to request/verify it
883            // again
884            self.data_man.invalidate_block(hash);
885        }
886        self.remove_blocks(&invalid_set);
887    }
888
889    fn remove_blocks(&mut self, to_remove_set: &HashSet<usize>) {
890        for index in to_remove_set {
891            let hash = self.arena[*index].block_header.hash();
892            self.not_ready_blocks_frontier.remove(index);
893            self.pos_not_ready_blocks_frontier.remove(index);
894            self.old_era_blocks_frontier_set.remove(index);
895            // This include invalid blocks and blocks not received after a long
896            // time.
897            self.block_to_fill_set.remove(&hash);
898
899            let parent = self.arena[*index].parent;
900            if parent != NULL {
901                self.arena[parent].children.retain(|&x| x != *index);
902            }
903            let parent_hash = *self.arena[*index].block_header.parent_hash();
904            let mut remove_parent_hash: bool = false;
905            if let Some(children) = self.children_by_hash.get_mut(&parent_hash)
906            {
907                children.retain(|&x| x != *index);
908                remove_parent_hash = children.len() == 0;
909            }
910            // clean empty hash key
911            if remove_parent_hash {
912                self.children_by_hash.remove(&parent_hash);
913            }
914
915            let referees: Vec<usize> =
916                self.arena[*index].referees.iter().map(|x| *x).collect();
917            for referee in referees {
918                self.arena[referee].referrers.retain(|&x| x != *index);
919            }
920            let referee_hashes: Vec<H256> = self.arena[*index]
921                .block_header
922                .referee_hashes()
923                .iter()
924                .map(|x| *x)
925                .collect();
926            for referee_hash in referee_hashes {
927                let mut remove_referee_hash: bool = false;
928                if let Some(referrers) =
929                    self.referrers_by_hash.get_mut(&referee_hash)
930                {
931                    referrers.retain(|&x| x != *index);
932                    remove_referee_hash = referrers.len() == 0;
933                }
934                // clean empty hash key
935                if remove_referee_hash {
936                    self.referrers_by_hash.remove(&referee_hash);
937                }
938            }
939
940            let children: Vec<usize> =
941                self.arena[*index].children.iter().map(|x| *x).collect();
942            for child in children {
943                debug_assert!(to_remove_set.contains(&child));
944                self.arena[child].parent = NULL;
945            }
946
947            let referrers: Vec<usize> =
948                self.arena[*index].referrers.iter().map(|x| *x).collect();
949            for referrer in referrers {
950                debug_assert!(to_remove_set.contains(&referrer));
951                self.arena[referrer].referees.retain(|&x| x != *index);
952            }
953
954            self.arena.remove(*index);
955            self.hash_to_arena_indices.remove(&hash);
956            // remove header/block in memory cache and header/block in db
957            self.data_man
958                .remove_useless_block(&hash, true /* remove_db */);
959        }
960    }
961
962    fn set_and_propagate_invalid(
963        &mut self, queue: &mut VecDeque<usize>,
964        invalid_set: &mut HashSet<usize>, index: usize,
965    ) {
966        let children =
967            mem::replace(&mut self.arena[index].children, Vec::new());
968        for child in &children {
969            if !invalid_set.contains(&child) {
970                self.arena[*child].graph_status = BLOCK_INVALID;
971                queue.push_back(*child);
972                invalid_set.insert(*child);
973            }
974        }
975        self.arena[index].children = children;
976        let referrers =
977            mem::replace(&mut self.arena[index].referrers, Vec::new());
978        for referrer in &referrers {
979            if !invalid_set.contains(&referrer) {
980                self.arena[*referrer].graph_status = BLOCK_INVALID;
981                queue.push_back(*referrer);
982                invalid_set.insert(*referrer);
983            }
984        }
985        self.arena[index].referrers = referrers;
986    }
987}
988
989/// Manages the lifecycle of the consensus worker thread.
990/// On drop, unsubscribes from the channel (closing the sender) so the worker's
991/// `recv_blocking()` returns `None` and the loop exits naturally, then joins.
992struct ConsensusWorkerHandle {
993    thread: Mutex<Option<thread::JoinHandle<()>>>,
994    /// Channel + subscription ID for unsubscribe-based shutdown.
995    new_block_hashes: Arc<Channel<H256>>,
996    worker_subscription_id: u64,
997}
998
999impl ConsensusWorkerHandle {
1000    fn stop(&self) {
1001        self.new_block_hashes
1002            .unsubscribe(self.worker_subscription_id);
1003        if let Some(handle) = self.thread.lock().take() {
1004            handle.join().expect("Consensus Worker should not panic");
1005        }
1006    }
1007}
1008
1009impl Drop for ConsensusWorkerHandle {
1010    fn drop(&mut self) { self.stop(); }
1011}
1012
1013pub struct SynchronizationGraph {
1014    pub inner: Arc<RwLock<SynchronizationGraphInner>>,
1015    pub consensus: SharedConsensusGraph,
1016    pub data_man: Arc<BlockDataManager>,
1017    pub pow: Arc<PowComputer>,
1018    pub verification_config: VerificationConfig,
1019    pub sync_config: SyncGraphConfig,
1020    pub statistics: SharedStatistics,
1021    /// This is the boolean state shared with the underlying consensus worker
1022    /// to indicate whether the worker is now finished all pending blocks.
1023    /// Since the critical section is very short, a `Mutex` is enough.
1024    consensus_unprocessed_count: Arc<AtomicUsize>,
1025
1026    /// Channel used to send block hashes to `ConsensusGraph` and PubSub.
1027    /// Each element is <block_hash, ignore_body>
1028    new_block_hashes: Arc<Channel<H256>>,
1029
1030    /// The blocks whose timestamps are near future.
1031    /// They will be inserted into sync graph inner at their timestamp.
1032    pub future_blocks: FutureBlockContainer,
1033
1034    machine: Arc<Machine>,
1035
1036    /// Handle to the consensus worker thread; joined on drop.
1037    #[allow(unused)]
1038    consensus_worker_handle: ConsensusWorkerHandle,
1039}
1040
1041impl MallocSizeOf for SynchronizationGraph {
1042    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
1043        let inner_size = self.inner.read().size_of(ops);
1044        let mut malloc_size = inner_size + self.data_man.size_of(ops);
1045
1046        // TODO: Add statistics for consortium.
1047        if !self.is_consortium() {
1048            let consensus_graph = &*self.consensus;
1049            malloc_size += consensus_graph.size_of(ops);
1050        }
1051        // Does not count size_of machine.
1052
1053        malloc_size
1054    }
1055}
1056
1057pub type SharedSynchronizationGraph = Arc<SynchronizationGraph>;
1058
1059impl SynchronizationGraph {
1060    pub fn new(
1061        consensus: SharedConsensusGraph, data_man: Arc<BlockDataManager>,
1062        statistics: SharedStatistics, verification_config: VerificationConfig,
1063        pow_config: ProofOfWorkConfig, pow: Arc<PowComputer>,
1064        sync_config: SyncGraphConfig, notifications: Arc<Notifications>,
1065        machine: Arc<Machine>, pos_verifier: Arc<PosVerifier>,
1066    ) -> Self {
1067        let genesis_hash = data_man.get_cur_consensus_era_genesis_hash();
1068        let genesis_block_header = data_man
1069            .block_header_by_hash(&genesis_hash)
1070            .expect("genesis block header should exist here");
1071
1072        // It should not be initialized to `true` now, otherwise consensus
1073        // worker will be blocked on waiting the first block forever.
1074        let consensus_unprocessed_count = Arc::new(AtomicUsize::new(0));
1075        let mut consensus_receiver = notifications.new_block_hashes.subscribe();
1076        let inner = Arc::new(RwLock::new(
1077            SynchronizationGraphInner::with_genesis_block(
1078                genesis_block_header.clone(),
1079                pow_config,
1080                pow.clone(),
1081                sync_config,
1082                data_man.clone(),
1083                machine.clone(),
1084                pos_verifier.clone(),
1085            ),
1086        ));
1087        let worker_subscription_id = consensus_receiver.id;
1088
1089        // Clone Arcs before moving them into the worker thread closure.
1090        let worker_data_man = data_man.clone();
1091        let worker_consensus = consensus.clone();
1092        let worker_unprocessed_count = consensus_unprocessed_count.clone();
1093
1094        // It receives `BLOCK_GRAPH_READY` blocks in order and handles them in
1095        // `ConsensusGraph`
1096        let handle = thread::Builder::new()
1097            .name("Consensus Worker".into())
1098            // TODO: extract it in a seperated file
1099            .spawn(move || {
1100                // The Consensus Worker will prioritize blocks based on its parent epoch number while respecting the topological order. This has the following two benefits:
1101                //
1102                // 1. It will almost make sure that the self mined block being processed first
1103                //
1104                // 2. In case of a DoS attack that a malicious player releases a large chunk of old blocks. This strategy will make the consensus to process the meaningful blocks first.
1105                let mut priority_queue: BinaryHeap<(u64, H256)> = BinaryHeap::new();
1106                let mut reverse_map : HashMap<H256, Vec<H256>> = HashMap::new();
1107                let mut counter_map = HashMap::new();
1108                let mut pos_started = false;
1109
1110                'outer: loop {
1111                    // Only block when we have processed all received blocks.
1112                    let mut blocking = priority_queue.is_empty();
1113                    'inner: loop {
1114                        // Use blocking `recv_blocking` for the first element, and then
1115                        // drain the receiver with non-blocking `try_recv`.
1116                        let maybe_item = if blocking {
1117                            blocking = false;
1118                            match consensus_receiver.recv_blocking() {
1119                                Some(item) => Ok(item),
1120                                None => break 'outer, // channel closed (unsubscribed)
1121                            }
1122                        } else {
1123                            consensus_receiver.try_recv()
1124                        };
1125
1126                        match maybe_item {
1127                            // FIXME: We need to investigate why duplicate hash may send to the consensus worker
1128                            Ok(hash) => if !reverse_map.contains_key(&hash) {
1129                                debug!("Worker thread receive: block = {}", hash);
1130                                let header = worker_data_man.block_header_by_hash(&hash).expect("Header must exist before sending to the consensus worker!");
1131
1132                                // start pos with an era advance.
1133                                if !pos_started && pos_verifier.is_enabled_at_height(header.height() + worker_consensus.config().inner_conf.era_epoch_count) {
1134                                    if let Err(e) = pos_verifier.initialize(worker_consensus.clone()) {
1135                                        info!("PoS cannot be started at the expected height: e={}", e);
1136                                    } else {
1137                                        pos_started = true;
1138                                    }
1139                                }
1140
1141                                let mut cnt: usize = 0;
1142                                let parent_hash = header.parent_hash();
1143                                if let Some(v) = reverse_map.get_mut(parent_hash) {
1144                                    v.push(hash.clone());
1145                                    cnt += 1;
1146                                }
1147                                for referee in header.referee_hashes() {
1148                                    if let Some(v) = reverse_map.get_mut(referee) {
1149                                        v.push(hash.clone());
1150                                        cnt += 1;
1151                                    }
1152                                }
1153                                if let Some(pivot_decision) = header.pos_reference().as_ref().and_then(|pos_reference| pos_verifier.get_pivot_decision(pos_reference)) {
1154                                    if let Some(v) = reverse_map.get_mut(&pivot_decision) {
1155                                        v.push(hash.clone());
1156                                        cnt += 1;
1157                                    }
1158                                }
1159                                reverse_map.insert(hash.clone(), Vec::new());
1160                                if cnt == 0 {
1161                                    let epoch_number = worker_consensus.get_block_epoch_number(parent_hash).unwrap_or(0);
1162                                    priority_queue.push((epoch_number, hash));
1163                                } else {
1164                                    counter_map.insert(hash, cnt);
1165                                }
1166                            } else {
1167                                warn!("Duplicate block = {} sent to the consensus worker", hash);
1168                            },
1169                            Err(TryRecvError::Empty) => break 'inner,
1170                            Err(TryRecvError::Disconnected) => break 'outer,
1171                        }
1172                    }
1173                    if let Some((_, hash)) = priority_queue.pop() {
1174                        CONSENSUS_WORKER_QUEUE.dequeue(1);
1175                        let successors = reverse_map.remove(&hash).unwrap();
1176                        for succ in successors {
1177                            let cnt_tuple = counter_map.get_mut(&succ).unwrap();
1178                            *cnt_tuple -= 1;
1179                            if *cnt_tuple == 0 {
1180                                counter_map.remove(&succ);
1181                                let header_succ = worker_data_man.block_header_by_hash(&succ).expect("Header must exist before sending to the consensus worker!");
1182                                let parent_succ = header_succ.parent_hash();
1183                                let epoch_number = worker_consensus.get_block_epoch_number(parent_succ).unwrap_or(0);
1184                                priority_queue.push((epoch_number, succ));
1185                            }
1186                        }
1187                        worker_consensus.on_new_block(
1188                            &hash,
1189                        );
1190                        worker_unprocessed_count.fetch_sub(1, Ordering::SeqCst);
1191                    }
1192                }
1193            })
1194            .expect("Cannot fail");
1195
1196        let consensus_worker_handle = ConsensusWorkerHandle {
1197            thread: Mutex::new(Some(handle)),
1198            new_block_hashes: notifications.new_block_hashes.clone(),
1199            worker_subscription_id,
1200        };
1201
1202        let sync_graph = SynchronizationGraph {
1203            inner: inner.clone(),
1204            future_blocks: FutureBlockContainer::new(
1205                sync_config.future_block_buffer_capacity,
1206            ),
1207            data_man: data_man.clone(),
1208            pow: pow.clone(),
1209            verification_config,
1210            sync_config,
1211            consensus: consensus.clone(),
1212            statistics: statistics.clone(),
1213            consensus_unprocessed_count: consensus_unprocessed_count.clone(),
1214            new_block_hashes: notifications.new_block_hashes.clone(),
1215            machine,
1216            consensus_worker_handle,
1217        };
1218        sync_graph
1219    }
1220
1221    pub fn is_consortium(&self) -> bool { self.sync_config.is_consortium }
1222
1223    pub fn machine(&self) -> Arc<Machine> { self.machine.clone() }
1224
1225    pub fn get_genesis_hash_and_height_in_current_era(&self) -> (H256, u64) {
1226        self.inner
1227            .read()
1228            .get_genesis_hash_and_height_in_current_era()
1229    }
1230
1231    /// Compute the expected difficulty for a block given its
1232    /// parent hash
1233    pub fn expected_difficulty(&self, parent_hash: &H256) -> U256 {
1234        self.consensus.expected_difficulty(parent_hash)
1235    }
1236
1237    pub fn get_to_propagate_trans(
1238        &self,
1239    ) -> HashMap<H256, Arc<SignedTransaction>> {
1240        self.consensus.tx_pool().get_to_be_propagated_transactions()
1241    }
1242
1243    pub fn set_to_propagate_trans(
1244        &self, transactions: HashMap<H256, Arc<SignedTransaction>>,
1245    ) {
1246        self.consensus
1247            .tx_pool()
1248            .set_to_be_propagated_transactions(transactions);
1249    }
1250
1251    /// In full/archive node, this function can be invoked during
1252    /// CatchUpRecoverBlockHeaderFromDbPhase phase.
1253    /// It tries to construct the consensus graph based on header
1254    /// information stored in db.
1255    pub fn recover_graph_from_db(&self) {
1256        info!("Start fast recovery of the block DAG from database");
1257
1258        // Recover the initial sequence number in consensus graph
1259        // based on the sequence number of genesis block in db.
1260        let genesis_hash = self.data_man.get_cur_consensus_era_genesis_hash();
1261        let genesis_local_info =
1262            self.data_man.local_block_info_by_hash(&genesis_hash);
1263        if genesis_local_info.is_none() {
1264            // Local info of genesis block must exist.
1265            panic!(
1266                "failed to get local block info from db for genesis[{}]",
1267                genesis_hash
1268            );
1269        }
1270        let genesis_seq_num = genesis_local_info.unwrap().get_seq_num();
1271        self.consensus.set_initial_sequence_number(genesis_seq_num);
1272        let genesis_header =
1273            self.data_man.block_header_by_hash(&genesis_hash).unwrap();
1274        debug!(
1275            "Get current genesis_block hash={:?}, height={}, seq_num={}",
1276            genesis_hash,
1277            genesis_header.height(),
1278            genesis_seq_num
1279        );
1280
1281        // Get terminals stored in db.
1282        let terminals_opt = self.data_man.terminals_from_db();
1283        if terminals_opt.is_none() {
1284            return;
1285        }
1286        let terminals = terminals_opt.unwrap();
1287        debug!("Get terminals {:?}", terminals);
1288
1289        // Reconstruct the consensus graph by traversing backward from
1290        // terminals. This traversal will visit all the blocks under the
1291        // future of current era genesis till the terminals. However,
1292        // some blocks may not be graph-ready since they may have
1293        // references or parents which are out of the current era. We
1294        // need to resolve these out-of-era dependencies later and make
1295        // those blocks be graph-ready again.
1296        let mut queue = VecDeque::new();
1297        let mut visited_blocks: HashSet<H256> = HashSet::new();
1298        for terminal in terminals {
1299            // header terminals and block terminals may contain the same hash
1300            if !visited_blocks.contains(&terminal) {
1301                queue.push_back(terminal);
1302                visited_blocks.insert(terminal);
1303            }
1304        }
1305
1306        // Remember the hashes of blocks that belong to the current genesis
1307        // era but are missed in db. The missed blocks will be fetched from
1308        // peers.
1309        let mut missed_hashes = HashSet::new();
1310        while let Some(hash) = queue.pop_front() {
1311            if hash == genesis_hash {
1312                // Genesis block is already in consensus graph.
1313                continue;
1314            }
1315
1316            // Ignore blocks beyond the future of current genesis era.
1317            // If block_local_info is missing, consider it is in current
1318            // genesis era.
1319            if let Some(block_local_info) =
1320                self.data_man.local_block_info_by_hash(&hash)
1321            {
1322                if block_local_info.get_seq_num() < genesis_seq_num {
1323                    debug!(
1324                        "Skip block {:?} before checkpoint: seq_num={}",
1325                        hash,
1326                        block_local_info.get_seq_num()
1327                    );
1328                    continue;
1329                }
1330            }
1331
1332            if let Some(block_header) =
1333                self.data_man.block_header_by_hash(&hash)
1334            {
1335                self.insert_block_header(
1336                    &mut block_header.as_ref().clone(),
1337                    true,  /* need_to_verify */
1338                    false, /* bench_mode */
1339                    true,  /* insert_to_consensus */
1340                    false, /* persistent */
1341                );
1342                let parent = block_header.parent_hash().clone();
1343                let referees = block_header.referee_hashes().clone();
1344                if !visited_blocks.contains(&parent) {
1345                    queue.push_back(parent);
1346                    visited_blocks.insert(parent);
1347                }
1348                for referee in referees {
1349                    if !visited_blocks.contains(&referee) {
1350                        queue.push_back(referee);
1351                        visited_blocks.insert(referee);
1352                    }
1353                }
1354            } else {
1355                missed_hashes.insert(hash);
1356            }
1357        }
1358
1359        debug!(
1360            "Current frontier after recover from db: {:?}",
1361            self.inner.read().not_ready_blocks_frontier.get_frontier()
1362        );
1363
1364        info!("Finish reconstructing the pivot chain of length {}, start to sync from peers", self.consensus.best_epoch_number());
1365    }
1366
1367    /// Return None if `hash` is not in sync graph
1368    pub fn block_header_by_hash(&self, hash: &H256) -> Option<BlockHeader> {
1369        if !self.contains_block_header(hash) {
1370            // Only return headers in sync graph
1371            return None;
1372        }
1373        self.data_man
1374            .block_header_by_hash(hash)
1375            .map(|header_ref| header_ref.as_ref().clone())
1376    }
1377
1378    /// Return None if `hash` is not in sync graph
1379    pub fn block_height_by_hash(&self, hash: &H256) -> Option<u64> {
1380        self.block_header_by_hash(hash)
1381            .map(|header| header.height())
1382    }
1383
1384    /// Return None if `hash` is not in sync graph
1385    pub fn block_timestamp_by_hash(&self, hash: &H256) -> Option<u64> {
1386        self.block_header_by_hash(hash)
1387            .map(|header| header.timestamp())
1388    }
1389
1390    /// TODO Be more specific about which functions only return in-memory data
1391    /// and which can return the in-database data
1392    pub fn block_by_hash(&self, hash: &H256) -> Option<Arc<Block>> {
1393        self.data_man.block_by_hash(hash, true /* update_cache */)
1394    }
1395
1396    pub fn contains_block_header(&self, hash: &H256) -> bool {
1397        self.inner.read().hash_to_arena_indices.contains_key(hash)
1398            || self.future_blocks.contains(hash)
1399    }
1400
1401    fn parent_or_referees_invalid(&self, header: &BlockHeader) -> bool {
1402        self.data_man.verified_invalid(header.parent_hash()).0
1403            || header
1404                .referee_hashes()
1405                .iter()
1406                .any(|referee| self.data_man.verified_invalid(referee).0)
1407    }
1408
1409    /// subroutine called by `insert_block_header` and `remove_expire_blocks`
1410    fn propagate_header_graph_status(
1411        &self, inner: &mut SynchronizationGraphInner,
1412        frontier_index_list: Vec<usize>, need_to_verify: bool,
1413        header_index_to_insert: usize, insert_to_consensus: bool,
1414        persistent: bool,
1415    ) -> (HashSet<usize>, Vec<H256>) {
1416        let now = SystemTime::now()
1417            .duration_since(UNIX_EPOCH)
1418            .unwrap()
1419            .as_secs();
1420        let mut need_to_relay: Vec<H256> = Vec::new();
1421        let mut invalid_set: HashSet<usize> = HashSet::new();
1422        let mut queue = VecDeque::new();
1423
1424        for index in frontier_index_list {
1425            if inner.arena[index].graph_status == BLOCK_INVALID {
1426                invalid_set.insert(index);
1427            }
1428            queue.push_back(index);
1429        }
1430
1431        while let Some(index) = queue.pop_front() {
1432            if inner.arena[index].graph_status == BLOCK_INVALID {
1433                inner.set_and_propagate_invalid(
1434                    &mut queue,
1435                    &mut invalid_set,
1436                    index,
1437                );
1438            } else if inner.new_to_be_header_graph_ready(index) {
1439                inner.arena[index].graph_status = BLOCK_HEADER_GRAPH_READY;
1440                inner.arena[index].last_update_timestamp = now;
1441                debug!("BlockIndex {} parent_index {} hash {:?} is header graph ready", index,
1442                           inner.arena[index].parent, inner.arena[index].block_header.hash());
1443
1444                let r = inner.verify_header_graph_ready_block(index);
1445
1446                if need_to_verify && r.is_err() {
1447                    warn!(
1448                        "Invalid header_arc! inserted_header={:?} err={:?}",
1449                        inner.arena[index].block_header.clone(),
1450                        r
1451                    );
1452                    invalid_set.insert(index);
1453                    inner.arena[index].graph_status = BLOCK_INVALID;
1454                    inner.set_and_propagate_invalid(
1455                        &mut queue,
1456                        &mut invalid_set,
1457                        index,
1458                    );
1459                    continue;
1460                }
1461
1462                // Maintain `old_era_blocks_frontier` for future garbage
1463                // collection after making a checkpoint.
1464                if inner.arena[index].parent_reclaimed {
1465                    inner.old_era_blocks_frontier.push_back(index);
1466                    inner.old_era_blocks_frontier_set.insert(index);
1467                }
1468
1469                // Note that when called by `insert_block_header` we have to
1470                // insert header here immediately instead of
1471                // after the loop because its children may
1472                // become ready and being processed in the loop later. It
1473                // requires this block already being inserted
1474                // into the BlockDataManager!
1475                if index == header_index_to_insert && persistent {
1476                    self.data_man.insert_block_header(
1477                        inner.arena[index].block_header.hash(),
1478                        inner.arena[index].block_header.clone(),
1479                        true,
1480                    );
1481                }
1482                if insert_to_consensus {
1483                    CONSENSUS_WORKER_QUEUE.enqueue(1);
1484
1485                    self.consensus_unprocessed_count
1486                        .fetch_add(1, Ordering::SeqCst);
1487                    assert!(
1488                        self.new_block_hashes
1489                            .send(inner.arena[index].block_header.hash(),),
1490                        "consensus receiver dropped"
1491                    );
1492
1493                    // maintain not_ready_blocks_frontier
1494                    inner.not_ready_blocks_frontier.remove(&index);
1495                    // The children will be automatically added in
1496                    // `new_to_be_header_graph_ready` if they should be added.
1497                    inner.pos_not_ready_blocks_frontier.remove(&index);
1498                    for child in &inner.arena[index].children {
1499                        inner.not_ready_blocks_frontier.insert(*child);
1500                    }
1501                }
1502
1503                // Passed verification on header_arc.
1504                if inner.arena[index].block_ready {
1505                    need_to_relay.push(inner.arena[index].block_header.hash());
1506                }
1507
1508                for child in &inner.arena[index].children {
1509                    if inner.arena[*child].graph_status
1510                        < BLOCK_HEADER_GRAPH_READY
1511                    {
1512                        queue.push_back(*child);
1513                    }
1514                }
1515                for referrer in &inner.arena[index].referrers {
1516                    if inner.arena[*referrer].graph_status
1517                        < BLOCK_HEADER_GRAPH_READY
1518                    {
1519                        queue.push_back(*referrer);
1520                    }
1521                }
1522            } else {
1523                debug!(
1524                    "BlockIndex {} parent_index {} hash {:?} is not ready",
1525                    index,
1526                    inner.arena[index].parent,
1527                    inner.arena[index].block_header.hash()
1528                );
1529                if index == header_index_to_insert && persistent {
1530                    self.data_man.insert_block_header(
1531                        inner.arena[index].block_header.hash(),
1532                        inner.arena[index].block_header.clone(),
1533                        true,
1534                    );
1535                }
1536            }
1537        }
1538        (invalid_set, need_to_relay)
1539    }
1540
1541    pub fn insert_block_header(
1542        &self, header: &mut BlockHeader, need_to_verify: bool,
1543        bench_mode: bool, insert_to_consensus: bool, persistent: bool,
1544    ) -> (BlockHeaderInsertionResult, Vec<H256>) {
1545        let _timer = MeterTimer::time_func(SYNC_INSERT_HEADER.as_ref());
1546        self.statistics.inc_sync_graph_inserted_header_count();
1547        let inner = &mut *self.inner.write();
1548        if inner.locked_for_catchup {
1549            // Ignore received headers when we are downloading block bodies.
1550            return (BlockHeaderInsertionResult::TemporarySkipped, Vec::new());
1551        }
1552        let hash = header.hash();
1553
1554        let (invalid, local_info_opt) = self.data_man.verified_invalid(&hash);
1555        if invalid {
1556            return (BlockHeaderInsertionResult::Invalid, Vec::new());
1557        }
1558
1559        if let Some(info) = local_info_opt {
1560            // If the block is ordered before current era genesis or it has
1561            // already entered consensus graph in this run, we do not need to
1562            // process it. And in these two cases, the block is considered
1563            // valid.
1564            let already_processed = info.get_seq_num()
1565                < self.consensus.current_era_genesis_seq_num()
1566                || info.get_instance_id() == self.data_man.get_instance_id();
1567            if already_processed {
1568                if need_to_verify && !self.is_consortium() {
1569                    // Compute pow_quality, because the input header may be used
1570                    // as a part of block later
1571                    VerificationConfig::get_or_fill_header_pow_quality(
1572                        &self.pow, header,
1573                    );
1574                }
1575                return (
1576                    BlockHeaderInsertionResult::AlreadyProcessedInConsensus,
1577                    Vec::new(),
1578                );
1579            }
1580        }
1581
1582        if inner.hash_to_arena_indices.contains_key(&hash) {
1583            if need_to_verify {
1584                // Compute pow_quality, because the input header may be used as
1585                // a part of block later
1586                VerificationConfig::get_or_fill_header_pow_quality(
1587                    &self.pow, header,
1588                );
1589            }
1590            return (
1591                BlockHeaderInsertionResult::AlreadyProcessedInSync,
1592                Vec::new(),
1593            );
1594        }
1595
1596        // skip check for consortium currently
1597        debug!("is_consortium={:?}", self.is_consortium());
1598        let verification_passed = if need_to_verify {
1599            self.is_consortium()
1600                || !(self.parent_or_referees_invalid(header)
1601                    || self
1602                        .verification_config
1603                        .verify_header_params(&self.pow, header)
1604                        .or_else(|e| {
1605                            warn!(
1606                                "Invalid header: err={} header={:?}",
1607                                e, header
1608                            );
1609                            Err(e)
1610                        })
1611                        .is_err())
1612        } else {
1613            if !bench_mode && !self.is_consortium() {
1614                self.verification_config
1615                    .verify_pow(&self.pow, header)
1616                    .expect("local mined block should pass this check!");
1617            }
1618            true
1619        };
1620
1621        let header_arc = Arc::new(header.clone());
1622        let me = if verification_passed {
1623            inner.insert(header_arc.clone())
1624        } else {
1625            inner.insert_invalid(header_arc.clone())
1626        };
1627
1628        // Currently, `inner.arena[me].graph_status` will only be
1629        //   1. `BLOCK_GRAPH_READY` for genesis block.
1630        //   2. `BLOCK_HEADER_ONLY` for non genesis block.
1631        //   3. `BLOCK_INVALID` for invalid block.
1632        if inner.arena[me].graph_status != BLOCK_GRAPH_READY {
1633            // This block will become a new `not_ready_blocks_frontier` if
1634            //   1. It's parent block has not inserted yet.
1635            //   2. We are in `Catch Up Blocks Phase` and the graph status of
1636            // parent block is `BLOCK_GRAPH_READY`.
1637            //   3. We are in `Catch Up Headers Phase` and the graph status of
1638            // parent block is `BLOCK_HEADER_GRAPH_READY`.
1639            //   4. The block is not graph ready because of not-ready
1640            // pos_reference, and parent is not in the frontier.
1641            if inner.arena[me].parent == NULL
1642                || inner.arena[inner.arena[me].parent].graph_status
1643                    == BLOCK_GRAPH_READY
1644                || (insert_to_consensus
1645                    && inner.arena[inner.arena[me].parent].graph_status
1646                        == BLOCK_HEADER_GRAPH_READY)
1647            {
1648                inner.not_ready_blocks_frontier.insert(me);
1649            }
1650            let mut to_be_removed = Vec::new();
1651            for child in &inner.arena[me].children {
1652                if inner.not_ready_blocks_frontier.contains(child) {
1653                    to_be_removed.push(*child);
1654                }
1655            }
1656            for x in to_be_removed {
1657                inner.not_ready_blocks_frontier.remove(&x);
1658            }
1659        }
1660
1661        debug!("insert_block_header() Block = {:?}, index = {}, need_to_verify = {}, bench_mode = {} insert_to_consensus = {}",
1662               header.hash(), me, need_to_verify, bench_mode, insert_to_consensus);
1663
1664        // Start to pass influence to descendants
1665        let (invalid_set, need_to_relay) = self.propagate_header_graph_status(
1666            inner,
1667            vec![me],
1668            need_to_verify,
1669            me,
1670            insert_to_consensus,
1671            persistent,
1672        );
1673
1674        let me_invalid = invalid_set.contains(&me);
1675
1676        // Post-processing invalid blocks.
1677        inner.process_invalid_blocks(&invalid_set);
1678
1679        if me_invalid {
1680            return (BlockHeaderInsertionResult::Invalid, need_to_relay);
1681        }
1682
1683        inner.try_clear_old_era_blocks();
1684
1685        (BlockHeaderInsertionResult::NewValid, need_to_relay)
1686    }
1687
1688    pub fn contains_block(&self, hash: &H256) -> bool {
1689        let inner = self.inner.read();
1690        if let Some(index) = inner.hash_to_arena_indices.get(hash) {
1691            inner.arena[*index].block_ready
1692        } else {
1693            false
1694        }
1695    }
1696
1697    fn set_graph_ready(
1698        &self, inner: &mut SynchronizationGraphInner, index: usize,
1699    ) {
1700        inner.arena[index].graph_status = BLOCK_GRAPH_READY;
1701
1702        // maintain not_ready_blocks_frontier
1703        inner.not_ready_blocks_frontier.remove(&index);
1704        // The children will be automatically added in
1705        // `new_to_be_block_graph_ready` if they should be added.
1706        inner.pos_not_ready_blocks_frontier.remove(&index);
1707        for child in &inner.arena[index].children {
1708            inner.not_ready_blocks_frontier.insert(*child);
1709        }
1710
1711        let h = inner.arena[index].block_header.hash();
1712        debug!("Block {:?} is graph ready", h);
1713        CONSENSUS_WORKER_QUEUE.enqueue(1);
1714
1715        self.consensus_unprocessed_count
1716            .fetch_add(1, Ordering::SeqCst);
1717        assert!(self.new_block_hashes.send(h), "consensus receiver dropped");
1718
1719        if inner.config.enable_state_expose {
1720            STATE_EXPOSER.sync_graph.lock().ready_block_vec.push(
1721                SyncGraphBlockState {
1722                    block_hash: h,
1723                    parent: inner.arena[index]
1724                        .block_header
1725                        .parent_hash()
1726                        .clone(),
1727                    referees: inner.arena[index]
1728                        .block_header
1729                        .referee_hashes()
1730                        .clone(),
1731                    nonce: inner.arena[index].block_header.nonce(),
1732                    timestamp: inner.arena[index].block_header.timestamp(),
1733                    adaptive: inner.arena[index].block_header.adaptive(),
1734                },
1735            );
1736        }
1737    }
1738
1739    /// subroutine called by `insert_block` and `remove_expire_blocks`
1740    fn propagate_graph_status(
1741        &self, inner: &mut SynchronizationGraphInner,
1742        frontier_index_list: Vec<usize>,
1743    ) -> HashSet<usize> {
1744        let mut queue = VecDeque::new();
1745        let mut invalid_set = HashSet::new();
1746        for index in frontier_index_list {
1747            if inner.arena[index].graph_status == BLOCK_INVALID {
1748                invalid_set.insert(index);
1749            }
1750            queue.push_back(index);
1751        }
1752
1753        while let Some(index) = queue.pop_front() {
1754            if inner.arena[index].graph_status == BLOCK_INVALID {
1755                inner.set_and_propagate_invalid(
1756                    &mut queue,
1757                    &mut invalid_set,
1758                    index,
1759                );
1760            } else if inner.new_to_be_block_graph_ready(index) {
1761                let verify_result = inner
1762                    .verify_graph_ready_block(index, &self.verification_config);
1763                if verify_result.is_err() {
1764                    warn!(
1765                        "Invalid block! inserted_header={:?} err={:?}",
1766                        inner.arena[index].block_header.clone(),
1767                        verify_result
1768                    );
1769                    invalid_set.insert(index);
1770                    inner.arena[index].graph_status = BLOCK_INVALID;
1771                    inner.set_and_propagate_invalid(
1772                        &mut queue,
1773                        &mut invalid_set,
1774                        index,
1775                    );
1776                    continue;
1777                }
1778                self.set_graph_ready(inner, index);
1779                for child in &inner.arena[index].children {
1780                    debug_assert!(
1781                        inner.arena[*child].graph_status < BLOCK_GRAPH_READY
1782                    );
1783                    queue.push_back(*child);
1784                }
1785                for referrer in &inner.arena[index].referrers {
1786                    debug_assert!(
1787                        inner.arena[*referrer].graph_status < BLOCK_GRAPH_READY
1788                    );
1789                    queue.push_back(*referrer);
1790                }
1791            } else {
1792                trace!("Block index {:?} not block_graph_ready, current frontier: {:?}", index, inner.not_ready_blocks_frontier.get_frontier());
1793            }
1794        }
1795
1796        invalid_set
1797    }
1798
1799    pub fn insert_block(
1800        &self, block: Block, need_to_verify: bool, persistent: bool,
1801        recover_from_db: bool,
1802    ) -> BlockInsertionResult {
1803        let _timer = MeterTimer::time_func(SYNC_INSERT_BLOCK.as_ref());
1804        let hash = block.hash();
1805
1806        debug!("insert_block {:?}", hash);
1807
1808        let inner = &mut *self.inner.write();
1809
1810        let contains_block =
1811            if let Some(index) = inner.hash_to_arena_indices.get(&hash) {
1812                inner.arena[*index].block_ready
1813            } else {
1814                // Sync graph is cleaned after inserting the header, so we can
1815                // ignore the block body
1816                return BlockInsertionResult::Ignored;
1817            };
1818
1819        if contains_block {
1820            return BlockInsertionResult::AlreadyProcessed;
1821        }
1822
1823        // We only insert the body after a valid header is inserted, so this has
1824        // been checked when we insert the header.
1825        debug_assert!(!self.data_man.verified_invalid(&hash).0);
1826
1827        self.statistics.inc_sync_graph_inserted_block_count();
1828
1829        let me = *inner.hash_to_arena_indices.get(&hash).unwrap();
1830
1831        debug_assert!(hash == inner.arena[me].block_header.hash());
1832        debug_assert!(!inner.arena[me].block_ready);
1833        inner.arena[me].block_ready = true;
1834
1835        if need_to_verify {
1836            let r = self.verification_config.verify_sync_graph_block_basic(
1837                &block,
1838                self.consensus.best_chain_id(),
1839            );
1840            match r {
1841                Err(Error::Block(BlockError::InvalidTransactionsRoot(e))) => {
1842                    warn!("BlockTransactionRoot not match! inserted_block={:?} err={:?}", block, e);
1843                    // If the transaction root does not match, it might be
1844                    // caused by receiving wrong
1845                    // transactions because of conflicting ShortId in
1846                    // CompactBlock, or caused by
1847                    // adversaries. In either case, we should request the block
1848                    // again, and the received block body is
1849                    // discarded.
1850                    inner.arena[me].block_ready = false;
1851                    return BlockInsertionResult::RequestAgain;
1852                }
1853                Err(e) => {
1854                    warn!(
1855                        "Invalid block! inserted_block={:?} err={:?}",
1856                        block.block_header, e
1857                    );
1858                    inner.arena[me].graph_status = BLOCK_INVALID;
1859                }
1860                _ => {}
1861            };
1862        }
1863
1864        let block = Arc::new(block);
1865        if inner.arena[me].graph_status != BLOCK_INVALID {
1866            // If we are rebuilding the graph from db, we do not insert all
1867            // blocks into memory
1868            if !recover_from_db {
1869                // Here we always build a new compact block because we should
1870                // not reuse the nonce
1871                self.data_man.insert_compact_block(block.to_compact());
1872                // block header was inserted in before, only insert block body
1873                // here
1874                self.data_man.insert_block_body(
1875                    block.hash(),
1876                    block.clone(),
1877                    persistent,
1878                );
1879            }
1880        }
1881
1882        // If we are locked for catch-up, make sure no new block will enter sync
1883        // graph.
1884        if inner.locked_for_catchup {
1885            if inner.arena[me].graph_status == BLOCK_INVALID {
1886                let invalid_set = self.propagate_graph_status(inner, vec![me]);
1887                // Invalid blocks will also be removed from
1888                // `block_to_fill_set`
1889                // in `process_invalid_blocks`.
1890                inner.process_invalid_blocks(&invalid_set);
1891                return BlockInsertionResult::Invalid;
1892            } else {
1893                debug!("Downloaded block body for {:?}", hash);
1894                inner.block_to_fill_set.remove(&hash);
1895                return BlockInsertionResult::AlreadyProcessed;
1896            }
1897        }
1898
1899        let invalid_set = self.propagate_graph_status(inner, vec![me]);
1900
1901        // Post-processing invalid blocks.
1902        inner.process_invalid_blocks(&invalid_set);
1903
1904        debug!(
1905            "new block inserted into graph: block_header={:?}, tx_count={}, block_size={}",
1906            block.block_header,
1907            block.transactions.len(),
1908            block.size(),
1909        );
1910
1911        // Note: If `me` is invalid, it has been removed from `arena` now,
1912        // so we cannot access its `graph_status`.
1913        if invalid_set.contains(&me) {
1914            BlockInsertionResult::Invalid
1915        } else if inner.arena[me].graph_status >= BLOCK_HEADER_GRAPH_READY {
1916            BlockInsertionResult::ShouldRelay
1917        } else {
1918            BlockInsertionResult::SuccessWithoutRelay
1919        }
1920    }
1921
1922    pub fn get_all_block_hashes_by_epoch(
1923        &self, epoch_number: u64,
1924    ) -> Result<Vec<H256>, ProviderBlockError> {
1925        let mut res = self.consensus.get_skipped_block_hashes_by_epoch(
1926            EpochNumber::Number(epoch_number.into()),
1927        )?;
1928        res.append(&mut self.consensus.get_block_hashes_by_epoch(
1929            EpochNumber::Number(epoch_number.into()),
1930        )?);
1931        Ok(res)
1932    }
1933
1934    pub fn log_statistics(&self) { self.statistics.log_statistics(); }
1935
1936    pub fn update_total_weight_delta_heartbeat(&self) {
1937        self.consensus.update_total_weight_delta_heartbeat();
1938    }
1939
1940    /// Get the current number of blocks in the synchronization graph
1941    /// This only returns cached block count, and this is enough since this is
1942    /// only used in test.
1943    pub fn block_count(&self) -> usize { self.data_man.cached_block_count() }
1944
1945    /// Remove all blocks which have not been updated for a long time. We
1946    /// maintain a set `not_ready_blocks_frontier` which is the root nodes in
1947    /// the parental tree formed by not graph ready blocks. Find all expire
1948    /// blocks which can be reached by `not_ready_blocks_frontier`.
1949    pub fn remove_expire_blocks(&self, expire_time: u64) {
1950        let inner = &mut *self.inner.write();
1951        let now = SystemTime::now()
1952            .duration_since(UNIX_EPOCH)
1953            .unwrap()
1954            .as_secs();
1955        let frontier = inner.not_ready_blocks_frontier.get_frontier().clone();
1956        let all_not_ready: HashSet<_> = inner.get_future(frontier);
1957        let mut expire_set = HashSet::new();
1958        for index in all_not_ready {
1959            if inner.arena[index].last_update_timestamp + expire_time < now {
1960                expire_set.insert(index);
1961            }
1962        }
1963
1964        // find blocks reached by previous found expired blocks
1965        let all_expire: HashSet<_> = inner.get_future(expire_set);
1966        debug!("all_expire: {:?}", all_expire);
1967        inner.remove_blocks(&all_expire);
1968    }
1969
1970    /// Remove all blocks in `to_remove_set` and their future set from the
1971    /// graph.
1972    pub fn remove_blocks_and_future(&self, to_remove_set: &HashSet<H256>) {
1973        let mut inner = self.inner.write();
1974        let mut index_set = Vec::new();
1975        for block_hash in to_remove_set {
1976            if let Some(index) = inner.hash_to_arena_indices.get(block_hash) {
1977                index_set.push(*index);
1978            }
1979        }
1980        let index_set_and_future: HashSet<_> = inner.get_future(index_set);
1981        inner.remove_blocks(&index_set_and_future);
1982    }
1983
1984    pub fn is_consensus_worker_busy(&self) -> bool {
1985        self.consensus_unprocessed_count.load(Ordering::SeqCst) != 0
1986    }
1987
1988    pub fn is_fill_block_completed(&self) -> bool {
1989        self.inner.read().block_to_fill_set.is_empty()
1990    }
1991
1992    /// Construct the states along the pivot chain, set all
1993    /// `BLOCK_HEADER_GRAPH_READY` blocks as `BLOCK_GRAPH_READY` and remove all
1994    /// other blocks. All blocks in the future can be processed normally in
1995    /// sync graph and consensus graph.
1996    ///
1997    /// If some blocks become invalid after validating their bodies, we need to
1998    /// remove them and reconstruct the consensus graph. Return `false` if
1999    /// there are blocks in the new consensus graph whose bodies are missing.
2000    /// Return `true` if we do not need to reconstruct consensus, or all blocks
2001    /// in the new consensus graph already have bodies.
2002    pub fn complete_filling_block_bodies(&self) -> bool {
2003        {
2004            let inner = &mut *self.inner.write();
2005
2006            // Bodies received while locked skipped `propagate_graph_status`,
2007            // so they have not been checked against their parent headers.
2008            let received: Vec<usize> = inner
2009                .hash_to_arena_indices
2010                .values()
2011                .copied()
2012                .filter(|index| {
2013                    inner.arena[*index].graph_status == BLOCK_HEADER_GRAPH_READY
2014                        && inner.arena[*index].block_ready
2015                })
2016                .collect();
2017            let mut invalid_frontier = Vec::new();
2018            for index in received {
2019                if let Err(e) = inner
2020                    .verify_graph_ready_block(index, &self.verification_config)
2021                {
2022                    warn!(
2023                        "Invalid block! inserted_header={:?} err={:?}",
2024                        inner.arena[index].block_header, e
2025                    );
2026                    inner.arena[index].graph_status = BLOCK_INVALID;
2027                    invalid_frontier.push(index);
2028                }
2029            }
2030            let invalid_set =
2031                self.propagate_graph_status(inner, invalid_frontier);
2032            inner.process_invalid_blocks(&invalid_set);
2033
2034            // Iterating over `hash_to_arena_indices` might be more efficient
2035            // than iterating over `arena`.
2036            let to_remove = {
2037                let arena = &mut inner.arena;
2038                inner
2039                    .hash_to_arena_indices
2040                    .iter()
2041                    .filter_map(|(_, index)| {
2042                        let graph_node = &mut arena[*index];
2043                        if graph_node.graph_status == BLOCK_HEADER_GRAPH_READY {
2044                            graph_node.block_ready = true;
2045                            graph_node.graph_status = BLOCK_GRAPH_READY;
2046                        }
2047                        if graph_node.graph_status != BLOCK_GRAPH_READY {
2048                            Some(*index)
2049                        } else {
2050                            None
2051                        }
2052                    })
2053                    .collect()
2054            };
2055            inner.remove_blocks(&to_remove);
2056
2057            // Check if we skip some block bodies. It's either because they are
2058            // never retrieved after a long time, or they have invalid
2059            // bodies. Blocks found invalid above have entered consensus with
2060            // their bodies persisted, so they are not seen as missing.
2061            let skipped_body_blocks =
2062                self.consensus.get_blocks_needing_bodies();
2063            if !skipped_body_blocks.is_empty() || !invalid_set.is_empty() {
2064                warn!("Has invalid blocks after downloading block bodies!");
2065                // Some headers should not enter consensus, so we just
2066                // reconstruct the consensus graph with the
2067                // current sync graph.
2068                self.consensus.reset();
2069
2070                let all_block_indices: HashSet<_> = inner
2071                    .hash_to_arena_indices
2072                    .iter()
2073                    .map(|(_, i)| *i)
2074                    .collect();
2075                // Send blocks in topological order.
2076                let sorted_blocks = inner.topological_sort(all_block_indices);
2077                for i in sorted_blocks {
2078                    self.consensus
2079                        .on_new_block(&inner.arena[i].block_header.hash());
2080                }
2081                let new_to_fill_blocks: HashSet<_> =
2082                    self.consensus.get_blocks_needing_bodies();
2083                if !new_to_fill_blocks.is_empty() {
2084                    // This should not happen if stable checkpoint is not
2085                    // reverted because we have downloaded
2086                    // all blocks in its subtree.
2087                    warn!(
2088                        "{} new block bodies to get",
2089                        new_to_fill_blocks.len()
2090                    );
2091                    inner.block_to_fill_set = new_to_fill_blocks;
2092                    return false;
2093                }
2094            }
2095        }
2096        self.consensus.construct_pivot_state();
2097        self.inner.write().locked_for_catchup = false;
2098        true
2099    }
2100
2101    /// TODO(lpl): Only triggered when pos commits new blocks?
2102    /// Check if not_ready_frontier blocks become ready now.
2103    /// Blocks that are not ready because of missing pos references only become
2104    /// ready here.
2105    pub fn check_not_ready_frontier(&self, header_only: bool) {
2106        debug!("check_not_ready_frontier starts");
2107        let mut inner = self.inner.write();
2108        if inner.locked_for_catchup {
2109            // Do not change sync graph or consensus graph during
2110            // `CatchUpFillBlockBodyPhase`.
2111            return;
2112        }
2113        if header_only {
2114            for b in inner.pos_not_ready_blocks_frontier.clone() {
2115                debug!(
2116                    "check_not_ready_frontier: check {:?}",
2117                    inner.arena[b].block_header.hash()
2118                );
2119                if inner.new_to_be_header_graph_ready(b) {
2120                    self.propagate_header_graph_status(
2121                        &mut *inner,
2122                        vec![b],
2123                        true, /* need_to_verify */
2124                        b,
2125                        true, /* insert_to_consensus */
2126                        true, /* persistent */
2127                    );
2128                }
2129            }
2130        } else {
2131            for b in inner.pos_not_ready_blocks_frontier.clone() {
2132                debug!(
2133                    "check_not_ready_frontier: check {:?}",
2134                    inner.arena[b].block_header.hash()
2135                );
2136                if inner.new_to_be_header_graph_ready(b) {
2137                    self.propagate_header_graph_status(
2138                        &mut *inner,
2139                        vec![b],
2140                        true, /* need_to_verify */
2141                        b,
2142                        false, /* insert_to_consensus */
2143                        true,  /* persistent */
2144                    );
2145                }
2146                // This will not introduce new invalid blocks, so we do not need
2147                // to process the return value.
2148                if inner.new_to_be_block_graph_ready(b) {
2149                    debug!("new graph ready found");
2150                    self.propagate_graph_status(&mut *inner, vec![b]);
2151                }
2152            }
2153        }
2154    }
2155}
2156
2157impl Graph for SynchronizationGraphInner {
2158    type NodeIndex = usize;
2159}
2160
2161impl TreeGraph for SynchronizationGraphInner {
2162    fn parent(&self, node_index: Self::NodeIndex) -> Option<Self::NodeIndex> {
2163        if self.arena[node_index].parent != NULL {
2164            Some(self.arena[node_index].parent)
2165        } else {
2166            None
2167        }
2168    }
2169
2170    fn referees(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex> {
2171        self.arena[node_index].referees.clone()
2172    }
2173}
2174
2175impl RichTreeGraph for SynchronizationGraphInner {
2176    fn children(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex> {
2177        self.arena[node_index].children.clone()
2178    }
2179
2180    fn referrers(&self, node_index: Self::NodeIndex) -> Vec<Self::NodeIndex> {
2181        self.arena[node_index].referrers.clone()
2182    }
2183}
2184
2185pub enum BlockInsertionResult {
2186    // The block is valid and already processed before.
2187    AlreadyProcessed,
2188    // The block is valid and is new to be block-graph-ready.
2189    ShouldRelay,
2190    // The block is valid but not block-graph-ready.
2191    SuccessWithoutRelay,
2192    // The block is definitely invalid. It's not inserted to sync graph
2193    // and should not be requested again.
2194    Invalid,
2195    // The case where transaction root does not match.
2196    // We should request again to get
2197    // the correct transactions for full verification.
2198    RequestAgain,
2199    // This is only for the case the header is removed, possibly because
2200    // we switch phases.
2201    // We ignore the block without verification.
2202    Ignored,
2203}
2204
2205impl BlockInsertionResult {
2206    pub fn is_valid(&self) -> bool {
2207        matches!(
2208            self,
2209            BlockInsertionResult::AlreadyProcessed
2210                | BlockInsertionResult::ShouldRelay
2211                | BlockInsertionResult::SuccessWithoutRelay
2212        )
2213    }
2214
2215    pub fn is_invalid(&self) -> bool {
2216        matches!(self, BlockInsertionResult::Invalid)
2217    }
2218
2219    pub fn should_relay(&self) -> bool {
2220        matches!(self, BlockInsertionResult::ShouldRelay)
2221    }
2222
2223    pub fn request_again(&self) -> bool {
2224        matches!(self, BlockInsertionResult::RequestAgain)
2225    }
2226}
2227
2228pub enum BlockHeaderInsertionResult {
2229    // The block is valid and already processed consensus before.
2230    // We should not process this block again.
2231    AlreadyProcessedInConsensus,
2232    // The block header has been inserted into sync graph. We can ingore this
2233    // header, but we should keep processing its body if needed.
2234    AlreadyProcessedInSync,
2235    // The block is valid and is processed for the first time.
2236    NewValid,
2237    // The block is definitely invalid. It's not inserted to sync graph
2238    // and should not be requested again.
2239    Invalid,
2240    // The header is received when we have locked sync graph.
2241    TemporarySkipped,
2242}
2243
2244impl BlockHeaderInsertionResult {
2245    pub fn is_new_valid(&self) -> bool {
2246        matches!(self, BlockHeaderInsertionResult::NewValid)
2247    }
2248
2249    pub fn is_invalid(&self) -> bool {
2250        matches!(self, BlockHeaderInsertionResult::Invalid)
2251    }
2252
2253    pub fn should_process_body(&self) -> bool {
2254        matches!(
2255            self,
2256            BlockHeaderInsertionResult::NewValid
2257                | BlockHeaderInsertionResult::AlreadyProcessedInSync
2258        )
2259    }
2260}