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