ckb_verification_contextual/
contextual_block_verifier.rs

1use crate::uncles_verifier::{UncleProvider, UnclesVerifier};
2use ckb_async_runtime::Handle;
3use ckb_chain_spec::{
4    consensus::{Consensus, ConsensusProvider},
5    versionbits::VersionbitsIndexer,
6};
7use ckb_dao::DaoCalculator;
8use ckb_dao_utils::DaoError;
9use ckb_error::{Error, InternalErrorKind};
10use ckb_logger::error_target;
11use ckb_merkle_mountain_range::MMRStore;
12use ckb_reward_calculator::RewardCalculator;
13use ckb_store::{ChainStore, data_loader_wrapper::AsDataLoader};
14use ckb_traits::HeaderProvider;
15use ckb_types::{
16    core::error::OutPointError,
17    core::{
18        BlockReward, BlockView, Capacity, Cycle, EpochExt, HeaderView, TransactionView,
19        cell::{HeaderChecker, ResolvedTransaction},
20    },
21    packed::{Byte32, CellOutput, HeaderDigest, Script},
22    prelude::*,
23    utilities::merkle_mountain_range::ChainRootMMR,
24};
25use ckb_verification::cache::{
26    TxVerificationCache, {CacheEntry, Completed},
27};
28use ckb_verification::{
29    BlockErrorKind, CellbaseError, CommitError, ContextualTransactionVerifier,
30    DaoScriptSizeVerifier, TimeRelativeTransactionVerifier, UnknownParentError,
31};
32use ckb_verification::{BlockTransactionsError, EpochError, TxVerifyEnv};
33use ckb_verification_traits::Switch;
34use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
35use std::collections::{HashMap, HashSet};
36use std::sync::Arc;
37use tokio::sync::{RwLock, oneshot};
38
39/// Context for context-dependent block verification
40pub struct VerifyContext<CS> {
41    pub(crate) store: Arc<CS>,
42    pub(crate) consensus: Arc<Consensus>,
43}
44
45impl<CS> Clone for VerifyContext<CS> {
46    fn clone(&self) -> Self {
47        VerifyContext {
48            store: Arc::clone(&self.store),
49            consensus: Arc::clone(&self.consensus),
50        }
51    }
52}
53
54impl<CS: ChainStore + VersionbitsIndexer> VerifyContext<CS> {
55    /// Create new VerifyContext from `Store` and `Consensus`
56    pub fn new(store: Arc<CS>, consensus: Arc<Consensus>) -> Self {
57        VerifyContext { store, consensus }
58    }
59
60    fn finalize_block_reward(
61        &self,
62        parent: &HeaderView,
63    ) -> Result<(Script, BlockReward), DaoError> {
64        RewardCalculator::new(&self.consensus, self.store.as_ref()).block_reward_to_finalize(parent)
65    }
66}
67
68impl<CS: ChainStore> HeaderProvider for VerifyContext<CS> {
69    fn get_header(&self, hash: &Byte32) -> Option<HeaderView> {
70        self.store.get_block_header(hash)
71    }
72}
73
74impl<CS: ChainStore> HeaderChecker for VerifyContext<CS> {
75    fn check_valid(&self, block_hash: &Byte32) -> Result<(), OutPointError> {
76        if !self.store.is_main_chain(block_hash) {
77            return Err(OutPointError::InvalidHeader(block_hash.clone()));
78        }
79        self.store
80            .get_block_header(block_hash)
81            .ok_or_else(|| OutPointError::InvalidHeader(block_hash.clone()))?;
82        Ok(())
83    }
84}
85
86impl<CS: ChainStore> ConsensusProvider for VerifyContext<CS> {
87    fn get_consensus(&self) -> &Consensus {
88        &self.consensus
89    }
90}
91
92pub struct UncleVerifierContext<'a, 'b, CS> {
93    epoch: &'b EpochExt,
94    context: &'a VerifyContext<CS>,
95}
96
97impl<'a, 'b, CS: ChainStore> UncleVerifierContext<'a, 'b, CS> {
98    pub(crate) fn new(context: &'a VerifyContext<CS>, epoch: &'b EpochExt) -> Self {
99        UncleVerifierContext { epoch, context }
100    }
101}
102
103impl<'a, 'b, CS: ChainStore> UncleProvider for UncleVerifierContext<'a, 'b, CS> {
104    fn double_inclusion(&self, hash: &Byte32) -> bool {
105        self.context.store.get_block_number(hash).is_some() || self.context.store.is_uncle(hash)
106    }
107
108    fn descendant(&self, uncle: &HeaderView) -> bool {
109        let parent_hash = uncle.data().raw().parent_hash();
110        let uncle_number = uncle.number();
111        let store = &self.context.store;
112
113        if store.get_block_number(&parent_hash).is_some() {
114            return store
115                .get_block_header(&parent_hash)
116                .map(|parent| (parent.number() + 1) == uncle_number)
117                .unwrap_or(false);
118        }
119
120        if let Some(uncle_parent) = store.get_uncle_header(&parent_hash) {
121            return (uncle_parent.number() + 1) == uncle_number;
122        }
123
124        false
125    }
126
127    fn epoch(&self) -> &EpochExt {
128        self.epoch
129    }
130
131    fn consensus(&self) -> &Consensus {
132        &self.context.consensus
133    }
134}
135
136pub struct TwoPhaseCommitVerifier<'a, CS> {
137    context: &'a VerifyContext<CS>,
138    block: &'a BlockView,
139}
140
141impl<'a, CS: ChainStore + VersionbitsIndexer> TwoPhaseCommitVerifier<'a, CS> {
142    pub fn new(context: &'a VerifyContext<CS>, block: &'a BlockView) -> Self {
143        TwoPhaseCommitVerifier { context, block }
144    }
145
146    pub fn verify(&self) -> Result<(), Error> {
147        if self.block.is_genesis() {
148            return Ok(());
149        }
150        let block_number = self.block.header().number();
151        let proposal_window = self.context.consensus.tx_proposal_window();
152        let proposal_start = block_number.saturating_sub(proposal_window.farthest());
153        let mut proposal_end = block_number.saturating_sub(proposal_window.closest());
154
155        let mut block_hash = self
156            .context
157            .store
158            .get_block_hash(proposal_end)
159            .ok_or(CommitError::AncestorNotFound)?;
160
161        let mut proposal_txs_ids = HashSet::new();
162
163        while proposal_end >= proposal_start {
164            let header = self
165                .context
166                .store
167                .get_block_header(&block_hash)
168                .ok_or(CommitError::AncestorNotFound)?;
169            if header.is_genesis() {
170                break;
171            }
172
173            if let Some(ids) = self.context.store.get_block_proposal_txs_ids(&block_hash) {
174                proposal_txs_ids.extend(ids);
175            }
176            if let Some(uncles) = self.context.store.get_block_uncles(&block_hash) {
177                uncles
178                    .data()
179                    .into_iter()
180                    .for_each(|uncle| proposal_txs_ids.extend(uncle.proposals()));
181            }
182
183            block_hash = header.data().raw().parent_hash();
184            proposal_end -= 1;
185        }
186
187        let committed_ids: HashSet<_> = self
188            .block
189            .transactions()
190            .iter()
191            .skip(1)
192            .map(TransactionView::proposal_short_id)
193            .collect();
194
195        if committed_ids.difference(&proposal_txs_ids).next().is_some() {
196            error_target!(
197                crate::LOG_TARGET,
198                "BlockView {} {}",
199                self.block.number(),
200                self.block.hash()
201            );
202            error_target!(crate::LOG_TARGET, "proposal_window {:?}", proposal_window);
203            error_target!(crate::LOG_TARGET, "Committed Ids:");
204            for committed_id in committed_ids.iter() {
205                error_target!(crate::LOG_TARGET, "    {:?}", committed_id);
206            }
207            error_target!(crate::LOG_TARGET, "Proposal Txs Ids:");
208            for proposal_txs_id in proposal_txs_ids.iter() {
209                error_target!(crate::LOG_TARGET, "    {:?}", proposal_txs_id);
210            }
211            return Err((CommitError::Invalid).into());
212        }
213        Ok(())
214    }
215}
216
217pub struct RewardVerifier<'a, 'b, CS> {
218    resolved: &'a [Arc<ResolvedTransaction>],
219    parent: &'b HeaderView,
220    context: &'a VerifyContext<CS>,
221}
222
223impl<'a, 'b, CS: ChainStore + VersionbitsIndexer> RewardVerifier<'a, 'b, CS> {
224    pub fn new(
225        context: &'a VerifyContext<CS>,
226        resolved: &'a [Arc<ResolvedTransaction>],
227        parent: &'b HeaderView,
228    ) -> Self {
229        RewardVerifier {
230            parent,
231            context,
232            resolved,
233        }
234    }
235
236    #[allow(clippy::int_plus_one)]
237    pub fn verify(&self) -> Result<(), Error> {
238        let cellbase = &self.resolved[0];
239        let no_finalization_target =
240            (self.parent.number() + 1) <= self.context.consensus.finalization_delay_length();
241
242        let (target_lock, block_reward) = self.context.finalize_block_reward(self.parent)?;
243        let output = CellOutput::new_builder()
244            .capacity(block_reward.total)
245            .lock(target_lock.clone())
246            .build();
247        let insufficient_reward_to_create_cell = output.is_lack_of_capacity(Capacity::zero())?;
248
249        if no_finalization_target || insufficient_reward_to_create_cell {
250            let ret = if cellbase.transaction.outputs().is_empty() {
251                Ok(())
252            } else {
253                Err((CellbaseError::InvalidRewardTarget).into())
254            };
255            return ret;
256        }
257
258        if !insufficient_reward_to_create_cell {
259            if cellbase.transaction.outputs_capacity()? != block_reward.total {
260                return Err((CellbaseError::InvalidRewardAmount).into());
261            }
262            if cellbase
263                .transaction
264                .outputs()
265                .get(0)
266                .expect("cellbase should have output")
267                .lock()
268                != target_lock
269            {
270                return Err((CellbaseError::InvalidRewardTarget).into());
271            }
272        }
273
274        Ok(())
275    }
276}
277
278struct DaoHeaderVerifier<'a, 'b, 'c, CS> {
279    context: &'a VerifyContext<CS>,
280    resolved: &'a [Arc<ResolvedTransaction>],
281    parent: &'b HeaderView,
282    header: &'c HeaderView,
283}
284
285impl<'a, 'b, 'c, CS: ChainStore + VersionbitsIndexer> DaoHeaderVerifier<'a, 'b, 'c, CS> {
286    pub fn new(
287        context: &'a VerifyContext<CS>,
288        resolved: &'a [Arc<ResolvedTransaction>],
289        parent: &'b HeaderView,
290        header: &'c HeaderView,
291    ) -> Self {
292        DaoHeaderVerifier {
293            context,
294            resolved,
295            parent,
296            header,
297        }
298    }
299
300    pub fn verify(&self) -> Result<(), Error> {
301        let dao = DaoCalculator::new(
302            &self.context.consensus,
303            &self.context.store.borrow_as_data_loader(),
304        )
305        .dao_field(self.resolved.iter().map(AsRef::as_ref), self.parent)
306        .map_err(|e| {
307            error_target!(
308                crate::LOG_TARGET,
309                "Error generating dao data for block {}: {:?}",
310                self.header.hash(),
311                e
312            );
313            e
314        })?;
315
316        if dao != self.header.dao() {
317            return Err((BlockErrorKind::InvalidDAO).into());
318        }
319        Ok(())
320    }
321}
322
323struct BlockTxsVerifier<'a, 'b, CS> {
324    context: VerifyContext<CS>,
325    header: HeaderView,
326    handle: &'a Handle,
327    txs_verify_cache: &'a Arc<RwLock<TxVerificationCache>>,
328    parent: &'b HeaderView,
329}
330
331impl<'a, 'b, CS: ChainStore + VersionbitsIndexer + 'static> BlockTxsVerifier<'a, 'b, CS> {
332    pub fn new(
333        context: VerifyContext<CS>,
334        header: HeaderView,
335        handle: &'a Handle,
336        txs_verify_cache: &'a Arc<RwLock<TxVerificationCache>>,
337        parent: &'b HeaderView,
338    ) -> Self {
339        BlockTxsVerifier {
340            context,
341            header,
342            handle,
343            txs_verify_cache,
344            parent,
345        }
346    }
347
348    fn fetched_cache(&self, rtxs: &'a [Arc<ResolvedTransaction>]) -> HashMap<Byte32, CacheEntry> {
349        let (sender, receiver) = oneshot::channel();
350        let txs_verify_cache = Arc::clone(self.txs_verify_cache);
351        let wtx_hashes: Vec<Byte32> = rtxs
352            .iter()
353            .skip(1)
354            .map(|rtx| rtx.transaction.witness_hash())
355            .collect();
356        self.handle.spawn(async move {
357            let guard = txs_verify_cache.read().await;
358            let ret = wtx_hashes
359                .into_iter()
360                .filter_map(|wtx_hash| {
361                    guard
362                        .peek(&wtx_hash)
363                        .cloned()
364                        .map(|value| (wtx_hash, value))
365                })
366                .collect();
367
368            if let Err(e) = sender.send(ret) {
369                error_target!(crate::LOG_TARGET, "TxsVerifier fetched_cache error {:?}", e);
370            };
371        });
372        self.handle
373            .block_on(receiver)
374            .expect("fetched cache no exception")
375    }
376
377    fn update_cache(&self, ret: Vec<(Byte32, Completed)>) {
378        let txs_verify_cache = Arc::clone(self.txs_verify_cache);
379        self.handle.spawn(async move {
380            let mut guard = txs_verify_cache.write().await;
381            for (k, v) in ret {
382                guard.put(k, v);
383            }
384        });
385    }
386
387    pub fn verify(
388        &self,
389        resolved: &'a [Arc<ResolvedTransaction>],
390        skip_script_verify: bool,
391    ) -> Result<(Cycle, Vec<Completed>), Error> {
392        // We should skip updating tx_verify_cache about the cellbase tx,
393        // putting it in cache that will never be used until lru cache expires.
394        let fetched_cache = if resolved.len() > 1 {
395            self.fetched_cache(resolved)
396        } else {
397            HashMap::new()
398        };
399
400        let tx_env = Arc::new(TxVerifyEnv::new_commit(&self.header));
401
402        // make verifiers orthogonal
403        let ret = resolved
404            .par_iter()
405            .enumerate()
406            .map(|(index, tx)| {
407                let wtx_hash = tx.transaction.witness_hash();
408
409                if let Some(completed) = fetched_cache.get(&wtx_hash) {
410                    TimeRelativeTransactionVerifier::new(
411                            Arc::clone(tx),
412                            Arc::clone(&self.context.consensus),
413                            self.context.store.as_data_loader(),
414                            Arc::clone(&tx_env),
415                        )
416                        .verify()
417                        .map_err(|error| {
418                            BlockTransactionsError {
419                                index: index as u32,
420                                error,
421                            }
422                            .into()
423                        })
424                        .map(|_| (wtx_hash, *completed))
425                } else {
426                    ContextualTransactionVerifier::new(
427                        Arc::clone(tx),
428                        Arc::clone(&self.context.consensus),
429                        self.context.store.as_data_loader(),
430                        Arc::clone(&tx_env),
431                    )
432                    .verify(
433                        self.context.consensus.max_block_cycles(),
434                        skip_script_verify,
435                    )
436                    .map_err(|error| {
437                        BlockTransactionsError {
438                            index: index as u32,
439                            error,
440                        }
441                        .into()
442                    })
443                    .map(|completed| (wtx_hash, completed))
444                }.and_then(|result| {
445                    if self.context.consensus.rfc0044_active(self.parent.epoch().number()) {
446                        DaoScriptSizeVerifier::new(
447                            Arc::clone(tx),
448                            Arc::clone(&self.context.consensus),
449                            self.context.store.as_data_loader(),
450                        ).verify()?;
451                    }
452                    Ok(result)
453                })
454            })
455            .skip(1) // skip cellbase tx
456            .collect::<Result<Vec<(Byte32, Completed)>, Error>>()?;
457
458        let sum: Cycle = ret.iter().map(|(_, cache_entry)| cache_entry.cycles).sum();
459        let cache_entires = ret
460            .iter()
461            .map(|(_, completed)| completed)
462            .cloned()
463            .collect();
464        if !ret.is_empty() {
465            self.update_cache(ret);
466        }
467
468        if sum > self.context.consensus.max_block_cycles() {
469            Err(BlockErrorKind::ExceededMaximumCycles.into())
470        } else {
471            Ok((sum, cache_entires))
472        }
473    }
474}
475/// EpochVerifier
476///
477/// Check for block epoch
478pub struct EpochVerifier<'a> {
479    epoch: &'a EpochExt,
480    block: &'a BlockView,
481}
482
483impl<'a> EpochVerifier<'a> {
484    pub fn new(epoch: &'a EpochExt, block: &'a BlockView) -> Self {
485        EpochVerifier { epoch, block }
486    }
487
488    pub fn verify(&self) -> Result<(), Error> {
489        let header = self.block.header();
490        let actual_epoch_with_fraction = header.epoch();
491        let block_number = header.number();
492        let epoch_with_fraction = self.epoch.number_with_fraction(block_number);
493        if actual_epoch_with_fraction != epoch_with_fraction {
494            return Err(EpochError::NumberMismatch {
495                expected: epoch_with_fraction.full_value(),
496                actual: actual_epoch_with_fraction.full_value(),
497            }
498            .into());
499        }
500        let actual_compact_target = header.compact_target();
501        if self.epoch.compact_target() != actual_compact_target {
502            return Err(EpochError::TargetMismatch {
503                expected: self.epoch.compact_target(),
504                actual: actual_compact_target,
505            }
506            .into());
507        }
508        Ok(())
509    }
510}
511
512/// BlockExtensionVerifier.
513///
514/// Check block extension.
515#[derive(Clone)]
516pub struct BlockExtensionVerifier<'a, 'b, CS, MS> {
517    context: &'a VerifyContext<CS>,
518    chain_root_mmr: &'a ChainRootMMR<MS>,
519    parent: &'b HeaderView,
520}
521
522impl<'a, 'b, CS: ChainStore + VersionbitsIndexer, MS: MMRStore<HeaderDigest>>
523    BlockExtensionVerifier<'a, 'b, CS, MS>
524{
525    pub fn new(
526        context: &'a VerifyContext<CS>,
527        chain_root_mmr: &'a ChainRootMMR<MS>,
528        parent: &'b HeaderView,
529    ) -> Self {
530        BlockExtensionVerifier {
531            context,
532            chain_root_mmr,
533            parent,
534        }
535    }
536
537    pub fn verify(&self, block: &BlockView) -> Result<(), Error> {
538        let extra_fields_count = block.data().count_extra_fields();
539
540        let mmr_active = self
541            .context
542            .consensus
543            .rfc0044_active(self.parent.epoch().number());
544        match extra_fields_count {
545            0 => {
546                if mmr_active {
547                    return Err(BlockErrorKind::NoBlockExtension.into());
548                }
549            }
550            1 => {
551                let extension = if let Some(data) = block.extension() {
552                    data
553                } else {
554                    return Err(BlockErrorKind::UnknownFields.into());
555                };
556                if extension.is_empty() {
557                    return Err(BlockErrorKind::EmptyBlockExtension.into());
558                }
559                if extension.len() > 96 {
560                    return Err(BlockErrorKind::ExceededMaximumBlockExtensionBytes.into());
561                }
562                if mmr_active {
563                    if extension.len() < 32 {
564                        return Err(BlockErrorKind::InvalidBlockExtension.into());
565                    }
566
567                    let chain_root = self
568                        .chain_root_mmr
569                        .get_root()
570                        .map_err(|e| InternalErrorKind::MMR.other(e))?;
571                    let actual_root_hash = chain_root.calc_mmr_hash();
572                    let expected_root_hash =
573                        Byte32::new_unchecked(extension.raw_data().slice(..32));
574                    if actual_root_hash != expected_root_hash {
575                        return Err(BlockErrorKind::InvalidChainRoot.into());
576                    }
577                }
578            }
579            _ => {
580                return Err(BlockErrorKind::UnknownFields.into());
581            }
582        }
583
584        let actual_extra_hash = block.calc_extra_hash().extra_hash();
585        if actual_extra_hash != block.extra_hash() {
586            return Err(BlockErrorKind::InvalidExtraHash.into());
587        }
588        Ok(())
589    }
590}
591
592/// Context-dependent verification checks for block
593///
594/// Contains:
595/// - [`EpochVerifier`](./struct.EpochVerifier.html)
596/// - [`UnclesVerifier`](./struct.UnclesVerifier.html)
597/// - [`TwoPhaseCommitVerifier`](./struct.TwoPhaseCommitVerifier.html)
598/// - [`DaoHeaderVerifier`](./struct.DaoHeaderVerifier.html)
599/// - [`RewardVerifier`](./struct.RewardVerifier.html)
600/// - [`BlockTxsVerifier`](./struct.BlockTxsVerifier.html)
601pub struct ContextualBlockVerifier<'a, CS, MS> {
602    context: VerifyContext<CS>,
603    switch: Switch,
604    handle: &'a Handle,
605    txs_verify_cache: Arc<RwLock<TxVerificationCache>>,
606    chain_root_mmr: &'a ChainRootMMR<MS>,
607}
608
609impl<'a, CS: ChainStore + VersionbitsIndexer + 'static, MS: MMRStore<HeaderDigest>>
610    ContextualBlockVerifier<'a, CS, MS>
611{
612    /// Create new ContextualBlockVerifier
613    pub fn new(
614        context: VerifyContext<CS>,
615        handle: &'a Handle,
616        switch: Switch,
617        txs_verify_cache: Arc<RwLock<TxVerificationCache>>,
618        chain_root_mmr: &'a ChainRootMMR<MS>,
619    ) -> Self {
620        ContextualBlockVerifier {
621            context,
622            handle,
623            switch,
624            txs_verify_cache,
625            chain_root_mmr,
626        }
627    }
628
629    /// Perform context-dependent verification checks for block
630    pub fn verify(
631        &'a self,
632        resolved: &'a [Arc<ResolvedTransaction>],
633        block: &'a BlockView,
634    ) -> Result<(Cycle, Vec<Completed>), Error> {
635        let parent_hash = block.data().header().raw().parent_hash();
636        let header = block.header();
637        let parent = self
638            .context
639            .store
640            .get_block_header(&parent_hash)
641            .ok_or_else(|| UnknownParentError {
642                parent_hash: parent_hash.clone(),
643            })?;
644
645        let epoch_ext = if block.is_genesis() {
646            self.context.consensus.genesis_epoch_ext().to_owned()
647        } else {
648            self.context
649                .consensus
650                .next_epoch_ext(&parent, &self.context.store.borrow_as_data_loader())
651                .ok_or_else(|| UnknownParentError {
652                    parent_hash: parent.hash(),
653                })?
654                .epoch()
655        };
656
657        if !self.switch.disable_epoch() {
658            EpochVerifier::new(&epoch_ext, block).verify()?;
659        }
660
661        if !self.switch.disable_uncles() {
662            let uncle_verifier_context = UncleVerifierContext::new(&self.context, &epoch_ext);
663            UnclesVerifier::new(uncle_verifier_context, block).verify()?;
664        }
665
666        if !self.switch.disable_two_phase_commit() {
667            TwoPhaseCommitVerifier::new(&self.context, block).verify()?;
668        }
669
670        if !self.switch.disable_daoheader() {
671            DaoHeaderVerifier::new(&self.context, resolved, &parent, &block.header()).verify()?;
672        }
673
674        if !self.switch.disable_reward() {
675            RewardVerifier::new(&self.context, resolved, &parent).verify()?;
676        }
677
678        if !self.switch.disable_extension() {
679            BlockExtensionVerifier::new(&self.context, self.chain_root_mmr, &parent)
680                .verify(block)?;
681        }
682
683        let ret = BlockTxsVerifier::new(
684            self.context.clone(),
685            header,
686            self.handle,
687            &self.txs_verify_cache,
688            &parent,
689        )
690        .verify(resolved, self.switch.disable_script())?;
691        Ok(ret)
692    }
693}