1use 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
52fn 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
63pub fn compute_transaction_root(
66 transactions: &Vec<Arc<SignedTransaction>>,
67) -> MerkleHash {
68 simple_mpt_merkle_root(&mut transaction_trie(transactions))
69}
70
71pub 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
81fn 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
92fn compute_block_receipts_root(block_receipts: &Vec<Receipt>) -> MerkleHash {
94 simple_mpt_merkle_root(&mut block_receipts_trie(block_receipts))
95}
96
97pub 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
107fn 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
119pub 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
143pub 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
165pub 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
176pub 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 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 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 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 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 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 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 #[inline]
340 pub fn verify_header_params(
341 &self, pow: &PowComputer, header: &mut BlockHeader,
342 ) -> Result<(), Error> {
343 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 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 matches =
387 header.custom_item(i).is_some_and(|b| &b == expected_bytes);
388 if !matches {
389 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 self.verify_pow(pow, header)?;
405
406 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 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 #[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 #[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 total_gas[t.space()] += *t.gas_limit();
502 }
503
504 if block.block_header.height()
505 >= self.machine.params().transition_heights.cip1559
506 {
507 self.check_base_fee(block, parent, total_gas)?;
508 } else {
509 self.check_hard_gas_limit(block, total_gas)?;
510 }
511 Ok(())
512 }
513
514 fn check_hard_gas_limit(
515 &self, block: &Block, total_gas: SpaceMap<U256>,
516 ) -> Result<(), Error> {
517 let block_height = block.block_header.height();
518
519 let evm_space_gas_limit =
520 if self.machine.params().can_pack_evm_transaction(block_height) {
521 *block.block_header.gas_limit()
522 / self.machine.params().evm_transaction_gas_ratio
523 } else {
524 U256::zero()
525 };
526
527 let evm_total_gas = total_gas[Space::Ethereum];
528 let block_total_gas = total_gas.map_sum(|x| *x);
529
530 if evm_total_gas > evm_space_gas_limit {
531 return Err(From::from(BlockError::InvalidPackedGasLimit(
532 OutOfBounds {
533 min: None,
534 max: Some(evm_space_gas_limit),
535 found: evm_total_gas,
536 },
537 )));
538 }
539
540 if block_total_gas > *block.block_header.gas_limit() {
541 return Err(From::from(BlockError::InvalidPackedGasLimit(
542 OutOfBounds {
543 min: None,
544 max: Some(*block.block_header.gas_limit()),
545 found: block_total_gas,
546 },
547 )));
548 }
549
550 Ok(())
551 }
552
553 fn check_base_fee(
554 &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap<U256>,
555 ) -> Result<(), Error> {
556 use Space::*;
557
558 let params = self.machine.params();
559 let cip1559_init = params.transition_heights.cip1559;
560 let block_height = block.block_header.height();
561
562 assert!(block_height >= cip1559_init);
563
564 let core_gas_limit = block.block_header.core_space_gas_limit();
565 let espace_gas_limit = block
566 .block_header
567 .espace_gas_limit(params.can_pack_evm_transaction(block_height));
568
569 if total_gas[Ethereum] > espace_gas_limit {
570 return Err(From::from(BlockError::InvalidPackedGasLimit(
571 OutOfBounds {
572 min: None,
573 max: Some(espace_gas_limit),
574 found: total_gas[Ethereum],
575 },
576 )));
577 }
578
579 if total_gas[Native] > core_gas_limit {
580 return Err(From::from(BlockError::InvalidPackedGasLimit(
581 OutOfBounds {
582 min: None,
583 max: Some(core_gas_limit),
584 found: total_gas[Native],
585 },
586 )));
587 }
588
589 let parent_base_price = if block_height == cip1559_init {
590 params.init_base_price()
591 } else {
592 parent.base_price().unwrap()
593 };
594
595 let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit);
596 let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER);
597 let min_base_price = params.min_base_price();
598
599 let expected_base_price = SpaceMap::zip4(
600 gas_target,
601 total_gas,
602 parent_base_price,
603 min_base_price,
604 )
605 .map_all(compute_next_price_tuple);
606
607 let actual_base_price = block.block_header.base_price().unwrap();
608
609 if actual_base_price != expected_base_price {
610 return Err(From::from(BlockError::InvalidBasePrice(Mismatch {
611 expected: expected_base_price,
612 found: actual_base_price,
613 })));
614 }
615
616 Ok(())
617 }
618
619 pub fn check_transaction_epoch_bound(
620 tx: &TypedNativeTransaction, block_height: u64,
621 transaction_epoch_bound: u64,
622 ) -> i8 {
623 if tx.epoch_height().wrapping_add(transaction_epoch_bound)
624 < block_height
625 {
626 -1
627 } else if *tx.epoch_height() > block_height + transaction_epoch_bound {
628 1
629 } else {
630 0
631 }
632 }
633
634 fn verify_transaction_epoch_height(
635 tx: &TypedNativeTransaction, block_height: u64,
636 transaction_epoch_bound: u64, mode: &VerifyTxMode,
637 ) -> Result<(), TransactionError> {
638 let result = Self::check_transaction_epoch_bound(
639 tx,
640 block_height,
641 transaction_epoch_bound,
642 );
643 let allow_larger_epoch = mode.is_maybe_later();
644
645 if result == 0 || (result > 0 && allow_larger_epoch) {
646 Ok(())
647 } else {
648 bail!(TransactionError::EpochHeightOutOfBound {
649 set: *tx.epoch_height(),
650 block_height,
651 transaction_epoch_bound,
652 });
653 }
654 }
655
656 fn fast_recheck_inner<F>(spec: &Spec, f: F) -> (bool, bool)
657 where F: Fn(&VerifyTxMode) -> bool {
658 let tx_pool_mode =
659 VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, spec);
660 let packing_mode = VerifyTxMode::Local(VerifyTxLocalMode::Full, spec);
661
662 (f(&packing_mode), f(&tx_pool_mode))
663 }
664
665 pub fn fast_recheck(
666 &self, tx: &TransactionWithSignature, height: BlockHeight,
667 transitions: &TransitionsEpochHeight, spec: &Spec,
668 ) -> PackingCheckResult {
669 let cip90a = height >= transitions.cip90a;
670 let cip1559 = height >= transitions.cip1559;
671 let cip7702 = height >= transitions.cip7702;
672 let cip645 = height >= transitions.cip645;
673
674 let (can_pack, later_pack) = Self::fast_recheck_inner(
675 spec,
676 |mode: &VerifyTxMode| {
677 if !Self::check_eip1559_transaction(tx, cip1559, mode) {
678 trace!(
679 "fast_recheck: EIP-1559 transaction check failed at height {} txhash={:?}",
680 height,
681 tx.hash()
682 );
683 return false;
684 }
685
686 if !Self::check_eip7702_transaction(tx, cip7702, mode) {
687 trace!(
688 "fast_recheck: EIP-7702 transaction check failed at height {} txhash={:?}",
689 height,
690 tx.hash()
691 );
692 return false;
693 }
694
695 if !Self::check_eip3860(tx, cip645) {
696 trace!(
697 "fast_recheck: EIP-3860 transaction check failed at height {} txhash={:?}",
698 height,
699 tx.hash()
700 );
701 return false;
702 }
703
704 if let Transaction::Native(ref tx) = tx.unsigned {
705 Self::verify_transaction_epoch_height(
706 tx,
707 height,
708 self.transaction_epoch_bound,
709 mode,
710 )
711 .is_ok()
712 } else {
713 Self::check_eip155_transaction(tx, cip90a, mode)
714 }
715 },
716 );
717
718 match (can_pack, later_pack) {
719 (true, _) => PackingCheckResult::Pack,
720 (false, true) => PackingCheckResult::Pending,
721 (false, false) => PackingCheckResult::Drop,
722 }
723 }
724
725 pub fn verify_transaction_common(
729 &self, tx: &TransactionWithSignature, chain_id: AllChainID,
730 height: BlockHeight, transitions: &TransitionsEpochHeight,
731 mode: VerifyTxMode,
732 ) -> Result<(), TransactionError> {
733 tx.check_low_s()?;
734 tx.check_y_parity()?;
735
736 if tx.is_unsigned() {
738 bail!(TransactionError::InvalidSignature(
739 "Transaction is unsigned".into()
740 ));
741 }
742
743 if let Some(tx_chain_id) = tx.chain_id() {
744 if tx_chain_id != chain_id.in_space(tx.space()) {
745 bail!(TransactionError::ChainIdMismatch {
746 expected: chain_id.in_space(tx.space()),
747 got: tx_chain_id,
748 space: tx.space(),
749 });
750 }
751 }
752
753 if tx.gas_price().is_zero() {
755 bail!(TransactionError::ZeroGasPrice);
756 }
757
758 if matches!(mode, VerifyTxMode::Local(..))
759 && tx.space() == Space::Native
760 {
761 if let Action::Call(ref address) = tx.transaction.action() {
762 if !address.is_genesis_valid_address() {
763 bail!(TransactionError::InvalidReceiver)
764 }
765 }
766 }
767
768 if let (VerifyTxMode::Local(..), Some(max_nonce)) =
769 (mode, self.max_nonce)
770 {
771 if tx.nonce() > &max_nonce {
772 bail!(TransactionError::TooLargeNonce)
773 }
774 }
775
776 let cip76 = height >= transitions.cip76;
781 let cip90a = height >= transitions.cip90a;
782 let cip130 =
783 height >= transitions.cip130 && height < transitions.align_evm;
784 let cip1559 = height >= transitions.cip1559;
785 let cip7702 = height >= transitions.cip7702;
786 let cip645 = height >= transitions.cip645;
787 let eip7623 = height >= transitions.eip7623;
788
789 if let Transaction::Native(ref tx) = tx.unsigned {
790 Self::verify_transaction_epoch_height(
791 tx,
792 height,
793 self.transaction_epoch_bound,
794 &mode,
795 )?;
796 }
797
798 if !Self::check_eip155_transaction(tx, cip90a, &mode) {
799 bail!(TransactionError::FutureTransactionType {
800 tx_type: LEGACY_TX_TYPE,
801 current_height: height,
802 enable_height: transitions.cip90a,
803 });
804 }
805
806 if !Self::check_eip1559_transaction(tx, cip1559, &mode) {
807 bail!(TransactionError::FutureTransactionType {
808 tx_type: EIP1559_TYPE,
809 current_height: height,
810 enable_height: transitions.cip1559,
811 })
812 }
813
814 if !Self::check_eip7702_transaction(tx, cip7702, &mode) {
815 bail!(TransactionError::FutureTransactionType {
816 tx_type: EIP7702_TYPE,
817 current_height: height,
818 enable_height: transitions.cip7702,
819 })
820 }
821
822 if !Self::check_eip3860(tx, cip645) {
823 bail!(TransactionError::CreateInitCodeSizeLimit)
824 }
825
826 Self::check_eip1559_validation(tx, cip645)?;
827 Self::check_eip7702_validation(tx)?;
828
829 Self::check_gas_limit(tx, cip76, eip7623, &mode)?;
830 Self::check_gas_limit_with_calldata(tx, cip130)?;
831
832 Ok(())
833 }
834
835 fn check_eip155_transaction(
836 tx: &TransactionWithSignature, cip90a: bool, mode: &VerifyTxMode,
837 ) -> bool {
838 if tx.space() == Space::Native {
839 return true;
840 }
841
842 use VerifyTxLocalMode::*;
843 match mode {
844 VerifyTxMode::Local(Full, spec) => cip90a && spec.cip90,
845 VerifyTxMode::Local(MaybeLater, _spec) => true,
846 VerifyTxMode::Remote(_) => cip90a,
847 }
848 }
849
850 fn check_eip1559_transaction(
851 tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode,
852 ) -> bool {
853 if tx.is_legacy() {
854 return true;
855 }
856
857 use VerifyTxLocalMode::*;
858 match mode {
859 VerifyTxMode::Local(Full, spec) => cip1559 && spec.cip1559,
860 VerifyTxMode::Local(MaybeLater, _spec) => true,
861 VerifyTxMode::Remote(_) => cip1559,
862 }
863 }
864
865 fn check_eip7702_transaction(
866 tx: &TransactionWithSignature, cip7702: bool, mode: &VerifyTxMode,
867 ) -> bool {
868 if !tx.after_7702() {
869 return true;
870 }
871
872 use VerifyTxLocalMode::*;
873 match mode {
874 VerifyTxMode::Local(Full, spec) => cip7702 && spec.cip7702,
875 VerifyTxMode::Local(MaybeLater, _spec) => true,
876 VerifyTxMode::Remote(_) => cip7702,
877 }
878 }
879
880 fn check_eip7702_validation(
881 tx: &TransactionWithSignature,
882 ) -> Result<(), TransactionError> {
883 if let Some(author_list) = tx.authorization_list() {
884 if author_list.is_empty() {
885 return Err(TransactionError::EmptyAuthorizationList);
886 }
887 }
888 Ok(())
889 }
890
891 fn check_eip1559_validation(
892 tx: &TransactionWithSignature, cip645: bool,
893 ) -> Result<(), TransactionError> {
894 if !cip645 || !tx.after_1559() {
895 return Ok(());
896 }
897
898 if tx.max_priority_gas_price() > tx.gas_price() {
899 return Err(TransactionError::PriortyGreaterThanMaxFee);
900 }
901 Ok(())
902 }
903
904 fn check_eip3860(tx: &TransactionWithSignature, cip645: bool) -> bool {
905 const SPEC: Spec = Spec::genesis_spec();
907 if !cip645 {
908 return true;
909 }
910 if tx.action() != Action::Create {
911 return true;
912 }
913
914 tx.data().len() <= SPEC.init_code_data_limit
915 }
916
917 fn check_gas_limit(
919 tx: &TransactionWithSignature, cip76: bool, eip7623: bool,
920 mode: &VerifyTxMode,
921 ) -> Result<(), TransactionError> {
922 let consensus_spec = match mode {
923 VerifyTxMode::Local(_, spec) => spec.to_consensus_spec(),
924 VerifyTxMode::Remote(spec) => {
925 if !eip7623 && cip76 {
926 return Ok(());
927 } else {
928 (*spec).clone()
929 }
930 }
931 };
932
933 let tx_intrinsic_gas = gas_required_for(
934 tx.action() == Action::Create,
935 &tx.data(),
936 tx.access_list(),
937 tx.authorization_len(),
938 &consensus_spec,
939 );
940
941 if *tx.gas() < tx_intrinsic_gas.into() {
942 bail!(TransactionError::NotEnoughBaseGas {
943 required: tx_intrinsic_gas.into(),
944 got: *tx.gas()
945 });
946 }
947
948 if eip7623 {
949 let floor_gas = eip7623_required_gas(&tx.data(), &consensus_spec);
950
951 if *tx.gas() < floor_gas.into() {
952 bail!(TransactionError::NotEnoughBaseGas {
953 required: floor_gas.into(),
954 got: *tx.gas()
955 });
956 }
957 }
958
959 Ok(())
960 }
961
962 fn check_gas_limit_with_calldata(
963 tx: &TransactionWithSignature, cip130: bool,
964 ) -> Result<(), TransactionError> {
965 if !cip130 {
966 return Ok(());
967 }
968 let data_length = tx.data().len();
969 let min_gas_limit = data_length.saturating_mul(100);
970 if tx.gas() < &U256::from(min_gas_limit) {
971 bail!(TransactionError::NotEnoughBaseGas {
972 required: min_gas_limit.into(),
973 got: *tx.gas()
974 });
975 }
976 Ok(())
977 }
978
979 pub fn check_tx_size(
980 &self, tx: &TransactionWithSignature,
981 ) -> Result<(), TransactionError> {
982 if tx.rlp_size() > self.max_block_size_in_bytes {
983 bail!(TransactionError::TooBig)
984 } else {
985 Ok(())
986 }
987 }
988}
989
990#[derive(Copy, Clone)]
991pub enum PackingCheckResult {
992 Pack,
993 Pending,
995 Drop, }
998
999#[derive(Copy, Clone)]
1000pub enum VerifyTxMode<'a> {
1001 Local(VerifyTxLocalMode, &'a Spec),
1003 Remote(&'a ConsensusGasSpec),
1006}
1007
1008#[derive(Copy, Clone)]
1009pub enum VerifyTxLocalMode {
1010 Full,
1012 MaybeLater,
1015}
1016
1017impl<'a> VerifyTxMode<'a> {
1018 fn is_maybe_later(&self) -> bool {
1019 if let VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, _) = self {
1020 true
1021 } else {
1022 false
1023 }
1024 }
1025}
1026
1027#[cfg(test)]
1028mod tests {
1029 use crate::verification::EpochReceiptProof;
1030 use cfx_storage::{
1031 CompressedPathRaw, TrieProof, TrieProofNode, VanillaChildrenTable,
1032 };
1033
1034 #[test]
1035 fn test_rlp_epoch_receipt_proof() {
1036 let proof = EpochReceiptProof::default();
1037 assert_eq!(proof, rlp::decode(&rlp::encode(&proof)).unwrap());
1038
1039 let serialized = serde_json::to_string(&proof).unwrap();
1040 let deserialized: EpochReceiptProof =
1041 serde_json::from_str(&serialized).unwrap();
1042 assert_eq!(proof, deserialized);
1043
1044 let node1 = TrieProofNode::new(
1045 Default::default(),
1046 Some(Box::new([0x03, 0x04, 0x05])),
1047 CompressedPathRaw::new(
1048 &[0x00, 0x01, 0x02],
1049 CompressedPathRaw::first_nibble_mask(),
1050 ),
1051 true,
1052 );
1053
1054 let root_node = {
1055 let mut children_table = VanillaChildrenTable::default();
1056 unsafe {
1057 *children_table.get_child_mut_unchecked(2) =
1058 *node1.get_merkle();
1059 *children_table.get_children_count_mut() = 1;
1060 }
1061 TrieProofNode::new(
1062 children_table,
1063 None,
1064 CompressedPathRaw::default(),
1065 false,
1066 )
1067 };
1068 let nodes = [root_node, node1]
1069 .iter()
1070 .cloned()
1071 .cycle()
1072 .take(20)
1073 .collect();
1074 let proof = TrieProof::new(nodes).unwrap();
1075
1076 let epoch_proof = EpochReceiptProof {
1077 block_index_proof: proof.clone(),
1078 block_receipt_proof: proof,
1079 };
1080
1081 assert_eq!(
1082 epoch_proof,
1083 rlp::decode(&rlp::encode(&epoch_proof)).unwrap()
1084 );
1085
1086 let serialized = serde_json::to_string(&epoch_proof).unwrap();
1087 let deserialized: EpochReceiptProof =
1088 serde_json::from_str(&serialized).unwrap();
1089 assert_eq!(epoch_proof, deserialized);
1090 }
1091}