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().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().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 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 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 self.verify_pow(pow, header)?;
404
405 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 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 #[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 #[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 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 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 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 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 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 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 Pending,
994 Drop, }
997
998#[derive(Copy, Clone)]
999pub enum VerifyTxMode<'a> {
1000 Local(VerifyTxLocalMode, &'a Spec),
1002 Remote(&'a ConsensusGasSpec),
1005}
1006
1007#[derive(Copy, Clone)]
1008pub enum VerifyTxLocalMode {
1009 Full,
1011 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 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 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}