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 let acc = &mut total_gas[t.space()];
504 *acc = acc.checked_add(*t.gas_limit()).ok_or_else(|| {
505 BlockError::InvalidPackedGasLimit(OutOfBounds {
506 min: None,
507 max: Some(*block.block_header.gas_limit()),
508 found: U256::MAX,
509 })
510 })?;
511 }
512
513 if block.block_header.height()
514 >= self.machine.params().transition_heights.cip1559
515 {
516 self.check_base_fee(block, parent, total_gas)?;
517 } else {
518 self.check_hard_gas_limit(block, total_gas)?;
519 }
520 Ok(())
521 }
522
523 fn check_hard_gas_limit(
524 &self, block: &Block, total_gas: SpaceMap<U256>,
525 ) -> Result<(), Error> {
526 let block_height = block.block_header.height();
527
528 let evm_space_gas_limit =
529 if self.machine.params().can_pack_evm_transaction(block_height) {
530 *block.block_header.gas_limit()
531 / self.machine.params().evm_transaction_gas_ratio
532 } else {
533 U256::zero()
534 };
535
536 let evm_total_gas = total_gas[Space::Ethereum];
537 let block_total_gas = total_gas[Space::Native]
540 .checked_add(total_gas[Space::Ethereum])
541 .ok_or_else(|| {
542 BlockError::InvalidPackedGasLimit(OutOfBounds {
543 min: None,
544 max: Some(*block.block_header.gas_limit()),
545 found: U256::MAX,
546 })
547 })?;
548
549 if evm_total_gas > evm_space_gas_limit {
550 return Err(From::from(BlockError::InvalidPackedGasLimit(
551 OutOfBounds {
552 min: None,
553 max: Some(evm_space_gas_limit),
554 found: evm_total_gas,
555 },
556 )));
557 }
558
559 if block_total_gas > *block.block_header.gas_limit() {
560 return Err(From::from(BlockError::InvalidPackedGasLimit(
561 OutOfBounds {
562 min: None,
563 max: Some(*block.block_header.gas_limit()),
564 found: block_total_gas,
565 },
566 )));
567 }
568
569 Ok(())
570 }
571
572 fn check_base_fee(
573 &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap<U256>,
574 ) -> Result<(), Error> {
575 use Space::*;
576
577 let params = self.machine.params();
578 let cip1559_init = params.transition_heights.cip1559;
579 let block_height = block.block_header.height();
580
581 assert!(block_height >= cip1559_init);
582
583 let core_gas_limit = block.block_header.core_space_gas_limit();
584 let espace_gas_limit = block
585 .block_header
586 .espace_gas_limit(params.can_pack_evm_transaction(block_height));
587
588 if total_gas[Ethereum] > espace_gas_limit {
589 return Err(From::from(BlockError::InvalidPackedGasLimit(
590 OutOfBounds {
591 min: None,
592 max: Some(espace_gas_limit),
593 found: total_gas[Ethereum],
594 },
595 )));
596 }
597
598 if total_gas[Native] > core_gas_limit {
599 return Err(From::from(BlockError::InvalidPackedGasLimit(
600 OutOfBounds {
601 min: None,
602 max: Some(core_gas_limit),
603 found: total_gas[Native],
604 },
605 )));
606 }
607
608 let parent_base_price = if block_height == cip1559_init {
609 params.init_base_price()
610 } else {
611 parent.base_price().unwrap()
612 };
613
614 let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit);
615 let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER);
616 let min_base_price = params.min_base_price();
617
618 let expected_base_price = SpaceMap::zip4(
619 gas_target,
620 total_gas,
621 parent_base_price,
622 min_base_price,
623 )
624 .map_all(compute_next_price_tuple);
625
626 let actual_base_price = block.block_header.base_price().unwrap();
627
628 if actual_base_price != expected_base_price {
629 return Err(From::from(BlockError::InvalidBasePrice(Mismatch {
630 expected: expected_base_price,
631 found: actual_base_price,
632 })));
633 }
634
635 Ok(())
636 }
637
638 pub fn check_transaction_epoch_bound(
639 tx: &TypedNativeTransaction, block_height: u64,
640 transaction_epoch_bound: u64,
641 ) -> i8 {
642 if tx.epoch_height().wrapping_add(transaction_epoch_bound)
643 < block_height
644 {
645 -1
646 } else if *tx.epoch_height() > block_height + transaction_epoch_bound {
647 1
648 } else {
649 0
650 }
651 }
652
653 fn verify_transaction_epoch_height(
654 tx: &TypedNativeTransaction, block_height: u64,
655 transaction_epoch_bound: u64, mode: &VerifyTxMode,
656 ) -> Result<(), TransactionError> {
657 let result = Self::check_transaction_epoch_bound(
658 tx,
659 block_height,
660 transaction_epoch_bound,
661 );
662 let allow_larger_epoch = mode.is_maybe_later();
663
664 if result == 0 || (result > 0 && allow_larger_epoch) {
665 Ok(())
666 } else {
667 bail!(TransactionError::EpochHeightOutOfBound {
668 set: *tx.epoch_height(),
669 block_height,
670 transaction_epoch_bound,
671 });
672 }
673 }
674
675 fn fast_recheck_inner<F>(spec: &Spec, f: F) -> (bool, bool)
676 where F: Fn(&VerifyTxMode) -> bool {
677 let tx_pool_mode =
678 VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, spec);
679 let packing_mode = VerifyTxMode::Local(VerifyTxLocalMode::Full, spec);
680
681 (f(&packing_mode), f(&tx_pool_mode))
682 }
683
684 pub fn fast_recheck(
685 &self, tx: &TransactionWithSignature, height: BlockHeight,
686 transitions: &TransitionsEpochHeight, spec: &Spec,
687 ) -> PackingCheckResult {
688 let cip90a = height >= transitions.cip90a;
689 let cip1559 = height >= transitions.cip1559;
690 let cip7702 = height >= transitions.cip7702;
691 let cip645 = height >= transitions.cip645;
692
693 let (can_pack, later_pack) = Self::fast_recheck_inner(
694 spec,
695 |mode: &VerifyTxMode| {
696 if !Self::check_eip1559_transaction(tx, cip1559, mode) {
697 trace!(
698 "fast_recheck: EIP-1559 transaction check failed at height {} txhash={:?}",
699 height,
700 tx.hash()
701 );
702 return false;
703 }
704
705 if !Self::check_eip7702_transaction(tx, cip7702, mode) {
706 trace!(
707 "fast_recheck: EIP-7702 transaction check failed at height {} txhash={:?}",
708 height,
709 tx.hash()
710 );
711 return false;
712 }
713
714 if !Self::check_eip3860(tx, cip645) {
715 trace!(
716 "fast_recheck: EIP-3860 transaction check failed at height {} txhash={:?}",
717 height,
718 tx.hash()
719 );
720 return false;
721 }
722
723 if let Transaction::Native(ref tx) = tx.unsigned {
724 Self::verify_transaction_epoch_height(
725 tx,
726 height,
727 self.transaction_epoch_bound,
728 mode,
729 )
730 .is_ok()
731 } else {
732 Self::check_eip155_transaction(tx, cip90a, mode)
733 }
734 },
735 );
736
737 match (can_pack, later_pack) {
738 (true, _) => PackingCheckResult::Pack,
739 (false, true) => PackingCheckResult::Pending,
740 (false, false) => PackingCheckResult::Drop,
741 }
742 }
743
744 pub fn verify_transaction_common(
748 &self, tx: &TransactionWithSignature, chain_id: AllChainID,
749 height: BlockHeight, transitions: &TransitionsEpochHeight,
750 mode: VerifyTxMode,
751 ) -> Result<(), TransactionError> {
752 tx.check_low_s()?;
753 tx.check_y_parity()?;
754
755 if tx.is_unsigned() {
757 bail!(TransactionError::InvalidSignature(
758 "Transaction is unsigned".into()
759 ));
760 }
761
762 if let Some(tx_chain_id) = tx.chain_id() {
763 if tx_chain_id != chain_id.in_space(tx.space()) {
764 bail!(TransactionError::ChainIdMismatch {
765 expected: chain_id.in_space(tx.space()),
766 got: tx_chain_id,
767 space: tx.space(),
768 });
769 }
770 }
771
772 if tx.gas_price().is_zero() {
774 bail!(TransactionError::ZeroGasPrice);
775 }
776
777 if matches!(mode, VerifyTxMode::Local(..))
778 && tx.space() == Space::Native
779 {
780 if let Action::Call(ref address) = tx.transaction.action() {
781 if !address.is_genesis_valid_address() {
782 bail!(TransactionError::InvalidReceiver)
783 }
784 }
785 }
786
787 if let (VerifyTxMode::Local(..), Some(max_nonce)) =
788 (mode, self.max_nonce)
789 {
790 if tx.nonce() > &max_nonce {
791 bail!(TransactionError::TooLargeNonce)
792 }
793 }
794
795 let cip76 = height >= transitions.cip76;
800 let cip90a = height >= transitions.cip90a;
801 let cip130 =
802 height >= transitions.cip130 && height < transitions.align_evm;
803 let cip1559 = height >= transitions.cip1559;
804 let cip7702 = height >= transitions.cip7702;
805 let cip645 = height >= transitions.cip645;
806 let eip7623 = height >= transitions.eip7623;
807 let cip172 = height >= transitions.cip172;
808
809 if let Transaction::Native(ref tx) = tx.unsigned {
810 Self::verify_transaction_epoch_height(
811 tx,
812 height,
813 self.transaction_epoch_bound,
814 &mode,
815 )?;
816 }
817
818 if !Self::check_eip155_transaction(tx, cip90a, &mode) {
819 bail!(TransactionError::FutureTransactionType {
820 tx_type: LEGACY_TX_TYPE,
821 current_height: height,
822 enable_height: transitions.cip90a,
823 });
824 }
825
826 if !Self::check_eip1559_transaction(tx, cip1559, &mode) {
827 bail!(TransactionError::FutureTransactionType {
828 tx_type: EIP1559_TYPE,
829 current_height: height,
830 enable_height: transitions.cip1559,
831 })
832 }
833
834 if !Self::check_eip7702_transaction(tx, cip7702, &mode) {
835 bail!(TransactionError::FutureTransactionType {
836 tx_type: EIP7702_TYPE,
837 current_height: height,
838 enable_height: transitions.cip7702,
839 })
840 }
841
842 if !Self::check_eip3860(tx, cip645) {
843 bail!(TransactionError::CreateInitCodeSizeLimit)
844 }
845
846 Self::check_eip1559_validation(tx, cip645)?;
847 Self::check_eip7702_validation(tx)?;
848
849 Self::check_gas_limit(tx, cip76, eip7623, &mode)?;
850 Self::check_gas_limit_with_calldata(tx, cip130)?;
851 Self::check_canonical_rlp(tx, cip172, &mode)?;
852
853 Ok(())
854 }
855
856 fn check_canonical_rlp(
861 tx: &TransactionWithSignature, cip172: bool, mode: &VerifyTxMode,
862 ) -> Result<(), TransactionError> {
863 if tx.is_canonical_rlp() {
864 return Ok(());
865 }
866 let rejected = match mode {
867 VerifyTxMode::Local(..) => true,
868 VerifyTxMode::Remote(_) => cip172,
869 };
870 if rejected {
871 bail!(TransactionError::InvalidRlp(
872 "non-canonical transaction RLP encoding".into()
873 ));
874 }
875 Ok(())
876 }
877
878 fn check_eip155_transaction(
879 tx: &TransactionWithSignature, cip90a: bool, mode: &VerifyTxMode,
880 ) -> bool {
881 if tx.space() == Space::Native {
882 return true;
883 }
884
885 use VerifyTxLocalMode::*;
886 match mode {
887 VerifyTxMode::Local(Full, spec) => cip90a && spec.cip90,
888 VerifyTxMode::Local(MaybeLater, _spec) => true,
889 VerifyTxMode::Remote(_) => cip90a,
890 }
891 }
892
893 fn check_eip1559_transaction(
894 tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode,
895 ) -> bool {
896 if tx.is_legacy() {
897 return true;
898 }
899
900 use VerifyTxLocalMode::*;
901 match mode {
902 VerifyTxMode::Local(Full, spec) => cip1559 && spec.cip1559,
903 VerifyTxMode::Local(MaybeLater, _spec) => true,
904 VerifyTxMode::Remote(_) => cip1559,
905 }
906 }
907
908 fn check_eip7702_transaction(
909 tx: &TransactionWithSignature, cip7702: bool, mode: &VerifyTxMode,
910 ) -> bool {
911 if !tx.after_7702() {
912 return true;
913 }
914
915 use VerifyTxLocalMode::*;
916 match mode {
917 VerifyTxMode::Local(Full, spec) => cip7702 && spec.cip7702,
918 VerifyTxMode::Local(MaybeLater, _spec) => true,
919 VerifyTxMode::Remote(_) => cip7702,
920 }
921 }
922
923 fn check_eip7702_validation(
924 tx: &TransactionWithSignature,
925 ) -> Result<(), TransactionError> {
926 if let Some(author_list) = tx.authorization_list() {
927 if author_list.is_empty() {
928 return Err(TransactionError::EmptyAuthorizationList);
929 }
930 }
931 Ok(())
932 }
933
934 fn check_eip1559_validation(
935 tx: &TransactionWithSignature, cip645: bool,
936 ) -> Result<(), TransactionError> {
937 if !cip645 || !tx.after_1559() {
938 return Ok(());
939 }
940
941 if tx.max_priority_gas_price() > tx.gas_price() {
942 return Err(TransactionError::PriortyGreaterThanMaxFee);
943 }
944 Ok(())
945 }
946
947 fn check_eip3860(tx: &TransactionWithSignature, cip645: bool) -> bool {
948 const SPEC: Spec = Spec::genesis_spec();
950 if !cip645 {
951 return true;
952 }
953 if tx.action() != Action::Create {
954 return true;
955 }
956
957 tx.data().len() <= SPEC.init_code_data_limit
958 }
959
960 fn check_gas_limit(
962 tx: &TransactionWithSignature, cip76: bool, eip7623: bool,
963 mode: &VerifyTxMode,
964 ) -> Result<(), TransactionError> {
965 let consensus_spec = match mode {
966 VerifyTxMode::Local(_, spec) => spec.to_consensus_spec(),
967 VerifyTxMode::Remote(spec) => {
968 if !eip7623 && cip76 {
969 return Ok(());
970 } else {
971 (*spec).clone()
972 }
973 }
974 };
975
976 let tx_intrinsic_gas = gas_required_for(
977 tx.action() == Action::Create,
978 &tx.data(),
979 tx.access_list(),
980 tx.authorization_len(),
981 &consensus_spec,
982 );
983
984 if *tx.gas() < tx_intrinsic_gas.into() {
985 bail!(TransactionError::NotEnoughBaseGas {
986 required: tx_intrinsic_gas.into(),
987 got: *tx.gas()
988 });
989 }
990
991 if eip7623 {
992 let floor_gas = eip7623_required_gas(&tx.data(), &consensus_spec);
993
994 if *tx.gas() < floor_gas.into() {
995 bail!(TransactionError::NotEnoughBaseGas {
996 required: floor_gas.into(),
997 got: *tx.gas()
998 });
999 }
1000 }
1001
1002 Ok(())
1003 }
1004
1005 fn check_gas_limit_with_calldata(
1006 tx: &TransactionWithSignature, cip130: bool,
1007 ) -> Result<(), TransactionError> {
1008 if !cip130 {
1009 return Ok(());
1010 }
1011 let data_length = tx.data().len();
1012 let min_gas_limit = data_length.saturating_mul(100);
1013 if tx.gas() < &U256::from(min_gas_limit) {
1014 bail!(TransactionError::NotEnoughBaseGas {
1015 required: min_gas_limit.into(),
1016 got: *tx.gas()
1017 });
1018 }
1019 Ok(())
1020 }
1021
1022 pub fn check_tx_size(
1023 &self, tx: &TransactionWithSignature,
1024 ) -> Result<(), TransactionError> {
1025 if tx.rlp_size() > self.max_block_size_in_bytes {
1026 bail!(TransactionError::TooBig)
1027 } else {
1028 Ok(())
1029 }
1030 }
1031}
1032
1033#[derive(Copy, Clone)]
1034pub enum PackingCheckResult {
1035 Pack,
1036 Pending,
1038 Drop, }
1041
1042#[derive(Copy, Clone)]
1043pub enum VerifyTxMode<'a> {
1044 Local(VerifyTxLocalMode, &'a Spec),
1046 Remote(&'a ConsensusGasSpec),
1049}
1050
1051#[derive(Copy, Clone)]
1052pub enum VerifyTxLocalMode {
1053 Full,
1055 MaybeLater,
1058}
1059
1060impl<'a> VerifyTxMode<'a> {
1061 fn is_maybe_later(&self) -> bool {
1062 if let VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, _) = self {
1063 true
1064 } else {
1065 false
1066 }
1067 }
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072 use crate::verification::EpochReceiptProof;
1073 use cfx_storage::{
1074 CompressedPathRaw, TrieProof, TrieProofNode, VanillaChildrenTable,
1075 };
1076
1077 #[test]
1078 fn test_rlp_epoch_receipt_proof() {
1079 let proof = EpochReceiptProof::default();
1080 assert_eq!(proof, rlp::decode(&rlp::encode(&proof)).unwrap());
1081
1082 let serialized = serde_json::to_string(&proof).unwrap();
1083 let deserialized: EpochReceiptProof =
1084 serde_json::from_str(&serialized).unwrap();
1085 assert_eq!(proof, deserialized);
1086
1087 let node1 = TrieProofNode::new(
1088 Default::default(),
1089 Some(Box::new([0x03, 0x04, 0x05])),
1090 CompressedPathRaw::new(
1091 &[0x00, 0x01, 0x02],
1092 CompressedPathRaw::first_nibble_mask(),
1093 ),
1094 true,
1095 );
1096
1097 let root_node = {
1098 let mut children_table = VanillaChildrenTable::default();
1099 unsafe {
1100 *children_table.get_child_mut_unchecked(2) =
1101 *node1.get_merkle();
1102 *children_table.get_children_count_mut() = 1;
1103 }
1104 TrieProofNode::new(
1105 children_table,
1106 None,
1107 CompressedPathRaw::default(),
1108 false,
1109 )
1110 };
1111 let nodes = [root_node, node1]
1112 .iter()
1113 .cloned()
1114 .cycle()
1115 .take(20)
1116 .collect();
1117 let proof = TrieProof::new(nodes).unwrap();
1118
1119 let epoch_proof = EpochReceiptProof {
1120 block_index_proof: proof.clone(),
1121 block_receipt_proof: proof,
1122 };
1123
1124 assert_eq!(
1125 epoch_proof,
1126 rlp::decode(&rlp::encode(&epoch_proof)).unwrap()
1127 );
1128
1129 let serialized = serde_json::to_string(&epoch_proof).unwrap();
1130 let deserialized: EpochReceiptProof =
1131 serde_json::from_str(&serialized).unwrap();
1132 assert_eq!(epoch_proof, deserialized);
1133 }
1134
1135 #[test]
1138 fn packed_gas_sum_overflow_is_rejected() {
1139 use crate::{
1140 core_error::{BlockError, CoreError as Error},
1141 verification::{compute_transaction_root, VerificationConfig},
1142 };
1143 use cfx_executor::{
1144 machine::{Machine, VmFactory},
1145 spec::CommonParams,
1146 };
1147 use cfx_types::U256;
1148 use cfxkey::{Generator, Random};
1149 use primitives::{
1150 transaction::native_transaction::{
1151 NativeTransaction, TypedNativeTransaction,
1152 },
1153 Action, Block, BlockHeaderBuilder, Transaction,
1154 };
1155 use std::sync::Arc;
1156
1157 let params = CommonParams::default();
1158 let chain_id = params.chain_id.read().get_chain_id(1);
1159 let machine =
1160 Arc::new(Machine::new_with_builtin(params, VmFactory::new(1024)));
1161 let config = VerificationConfig::new(
1162 false,
1163 200,
1164 200 * 1024,
1165 100_000,
1166 128,
1167 u64::MAX,
1168 machine,
1169 );
1170
1171 let keypair = Random.generate().unwrap();
1172 let tx = |nonce: u64| {
1173 Arc::new(
1174 Transaction::Native(TypedNativeTransaction::Cip155(
1175 NativeTransaction {
1176 nonce: nonce.into(),
1177 gas_price: U256::one(),
1178 gas: U256::one() << 255,
1180 action: Action::Create,
1181 value: U256::zero(),
1182 storage_limit: 0,
1183 epoch_height: 1,
1184 chain_id: chain_id.in_native_space(),
1185 data: vec![],
1186 },
1187 ))
1188 .sign(keypair.secret()),
1189 )
1190 };
1191 let txs = vec![tx(0), tx(1)];
1192
1193 let parent = BlockHeaderBuilder::new().with_height(0).build();
1194 let header = BlockHeaderBuilder::new()
1195 .with_height(1)
1196 .with_parent_hash(parent.hash())
1197 .with_transactions_root(compute_transaction_root(&txs))
1198 .with_gas_limit(30_000_000.into())
1199 .build();
1200 let block = Block::new(header, txs);
1201
1202 assert!(matches!(
1203 config.verify_sync_graph_ready_block(&block, &parent),
1204 Err(Error::Block(BlockError::InvalidPackedGasLimit(_))),
1205 ));
1206 }
1207}