1use crate::{
6 core_error::{BlockError, CoreError as Error},
7 pow::{
8 self, nonce_has_non_zero_high_128_bits, nonce_to_lower_bound,
9 PowComputer, ProofOfWorkProblem, MAX_POW_HEIGHT,
10 },
11 sync::Error as SyncError,
12};
13use cfx_executor::{
14 executive::{eip7623_required_gas, gas_required_for},
15 machine::Machine,
16 spec::TransitionsEpochHeight,
17};
18use cfx_parameters::{block::*, consensus_internal::ELASTICITY_MULTIPLIER};
19use cfx_storage::{
20 into_simple_mpt_key, make_simple_mpt, simple_mpt_merkle_root,
21 simple_mpt_proof, SimpleMpt, TrieProof,
22};
23use cfx_types::{
24 address_util::AddressUtil, AllChainID, BigEndianHash, Space, SpaceMap,
25 H256, U256,
26};
27use cfx_vm_types::{ConsensusGasSpec, Spec};
28use primitives::{
29 block::BlockHeight,
30 block_header::compute_next_price_tuple,
31 transaction::{
32 native_transaction::TypedNativeTransaction, TransactionError,
33 EIP1559_TYPE, EIP7702_TYPE, LEGACY_TX_TYPE,
34 },
35 Action, Block, BlockHeader, BlockReceipts, MerkleHash, Receipt,
36 SignedTransaction, Transaction, TransactionWithSignature,
37};
38use rlp::Encodable;
39use rlp_derive::{RlpDecodable, RlpEncodable};
40use serde_derive::{Deserialize, Serialize};
41use std::{collections::HashSet, convert::TryInto, sync::Arc};
42use unexpected::{Mismatch, OutOfBounds};
43
44#[derive(Clone)]
45pub struct VerificationConfig {
46 pub verify_timestamp: bool,
47 pub referee_bound: usize,
48 pub max_block_size_in_bytes: usize,
49 pub transaction_epoch_bound: u64,
50 pub max_nonce: Option<U256>,
51 machine: Arc<Machine>,
52 pos_enable_height: u64,
53 min_difficulty: U256,
59}
60
61fn transaction_trie(transactions: &Vec<Arc<SignedTransaction>>) -> SimpleMpt {
64 make_simple_mpt(
65 transactions
66 .iter()
67 .map(|tx| tx.hash.as_bytes().into())
68 .collect(),
69 )
70}
71
72pub fn compute_transaction_root(
75 transactions: &Vec<Arc<SignedTransaction>>,
76) -> MerkleHash {
77 simple_mpt_merkle_root(&mut transaction_trie(transactions))
78}
79
80pub fn compute_transaction_proof(
82 transactions: &Vec<Arc<SignedTransaction>>, tx_index_in_block: usize,
83) -> TrieProof {
84 simple_mpt_proof(
85 &mut transaction_trie(transactions),
86 &into_simple_mpt_key(tx_index_in_block, transactions.len()),
87 )
88}
89
90fn block_receipts_trie(block_receipts: &Vec<Receipt>) -> SimpleMpt {
93 make_simple_mpt(
94 block_receipts
95 .iter()
96 .map(|receipt| receipt.rlp_bytes().to_vec().into_boxed_slice())
97 .collect(),
98 )
99}
100
101fn compute_block_receipts_root(block_receipts: &Vec<Receipt>) -> MerkleHash {
103 simple_mpt_merkle_root(&mut block_receipts_trie(block_receipts))
104}
105
106pub fn compute_block_receipt_proof(
108 block_receipts: &Vec<Receipt>, tx_index_in_block: usize,
109) -> TrieProof {
110 simple_mpt_proof(
111 &mut block_receipts_trie(block_receipts),
112 &into_simple_mpt_key(tx_index_in_block, block_receipts.len()),
113 )
114}
115
116fn epoch_receipts_trie(epoch_receipts: &Vec<Arc<BlockReceipts>>) -> SimpleMpt {
119 make_simple_mpt(
120 epoch_receipts
121 .iter()
122 .map(|block_receipts| &block_receipts.receipts)
123 .map(|rs| compute_block_receipts_root(&rs).as_bytes().into())
124 .collect(),
125 )
126}
127
128pub fn compute_receipts_root(
131 epoch_receipts: &Vec<Arc<BlockReceipts>>,
132) -> MerkleHash {
133 simple_mpt_merkle_root(&mut epoch_receipts_trie(epoch_receipts))
134}
135
136#[derive(
137 Clone,
138 Debug,
139 RlpEncodable,
140 RlpDecodable,
141 Default,
142 PartialEq,
143 Serialize,
144 Deserialize,
145)]
146#[serde(rename_all = "camelCase")]
147pub struct EpochReceiptProof {
148 pub block_index_proof: TrieProof,
149 pub block_receipt_proof: TrieProof,
150}
151
152pub fn compute_epoch_receipt_proof(
155 epoch_receipts: &Vec<Arc<BlockReceipts>>, block_index_in_epoch: usize,
156 tx_index_in_block: usize,
157) -> EpochReceiptProof {
158 let block_receipt_proof = compute_block_receipt_proof(
159 &epoch_receipts[block_index_in_epoch].receipts,
160 tx_index_in_block,
161 );
162
163 let block_index_proof = simple_mpt_proof(
164 &mut epoch_receipts_trie(epoch_receipts),
165 &into_simple_mpt_key(block_index_in_epoch, epoch_receipts.len()),
166 );
167
168 EpochReceiptProof {
169 block_index_proof,
170 block_receipt_proof,
171 }
172}
173
174pub fn is_valid_tx_inclusion_proof(
178 block_tx_root: MerkleHash, tx_index_in_block: usize,
179 num_txs_in_block: usize, tx_hash: H256, proof: &TrieProof,
180) -> bool {
181 let key = &into_simple_mpt_key(tx_index_in_block, num_txs_in_block);
182 proof.is_valid_kv(key, Some(tx_hash.as_bytes()), &block_tx_root)
183}
184
185pub fn is_valid_receipt_inclusion_proof(
192 verified_epoch_receipts_root: MerkleHash, block_index_in_epoch: usize,
193 num_blocks_in_epoch: usize, block_index_proof: &TrieProof,
194 tx_index_in_block: usize, num_txs_in_block: usize, receipt: &Receipt,
195 block_receipt_proof: &TrieProof,
196) -> bool {
197 let key = &into_simple_mpt_key(block_index_in_epoch, num_blocks_in_epoch);
200
201 let block_receipts_root_bytes =
202 match block_index_proof.get_value(key, &verified_epoch_receipts_root) {
203 (false, _) => return false,
204 (true, None) => return false,
205 (true, Some(val)) => val,
206 };
207
208 let block_receipts_root: H256 = match TryInto::<[u8; 32]>::try_into(
210 block_receipts_root_bytes,
211 ) {
212 Ok(hash) => hash.into(),
213 Err(e) => {
214 error!(
216 "Invalid content found in valid MPT: key = {:?}, value = {:?}; error = {:?}",
217 key, block_receipts_root_bytes, e,
218 );
219 return false;
220 }
221 };
222
223 let key = &into_simple_mpt_key(tx_index_in_block, num_txs_in_block);
225
226 block_receipt_proof.is_valid_kv(
227 key,
228 Some(&receipt.rlp_bytes()[..]),
229 &block_receipts_root,
230 )
231}
232
233impl VerificationConfig {
234 pub fn new(
235 test_mode: bool, referee_bound: usize, max_block_size_in_bytes: usize,
236 transaction_epoch_bound: u64, tx_pool_nonce_bits: usize,
237 pos_enable_height: u64, initial_difficulty: u64, machine: Arc<Machine>,
238 ) -> Self {
239 let max_nonce = if tx_pool_nonce_bits < 256 {
240 Some((U256::one() << tx_pool_nonce_bits) - 1)
241 } else {
242 None
243 };
244 VerificationConfig {
245 verify_timestamp: !test_mode,
246 referee_bound,
247 max_block_size_in_bytes,
248 transaction_epoch_bound,
249 machine,
250 pos_enable_height,
251 max_nonce,
252 min_difficulty: initial_difficulty.max(1).into(),
254 }
255 }
256
257 #[inline]
258 pub fn get_or_fill_header_pow_hash(
260 pow: &PowComputer, header: &mut BlockHeader,
261 ) -> H256 {
262 if header.pow_hash.is_none() {
263 header.pow_hash = Some(Self::compute_pow_hash(pow, header));
264 }
265 header.pow_hash.unwrap()
266 }
267
268 pub fn get_or_fill_header_pow_quality(
269 pow: &PowComputer, header: &mut BlockHeader,
270 ) -> U256 {
271 let pow_hash = Self::get_or_fill_header_pow_hash(pow, header);
272 pow::pow_hash_to_quality(&pow_hash, &header.nonce())
273 }
274
275 pub fn get_or_compute_header_pow_quality(
276 pow: &PowComputer, header: &BlockHeader,
277 ) -> U256 {
278 let pow_hash = header
279 .pow_hash
280 .unwrap_or_else(|| Self::compute_pow_hash(pow, header));
281 pow::pow_hash_to_quality(&pow_hash, &header.nonce())
282 }
283
284 fn compute_pow_hash(pow: &PowComputer, header: &BlockHeader) -> H256 {
285 let nonce = header.nonce();
286 pow.compute(&nonce, &header.problem_hash(), header.height())
287 }
288
289 #[inline]
290 pub fn verify_pow(
291 &self, pow: &PowComputer, header: &mut BlockHeader,
292 ) -> Result<(), Error> {
293 if header.height() > MAX_POW_HEIGHT {
298 return Err(BlockError::InvalidHeight(Mismatch {
299 expected: MAX_POW_HEIGHT,
300 found: header.height(),
301 })
302 .into());
303 }
304 if *header.difficulty() < self.min_difficulty {
305 return Err(BlockError::InvalidDifficulty(OutOfBounds {
306 min: Some(self.min_difficulty),
307 max: None,
308 found: *header.difficulty(),
309 })
310 .into());
311 }
312 if header.height()
313 >= self.machine.params().transition_heights.cip_hn_fix
314 {
315 if nonce_has_non_zero_high_128_bits(&header.nonce()) {
316 return Err(From::from(BlockError::InvalidNonce));
317 }
318 }
319 let pow_hash = Self::get_or_fill_header_pow_hash(pow, header);
320 let boundary = pow::difficulty_to_boundary(header.difficulty());
321 if !ProofOfWorkProblem::validate_hash_against_boundary(
322 &pow_hash,
323 &header.nonce(),
324 &boundary,
325 ) {
326 let lower_bound = nonce_to_lower_bound(&header.nonce());
327 let (upper_bound, _) = lower_bound.overflowing_add(boundary);
331 warn!("block {} has invalid proof of work. boundary: [{}, {}), pow_hash: {}",
332 header.hash(), lower_bound.clone(), upper_bound.clone(), pow_hash.clone());
333 return Err(From::from(BlockError::InvalidProofOfWork(
334 OutOfBounds {
335 min: Some(BigEndianHash::from_uint(&lower_bound)),
336 max: Some(BigEndianHash::from_uint(&upper_bound)),
337 found: pow_hash,
338 },
339 )));
340 }
341
342 assert!(
343 Self::get_or_fill_header_pow_quality(pow, header)
344 >= *header.difficulty()
345 );
346
347 Ok(())
348 }
349
350 #[inline]
351 pub fn validate_header_timestamp(
352 &self, header: &BlockHeader, now: u64,
353 ) -> Result<(), SyncError> {
354 let invalid_threshold = now + VALID_TIME_DRIFT;
355 if header.timestamp() > invalid_threshold {
356 warn!("block {} has incorrect timestamp", header.hash());
357 return Err(SyncError::InvalidTimestamp.into());
358 }
359 Ok(())
360 }
361
362 fn is_pos_enabled_at_height(&self, height: u64) -> bool {
363 height >= self.pos_enable_height
364 }
365
366 #[inline]
369 pub fn verify_header_params(
370 &self, pow: &PowComputer, header: &mut BlockHeader,
371 ) -> Result<(), Error> {
372 let custom_len = header.custom_data_len();
374 if custom_len > HEADER_CUSTOM_LENGTH_BOUND {
375 return Err(From::from(BlockError::TooLongCustomInHeader(
376 OutOfBounds {
377 min: Some(0),
378 max: Some(HEADER_CUSTOM_LENGTH_BOUND),
379 found: custom_len,
380 },
381 )));
382 }
383
384 if self.is_pos_enabled_at_height(header.height()) {
385 if header.pos_reference().is_none() {
386 bail!(BlockError::MissingPosReference);
387 }
388 } else {
389 if header.pos_reference().is_some() {
390 bail!(BlockError::UnexpectedPosReference);
391 }
392 }
393
394 if header.height() >= self.machine.params().transition_heights.cip1559 {
395 if header.base_price().is_none() {
396 bail!(BlockError::MissingBaseFee);
397 }
398 } else {
399 if header.base_price().is_some() {
400 bail!(BlockError::UnexpectedBaseFee);
401 }
402 }
403
404 if let Some(expected_custom_prefix) =
410 self.machine.params().custom_prefix(header.height())
411 {
412 for (i, expected_bytes) in expected_custom_prefix.iter().enumerate()
413 {
414 let matches =
416 header.custom_item(i).is_some_and(|b| &b == expected_bytes);
417 if !matches {
418 let header_prefix = (0..expected_custom_prefix.len())
421 .filter_map(|j| header.custom_item(j))
422 .collect();
423 return Err(BlockError::InvalidCustom(
424 header_prefix,
425 expected_custom_prefix.clone(),
426 )
427 .into());
428 }
429 }
430 }
431
432 self.verify_pow(pow, header)?;
434
435 if header.referee_hashes().len() > self.referee_bound {
437 return Err(From::from(BlockError::TooManyReferees(OutOfBounds {
438 min: Some(0),
439 max: Some(self.referee_bound),
440 found: header.referee_hashes().len(),
441 })));
442 }
443
444 let mut direct_ancestor_hashes = HashSet::new();
446 let parent_hash = header.parent_hash();
447 direct_ancestor_hashes.insert(parent_hash.clone());
448 for referee_hash in header.referee_hashes() {
449 if direct_ancestor_hashes.contains(referee_hash) {
450 warn!(
451 "block {} has duplicate parent or referee hashes",
452 header.hash()
453 );
454 return Err(From::from(
455 BlockError::DuplicateParentOrRefereeHashes(
456 referee_hash.clone(),
457 ),
458 ));
459 }
460 direct_ancestor_hashes.insert(referee_hash.clone());
461 }
462
463 Ok(())
464 }
465
466 #[inline]
468 fn verify_block_integrity(&self, block: &Block) -> Result<(), Error> {
469 let expected_root = compute_transaction_root(&block.transactions);
470 if &expected_root != block.block_header.transactions_root() {
471 warn!("Invalid transaction root");
472 bail!(BlockError::InvalidTransactionsRoot(Mismatch {
473 expected: expected_root,
474 found: *block.block_header.transactions_root(),
475 }));
476 }
477 Ok(())
478 }
479
480 #[inline]
490 pub fn verify_sync_graph_block_basic(
491 &self, block: &Block, chain_id: AllChainID,
492 ) -> Result<(), Error> {
493 self.verify_block_integrity(block)?;
494
495 let block_height = block.block_header.height();
496
497 let mut block_size = 0;
498 let transitions = &self.machine.params().transition_heights;
499 let consensus_spec =
500 &self.machine.params().consensus_spec(block_height);
501
502 for t in &block.transactions {
503 self.verify_transaction_common(
504 t,
505 chain_id,
506 block_height,
507 transitions,
508 VerifyTxMode::Remote(consensus_spec),
509 )?;
510 block_size += t.rlp_size();
511 }
512
513 if block_size > self.max_block_size_in_bytes {
514 return Err(From::from(BlockError::InvalidBlockSize(
515 OutOfBounds {
516 min: None,
517 max: Some(self.max_block_size_in_bytes as u64),
518 found: block_size as u64,
519 },
520 )));
521 }
522 Ok(())
523 }
524
525 pub fn verify_sync_graph_ready_block(
526 &self, block: &Block, parent: &BlockHeader,
527 ) -> Result<(), Error> {
528 let mut total_gas: SpaceMap<U256> = SpaceMap::default();
529 for t in &block.transactions {
530 let acc = &mut total_gas[t.space()];
533 *acc = acc.checked_add(*t.gas_limit()).ok_or_else(|| {
534 BlockError::InvalidPackedGasLimit(OutOfBounds {
535 min: None,
536 max: Some(*block.block_header.gas_limit()),
537 found: U256::MAX,
538 })
539 })?;
540 }
541
542 if block.block_header.height()
543 >= self.machine.params().transition_heights.cip1559
544 {
545 self.check_base_fee(block, parent, total_gas)?;
546 } else {
547 self.check_hard_gas_limit(block, total_gas)?;
548 }
549 Ok(())
550 }
551
552 fn check_hard_gas_limit(
553 &self, block: &Block, total_gas: SpaceMap<U256>,
554 ) -> Result<(), Error> {
555 let block_height = block.block_header.height();
556
557 let evm_space_gas_limit =
558 if self.machine.params().can_pack_evm_transaction(block_height) {
559 *block.block_header.gas_limit()
560 / self.machine.params().evm_transaction_gas_ratio
561 } else {
562 U256::zero()
563 };
564
565 let evm_total_gas = total_gas[Space::Ethereum];
566 let block_total_gas = total_gas[Space::Native]
569 .checked_add(total_gas[Space::Ethereum])
570 .ok_or_else(|| {
571 BlockError::InvalidPackedGasLimit(OutOfBounds {
572 min: None,
573 max: Some(*block.block_header.gas_limit()),
574 found: U256::MAX,
575 })
576 })?;
577
578 if evm_total_gas > evm_space_gas_limit {
579 return Err(From::from(BlockError::InvalidPackedGasLimit(
580 OutOfBounds {
581 min: None,
582 max: Some(evm_space_gas_limit),
583 found: evm_total_gas,
584 },
585 )));
586 }
587
588 if block_total_gas > *block.block_header.gas_limit() {
589 return Err(From::from(BlockError::InvalidPackedGasLimit(
590 OutOfBounds {
591 min: None,
592 max: Some(*block.block_header.gas_limit()),
593 found: block_total_gas,
594 },
595 )));
596 }
597
598 Ok(())
599 }
600
601 fn check_base_fee(
602 &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap<U256>,
603 ) -> Result<(), Error> {
604 use Space::*;
605
606 let params = self.machine.params();
607 let cip1559_init = params.transition_heights.cip1559;
608 let block_height = block.block_header.height();
609
610 assert!(block_height >= cip1559_init);
611
612 let core_gas_limit = block.block_header.core_space_gas_limit();
613 let espace_gas_limit = block
614 .block_header
615 .espace_gas_limit(params.can_pack_evm_transaction(block_height));
616
617 if total_gas[Ethereum] > espace_gas_limit {
618 return Err(From::from(BlockError::InvalidPackedGasLimit(
619 OutOfBounds {
620 min: None,
621 max: Some(espace_gas_limit),
622 found: total_gas[Ethereum],
623 },
624 )));
625 }
626
627 if total_gas[Native] > core_gas_limit {
628 return Err(From::from(BlockError::InvalidPackedGasLimit(
629 OutOfBounds {
630 min: None,
631 max: Some(core_gas_limit),
632 found: total_gas[Native],
633 },
634 )));
635 }
636
637 let parent_base_price = if block_height == cip1559_init {
638 params.init_base_price()
639 } else {
640 parent.base_price().unwrap()
641 };
642
643 let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit);
644 let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER);
645 let min_base_price = params.min_base_price();
646
647 let expected_base_price = SpaceMap::zip4(
648 gas_target,
649 total_gas,
650 parent_base_price,
651 min_base_price,
652 )
653 .map_all(compute_next_price_tuple);
654
655 let actual_base_price = block.block_header.base_price().unwrap();
656
657 if actual_base_price != expected_base_price {
658 return Err(From::from(BlockError::InvalidBasePrice(Mismatch {
659 expected: expected_base_price,
660 found: actual_base_price,
661 })));
662 }
663
664 Ok(())
665 }
666
667 pub fn check_transaction_epoch_bound(
668 tx: &TypedNativeTransaction, block_height: u64,
669 transaction_epoch_bound: u64,
670 ) -> i8 {
671 if tx.epoch_height().wrapping_add(transaction_epoch_bound)
672 < block_height
673 {
674 -1
675 } else if *tx.epoch_height() > block_height + transaction_epoch_bound {
676 1
677 } else {
678 0
679 }
680 }
681
682 fn verify_transaction_epoch_height(
683 tx: &TypedNativeTransaction, block_height: u64,
684 transaction_epoch_bound: u64, mode: &VerifyTxMode,
685 ) -> Result<(), TransactionError> {
686 let result = Self::check_transaction_epoch_bound(
687 tx,
688 block_height,
689 transaction_epoch_bound,
690 );
691 let allow_larger_epoch = mode.is_maybe_later();
692
693 if result == 0 || (result > 0 && allow_larger_epoch) {
694 Ok(())
695 } else {
696 bail!(TransactionError::EpochHeightOutOfBound {
697 set: *tx.epoch_height(),
698 block_height,
699 transaction_epoch_bound,
700 });
701 }
702 }
703
704 fn fast_recheck_inner<F>(spec: &Spec, f: F) -> (bool, bool)
705 where F: Fn(&VerifyTxMode) -> bool {
706 let tx_pool_mode =
707 VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, spec);
708 let packing_mode = VerifyTxMode::Local(VerifyTxLocalMode::Full, spec);
709
710 (f(&packing_mode), f(&tx_pool_mode))
711 }
712
713 pub fn fast_recheck(
714 &self, tx: &TransactionWithSignature, height: BlockHeight,
715 transitions: &TransitionsEpochHeight, spec: &Spec,
716 ) -> PackingCheckResult {
717 let cip90a = height >= transitions.cip90a;
718 let cip1559 = height >= transitions.cip1559;
719 let cip7702 = height >= transitions.cip7702;
720 let cip645 = height >= transitions.cip645;
721
722 let (can_pack, later_pack) = Self::fast_recheck_inner(
723 spec,
724 |mode: &VerifyTxMode| {
725 if !Self::check_eip1559_transaction(tx, cip1559, mode) {
726 trace!(
727 "fast_recheck: EIP-1559 transaction check failed at height {} txhash={:?}",
728 height,
729 tx.hash()
730 );
731 return false;
732 }
733
734 if !Self::check_eip7702_transaction(tx, cip7702, mode) {
735 trace!(
736 "fast_recheck: EIP-7702 transaction check failed at height {} txhash={:?}",
737 height,
738 tx.hash()
739 );
740 return false;
741 }
742
743 if !Self::check_eip3860(tx, cip645) {
744 trace!(
745 "fast_recheck: EIP-3860 transaction check failed at height {} txhash={:?}",
746 height,
747 tx.hash()
748 );
749 return false;
750 }
751
752 if let Transaction::Native(ref tx) = tx.unsigned {
753 Self::verify_transaction_epoch_height(
754 tx,
755 height,
756 self.transaction_epoch_bound,
757 mode,
758 )
759 .is_ok()
760 } else {
761 Self::check_eip155_transaction(tx, cip90a, mode)
762 }
763 },
764 );
765
766 match (can_pack, later_pack) {
767 (true, _) => PackingCheckResult::Pack,
768 (false, true) => PackingCheckResult::Pending,
769 (false, false) => PackingCheckResult::Drop,
770 }
771 }
772
773 pub fn verify_transaction_common(
777 &self, tx: &TransactionWithSignature, chain_id: AllChainID,
778 height: BlockHeight, transitions: &TransitionsEpochHeight,
779 mode: VerifyTxMode,
780 ) -> Result<(), TransactionError> {
781 tx.check_low_s()?;
782 tx.check_y_parity()?;
783
784 if tx.is_unsigned() {
786 bail!(TransactionError::InvalidSignature(
787 "Transaction is unsigned".into()
788 ));
789 }
790
791 if let Some(tx_chain_id) = tx.chain_id() {
792 if tx_chain_id != chain_id.in_space(tx.space()) {
793 bail!(TransactionError::ChainIdMismatch {
794 expected: chain_id.in_space(tx.space()),
795 got: tx_chain_id,
796 space: tx.space(),
797 });
798 }
799 }
800
801 if tx.gas_price().is_zero() {
803 bail!(TransactionError::ZeroGasPrice);
804 }
805
806 if matches!(mode, VerifyTxMode::Local(..))
807 && tx.space() == Space::Native
808 {
809 if let Action::Call(ref address) = tx.transaction.action() {
810 if !address.is_genesis_valid_address() {
811 bail!(TransactionError::InvalidReceiver)
812 }
813 }
814 }
815
816 if let (VerifyTxMode::Local(..), Some(max_nonce)) =
817 (mode, self.max_nonce)
818 {
819 if tx.nonce() > &max_nonce {
820 bail!(TransactionError::TooLargeNonce)
821 }
822 }
823
824 let cip76 = height >= transitions.cip76;
829 let cip90a = height >= transitions.cip90a;
830 let cip130 =
831 height >= transitions.cip130 && height < transitions.align_evm;
832 let cip1559 = height >= transitions.cip1559;
833 let cip7702 = height >= transitions.cip7702;
834 let cip645 = height >= transitions.cip645;
835 let eip7623 = height >= transitions.eip7623;
836 let cip172 = height >= transitions.cip172;
837
838 if let Transaction::Native(ref tx) = tx.unsigned {
839 Self::verify_transaction_epoch_height(
840 tx,
841 height,
842 self.transaction_epoch_bound,
843 &mode,
844 )?;
845 }
846
847 if !Self::check_eip155_transaction(tx, cip90a, &mode) {
848 bail!(TransactionError::FutureTransactionType {
849 tx_type: LEGACY_TX_TYPE,
850 current_height: height,
851 enable_height: transitions.cip90a,
852 });
853 }
854
855 if !Self::check_eip1559_transaction(tx, cip1559, &mode) {
856 bail!(TransactionError::FutureTransactionType {
857 tx_type: EIP1559_TYPE,
858 current_height: height,
859 enable_height: transitions.cip1559,
860 })
861 }
862
863 if !Self::check_eip7702_transaction(tx, cip7702, &mode) {
864 bail!(TransactionError::FutureTransactionType {
865 tx_type: EIP7702_TYPE,
866 current_height: height,
867 enable_height: transitions.cip7702,
868 })
869 }
870
871 if !Self::check_eip3860(tx, cip645) {
872 bail!(TransactionError::CreateInitCodeSizeLimit)
873 }
874
875 Self::check_eip1559_validation(tx, cip645)?;
876 Self::check_eip7702_validation(tx)?;
877
878 Self::check_gas_limit(tx, cip76, eip7623, &mode)?;
879 Self::check_gas_limit_with_calldata(tx, cip130)?;
880 Self::check_canonical_rlp(tx, cip172, &mode)?;
881
882 Ok(())
883 }
884
885 fn check_canonical_rlp(
890 tx: &TransactionWithSignature, cip172: bool, mode: &VerifyTxMode,
891 ) -> Result<(), TransactionError> {
892 if tx.is_canonical_rlp() {
893 return Ok(());
894 }
895 let rejected = match mode {
896 VerifyTxMode::Local(..) => true,
897 VerifyTxMode::Remote(_) => cip172,
898 };
899 if rejected {
900 bail!(TransactionError::InvalidRlp(
901 "non-canonical transaction RLP encoding".into()
902 ));
903 }
904 Ok(())
905 }
906
907 fn check_eip155_transaction(
908 tx: &TransactionWithSignature, cip90a: bool, mode: &VerifyTxMode,
909 ) -> bool {
910 if tx.space() == Space::Native {
911 return true;
912 }
913
914 use VerifyTxLocalMode::*;
915 match mode {
916 VerifyTxMode::Local(Full, spec) => cip90a && spec.cip90,
917 VerifyTxMode::Local(MaybeLater, _spec) => true,
918 VerifyTxMode::Remote(_) => cip90a,
919 }
920 }
921
922 fn check_eip1559_transaction(
923 tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode,
924 ) -> bool {
925 if tx.is_legacy() {
926 return true;
927 }
928
929 use VerifyTxLocalMode::*;
930 match mode {
931 VerifyTxMode::Local(Full, spec) => cip1559 && spec.cip1559,
932 VerifyTxMode::Local(MaybeLater, _spec) => true,
933 VerifyTxMode::Remote(_) => cip1559,
934 }
935 }
936
937 fn check_eip7702_transaction(
938 tx: &TransactionWithSignature, cip7702: bool, mode: &VerifyTxMode,
939 ) -> bool {
940 if !tx.after_7702() {
941 return true;
942 }
943
944 use VerifyTxLocalMode::*;
945 match mode {
946 VerifyTxMode::Local(Full, spec) => cip7702 && spec.cip7702,
947 VerifyTxMode::Local(MaybeLater, _spec) => true,
948 VerifyTxMode::Remote(_) => cip7702,
949 }
950 }
951
952 fn check_eip7702_validation(
953 tx: &TransactionWithSignature,
954 ) -> Result<(), TransactionError> {
955 if let Some(author_list) = tx.authorization_list() {
956 if author_list.is_empty() {
957 return Err(TransactionError::EmptyAuthorizationList);
958 }
959 }
960 Ok(())
961 }
962
963 fn check_eip1559_validation(
964 tx: &TransactionWithSignature, cip645: bool,
965 ) -> Result<(), TransactionError> {
966 if !cip645 || !tx.after_1559() {
967 return Ok(());
968 }
969
970 if tx.max_priority_gas_price() > tx.gas_price() {
971 return Err(TransactionError::PriortyGreaterThanMaxFee);
972 }
973 Ok(())
974 }
975
976 fn check_eip3860(tx: &TransactionWithSignature, cip645: bool) -> bool {
977 const SPEC: Spec = Spec::genesis_spec();
979 if !cip645 {
980 return true;
981 }
982 if tx.action() != Action::Create {
983 return true;
984 }
985
986 tx.data().len() <= SPEC.init_code_data_limit
987 }
988
989 fn check_gas_limit(
991 tx: &TransactionWithSignature, cip76: bool, eip7623: bool,
992 mode: &VerifyTxMode,
993 ) -> Result<(), TransactionError> {
994 let consensus_spec = match mode {
995 VerifyTxMode::Local(_, spec) => spec.to_consensus_spec(),
996 VerifyTxMode::Remote(spec) => {
997 if !eip7623 && cip76 {
998 return Ok(());
999 } else {
1000 (*spec).clone()
1001 }
1002 }
1003 };
1004
1005 let tx_intrinsic_gas = gas_required_for(
1006 tx.action() == Action::Create,
1007 &tx.data(),
1008 tx.access_list(),
1009 tx.authorization_len(),
1010 &consensus_spec,
1011 );
1012
1013 if *tx.gas() < tx_intrinsic_gas.into() {
1014 bail!(TransactionError::NotEnoughBaseGas {
1015 required: tx_intrinsic_gas.into(),
1016 got: *tx.gas()
1017 });
1018 }
1019
1020 if eip7623 {
1021 let floor_gas = eip7623_required_gas(&tx.data(), &consensus_spec);
1022
1023 if *tx.gas() < floor_gas.into() {
1024 bail!(TransactionError::NotEnoughBaseGas {
1025 required: floor_gas.into(),
1026 got: *tx.gas()
1027 });
1028 }
1029 }
1030
1031 Ok(())
1032 }
1033
1034 fn check_gas_limit_with_calldata(
1035 tx: &TransactionWithSignature, cip130: bool,
1036 ) -> Result<(), TransactionError> {
1037 if !cip130 {
1038 return Ok(());
1039 }
1040 let data_length = tx.data().len();
1041 let min_gas_limit = data_length.saturating_mul(100);
1042 if tx.gas() < &U256::from(min_gas_limit) {
1043 bail!(TransactionError::NotEnoughBaseGas {
1044 required: min_gas_limit.into(),
1045 got: *tx.gas()
1046 });
1047 }
1048 Ok(())
1049 }
1050
1051 pub fn check_tx_size(
1052 &self, tx: &TransactionWithSignature,
1053 ) -> Result<(), TransactionError> {
1054 if tx.rlp_size() > self.max_block_size_in_bytes {
1055 bail!(TransactionError::TooBig)
1056 } else {
1057 Ok(())
1058 }
1059 }
1060}
1061
1062#[derive(Copy, Clone)]
1063pub enum PackingCheckResult {
1064 Pack,
1065 Pending,
1067 Drop, }
1070
1071#[derive(Copy, Clone)]
1072pub enum VerifyTxMode<'a> {
1073 Local(VerifyTxLocalMode, &'a Spec),
1075 Remote(&'a ConsensusGasSpec),
1078}
1079
1080#[derive(Copy, Clone)]
1081pub enum VerifyTxLocalMode {
1082 Full,
1084 MaybeLater,
1087}
1088
1089impl<'a> VerifyTxMode<'a> {
1090 fn is_maybe_later(&self) -> bool {
1091 if let VerifyTxMode::Local(VerifyTxLocalMode::MaybeLater, _) = self {
1092 true
1093 } else {
1094 false
1095 }
1096 }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101 use crate::verification::EpochReceiptProof;
1102 use cfx_storage::{
1103 CompressedPathRaw, TrieProof, TrieProofNode, VanillaChildrenTable,
1104 };
1105
1106 #[test]
1107 fn test_rlp_epoch_receipt_proof() {
1108 let proof = EpochReceiptProof::default();
1109 assert_eq!(proof, rlp::decode(&rlp::encode(&proof)).unwrap());
1110
1111 let serialized = serde_json::to_string(&proof).unwrap();
1112 let deserialized: EpochReceiptProof =
1113 serde_json::from_str(&serialized).unwrap();
1114 assert_eq!(proof, deserialized);
1115
1116 let node1 = TrieProofNode::new(
1117 Default::default(),
1118 Some(Box::new([0x03, 0x04, 0x05])),
1119 CompressedPathRaw::new(
1120 &[0x00, 0x01, 0x02],
1121 CompressedPathRaw::first_nibble_mask(),
1122 ),
1123 true,
1124 );
1125
1126 let root_node = {
1127 let mut children_table = VanillaChildrenTable::default();
1128 unsafe {
1129 *children_table.get_child_mut_unchecked(2) =
1130 *node1.get_merkle();
1131 *children_table.get_children_count_mut() = 1;
1132 }
1133 TrieProofNode::new(
1134 children_table,
1135 None,
1136 CompressedPathRaw::default(),
1137 false,
1138 )
1139 };
1140 let nodes = [root_node, node1]
1141 .iter()
1142 .cloned()
1143 .cycle()
1144 .take(20)
1145 .collect();
1146 let proof = TrieProof::new(nodes).unwrap();
1147
1148 let epoch_proof = EpochReceiptProof {
1149 block_index_proof: proof.clone(),
1150 block_receipt_proof: proof,
1151 };
1152
1153 assert_eq!(
1154 epoch_proof,
1155 rlp::decode(&rlp::encode(&epoch_proof)).unwrap()
1156 );
1157
1158 let serialized = serde_json::to_string(&epoch_proof).unwrap();
1159 let deserialized: EpochReceiptProof =
1160 serde_json::from_str(&serialized).unwrap();
1161 assert_eq!(epoch_proof, deserialized);
1162 }
1163
1164 #[test]
1167 fn packed_gas_sum_overflow_is_rejected() {
1168 use crate::{
1169 core_error::{BlockError, CoreError as Error},
1170 verification::{compute_transaction_root, VerificationConfig},
1171 };
1172 use cfx_executor::{
1173 machine::{Machine, VmFactory},
1174 spec::CommonParams,
1175 };
1176 use cfx_types::U256;
1177 use cfxkey::{Generator, Random};
1178 use primitives::{
1179 transaction::native_transaction::{
1180 NativeTransaction, TypedNativeTransaction,
1181 },
1182 Action, Block, BlockHeaderBuilder, Transaction,
1183 };
1184 use std::sync::Arc;
1185
1186 let params = CommonParams::default();
1187 let chain_id = params.chain_id.read().get_chain_id(1);
1188 let machine =
1189 Arc::new(Machine::new_with_builtin(params, VmFactory::new(1024)));
1190 let config = VerificationConfig::new(
1191 false,
1192 200,
1193 200 * 1024,
1194 100_000,
1195 128,
1196 u64::MAX,
1197 1,
1198 machine,
1199 );
1200
1201 let keypair = Random.generate().unwrap();
1202 let tx = |nonce: u64| {
1203 Arc::new(
1204 Transaction::Native(TypedNativeTransaction::Cip155(
1205 NativeTransaction {
1206 nonce: nonce.into(),
1207 gas_price: U256::one(),
1208 gas: U256::one() << 255,
1210 action: Action::Create,
1211 value: U256::zero(),
1212 storage_limit: 0,
1213 epoch_height: 1,
1214 chain_id: chain_id.in_native_space(),
1215 data: vec![],
1216 },
1217 ))
1218 .sign(keypair.secret()),
1219 )
1220 };
1221 let txs = vec![tx(0), tx(1)];
1222
1223 let parent = BlockHeaderBuilder::new().with_height(0).build();
1224 let header = BlockHeaderBuilder::new()
1225 .with_height(1)
1226 .with_parent_hash(parent.hash())
1227 .with_transactions_root(compute_transaction_root(&txs))
1228 .with_gas_limit(30_000_000.into())
1229 .build();
1230 let block = Block::new(header, txs);
1231
1232 assert!(matches!(
1233 config.verify_sync_graph_ready_block(&block, &parent),
1234 Err(Error::Block(BlockError::InvalidPackedGasLimit(_))),
1235 ));
1236 }
1237
1238 #[test]
1243 fn verify_pow_rejects_before_computing_hash() {
1244 use crate::{
1245 core_error::{BlockError, CoreError as Error},
1246 pow::PowComputer,
1247 verification::VerificationConfig,
1248 };
1249 use cfx_executor::{
1250 machine::{Machine, VmFactory},
1251 spec::CommonParams,
1252 };
1253 use primitives::BlockHeaderBuilder;
1254 use std::sync::Arc;
1255
1256 let machine = Arc::new(Machine::new_with_builtin(
1257 CommonParams::default(),
1258 VmFactory::new(1024),
1259 ));
1260 let config = VerificationConfig::new(
1261 false,
1262 200,
1263 200 * 1024,
1264 100_000,
1265 128,
1266 u64::MAX,
1267 4, machine,
1269 );
1270 let pow = PowComputer::new(true );
1271
1272 let mut header = BlockHeaderBuilder::new()
1273 .with_height(u64::MAX)
1274 .with_difficulty(4.into())
1275 .build();
1276 assert!(matches!(
1277 config.verify_pow(&pow, &mut header),
1278 Err(Error::Block(BlockError::InvalidHeight(_))),
1279 ));
1280
1281 let mut header = BlockHeaderBuilder::new()
1282 .with_height(u64::MAX)
1283 .with_difficulty(3.into())
1284 .build();
1285 assert!(matches!(
1286 config.verify_pow(&pow, &mut header),
1287 Err(Error::Block(BlockError::InvalidHeight(_))),
1288 ));
1289
1290 let mut header = BlockHeaderBuilder::new()
1291 .with_height(1)
1292 .with_difficulty(3.into())
1293 .build();
1294 assert!(matches!(
1295 config.verify_pow(&pow, &mut header),
1296 Err(Error::Block(BlockError::InvalidDifficulty(_))),
1297 ));
1298 }
1299}