cfxcore/
verification.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 crate::{
6    core_error::{BlockError, CoreError as Error},
7    pow::{
8        self, nonce_has_non_zero_high_128_bits, nonce_to_lower_bound,
9        PowComputer, ProofOfWorkProblem, MAX_POW_HEIGHT,
10    },
11    sync::Error as SyncError,
12};
13use cfx_executor::{
14    executive::{eip7623_required_gas, gas_required_for},
15    machine::Machine,
16    spec::TransitionsEpochHeight,
17};
18use cfx_parameters::{block::*, consensus_internal::ELASTICITY_MULTIPLIER};
19use cfx_storage::{
20    into_simple_mpt_key, make_simple_mpt, simple_mpt_merkle_root,
21    simple_mpt_proof, SimpleMpt, TrieProof,
22};
23use cfx_types::{
24    address_util::AddressUtil, AllChainID, BigEndianHash, Space, SpaceMap,
25    H256, U256,
26};
27use cfx_vm_types::{ConsensusGasSpec, Spec};
28use primitives::{
29    block::BlockHeight,
30    block_header::compute_next_price_tuple,
31    transaction::{
32        native_transaction::TypedNativeTransaction, TransactionError,
33        EIP1559_TYPE, EIP7702_TYPE, LEGACY_TX_TYPE,
34    },
35    Action, Block, BlockHeader, BlockReceipts, MerkleHash, Receipt,
36    SignedTransaction, Transaction, TransactionWithSignature,
37};
38use rlp::Encodable;
39use rlp_derive::{RlpDecodable, RlpEncodable};
40use serde_derive::{Deserialize, Serialize};
41use std::{collections::HashSet, convert::TryInto, sync::Arc};
42use unexpected::{Mismatch, OutOfBounds};
43
44#[derive(Clone)]
45pub struct VerificationConfig {
46    pub verify_timestamp: bool,
47    pub referee_bound: usize,
48    pub max_block_size_in_bytes: usize,
49    pub transaction_epoch_bound: u64,
50    pub max_nonce: Option<U256>,
51    machine: Arc<Machine>,
52    pos_enable_height: u64,
53    /// Consensus already requires block difficulty to be no less than
54    /// `pow_config.initial_difficulty`: see the difficulty check in
55    /// [`SynchronizationGraphInner::verify_header_graph_ready_block`].
56    ///
57    /// [`SynchronizationGraphInner::verify_header_graph_ready_block`]: crate::sync::SynchronizationGraphInner::verify_header_graph_ready_block
58    min_difficulty: U256,
59}
60
61/// Create an MPT from the ordered list of block transactions.
62/// Keys are transaction indices, values are transaction hashes.
63fn transaction_trie(transactions: &Vec<Arc<SignedTransaction>>) -> SimpleMpt {
64    make_simple_mpt(
65        transactions
66            .iter()
67            .map(|tx| tx.hash.as_bytes().into())
68            .collect(),
69    )
70}
71
72/// Compute block transaction root.
73/// This value is stored in the `transactions_root` header field.
74pub fn compute_transaction_root(
75    transactions: &Vec<Arc<SignedTransaction>>,
76) -> MerkleHash {
77    simple_mpt_merkle_root(&mut transaction_trie(transactions))
78}
79
80/// Compute a proof for the `tx_index_in_block`-th transaction in a block.
81pub fn compute_transaction_proof(
82    transactions: &Vec<Arc<SignedTransaction>>, tx_index_in_block: usize,
83) -> TrieProof {
84    simple_mpt_proof(
85        &mut transaction_trie(transactions),
86        &into_simple_mpt_key(tx_index_in_block, transactions.len()),
87    )
88}
89
90/// Create an MPT from the ordered list of block receipts.
91/// Keys are receipt indices, values are RLP serialized receipts.
92fn block_receipts_trie(block_receipts: &Vec<Receipt>) -> SimpleMpt {
93    make_simple_mpt(
94        block_receipts
95            .iter()
96            .map(|receipt| receipt.rlp_bytes().to_vec().into_boxed_slice())
97            .collect(),
98    )
99}
100
101/// Compute block receipts root.
102fn compute_block_receipts_root(block_receipts: &Vec<Receipt>) -> MerkleHash {
103    simple_mpt_merkle_root(&mut block_receipts_trie(block_receipts))
104}
105
106/// Compute a proof for the `tx_index_in_block`-th receipt in a block.
107pub fn compute_block_receipt_proof(
108    block_receipts: &Vec<Receipt>, tx_index_in_block: usize,
109) -> TrieProof {
110    simple_mpt_proof(
111        &mut block_receipts_trie(block_receipts),
112        &into_simple_mpt_key(tx_index_in_block, block_receipts.len()),
113    )
114}
115
116/// Create an MPT from the ordered list of epoch receipts.
117/// Keys are block indices in the epoch, values are block receipts roots.
118fn epoch_receipts_trie(epoch_receipts: &Vec<Arc<BlockReceipts>>) -> SimpleMpt {
119    make_simple_mpt(
120        epoch_receipts
121            .iter()
122            .map(|block_receipts| &block_receipts.receipts)
123            .map(|rs| compute_block_receipts_root(&rs).as_bytes().into())
124            .collect(),
125    )
126}
127
128/// Compute epoch receipts root.
129/// This value is stored in the `deferred_receipts_root` header field.
130pub fn compute_receipts_root(
131    epoch_receipts: &Vec<Arc<BlockReceipts>>,
132) -> MerkleHash {
133    simple_mpt_merkle_root(&mut epoch_receipts_trie(epoch_receipts))
134}
135
136#[derive(
137    Clone,
138    Debug,
139    RlpEncodable,
140    RlpDecodable,
141    Default,
142    PartialEq,
143    Serialize,
144    Deserialize,
145)]
146#[serde(rename_all = "camelCase")]
147pub struct EpochReceiptProof {
148    pub block_index_proof: TrieProof,
149    pub block_receipt_proof: TrieProof,
150}
151
152/// Compute a proof for the `tx_index_in_block`-th receipt
153/// in the `block_index_in_epoch`-th block in an epoch.
154pub fn compute_epoch_receipt_proof(
155    epoch_receipts: &Vec<Arc<BlockReceipts>>, block_index_in_epoch: usize,
156    tx_index_in_block: usize,
157) -> EpochReceiptProof {
158    let block_receipt_proof = compute_block_receipt_proof(
159        &epoch_receipts[block_index_in_epoch].receipts,
160        tx_index_in_block,
161    );
162
163    let block_index_proof = simple_mpt_proof(
164        &mut epoch_receipts_trie(epoch_receipts),
165        &into_simple_mpt_key(block_index_in_epoch, epoch_receipts.len()),
166    );
167
168    EpochReceiptProof {
169        block_index_proof,
170        block_receipt_proof,
171    }
172}
173
174/// Use `proof` to verify that `tx_hash` is indeed the `tx_index_in_block`-th
175/// transaction in a block with `num_txs_in_block` transactions and transaction
176/// root `block_tx_root`.
177pub fn is_valid_tx_inclusion_proof(
178    block_tx_root: MerkleHash, tx_index_in_block: usize,
179    num_txs_in_block: usize, tx_hash: H256, proof: &TrieProof,
180) -> bool {
181    let key = &into_simple_mpt_key(tx_index_in_block, num_txs_in_block);
182    proof.is_valid_kv(key, Some(tx_hash.as_bytes()), &block_tx_root)
183}
184
185/// Use `block_index_proof` to get the correct block receipts trie root for the
186/// `block_index_in_epoch`-th block in an epoch with `num_blocks_in_epoch`
187/// blocks and receipts root `verified_epoch_receipts_root`.
188/// Then, use `block_receipt_proof` to verify that `receipt` is indeed the
189/// `tx_index_in_block`-th receipt in a block with `num_txs_in_block`
190/// transactions and the transaction root from the previous step.
191pub fn is_valid_receipt_inclusion_proof(
192    verified_epoch_receipts_root: MerkleHash, block_index_in_epoch: usize,
193    num_blocks_in_epoch: usize, block_index_proof: &TrieProof,
194    tx_index_in_block: usize, num_txs_in_block: usize, receipt: &Receipt,
195    block_receipt_proof: &TrieProof,
196) -> bool {
197    // get block receipts root from block index trie (proof)
198    // traversing along `key` also means we're validating the proof
199    let key = &into_simple_mpt_key(block_index_in_epoch, num_blocks_in_epoch);
200
201    let block_receipts_root_bytes =
202        match block_index_proof.get_value(key, &verified_epoch_receipts_root) {
203            (false, _) => return false,
204            (true, None) => return false,
205            (true, Some(val)) => val,
206        };
207
208    // parse block receipts root as H256
209    let block_receipts_root: H256 = match TryInto::<[u8; 32]>::try_into(
210        block_receipts_root_bytes,
211    ) {
212        Ok(hash) => hash.into(),
213        Err(e) => {
214            // this should not happen
215            error!(
216                "Invalid content found in valid MPT: key = {:?}, value = {:?}; error = {:?}",
217                key, block_receipts_root_bytes, e,
218            );
219            return false;
220        }
221    };
222
223    // validate receipt in the block receipts trie
224    let key = &into_simple_mpt_key(tx_index_in_block, num_txs_in_block);
225
226    block_receipt_proof.is_valid_kv(
227        key,
228        Some(&receipt.rlp_bytes()[..]),
229        &block_receipts_root,
230    )
231}
232
233impl VerificationConfig {
234    pub fn new(
235        test_mode: bool, referee_bound: usize, max_block_size_in_bytes: usize,
236        transaction_epoch_bound: u64, tx_pool_nonce_bits: usize,
237        pos_enable_height: u64, initial_difficulty: u64, machine: Arc<Machine>,
238    ) -> Self {
239        let max_nonce = if tx_pool_nonce_bits < 256 {
240            Some((U256::one() << tx_pool_nonce_bits) - 1)
241        } else {
242            None
243        };
244        VerificationConfig {
245            verify_timestamp: !test_mode,
246            referee_bound,
247            max_block_size_in_bytes,
248            transaction_epoch_bound,
249            machine,
250            pos_enable_height,
251            max_nonce,
252            // Difficulty 0 has no boundary; guard it even if misconfigured.
253            min_difficulty: initial_difficulty.max(1).into(),
254        }
255    }
256
257    #[inline]
258    /// Note that this function returns *pow_hash* of the block, not its quality
259    pub fn get_or_fill_header_pow_hash(
260        pow: &PowComputer, header: &mut BlockHeader,
261    ) -> H256 {
262        if header.pow_hash.is_none() {
263            header.pow_hash = Some(Self::compute_pow_hash(pow, header));
264        }
265        header.pow_hash.unwrap()
266    }
267
268    pub fn get_or_fill_header_pow_quality(
269        pow: &PowComputer, header: &mut BlockHeader,
270    ) -> U256 {
271        let pow_hash = Self::get_or_fill_header_pow_hash(pow, header);
272        pow::pow_hash_to_quality(&pow_hash, &header.nonce())
273    }
274
275    pub fn get_or_compute_header_pow_quality(
276        pow: &PowComputer, header: &BlockHeader,
277    ) -> U256 {
278        let pow_hash = header
279            .pow_hash
280            .unwrap_or_else(|| Self::compute_pow_hash(pow, header));
281        pow::pow_hash_to_quality(&pow_hash, &header.nonce())
282    }
283
284    fn compute_pow_hash(pow: &PowComputer, header: &BlockHeader) -> H256 {
285        let nonce = header.nonce();
286        pow.compute(&nonce, &header.problem_hash(), header.height())
287    }
288
289    #[inline]
290    pub fn verify_pow(
291        &self, pow: &PowComputer, header: &mut BlockHeader,
292    ) -> Result<(), Error> {
293        // Both checks precede the hash computation, which builds a cache
294        // sized by the height. The difficulty check is an existing consensus
295        // rule (see `min_difficulty`) applied early so that a cheap forgery
296        // is not persisted.
297        if header.height() > MAX_POW_HEIGHT {
298            return Err(BlockError::InvalidHeight(Mismatch {
299                expected: MAX_POW_HEIGHT,
300                found: header.height(),
301            })
302            .into());
303        }
304        if *header.difficulty() < self.min_difficulty {
305            return Err(BlockError::InvalidDifficulty(OutOfBounds {
306                min: Some(self.min_difficulty),
307                max: None,
308                found: *header.difficulty(),
309            })
310            .into());
311        }
312        if header.height()
313            >= self.machine.params().transition_heights.cip_hn_fix
314        {
315            if nonce_has_non_zero_high_128_bits(&header.nonce()) {
316                return Err(From::from(BlockError::InvalidNonce));
317            }
318        }
319        let pow_hash = Self::get_or_fill_header_pow_hash(pow, header);
320        let boundary = pow::difficulty_to_boundary(header.difficulty());
321        if !ProofOfWorkProblem::validate_hash_against_boundary(
322            &pow_hash,
323            &header.nonce(),
324            &boundary,
325        ) {
326            let lower_bound = nonce_to_lower_bound(&header.nonce());
327            // Because the lower_bound first bit is always zero, as long as the
328            // difficulty is not 1, this should not overflow.
329            // We just use overflowing_add() here to be safe.
330            let (upper_bound, _) = lower_bound.overflowing_add(boundary);
331            warn!("block {} has invalid proof of work. boundary: [{}, {}), pow_hash: {}",
332                  header.hash(), lower_bound.clone(), upper_bound.clone(), pow_hash.clone());
333            return Err(From::from(BlockError::InvalidProofOfWork(
334                OutOfBounds {
335                    min: Some(BigEndianHash::from_uint(&lower_bound)),
336                    max: Some(BigEndianHash::from_uint(&upper_bound)),
337                    found: pow_hash,
338                },
339            )));
340        }
341
342        assert!(
343            Self::get_or_fill_header_pow_quality(pow, header)
344                >= *header.difficulty()
345        );
346
347        Ok(())
348    }
349
350    #[inline]
351    pub fn validate_header_timestamp(
352        &self, header: &BlockHeader, now: u64,
353    ) -> Result<(), SyncError> {
354        let invalid_threshold = now + VALID_TIME_DRIFT;
355        if header.timestamp() > invalid_threshold {
356            warn!("block {} has incorrect timestamp", header.hash());
357            return Err(SyncError::InvalidTimestamp.into());
358        }
359        Ok(())
360    }
361
362    fn is_pos_enabled_at_height(&self, height: u64) -> bool {
363        height >= self.pos_enable_height
364    }
365
366    /// Check basic header parameters.
367    /// This does not require header to be graph or parental tree ready.
368    #[inline]
369    pub fn verify_header_params(
370        &self, pow: &PowComputer, header: &mut BlockHeader,
371    ) -> Result<(), Error> {
372        // Check header custom data length
373        let custom_len = header.custom_data_len();
374        if custom_len > HEADER_CUSTOM_LENGTH_BOUND {
375            return Err(From::from(BlockError::TooLongCustomInHeader(
376                OutOfBounds {
377                    min: Some(0),
378                    max: Some(HEADER_CUSTOM_LENGTH_BOUND),
379                    found: custom_len,
380                },
381            )));
382        }
383
384        if self.is_pos_enabled_at_height(header.height()) {
385            if header.pos_reference().is_none() {
386                bail!(BlockError::MissingPosReference);
387            }
388        } else {
389            if header.pos_reference().is_some() {
390                bail!(BlockError::UnexpectedPosReference);
391            }
392        }
393
394        if header.height() >= self.machine.params().transition_heights.cip1559 {
395            if header.base_price().is_none() {
396                bail!(BlockError::MissingBaseFee);
397            }
398        } else {
399            if header.base_price().is_some() {
400                bail!(BlockError::UnexpectedBaseFee);
401            }
402        }
403
404        // Note that this is just used to rule out deprecated blocks, so the
405        // change of header struct actually happens before the change of
406        // reward is reflected in the state root. The first state root
407        // including results of new rewards will in the header after another
408        // REWARD_EPOCH_COUNT + DEFERRED_STATE_EPOCH_COUNT epochs.
409        if let Some(expected_custom_prefix) =
410            self.machine.params().custom_prefix(header.height())
411        {
412            for (i, expected_bytes) in expected_custom_prefix.iter().enumerate()
413            {
414                // `None` => header custom too short; else prefix mismatch.
415                let matches =
416                    header.custom_item(i).is_some_and(|b| &b == expected_bytes);
417                if !matches {
418                    // Bound the error to the compared prefix; the header may
419                    // carry a huge number of items.
420                    let header_prefix = (0..expected_custom_prefix.len())
421                        .filter_map(|j| header.custom_item(j))
422                        .collect();
423                    return Err(BlockError::InvalidCustom(
424                        header_prefix,
425                        expected_custom_prefix.clone(),
426                    )
427                    .into());
428                }
429            }
430        }
431
432        // verify POW
433        self.verify_pow(pow, header)?;
434
435        // A block will be invalid if it has more than REFEREE_BOUND referees
436        if header.referee_hashes().len() > self.referee_bound {
437            return Err(From::from(BlockError::TooManyReferees(OutOfBounds {
438                min: Some(0),
439                max: Some(self.referee_bound),
440                found: header.referee_hashes().len(),
441            })));
442        }
443
444        // verify non-duplicated parent and referee hashes
445        let mut direct_ancestor_hashes = HashSet::new();
446        let parent_hash = header.parent_hash();
447        direct_ancestor_hashes.insert(parent_hash.clone());
448        for referee_hash in header.referee_hashes() {
449            if direct_ancestor_hashes.contains(referee_hash) {
450                warn!(
451                    "block {} has duplicate parent or referee hashes",
452                    header.hash()
453                );
454                return Err(From::from(
455                    BlockError::DuplicateParentOrRefereeHashes(
456                        referee_hash.clone(),
457                    ),
458                ));
459            }
460            direct_ancestor_hashes.insert(referee_hash.clone());
461        }
462
463        Ok(())
464    }
465
466    /// Verify block data against header: transactions root
467    #[inline]
468    fn verify_block_integrity(&self, block: &Block) -> Result<(), Error> {
469        let expected_root = compute_transaction_root(&block.transactions);
470        if &expected_root != block.block_header.transactions_root() {
471            warn!("Invalid transaction root");
472            bail!(BlockError::InvalidTransactionsRoot(Mismatch {
473                expected: expected_root,
474                found: *block.block_header.transactions_root(),
475            }));
476        }
477        Ok(())
478    }
479
480    /// Phase 1 quick block verification. Only does checks that are cheap.
481    /// Operates on a single block.
482    /// Note that we should first check whether the block body matches its
483    /// header, e.g., check transaction root correctness, and then check the
484    /// body itself. If body does not match header, the block may still be
485    /// valid but we get the wrong body from evil node. So we try to get
486    /// body again from others. However, if the body matches the header and
487    /// the body is incorrect, this means the block is invalid, and we
488    /// should discard this block and all its descendants.
489    #[inline]
490    pub fn verify_sync_graph_block_basic(
491        &self, block: &Block, chain_id: AllChainID,
492    ) -> Result<(), Error> {
493        self.verify_block_integrity(block)?;
494
495        let block_height = block.block_header.height();
496
497        let mut block_size = 0;
498        let transitions = &self.machine.params().transition_heights;
499        let consensus_spec =
500            &self.machine.params().consensus_spec(block_height);
501
502        for t in &block.transactions {
503            self.verify_transaction_common(
504                t,
505                chain_id,
506                block_height,
507                transitions,
508                VerifyTxMode::Remote(consensus_spec),
509            )?;
510            block_size += t.rlp_size();
511        }
512
513        if block_size > self.max_block_size_in_bytes {
514            return Err(From::from(BlockError::InvalidBlockSize(
515                OutOfBounds {
516                    min: None,
517                    max: Some(self.max_block_size_in_bytes as u64),
518                    found: block_size as u64,
519                },
520            )));
521        }
522        Ok(())
523    }
524
525    pub fn verify_sync_graph_ready_block(
526        &self, block: &Block, parent: &BlockHeader,
527    ) -> Result<(), Error> {
528        let mut total_gas: SpaceMap<U256> = SpaceMap::default();
529        for t in &block.transactions {
530            // gas_limit is unbounded on this path, so the per-space sum can
531            // exceed U256; a bare `+=` would panic instead of rejecting.
532            let acc = &mut total_gas[t.space()];
533            *acc = acc.checked_add(*t.gas_limit()).ok_or_else(|| {
534                BlockError::InvalidPackedGasLimit(OutOfBounds {
535                    min: None,
536                    max: Some(*block.block_header.gas_limit()),
537                    found: U256::MAX,
538                })
539            })?;
540        }
541
542        if block.block_header.height()
543            >= self.machine.params().transition_heights.cip1559
544        {
545            self.check_base_fee(block, parent, total_gas)?;
546        } else {
547            self.check_hard_gas_limit(block, total_gas)?;
548        }
549        Ok(())
550    }
551
552    fn check_hard_gas_limit(
553        &self, block: &Block, total_gas: SpaceMap<U256>,
554    ) -> Result<(), Error> {
555        let block_height = block.block_header.height();
556
557        let evm_space_gas_limit =
558            if self.machine.params().can_pack_evm_transaction(block_height) {
559                *block.block_header.gas_limit()
560                    / self.machine.params().evm_transaction_gas_ratio
561            } else {
562                U256::zero()
563            };
564
565        let evm_total_gas = total_gas[Space::Ethereum];
566        // native + evm can exceed U256 even when each fits; avoid a
567        // panicking sum.
568        let block_total_gas = total_gas[Space::Native]
569            .checked_add(total_gas[Space::Ethereum])
570            .ok_or_else(|| {
571                BlockError::InvalidPackedGasLimit(OutOfBounds {
572                    min: None,
573                    max: Some(*block.block_header.gas_limit()),
574                    found: U256::MAX,
575                })
576            })?;
577
578        if evm_total_gas > evm_space_gas_limit {
579            return Err(From::from(BlockError::InvalidPackedGasLimit(
580                OutOfBounds {
581                    min: None,
582                    max: Some(evm_space_gas_limit),
583                    found: evm_total_gas,
584                },
585            )));
586        }
587
588        if block_total_gas > *block.block_header.gas_limit() {
589            return Err(From::from(BlockError::InvalidPackedGasLimit(
590                OutOfBounds {
591                    min: None,
592                    max: Some(*block.block_header.gas_limit()),
593                    found: block_total_gas,
594                },
595            )));
596        }
597
598        Ok(())
599    }
600
601    fn check_base_fee(
602        &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap<U256>,
603    ) -> Result<(), Error> {
604        use Space::*;
605
606        let params = self.machine.params();
607        let cip1559_init = params.transition_heights.cip1559;
608        let block_height = block.block_header.height();
609
610        assert!(block_height >= cip1559_init);
611
612        let core_gas_limit = block.block_header.core_space_gas_limit();
613        let espace_gas_limit = block
614            .block_header
615            .espace_gas_limit(params.can_pack_evm_transaction(block_height));
616
617        if total_gas[Ethereum] > espace_gas_limit {
618            return Err(From::from(BlockError::InvalidPackedGasLimit(
619                OutOfBounds {
620                    min: None,
621                    max: Some(espace_gas_limit),
622                    found: total_gas[Ethereum],
623                },
624            )));
625        }
626
627        if total_gas[Native] > core_gas_limit {
628            return Err(From::from(BlockError::InvalidPackedGasLimit(
629                OutOfBounds {
630                    min: None,
631                    max: Some(core_gas_limit),
632                    found: total_gas[Native],
633                },
634            )));
635        }
636
637        let parent_base_price = if block_height == cip1559_init {
638            params.init_base_price()
639        } else {
640            parent.base_price().unwrap()
641        };
642
643        let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit);
644        let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER);
645        let min_base_price = params.min_base_price();
646
647        let expected_base_price = SpaceMap::zip4(
648            gas_target,
649            total_gas,
650            parent_base_price,
651            min_base_price,
652        )
653        .map_all(compute_next_price_tuple);
654
655        let actual_base_price = block.block_header.base_price().unwrap();
656
657        if actual_base_price != expected_base_price {
658            return Err(From::from(BlockError::InvalidBasePrice(Mismatch {
659                expected: expected_base_price,
660                found: actual_base_price,
661            })));
662        }
663
664        Ok(())
665    }
666
667    pub fn check_transaction_epoch_bound(
668        tx: &TypedNativeTransaction, block_height: u64,
669        transaction_epoch_bound: u64,
670    ) -> i8 {
671        if tx.epoch_height().wrapping_add(transaction_epoch_bound)
672            < block_height
673        {
674            -1
675        } else if *tx.epoch_height() > block_height + transaction_epoch_bound {
676            1
677        } else {
678            0
679        }
680    }
681
682    fn verify_transaction_epoch_height(
683        tx: &TypedNativeTransaction, block_height: u64,
684        transaction_epoch_bound: u64, mode: &VerifyTxMode,
685    ) -> Result<(), TransactionError> {
686        let result = Self::check_transaction_epoch_bound(
687            tx,
688            block_height,
689            transaction_epoch_bound,
690        );
691        let allow_larger_epoch = mode.is_maybe_later();
692
693        if result == 0 || (result > 0 && allow_larger_epoch) {
694            Ok(())
695        } else {
696            bail!(TransactionError::EpochHeightOutOfBound {
697                set: *tx.epoch_height(),
698                block_height,
699                transaction_epoch_bound,
700            });
701        }
702    }
703
704    fn fast_recheck_inner<F>(spec: &Spec, f: F) -> (bool, bool)
705    where F: Fn(&VerifyTxMode) -> bool {
706        let tx_pool_mode =
707            VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, spec);
708        let packing_mode = VerifyTxMode::Local(VerifyTxLocalMode::Full, spec);
709
710        (f(&packing_mode), f(&tx_pool_mode))
711    }
712
713    pub fn fast_recheck(
714        &self, tx: &TransactionWithSignature, height: BlockHeight,
715        transitions: &TransitionsEpochHeight, spec: &Spec,
716    ) -> PackingCheckResult {
717        let cip90a = height >= transitions.cip90a;
718        let cip1559 = height >= transitions.cip1559;
719        let cip7702 = height >= transitions.cip7702;
720        let cip645 = height >= transitions.cip645;
721
722        let (can_pack, later_pack) = Self::fast_recheck_inner(
723            spec,
724            |mode: &VerifyTxMode| {
725                if !Self::check_eip1559_transaction(tx, cip1559, mode) {
726                    trace!(
727                        "fast_recheck: EIP-1559 transaction check failed at height {} txhash={:?}",
728                        height,
729                        tx.hash()
730                    );
731                    return false;
732                }
733
734                if !Self::check_eip7702_transaction(tx, cip7702, mode) {
735                    trace!(
736                        "fast_recheck: EIP-7702 transaction check failed at height {} txhash={:?}",
737                        height,
738                        tx.hash()
739                    );
740                    return false;
741                }
742
743                if !Self::check_eip3860(tx, cip645) {
744                    trace!(
745                        "fast_recheck: EIP-3860 transaction check failed at height {} txhash={:?}",
746                        height,
747                        tx.hash()
748                    );
749                    return false;
750                }
751
752                if let Transaction::Native(ref tx) = tx.unsigned {
753                    Self::verify_transaction_epoch_height(
754                        tx,
755                        height,
756                        self.transaction_epoch_bound,
757                        mode,
758                    )
759                    .is_ok()
760                } else {
761                    Self::check_eip155_transaction(tx, cip90a, mode)
762                }
763            },
764        );
765
766        match (can_pack, later_pack) {
767            (true, _) => PackingCheckResult::Pack,
768            (false, true) => PackingCheckResult::Pending,
769            (false, false) => PackingCheckResult::Drop,
770        }
771    }
772
773    // Packing transactions, verifying transaction in sync graph and inserting
774    // transactions may have different logics. But they share a lot of similar
775    // rules. We combine them together for convenient in the future upgrades..
776    pub fn verify_transaction_common(
777        &self, tx: &TransactionWithSignature, chain_id: AllChainID,
778        height: BlockHeight, transitions: &TransitionsEpochHeight,
779        mode: VerifyTxMode,
780    ) -> Result<(), TransactionError> {
781        tx.check_low_s()?;
782        tx.check_y_parity()?;
783
784        // Disallow unsigned transactions
785        if tx.is_unsigned() {
786            bail!(TransactionError::InvalidSignature(
787                "Transaction is unsigned".into()
788            ));
789        }
790
791        if let Some(tx_chain_id) = tx.chain_id() {
792            if tx_chain_id != chain_id.in_space(tx.space()) {
793                bail!(TransactionError::ChainIdMismatch {
794                    expected: chain_id.in_space(tx.space()),
795                    got: tx_chain_id,
796                    space: tx.space(),
797                });
798            }
799        }
800
801        // Forbid zero-gas-price tx
802        if tx.gas_price().is_zero() {
803            bail!(TransactionError::ZeroGasPrice);
804        }
805
806        if matches!(mode, VerifyTxMode::Local(..))
807            && tx.space() == Space::Native
808        {
809            if let Action::Call(ref address) = tx.transaction.action() {
810                if !address.is_genesis_valid_address() {
811                    bail!(TransactionError::InvalidReceiver)
812                }
813            }
814        }
815
816        if let (VerifyTxMode::Local(..), Some(max_nonce)) =
817            (mode, self.max_nonce)
818        {
819            if tx.nonce() > &max_nonce {
820                bail!(TransactionError::TooLargeNonce)
821            }
822        }
823
824        // ******************************************
825        // Each constraint depends on a mode or a CIP should be
826        // implemented in a seperated function.
827        // ******************************************
828        let cip76 = height >= transitions.cip76;
829        let cip90a = height >= transitions.cip90a;
830        let cip130 =
831            height >= transitions.cip130 && height < transitions.align_evm;
832        let cip1559 = height >= transitions.cip1559;
833        let cip7702 = height >= transitions.cip7702;
834        let cip645 = height >= transitions.cip645;
835        let eip7623 = height >= transitions.eip7623;
836        let cip172 = height >= transitions.cip172;
837
838        if let Transaction::Native(ref tx) = tx.unsigned {
839            Self::verify_transaction_epoch_height(
840                tx,
841                height,
842                self.transaction_epoch_bound,
843                &mode,
844            )?;
845        }
846
847        if !Self::check_eip155_transaction(tx, cip90a, &mode) {
848            bail!(TransactionError::FutureTransactionType {
849                tx_type: LEGACY_TX_TYPE,
850                current_height: height,
851                enable_height: transitions.cip90a,
852            });
853        }
854
855        if !Self::check_eip1559_transaction(tx, cip1559, &mode) {
856            bail!(TransactionError::FutureTransactionType {
857                tx_type: EIP1559_TYPE,
858                current_height: height,
859                enable_height: transitions.cip1559,
860            })
861        }
862
863        if !Self::check_eip7702_transaction(tx, cip7702, &mode) {
864            bail!(TransactionError::FutureTransactionType {
865                tx_type: EIP7702_TYPE,
866                current_height: height,
867                enable_height: transitions.cip7702,
868            })
869        }
870
871        if !Self::check_eip3860(tx, cip645) {
872            bail!(TransactionError::CreateInitCodeSizeLimit)
873        }
874
875        Self::check_eip1559_validation(tx, cip645)?;
876        Self::check_eip7702_validation(tx)?;
877
878        Self::check_gas_limit(tx, cip76, eip7623, &mode)?;
879        Self::check_gas_limit_with_calldata(tx, cip130)?;
880        Self::check_canonical_rlp(tx, cip172, &mode)?;
881
882        Ok(())
883    }
884
885    /// A non-canonical encoding hashes to a value the canonical re-encoding
886    /// won't reproduce, so packing one orphans the block. Local (mempool /
887    /// packing / RPC) rejects it outright; as a block-validity rule this is a
888    /// consensus change, so Remote only rejects at/after the gate height.
889    fn check_canonical_rlp(
890        tx: &TransactionWithSignature, cip172: bool, mode: &VerifyTxMode,
891    ) -> Result<(), TransactionError> {
892        if tx.is_canonical_rlp() {
893            return Ok(());
894        }
895        let rejected = match mode {
896            VerifyTxMode::Local(..) => true,
897            VerifyTxMode::Remote(_) => cip172,
898        };
899        if rejected {
900            bail!(TransactionError::InvalidRlp(
901                "non-canonical transaction RLP encoding".into()
902            ));
903        }
904        Ok(())
905    }
906
907    fn check_eip155_transaction(
908        tx: &TransactionWithSignature, cip90a: bool, mode: &VerifyTxMode,
909    ) -> bool {
910        if tx.space() == Space::Native {
911            return true;
912        }
913
914        use VerifyTxLocalMode::*;
915        match mode {
916            VerifyTxMode::Local(Full, spec) => cip90a && spec.cip90,
917            VerifyTxMode::Local(MaybeLater, _spec) => true,
918            VerifyTxMode::Remote(_) => cip90a,
919        }
920    }
921
922    fn check_eip1559_transaction(
923        tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode,
924    ) -> bool {
925        if tx.is_legacy() {
926            return true;
927        }
928
929        use VerifyTxLocalMode::*;
930        match mode {
931            VerifyTxMode::Local(Full, spec) => cip1559 && spec.cip1559,
932            VerifyTxMode::Local(MaybeLater, _spec) => true,
933            VerifyTxMode::Remote(_) => cip1559,
934        }
935    }
936
937    fn check_eip7702_transaction(
938        tx: &TransactionWithSignature, cip7702: bool, mode: &VerifyTxMode,
939    ) -> bool {
940        if !tx.after_7702() {
941            return true;
942        }
943
944        use VerifyTxLocalMode::*;
945        match mode {
946            VerifyTxMode::Local(Full, spec) => cip7702 && spec.cip7702,
947            VerifyTxMode::Local(MaybeLater, _spec) => true,
948            VerifyTxMode::Remote(_) => cip7702,
949        }
950    }
951
952    fn check_eip7702_validation(
953        tx: &TransactionWithSignature,
954    ) -> Result<(), TransactionError> {
955        if let Some(author_list) = tx.authorization_list() {
956            if author_list.is_empty() {
957                return Err(TransactionError::EmptyAuthorizationList);
958            }
959        }
960        Ok(())
961    }
962
963    fn check_eip1559_validation(
964        tx: &TransactionWithSignature, cip645: bool,
965    ) -> Result<(), TransactionError> {
966        if !cip645 || !tx.after_1559() {
967            return Ok(());
968        }
969
970        if tx.max_priority_gas_price() > tx.gas_price() {
971            return Err(TransactionError::PriortyGreaterThanMaxFee);
972        }
973        Ok(())
974    }
975
976    fn check_eip3860(tx: &TransactionWithSignature, cip645: bool) -> bool {
977        // TODO: better way to get the specification
978        const SPEC: Spec = Spec::genesis_spec();
979        if !cip645 {
980            return true;
981        }
982        if tx.action() != Action::Create {
983            return true;
984        }
985
986        tx.data().len() <= SPEC.init_code_data_limit
987    }
988
989    /// Check transaction intrinsic gas. Influenced by CIP-76 and EIP-7623.
990    fn check_gas_limit(
991        tx: &TransactionWithSignature, cip76: bool, eip7623: bool,
992        mode: &VerifyTxMode,
993    ) -> Result<(), TransactionError> {
994        let consensus_spec = match mode {
995            VerifyTxMode::Local(_, spec) => spec.to_consensus_spec(),
996            VerifyTxMode::Remote(spec) => {
997                if !eip7623 && cip76 {
998                    return Ok(());
999                } else {
1000                    (*spec).clone()
1001                }
1002            }
1003        };
1004
1005        let tx_intrinsic_gas = gas_required_for(
1006            tx.action() == Action::Create,
1007            &tx.data(),
1008            tx.access_list(),
1009            tx.authorization_len(),
1010            &consensus_spec,
1011        );
1012
1013        if *tx.gas() < tx_intrinsic_gas.into() {
1014            bail!(TransactionError::NotEnoughBaseGas {
1015                required: tx_intrinsic_gas.into(),
1016                got: *tx.gas()
1017            });
1018        }
1019
1020        if eip7623 {
1021            let floor_gas = eip7623_required_gas(&tx.data(), &consensus_spec);
1022
1023            if *tx.gas() < floor_gas.into() {
1024                bail!(TransactionError::NotEnoughBaseGas {
1025                    required: floor_gas.into(),
1026                    got: *tx.gas()
1027                });
1028            }
1029        }
1030
1031        Ok(())
1032    }
1033
1034    fn check_gas_limit_with_calldata(
1035        tx: &TransactionWithSignature, cip130: bool,
1036    ) -> Result<(), TransactionError> {
1037        if !cip130 {
1038            return Ok(());
1039        }
1040        let data_length = tx.data().len();
1041        let min_gas_limit = data_length.saturating_mul(100);
1042        if tx.gas() < &U256::from(min_gas_limit) {
1043            bail!(TransactionError::NotEnoughBaseGas {
1044                required: min_gas_limit.into(),
1045                got: *tx.gas()
1046            });
1047        }
1048        Ok(())
1049    }
1050
1051    pub fn check_tx_size(
1052        &self, tx: &TransactionWithSignature,
1053    ) -> Result<(), TransactionError> {
1054        if tx.rlp_size() > self.max_block_size_in_bytes {
1055            bail!(TransactionError::TooBig)
1056        } else {
1057            Ok(())
1058        }
1059    }
1060}
1061
1062#[derive(Copy, Clone)]
1063pub enum PackingCheckResult {
1064    Pack,
1065    // Transaction can be packed.
1066    Pending,
1067    // Transaction may be ready to packed in the future.
1068    Drop, // Transaction can never be packed.
1069}
1070
1071#[derive(Copy, Clone)]
1072pub enum VerifyTxMode<'a> {
1073    /// Check transactions in local mode, may have more constraints
1074    Local(VerifyTxLocalMode, &'a Spec),
1075    /// Check transactions for received blocks in sync graph, may have less
1076    /// constraints
1077    Remote(&'a ConsensusGasSpec),
1078}
1079
1080#[derive(Copy, Clone)]
1081pub enum VerifyTxLocalMode {
1082    /// Apply all checks
1083    Full,
1084    /// If a transaction is not valid now, but can become valid in the future,
1085    /// the check sould pass
1086    MaybeLater,
1087}
1088
1089impl<'a> VerifyTxMode<'a> {
1090    fn is_maybe_later(&self) -> bool {
1091        if let VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, _) = self {
1092            true
1093        } else {
1094            false
1095        }
1096    }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101    use crate::verification::EpochReceiptProof;
1102    use cfx_storage::{
1103        CompressedPathRaw, TrieProof, TrieProofNode, VanillaChildrenTable,
1104    };
1105
1106    #[test]
1107    fn test_rlp_epoch_receipt_proof() {
1108        let proof = EpochReceiptProof::default();
1109        assert_eq!(proof, rlp::decode(&rlp::encode(&proof)).unwrap());
1110
1111        let serialized = serde_json::to_string(&proof).unwrap();
1112        let deserialized: EpochReceiptProof =
1113            serde_json::from_str(&serialized).unwrap();
1114        assert_eq!(proof, deserialized);
1115
1116        let node1 = TrieProofNode::new(
1117            Default::default(),
1118            Some(Box::new([0x03, 0x04, 0x05])),
1119            CompressedPathRaw::new(
1120                &[0x00, 0x01, 0x02],
1121                CompressedPathRaw::first_nibble_mask(),
1122            ),
1123            /* path_without_first_nibble = */ true,
1124        );
1125
1126        let root_node = {
1127            let mut children_table = VanillaChildrenTable::default();
1128            unsafe {
1129                *children_table.get_child_mut_unchecked(2) =
1130                    *node1.get_merkle();
1131                *children_table.get_children_count_mut() = 1;
1132            }
1133            TrieProofNode::new(
1134                children_table,
1135                None,
1136                CompressedPathRaw::default(),
1137                /* path_without_first_nibble = */ false,
1138            )
1139        };
1140        let nodes = [root_node, node1]
1141            .iter()
1142            .cloned()
1143            .cycle()
1144            .take(20)
1145            .collect();
1146        let proof = TrieProof::new(nodes).unwrap();
1147
1148        let epoch_proof = EpochReceiptProof {
1149            block_index_proof: proof.clone(),
1150            block_receipt_proof: proof,
1151        };
1152
1153        assert_eq!(
1154            epoch_proof,
1155            rlp::decode(&rlp::encode(&epoch_proof)).unwrap()
1156        );
1157
1158        let serialized = serde_json::to_string(&epoch_proof).unwrap();
1159        let deserialized: EpochReceiptProof =
1160            serde_json::from_str(&serialized).unwrap();
1161        assert_eq!(epoch_proof, deserialized);
1162    }
1163
1164    // A miner can pack transactions whose gas_limit values sum past U256; the
1165    // summation used to panic instead of rejecting the block.
1166    #[test]
1167    fn packed_gas_sum_overflow_is_rejected() {
1168        use crate::{
1169            core_error::{BlockError, CoreError as Error},
1170            verification::{compute_transaction_root, VerificationConfig},
1171        };
1172        use cfx_executor::{
1173            machine::{Machine, VmFactory},
1174            spec::CommonParams,
1175        };
1176        use cfx_types::U256;
1177        use cfxkey::{Generator, Random};
1178        use primitives::{
1179            transaction::native_transaction::{
1180                NativeTransaction, TypedNativeTransaction,
1181            },
1182            Action, Block, BlockHeaderBuilder, Transaction,
1183        };
1184        use std::sync::Arc;
1185
1186        let params = CommonParams::default();
1187        let chain_id = params.chain_id.read().get_chain_id(1);
1188        let machine =
1189            Arc::new(Machine::new_with_builtin(params, VmFactory::new(1024)));
1190        let config = VerificationConfig::new(
1191            false,
1192            200,
1193            200 * 1024,
1194            100_000,
1195            128,
1196            u64::MAX,
1197            1,
1198            machine,
1199        );
1200
1201        let keypair = Random.generate().unwrap();
1202        let tx = |nonce: u64| {
1203            Arc::new(
1204                Transaction::Native(TypedNativeTransaction::Cip155(
1205                    NativeTransaction {
1206                        nonce: nonce.into(),
1207                        gas_price: U256::one(),
1208                        // 2^255; two of these sum to 2^256.
1209                        gas: U256::one() << 255,
1210                        action: Action::Create,
1211                        value: U256::zero(),
1212                        storage_limit: 0,
1213                        epoch_height: 1,
1214                        chain_id: chain_id.in_native_space(),
1215                        data: vec![],
1216                    },
1217                ))
1218                .sign(keypair.secret()),
1219            )
1220        };
1221        let txs = vec![tx(0), tx(1)];
1222
1223        let parent = BlockHeaderBuilder::new().with_height(0).build();
1224        let header = BlockHeaderBuilder::new()
1225            .with_height(1)
1226            .with_parent_hash(parent.hash())
1227            .with_transactions_root(compute_transaction_root(&txs))
1228            .with_gas_limit(30_000_000.into())
1229            .build();
1230        let block = Block::new(header, txs);
1231
1232        assert!(matches!(
1233            config.verify_sync_graph_ready_block(&block, &parent),
1234            Err(Error::Block(BlockError::InvalidPackedGasLimit(_))),
1235        ));
1236    }
1237
1238    // The PoW light cache is sized by the header height and built when the
1239    // hash is computed. With Octopus enabled, computing the hash for a height
1240    // of `u64::MAX` would try to allocate an exabyte-scale cache, so this test
1241    // only passes if the height and difficulty checks run first.
1242    #[test]
1243    fn verify_pow_rejects_before_computing_hash() {
1244        use crate::{
1245            core_error::{BlockError, CoreError as Error},
1246            pow::PowComputer,
1247            verification::VerificationConfig,
1248        };
1249        use cfx_executor::{
1250            machine::{Machine, VmFactory},
1251            spec::CommonParams,
1252        };
1253        use primitives::BlockHeaderBuilder;
1254        use std::sync::Arc;
1255
1256        let machine = Arc::new(Machine::new_with_builtin(
1257            CommonParams::default(),
1258            VmFactory::new(1024),
1259        ));
1260        let config = VerificationConfig::new(
1261            false,
1262            200,
1263            200 * 1024,
1264            100_000,
1265            128,
1266            u64::MAX,
1267            4, /* initial_difficulty */
1268            machine,
1269        );
1270        let pow = PowComputer::new(true /* use_octopus */);
1271
1272        let mut header = BlockHeaderBuilder::new()
1273            .with_height(u64::MAX)
1274            .with_difficulty(4.into())
1275            .build();
1276        assert!(matches!(
1277            config.verify_pow(&pow, &mut header),
1278            Err(Error::Block(BlockError::InvalidHeight(_))),
1279        ));
1280
1281        let mut header = BlockHeaderBuilder::new()
1282            .with_height(u64::MAX)
1283            .with_difficulty(3.into())
1284            .build();
1285        assert!(matches!(
1286            config.verify_pow(&pow, &mut header),
1287            Err(Error::Block(BlockError::InvalidHeight(_))),
1288        ));
1289
1290        let mut header = BlockHeaderBuilder::new()
1291            .with_height(1)
1292            .with_difficulty(3.into())
1293            .build();
1294        assert!(matches!(
1295            config.verify_pow(&pow, &mut header),
1296            Err(Error::Block(BlockError::InvalidDifficulty(_))),
1297        ));
1298    }
1299}