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().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().iter().fold(0, |acc, x| acc + x.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                let header_custum = header.custom();
386                // Header custom is too short.
387                let b =
388                    header_custum.get(i).ok_or(BlockError::InvalidCustom(
389                        header_custum.clone(),
390                        expected_custom_prefix.clone(),
391                    ))?;
392                if b != expected_bytes {
393                    return Err(BlockError::InvalidCustom(
394                        header_custum.clone(),
395                        expected_custom_prefix.clone(),
396                    )
397                    .into());
398                }
399            }
400        }
401
402        // verify POW
403        self.verify_pow(pow, header)?;
404
405        // A block will be invalid if it has more than REFEREE_BOUND referees
406        if header.referee_hashes().len() > self.referee_bound {
407            return Err(From::from(BlockError::TooManyReferees(OutOfBounds {
408                min: Some(0),
409                max: Some(self.referee_bound),
410                found: header.referee_hashes().len(),
411            })));
412        }
413
414        // verify non-duplicated parent and referee hashes
415        let mut direct_ancestor_hashes = HashSet::new();
416        let parent_hash = header.parent_hash();
417        direct_ancestor_hashes.insert(parent_hash.clone());
418        for referee_hash in header.referee_hashes() {
419            if direct_ancestor_hashes.contains(referee_hash) {
420                warn!(
421                    "block {} has duplicate parent or referee hashes",
422                    header.hash()
423                );
424                return Err(From::from(
425                    BlockError::DuplicateParentOrRefereeHashes(
426                        referee_hash.clone(),
427                    ),
428                ));
429            }
430            direct_ancestor_hashes.insert(referee_hash.clone());
431        }
432
433        Ok(())
434    }
435
436    /// Verify block data against header: transactions root
437    #[inline]
438    fn verify_block_integrity(&self, block: &Block) -> Result<(), Error> {
439        let expected_root = compute_transaction_root(&block.transactions);
440        if &expected_root != block.block_header.transactions_root() {
441            warn!("Invalid transaction root");
442            bail!(BlockError::InvalidTransactionsRoot(Mismatch {
443                expected: expected_root,
444                found: *block.block_header.transactions_root(),
445            }));
446        }
447        Ok(())
448    }
449
450    /// Phase 1 quick block verification. Only does checks that are cheap.
451    /// Operates on a single block.
452    /// Note that we should first check whether the block body matches its
453    /// header, e.g., check transaction root correctness, and then check the
454    /// body itself. If body does not match header, the block may still be
455    /// valid but we get the wrong body from evil node. So we try to get
456    /// body again from others. However, if the body matches the header and
457    /// the body is incorrect, this means the block is invalid, and we
458    /// should discard this block and all its descendants.
459    #[inline]
460    pub fn verify_sync_graph_block_basic(
461        &self, block: &Block, chain_id: AllChainID,
462    ) -> Result<(), Error> {
463        self.verify_block_integrity(block)?;
464
465        let block_height = block.block_header.height();
466
467        let mut block_size = 0;
468        let transitions = &self.machine.params().transition_heights;
469        let consensus_spec =
470            &self.machine.params().consensus_spec(block_height);
471
472        for t in &block.transactions {
473            self.verify_transaction_common(
474                t,
475                chain_id,
476                block_height,
477                transitions,
478                VerifyTxMode::Remote(consensus_spec),
479            )?;
480            block_size += t.rlp_size();
481        }
482
483        if block_size > self.max_block_size_in_bytes {
484            return Err(From::from(BlockError::InvalidBlockSize(
485                OutOfBounds {
486                    min: None,
487                    max: Some(self.max_block_size_in_bytes as u64),
488                    found: block_size as u64,
489                },
490            )));
491        }
492        Ok(())
493    }
494
495    pub fn verify_sync_graph_ready_block(
496        &self, block: &Block, parent: &BlockHeader,
497    ) -> Result<(), Error> {
498        let mut total_gas: SpaceMap<U256> = SpaceMap::default();
499        for t in &block.transactions {
500            total_gas[t.space()] += *t.gas_limit();
501        }
502
503        if block.block_header.height()
504            >= self.machine.params().transition_heights.cip1559
505        {
506            self.check_base_fee(block, parent, total_gas)?;
507        } else {
508            self.check_hard_gas_limit(block, total_gas)?;
509        }
510        Ok(())
511    }
512
513    fn check_hard_gas_limit(
514        &self, block: &Block, total_gas: SpaceMap<U256>,
515    ) -> Result<(), Error> {
516        let block_height = block.block_header.height();
517
518        let evm_space_gas_limit =
519            if self.machine.params().can_pack_evm_transaction(block_height) {
520                *block.block_header.gas_limit()
521                    / self.machine.params().evm_transaction_gas_ratio
522            } else {
523                U256::zero()
524            };
525
526        let evm_total_gas = total_gas[Space::Ethereum];
527        let block_total_gas = total_gas.map_sum(|x| *x);
528
529        if evm_total_gas > evm_space_gas_limit {
530            return Err(From::from(BlockError::InvalidPackedGasLimit(
531                OutOfBounds {
532                    min: None,
533                    max: Some(evm_space_gas_limit),
534                    found: evm_total_gas,
535                },
536            )));
537        }
538
539        if block_total_gas > *block.block_header.gas_limit() {
540            return Err(From::from(BlockError::InvalidPackedGasLimit(
541                OutOfBounds {
542                    min: None,
543                    max: Some(*block.block_header.gas_limit()),
544                    found: block_total_gas,
545                },
546            )));
547        }
548
549        Ok(())
550    }
551
552    fn check_base_fee(
553        &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap<U256>,
554    ) -> Result<(), Error> {
555        use Space::*;
556
557        let params = self.machine.params();
558        let cip1559_init = params.transition_heights.cip1559;
559        let block_height = block.block_header.height();
560
561        assert!(block_height >= cip1559_init);
562
563        let core_gas_limit = block.block_header.core_space_gas_limit();
564        let espace_gas_limit = block
565            .block_header
566            .espace_gas_limit(params.can_pack_evm_transaction(block_height));
567
568        if total_gas[Ethereum] > espace_gas_limit {
569            return Err(From::from(BlockError::InvalidPackedGasLimit(
570                OutOfBounds {
571                    min: None,
572                    max: Some(espace_gas_limit),
573                    found: total_gas[Ethereum],
574                },
575            )));
576        }
577
578        if total_gas[Native] > core_gas_limit {
579            return Err(From::from(BlockError::InvalidPackedGasLimit(
580                OutOfBounds {
581                    min: None,
582                    max: Some(core_gas_limit),
583                    found: total_gas[Native],
584                },
585            )));
586        }
587
588        let parent_base_price = if block_height == cip1559_init {
589            params.init_base_price()
590        } else {
591            parent.base_price().unwrap()
592        };
593
594        let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit);
595        let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER);
596        let min_base_price = params.min_base_price();
597
598        let expected_base_price = SpaceMap::zip4(
599            gas_target,
600            total_gas,
601            parent_base_price,
602            min_base_price,
603        )
604        .map_all(compute_next_price_tuple);
605
606        let actual_base_price = block.block_header.base_price().unwrap();
607
608        if actual_base_price != expected_base_price {
609            return Err(From::from(BlockError::InvalidBasePrice(Mismatch {
610                expected: expected_base_price,
611                found: actual_base_price,
612            })));
613        }
614
615        Ok(())
616    }
617
618    pub fn check_transaction_epoch_bound(
619        tx: &TypedNativeTransaction, block_height: u64,
620        transaction_epoch_bound: u64,
621    ) -> i8 {
622        if tx.epoch_height().wrapping_add(transaction_epoch_bound)
623            < block_height
624        {
625            -1
626        } else if *tx.epoch_height() > block_height + transaction_epoch_bound {
627            1
628        } else {
629            0
630        }
631    }
632
633    fn verify_transaction_epoch_height(
634        tx: &TypedNativeTransaction, block_height: u64,
635        transaction_epoch_bound: u64, mode: &VerifyTxMode,
636    ) -> Result<(), TransactionError> {
637        let result = Self::check_transaction_epoch_bound(
638            tx,
639            block_height,
640            transaction_epoch_bound,
641        );
642        let allow_larger_epoch = mode.is_maybe_later();
643
644        if result == 0 || (result > 0 && allow_larger_epoch) {
645            Ok(())
646        } else {
647            bail!(TransactionError::EpochHeightOutOfBound {
648                set: *tx.epoch_height(),
649                block_height,
650                transaction_epoch_bound,
651            });
652        }
653    }
654
655    fn fast_recheck_inner<F>(spec: &Spec, f: F) -> (bool, bool)
656    where F: Fn(&VerifyTxMode) -> bool {
657        let tx_pool_mode =
658            VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, spec);
659        let packing_mode = VerifyTxMode::Local(VerifyTxLocalMode::Full, spec);
660
661        (f(&packing_mode), f(&tx_pool_mode))
662    }
663
664    pub fn fast_recheck(
665        &self, tx: &TransactionWithSignature, height: BlockHeight,
666        transitions: &TransitionsEpochHeight, spec: &Spec,
667    ) -> PackingCheckResult {
668        let cip90a = height >= transitions.cip90a;
669        let cip1559 = height >= transitions.cip1559;
670        let cip7702 = height >= transitions.cip7702;
671        let cip645 = height >= transitions.cip645;
672
673        let (can_pack, later_pack) = Self::fast_recheck_inner(
674            spec,
675            |mode: &VerifyTxMode| {
676                if !Self::check_eip1559_transaction(tx, cip1559, mode) {
677                    trace!(
678                        "fast_recheck: EIP-1559 transaction check failed at height {} txhash={:?}",
679                        height,
680                        tx.hash()
681                    );
682                    return false;
683                }
684
685                if !Self::check_eip7702_transaction(tx, cip7702, mode) {
686                    trace!(
687                        "fast_recheck: EIP-7702 transaction check failed at height {} txhash={:?}",
688                        height,
689                        tx.hash()
690                    );
691                    return false;
692                }
693
694                if !Self::check_eip3860(tx, cip645) {
695                    trace!(
696                        "fast_recheck: EIP-3860 transaction check failed at height {} txhash={:?}",
697                        height,
698                        tx.hash()
699                    );
700                    return false;
701                }
702
703                if let Transaction::Native(ref tx) = tx.unsigned {
704                    Self::verify_transaction_epoch_height(
705                        tx,
706                        height,
707                        self.transaction_epoch_bound,
708                        mode,
709                    )
710                    .is_ok()
711                } else {
712                    Self::check_eip155_transaction(tx, cip90a, mode)
713                }
714            },
715        );
716
717        match (can_pack, later_pack) {
718            (true, _) => PackingCheckResult::Pack,
719            (false, true) => PackingCheckResult::Pending,
720            (false, false) => PackingCheckResult::Drop,
721        }
722    }
723
724    // Packing transactions, verifying transaction in sync graph and inserting
725    // transactions may have different logics. But they share a lot of similar
726    // rules. We combine them together for convenient in the future upgrades..
727    pub fn verify_transaction_common(
728        &self, tx: &TransactionWithSignature, chain_id: AllChainID,
729        height: BlockHeight, transitions: &TransitionsEpochHeight,
730        mode: VerifyTxMode,
731    ) -> Result<(), TransactionError> {
732        tx.check_low_s()?;
733        tx.check_y_parity()?;
734
735        // Disallow unsigned transactions
736        if tx.is_unsigned() {
737            bail!(TransactionError::InvalidSignature(
738                "Transaction is unsigned".into()
739            ));
740        }
741
742        if let Some(tx_chain_id) = tx.chain_id() {
743            if tx_chain_id != chain_id.in_space(tx.space()) {
744                bail!(TransactionError::ChainIdMismatch {
745                    expected: chain_id.in_space(tx.space()),
746                    got: tx_chain_id,
747                    space: tx.space(),
748                });
749            }
750        }
751
752        // Forbid zero-gas-price tx
753        if tx.gas_price().is_zero() {
754            bail!(TransactionError::ZeroGasPrice);
755        }
756
757        if matches!(mode, VerifyTxMode::Local(..))
758            && tx.space() == Space::Native
759        {
760            if let Action::Call(ref address) = tx.transaction.action() {
761                if !address.is_genesis_valid_address() {
762                    bail!(TransactionError::InvalidReceiver)
763                }
764            }
765        }
766
767        if let (VerifyTxMode::Local(..), Some(max_nonce)) =
768            (mode, self.max_nonce)
769        {
770            if tx.nonce() > &max_nonce {
771                bail!(TransactionError::TooLargeNonce)
772            }
773        }
774
775        // ******************************************
776        // Each constraint depends on a mode or a CIP should be
777        // implemented in a seperated function.
778        // ******************************************
779        let cip76 = height >= transitions.cip76;
780        let cip90a = height >= transitions.cip90a;
781        let cip130 =
782            height >= transitions.cip130 && height < transitions.align_evm;
783        let cip1559 = height >= transitions.cip1559;
784        let cip7702 = height >= transitions.cip7702;
785        let cip645 = height >= transitions.cip645;
786        let eip7623 = height >= transitions.eip7623;
787
788        if let Transaction::Native(ref tx) = tx.unsigned {
789            Self::verify_transaction_epoch_height(
790                tx,
791                height,
792                self.transaction_epoch_bound,
793                &mode,
794            )?;
795        }
796
797        if !Self::check_eip155_transaction(tx, cip90a, &mode) {
798            bail!(TransactionError::FutureTransactionType {
799                tx_type: LEGACY_TX_TYPE,
800                current_height: height,
801                enable_height: transitions.cip90a,
802            });
803        }
804
805        if !Self::check_eip1559_transaction(tx, cip1559, &mode) {
806            bail!(TransactionError::FutureTransactionType {
807                tx_type: EIP1559_TYPE,
808                current_height: height,
809                enable_height: transitions.cip1559,
810            })
811        }
812
813        if !Self::check_eip7702_transaction(tx, cip7702, &mode) {
814            bail!(TransactionError::FutureTransactionType {
815                tx_type: EIP7702_TYPE,
816                current_height: height,
817                enable_height: transitions.cip7702,
818            })
819        }
820
821        if !Self::check_eip3860(tx, cip645) {
822            bail!(TransactionError::CreateInitCodeSizeLimit)
823        }
824
825        Self::check_eip1559_validation(tx, cip645)?;
826        Self::check_eip7702_validation(tx)?;
827
828        Self::check_gas_limit(tx, cip76, eip7623, &mode)?;
829        Self::check_gas_limit_with_calldata(tx, cip130)?;
830
831        Ok(())
832    }
833
834    fn check_eip155_transaction(
835        tx: &TransactionWithSignature, cip90a: bool, mode: &VerifyTxMode,
836    ) -> bool {
837        if tx.space() == Space::Native {
838            return true;
839        }
840
841        use VerifyTxLocalMode::*;
842        match mode {
843            VerifyTxMode::Local(Full, spec) => cip90a && spec.cip90,
844            VerifyTxMode::Local(MaybeLater, _spec) => true,
845            VerifyTxMode::Remote(_) => cip90a,
846        }
847    }
848
849    fn check_eip1559_transaction(
850        tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode,
851    ) -> bool {
852        if tx.is_legacy() {
853            return true;
854        }
855
856        use VerifyTxLocalMode::*;
857        match mode {
858            VerifyTxMode::Local(Full, spec) => cip1559 && spec.cip1559,
859            VerifyTxMode::Local(MaybeLater, _spec) => true,
860            VerifyTxMode::Remote(_) => cip1559,
861        }
862    }
863
864    fn check_eip7702_transaction(
865        tx: &TransactionWithSignature, cip7702: bool, mode: &VerifyTxMode,
866    ) -> bool {
867        if !tx.after_7702() {
868            return true;
869        }
870
871        use VerifyTxLocalMode::*;
872        match mode {
873            VerifyTxMode::Local(Full, spec) => cip7702 && spec.cip7702,
874            VerifyTxMode::Local(MaybeLater, _spec) => true,
875            VerifyTxMode::Remote(_) => cip7702,
876        }
877    }
878
879    fn check_eip7702_validation(
880        tx: &TransactionWithSignature,
881    ) -> Result<(), TransactionError> {
882        if let Some(author_list) = tx.authorization_list() {
883            if author_list.is_empty() {
884                return Err(TransactionError::EmptyAuthorizationList);
885            }
886        }
887        Ok(())
888    }
889
890    fn check_eip1559_validation(
891        tx: &TransactionWithSignature, cip645: bool,
892    ) -> Result<(), TransactionError> {
893        if !cip645 || !tx.after_1559() {
894            return Ok(());
895        }
896
897        if tx.max_priority_gas_price() > tx.gas_price() {
898            return Err(TransactionError::PriortyGreaterThanMaxFee);
899        }
900        Ok(())
901    }
902
903    fn check_eip3860(tx: &TransactionWithSignature, cip645: bool) -> bool {
904        // TODO: better way to get the specification
905        const SPEC: Spec = Spec::genesis_spec();
906        if !cip645 {
907            return true;
908        }
909        if tx.action() != Action::Create {
910            return true;
911        }
912
913        tx.data().len() <= SPEC.init_code_data_limit
914    }
915
916    /// Check transaction intrinsic gas. Influenced by CIP-76 and EIP-7623.
917    fn check_gas_limit(
918        tx: &TransactionWithSignature, cip76: bool, eip7623: bool,
919        mode: &VerifyTxMode,
920    ) -> Result<(), TransactionError> {
921        let consensus_spec = match mode {
922            VerifyTxMode::Local(_, spec) => spec.to_consensus_spec(),
923            VerifyTxMode::Remote(spec) => {
924                if !eip7623 && cip76 {
925                    return Ok(());
926                } else {
927                    (*spec).clone()
928                }
929            }
930        };
931
932        let tx_intrinsic_gas = gas_required_for(
933            tx.action() == Action::Create,
934            &tx.data(),
935            tx.access_list(),
936            tx.authorization_len(),
937            &consensus_spec,
938        );
939
940        if *tx.gas() < tx_intrinsic_gas.into() {
941            bail!(TransactionError::NotEnoughBaseGas {
942                required: tx_intrinsic_gas.into(),
943                got: *tx.gas()
944            });
945        }
946
947        if eip7623 {
948            let floor_gas = eip7623_required_gas(&tx.data(), &consensus_spec);
949
950            if *tx.gas() < floor_gas.into() {
951                bail!(TransactionError::NotEnoughBaseGas {
952                    required: floor_gas.into(),
953                    got: *tx.gas()
954                });
955            }
956        }
957
958        Ok(())
959    }
960
961    fn check_gas_limit_with_calldata(
962        tx: &TransactionWithSignature, cip130: bool,
963    ) -> Result<(), TransactionError> {
964        if !cip130 {
965            return Ok(());
966        }
967        let data_length = tx.data().len();
968        let min_gas_limit = data_length.saturating_mul(100);
969        if tx.gas() < &U256::from(min_gas_limit) {
970            bail!(TransactionError::NotEnoughBaseGas {
971                required: min_gas_limit.into(),
972                got: *tx.gas()
973            });
974        }
975        Ok(())
976    }
977
978    pub fn check_tx_size(
979        &self, tx: &TransactionWithSignature,
980    ) -> Result<(), TransactionError> {
981        if tx.rlp_size() > self.max_block_size_in_bytes {
982            bail!(TransactionError::TooBig)
983        } else {
984            Ok(())
985        }
986    }
987}
988
989#[derive(Copy, Clone)]
990pub enum PackingCheckResult {
991    Pack,
992    // Transaction can be packed.
993    Pending,
994    // Transaction may be ready to packed in the future.
995    Drop, // Transaction can never be packed.
996}
997
998#[derive(Copy, Clone)]
999pub enum VerifyTxMode<'a> {
1000    /// Check transactions in local mode, may have more constraints
1001    Local(VerifyTxLocalMode, &'a Spec),
1002    /// Check transactions for received blocks in sync graph, may have less
1003    /// constraints
1004    Remote(&'a ConsensusGasSpec),
1005}
1006
1007#[derive(Copy, Clone)]
1008pub enum VerifyTxLocalMode {
1009    /// Apply all checks
1010    Full,
1011    /// If a transaction is not valid now, but can become valid in the future,
1012    /// the check sould pass
1013    MaybeLater,
1014}
1015
1016impl<'a> VerifyTxMode<'a> {
1017    fn is_maybe_later(&self) -> bool {
1018        if let VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, _) = self {
1019            true
1020        } else {
1021            false
1022        }
1023    }
1024}
1025
1026#[cfg(test)]
1027mod tests {
1028    use crate::verification::EpochReceiptProof;
1029    use cfx_storage::{
1030        CompressedPathRaw, TrieProof, TrieProofNode, VanillaChildrenTable,
1031    };
1032
1033    #[test]
1034    fn test_rlp_epoch_receipt_proof() {
1035        let proof = EpochReceiptProof::default();
1036        assert_eq!(proof, rlp::decode(&rlp::encode(&proof)).unwrap());
1037
1038        let serialized = serde_json::to_string(&proof).unwrap();
1039        let deserialized: EpochReceiptProof =
1040            serde_json::from_str(&serialized).unwrap();
1041        assert_eq!(proof, deserialized);
1042
1043        let node1 = TrieProofNode::new(
1044            Default::default(),
1045            Some(Box::new([0x03, 0x04, 0x05])),
1046            CompressedPathRaw::new(
1047                &[0x00, 0x01, 0x02],
1048                CompressedPathRaw::first_nibble_mask(),
1049            ),
1050            /* path_without_first_nibble = */ true,
1051        );
1052
1053        let root_node = {
1054            let mut children_table = VanillaChildrenTable::default();
1055            unsafe {
1056                *children_table.get_child_mut_unchecked(2) =
1057                    *node1.get_merkle();
1058                *children_table.get_children_count_mut() = 1;
1059            }
1060            TrieProofNode::new(
1061                children_table,
1062                None,
1063                CompressedPathRaw::default(),
1064                /* path_without_first_nibble = */ false,
1065            )
1066        };
1067        let nodes = [root_node, node1]
1068            .iter()
1069            .cloned()
1070            .cycle()
1071            .take(20)
1072            .collect();
1073        let proof = TrieProof::new(nodes).unwrap();
1074
1075        let epoch_proof = EpochReceiptProof {
1076            block_index_proof: proof.clone(),
1077            block_receipt_proof: proof,
1078        };
1079
1080        assert_eq!(
1081            epoch_proof,
1082            rlp::decode(&rlp::encode(&epoch_proof)).unwrap()
1083        );
1084
1085        let serialized = serde_json::to_string(&epoch_proof).unwrap();
1086        let deserialized: EpochReceiptProof =
1087            serde_json::from_str(&serialized).unwrap();
1088        assert_eq!(epoch_proof, deserialized);
1089    }
1090}