diff --git a/benches/benches/benchmarks/resolve.rs b/benches/benches/benchmarks/resolve.rs index 514ad01e99..1373252a11 100644 --- a/benches/benches/benchmarks/resolve.rs +++ b/benches/benches/benchmarks/resolve.rs @@ -132,7 +132,8 @@ fn bench(c: &mut Criterion) { let mut seen_inputs = HashSet::new(); for tx in txs.clone() { - resolve_transaction(tx, &mut seen_inputs, &provider, snapshot).unwrap(); + resolve_transaction(tx, &mut seen_inputs, &provider, snapshot, false) + .unwrap(); } i -= 1; @@ -158,7 +159,8 @@ fn bench(c: &mut Criterion) { let rtxs: Vec<_> = txs .into_iter() .map(|tx| { - resolve_transaction(tx, &mut seen_inputs, &provider, snapshot).unwrap() + resolve_transaction(tx, &mut seen_inputs, &provider, snapshot, false) + .unwrap() }) .collect(); diff --git a/benches/benches/benchmarks/util.rs b/benches/benches/benchmarks/util.rs index d99f76f20a..97498bd8bf 100644 --- a/benches/benches/benchmarks/util.rs +++ b/benches/benches/benchmarks/util.rs @@ -487,7 +487,13 @@ pub fn dao_data(shared: &Shared, parent: &HeaderView, txs: &[TransactionView]) - let snapshot: &Snapshot = &shared.snapshot(); let overlay_cell_provider = OverlayCellProvider::new(&transactions_provider, snapshot); let rtxs = txs.iter().cloned().try_fold(vec![], |mut rtxs, tx| { - let rtx = resolve_transaction(tx, &mut seen_inputs, &overlay_cell_provider, snapshot); + let rtx = resolve_transaction( + tx, + &mut seen_inputs, + &overlay_cell_provider, + snapshot, + false, + ); match rtx { Ok(rtx) => { rtxs.push(rtx); diff --git a/chain/src/chain.rs b/chain/src/chain.rs index b88efe00e0..993c287fd6 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -684,6 +684,7 @@ impl ChainService { let verify_context = VerifyContext::new(txn, self.shared.consensus()); let async_handle = self.shared.tx_pool_controller().handle(); + let hardfork_switch = self.shared.consensus().hardfork_switch(); let mut found_error = None; for (ext, b) in fork @@ -704,6 +705,8 @@ impl ChainService { }; let transactions = b.transactions(); + let allow_in_txpool = hardfork_switch + .is_allow_cell_data_hash_in_txpool_enabled(b.epoch().number()); let resolved = { let txn_cell_provider = txn.cell_provider(); let cell_provider = OverlayCellProvider::new(&block_cp, &txn_cell_provider); @@ -716,6 +719,7 @@ impl ChainService { &mut seen_inputs, &cell_provider, &verify_context, + allow_in_txpool, ) }) .collect::, _>>() diff --git a/chain/src/tests/basic.rs b/chain/src/tests/basic.rs index 292c1207d5..3fff143171 100644 --- a/chain/src/tests/basic.rs +++ b/chain/src/tests/basic.rs @@ -115,7 +115,7 @@ fn test_genesis_transaction_spend() { assert_eq!( shared .snapshot() - .cell(&OutPoint::new(genesis_tx_hash, 0), false), + .cell(&OutPoint::new(genesis_tx_hash, 0), false, false), CellStatus::Unknown ); } @@ -142,7 +142,7 @@ fn test_transaction_spend_in_same_block() { assert_eq!( shared .snapshot() - .cell(&OutPoint::new(hash.to_owned().to_owned(), 0), false), + .cell(&OutPoint::new(hash.to_owned().to_owned(), 0), false, false), CellStatus::Unknown ); } @@ -171,12 +171,14 @@ fn test_transaction_spend_in_same_block() { assert_eq!( shared .snapshot() - .cell(&OutPoint::new(last_cellbase_hash, 0), false), + .cell(&OutPoint::new(last_cellbase_hash, 0), false, false), CellStatus::Unknown ); assert_eq!( - shared.snapshot().cell(&OutPoint::new(tx1_hash, 0), false), + shared + .snapshot() + .cell(&OutPoint::new(tx1_hash, 0), false, false), CellStatus::Unknown ); @@ -189,7 +191,7 @@ fn test_transaction_spend_in_same_block() { assert_eq!( shared .snapshot() - .cell(&OutPoint::new(tx2_hash.clone(), 0), false), + .cell(&OutPoint::new(tx2_hash.clone(), 0), false, false), CellStatus::live_cell(CellMeta { cell_output: tx2_output, data_bytes: tx2_output_data.len() as u64, @@ -375,7 +377,7 @@ fn test_genesis_transaction_fetch() { let (_chain_controller, shared, _parent) = start_chain(Some(consensus)); let out_point = OutPoint::new(root_hash, 0); - let state = shared.snapshot().cell(&out_point, false); + let state = shared.snapshot().cell(&out_point, false, false); assert!(state.is_live()); } diff --git a/chain/src/tests/cell.rs b/chain/src/tests/cell.rs index 41ca204d4f..68ed98a8f1 100644 --- a/chain/src/tests/cell.rs +++ b/chain/src/tests/cell.rs @@ -144,18 +144,21 @@ fn test_block_cells_update() { for tx in block.transactions()[1..4].iter() { for pt in tx.output_pts() { // full spent - assert_eq!(txn_cell_provider.cell(&pt, false), CellStatus::Unknown); + assert_eq!( + txn_cell_provider.cell(&pt, false, false), + CellStatus::Unknown + ); } } // ensure tx3 outputs is unspent after attach_block_cell for pt in block.transactions()[4].output_pts() { - assert!(txn_cell_provider.cell(&pt, false).is_live()); + assert!(txn_cell_provider.cell(&pt, false, false).is_live()); } // ensure issue_tx outputs is spent after attach_block_cell assert_eq!( - txn_cell_provider.cell(&issue_tx.output_pts()[0], false), + txn_cell_provider.cell(&issue_tx.output_pts()[0], false, false), CellStatus::Unknown ); @@ -164,12 +167,15 @@ fn test_block_cells_update() { // ensure tx0-3 outputs is unknown after detach_block_cell for tx in block.transactions()[1..=4].iter() { for pt in tx.output_pts() { - assert_eq!(txn_cell_provider.cell(&pt, false), CellStatus::Unknown); + assert_eq!( + txn_cell_provider.cell(&pt, false, false), + CellStatus::Unknown + ); } } // ensure issue_tx outputs is back to live after detach_block_cell assert!(txn_cell_provider - .cell(&issue_tx.output_pts()[0], false) + .cell(&issue_tx.output_pts()[0], false, false) .is_live()); } diff --git a/chain/src/tests/load_input_data_hash_cell.rs b/chain/src/tests/load_input_data_hash_cell.rs index f43e7770a5..65fb76663e 100644 --- a/chain/src/tests/load_input_data_hash_cell.rs +++ b/chain/src/tests/load_input_data_hash_cell.rs @@ -4,12 +4,13 @@ use crate::tests::util::{ use ckb_chain_spec::consensus::ConsensusBuilder; use ckb_dao_utils::genesis_dao_data; use ckb_test_chain_utils::load_input_data_hash_cell; +use ckb_tx_pool::{PlugTarget, TxEntry}; use ckb_types::prelude::*; use ckb_types::{ bytes::Bytes, core::{ - capacity_bytes, BlockBuilder, Capacity, EpochNumberWithFraction, TransactionBuilder, - TransactionView, + capacity_bytes, hardfork::HardForkSwitch, BlockBuilder, Capacity, EpochNumberWithFraction, + TransactionBuilder, TransactionView, }, packed::{CellDep, CellInput, CellOutputBuilder, OutPoint}, utilities::DIFF_TWO, @@ -48,7 +49,8 @@ pub(crate) fn create_load_input_data_hash_transaction( .build() } -// Ensure tx-pool accept tx which calls syscall load_cell_data_hash from input +// Ensure tx-pool reject or accept tx which calls syscall load_cell_data_hash from input base on +// hardfork features. #[test] fn test_load_input_data_hash_cell() { let (_, _, load_input_data_hash_script) = load_input_data_hash_cell(); @@ -74,20 +76,57 @@ fn test_load_input_data_hash_cell() { .dao(dao) .build(); - let consensus = ConsensusBuilder::default() - .cellbase_maturity(EpochNumberWithFraction::new(0, 0, 1)) - .genesis_block(genesis_block) - .build(); + { + // Test CKB v2019 reject + let hardfork_switch = HardForkSwitch::new_without_any_enabled(); + let consensus = ConsensusBuilder::default() + .cellbase_maturity(EpochNumberWithFraction::new(0, 0, 1)) + .genesis_block(genesis_block.clone()) + .hardfork_switch(hardfork_switch) + .build(); + + let (_chain_controller, shared, _parent) = start_chain(Some(consensus)); + + let tx0 = create_load_input_data_hash_transaction(&issue_tx, 0); + let tx1 = create_load_input_data_hash_transaction(&tx0, 0); + + let tx_pool = shared.tx_pool_controller(); + let ret = tx_pool.submit_local_tx(tx0.clone()).unwrap(); + assert!(ret.is_err()); + //ValidationFailure(2) missing item + assert!(format!("{}", ret.err().unwrap()).contains("ValidationFailure(2)")); + + let entry0 = vec![TxEntry::dummy_resolve(tx0, 0, Capacity::shannons(0), 100)]; + tx_pool.plug_entry(entry0, PlugTarget::Proposed).unwrap(); + + // Ensure tx which calls syscall load_cell_data_hash will got reject even previous tx is already in tx-pool + let ret = tx_pool.submit_local_tx(tx1).unwrap(); + assert!(ret.is_err()); + assert!(format!("{}", ret.err().unwrap()).contains("ValidationFailure(2)")); + } + { + // Test CKB v2021 accept + let hardfork_switch = HardForkSwitch::new_without_any_enabled() + .as_builder() + .rfc_pr_0228(0) + .build() + .unwrap(); + let consensus = ConsensusBuilder::default() + .cellbase_maturity(EpochNumberWithFraction::new(0, 0, 1)) + .genesis_block(genesis_block) + .hardfork_switch(hardfork_switch) + .build(); - let (_chain_controller, shared, _parent) = start_chain(Some(consensus)); + let (_chain_controller, shared, _parent) = start_chain(Some(consensus)); - let tx0 = create_load_input_data_hash_transaction(&issue_tx, 0); - let tx1 = create_load_input_data_hash_transaction(&tx0, 0); + let tx0 = create_load_input_data_hash_transaction(&issue_tx, 0); + let tx1 = create_load_input_data_hash_transaction(&tx0, 0); - let tx_pool = shared.tx_pool_controller(); - let ret = tx_pool.submit_local_tx(tx0).unwrap(); - assert!(ret.is_ok()); + let tx_pool = shared.tx_pool_controller(); + let ret = tx_pool.submit_local_tx(tx0).unwrap(); + assert!(ret.is_ok()); - let ret = tx_pool.submit_local_tx(tx1).unwrap(); - assert!(ret.is_ok()); + let ret = tx_pool.submit_local_tx(tx1).unwrap(); + assert!(ret.is_ok()); + } } diff --git a/chain/src/tests/non_contextual_block_txs_verify.rs b/chain/src/tests/non_contextual_block_txs_verify.rs index 1266f4c3f6..8201fb36d9 100644 --- a/chain/src/tests/non_contextual_block_txs_verify.rs +++ b/chain/src/tests/non_contextual_block_txs_verify.rs @@ -50,7 +50,6 @@ pub(crate) fn create_cellbase( } } -#[allow(clippy::too_many_arguments)] pub(crate) fn gen_block( parent_header: &HeaderView, transactions: Vec, diff --git a/chain/src/tests/util.rs b/chain/src/tests/util.rs index f3acf25321..5506e9bdc7 100644 --- a/chain/src/tests/util.rs +++ b/chain/src/tests/util.rs @@ -531,7 +531,13 @@ pub fn dao_data( let transactions_provider = TransactionsProvider::new(txs.iter()); let overlay_cell_provider = OverlayCellProvider::new(&transactions_provider, store); let rtxs = txs.iter().try_fold(vec![], |mut rtxs, tx| { - let rtx = resolve_transaction(tx.clone(), &mut seen_inputs, &overlay_cell_provider, store); + let rtx = resolve_transaction( + tx.clone(), + &mut seen_inputs, + &overlay_cell_provider, + store, + false, + ); match rtx { Ok(rtx) => { rtxs.push(rtx); diff --git a/rpc/src/module/chain.rs b/rpc/src/module/chain.rs index 8361c1911d..2a43c508a5 100644 --- a/rpc/src/module/chain.rs +++ b/rpc/src/module/chain.rs @@ -1393,7 +1393,10 @@ impl ChainRpc for ChainRpcImpl { } fn get_live_cell(&self, out_point: OutPoint, with_data: bool) -> Result { - let cell_status = self.shared.snapshot().cell(&out_point.into(), with_data); + let cell_status = self + .shared + .snapshot() + .cell(&out_point.into(), with_data, true); Ok(cell_status.into()) } diff --git a/rpc/src/module/experiment.rs b/rpc/src/module/experiment.rs index b187462dfa..814d5ef0af 100644 --- a/rpc/src/module/experiment.rs +++ b/rpc/src/module/experiment.rs @@ -214,7 +214,12 @@ pub(crate) struct DryRunner<'a> { } impl<'a> CellProvider for DryRunner<'a> { - fn cell(&self, out_point: &packed::OutPoint, with_data: bool) -> CellStatus { + fn cell( + &self, + out_point: &packed::OutPoint, + with_data: bool, + _allow_in_txpool: bool, + ) -> CellStatus { let snapshot = self.shared.snapshot(); snapshot .get_cell(out_point) @@ -244,12 +249,27 @@ impl<'a> DryRunner<'a> { pub(crate) fn run(&self, tx: packed::Transaction) -> Result { let snapshot: &Snapshot = &self.shared.snapshot(); - match resolve_transaction(tx.into_view(), &mut HashSet::new(), self, self) { + let consensus = snapshot.consensus(); + let tx_env = { + let tip_header = snapshot.tip_header(); + TxVerifyEnv::new_submit(&tip_header) + }; + let allow_in_txpool = { + let proposal_window = consensus.tx_proposal_window(); + let epoch_number = tx_env.epoch_number(proposal_window); + consensus + .hardfork_switch() + .is_allow_cell_data_hash_in_txpool_enabled(epoch_number) + }; + match resolve_transaction( + tx.into_view(), + &mut HashSet::new(), + self, + self, + allow_in_txpool, + ) { Ok(resolved) => { - let consensus = snapshot.consensus(); let max_cycles = consensus.max_block_cycles; - let tip_header = snapshot.tip_header(); - let tx_env = TxVerifyEnv::new_submit(&tip_header); match ScriptVerifier::new( &resolved, consensus, diff --git a/rpc/src/module/test.rs b/rpc/src/module/test.rs index ea364525c9..93641af36e 100644 --- a/rpc/src/module/test.rs +++ b/rpc/src/module/test.rs @@ -2,7 +2,9 @@ use crate::error::RPCError; use ckb_app_config::BlockAssemblerConfig; use ckb_chain::chain::ChainController; use ckb_dao::DaoCalculator; -use ckb_jsonrpc_types::{Block, BlockTemplate, Cycle, JsonBytes, Script, Transaction}; +use ckb_jsonrpc_types::{ + AsEpochNumberWithFraction, Block, BlockTemplate, Cycle, JsonBytes, Script, Transaction, +}; use ckb_logger::error; use ckb_network::{NetworkController, SupportProtocols}; use ckb_shared::{shared::Shared, Snapshot}; @@ -179,12 +181,16 @@ impl IntegrationTestRpc for IntegrationTestRpcImpl { let transactions_provider = TransactionsProvider::new(txs.as_slice().iter()); let overlay_cell_provider = OverlayCellProvider::new(&transactions_provider, snapshot); + let allow_in_txpool = consensus + .hardfork_switch() + .is_allow_cell_data_hash_in_txpool_enabled(block_template.epoch.epoch_number()); let rtxs = txs.iter().map(|tx| { resolve_transaction( tx.clone(), &mut seen_inputs, &overlay_cell_provider, snapshot, + allow_in_txpool, ).map_err(|err| { error!( "resolve transactions error when generating block with block template, error: {:?}", diff --git a/rpc/src/test.rs b/rpc/src/test.rs index e166d74e0e..d0639cd5b1 100644 --- a/rpc/src/test.rs +++ b/rpc/src/test.rs @@ -110,8 +110,14 @@ fn next_block(shared: &Shared, parent: &HeaderView) -> BlockView { let cellbase = always_success_cellbase(parent.number() + 1, reward.total, shared.consensus()); let dao = { - let resolved_cellbase = - resolve_transaction(cellbase.clone(), &mut HashSet::new(), snapshot, snapshot).unwrap(); + let resolved_cellbase = resolve_transaction( + cellbase.clone(), + &mut HashSet::new(), + snapshot, + snapshot, + false, + ) + .unwrap(); let data_loader = shared.store().as_data_provider(); DaoCalculator::new(shared.consensus(), &data_loader) .dao_field(&[resolved_cellbase], parent) diff --git a/script/src/syscalls/load_cell.rs b/script/src/syscalls/load_cell.rs index 9a2d85710d..3f19738496 100644 --- a/script/src/syscalls/load_cell.rs +++ b/script/src/syscalls/load_cell.rs @@ -24,6 +24,7 @@ pub struct LoadCell<'a, DL> { resolved_cell_deps: &'a [CellMeta], group_inputs: &'a [usize], group_outputs: &'a [usize], + allow_cell_data_hash_in_txpool: bool, } impl<'a, DL: CellDataProvider + 'a> LoadCell<'a, DL> { @@ -34,6 +35,7 @@ impl<'a, DL: CellDataProvider + 'a> LoadCell<'a, DL> { resolved_cell_deps: &'a [CellMeta], group_inputs: &'a [usize], group_outputs: &'a [usize], + allow_cell_data_hash_in_txpool: bool, ) -> LoadCell<'a, DL> { LoadCell { data_loader, @@ -42,6 +44,7 @@ impl<'a, DL: CellDataProvider + 'a> LoadCell<'a, DL> { resolved_cell_deps, group_inputs, group_outputs, + allow_cell_data_hash_in_txpool, } } @@ -102,8 +105,15 @@ impl<'a, DL: CellDataProvider + 'a> LoadCell<'a, DL> { (SUCCESS, store_data(machine, &buffer)?) } CellField::DataHash => { - if let Some(bytes) = self.data_loader.load_cell_data_hash(cell) { - (SUCCESS, store_data(machine, &bytes.as_bytes())?) + if self.allow_cell_data_hash_in_txpool { + if let Some(bytes) = self.data_loader.load_cell_data_hash(cell) { + (SUCCESS, store_data(machine, &bytes.as_bytes())?) + } else { + (ITEM_MISSING, 0) + } + } else if let Some(data_hash) = &cell.mem_cell_data_hash { + let bytes = data_hash.raw_data(); + (SUCCESS, store_data(machine, &bytes)?) } else { (ITEM_MISSING, 0) } diff --git a/script/src/syscalls/mod.rs b/script/src/syscalls/mod.rs index f3864aa036..dfe6543811 100644 --- a/script/src/syscalls/mod.rs +++ b/script/src/syscalls/mod.rs @@ -253,6 +253,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); prop_assert!(load_cell.ecall(&mut machine).is_ok()); @@ -297,6 +298,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); let input_correct_data = input_cell.cell_output.as_slice(); @@ -389,6 +391,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); let input_correct_data = input_cell.cell_output.as_slice(); @@ -443,6 +446,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); let input_correct_data = input_cell.cell_output.as_slice(); @@ -510,6 +514,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); prop_assert!(machine.memory_mut().store64(&size_addr, &16).is_ok()); @@ -564,6 +569,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); assert!(machine.memory_mut().store64(&size_addr, &100).is_ok()); @@ -905,6 +911,7 @@ mod tests { &resolved_cell_deps, &group_inputs, &group_outputs, + false, ); prop_assert!(machine.memory_mut().store64(&size_addr, &64).is_ok()); diff --git a/script/src/verify.rs b/script/src/verify.rs index b6b8f4e892..9c10d5d857 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -240,6 +240,7 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D &'a self, group_inputs: &'a [usize], group_outputs: &'a [usize], + allow_cell_data_hash_in_txpool: bool, ) -> LoadCell<'a, DL> { LoadCell::new( &self.data_loader, @@ -248,6 +249,7 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D self.resolved_cell_deps(), group_inputs, group_outputs, + allow_cell_data_hash_in_txpool, ) } @@ -428,12 +430,20 @@ impl<'a, DL: CellDataProvider + HeaderProvider> TransactionScriptsVerifier<'a, D script_group: &'a ScriptGroup, ) -> Vec + 'a)>> { let current_script_hash = script_group.script.calc_script_hash(); + let proposal_window = self.consensus.tx_proposal_window(); + let epoch_number = self.tx_env.epoch_number(proposal_window); + let allow_cell_data_hash_in_txpool = self + .consensus + .hardfork_switch() + .is_allow_cell_data_hash_in_txpool_enabled(epoch_number); vec![ Box::new(self.build_load_script_hash(current_script_hash.clone())), Box::new(self.build_load_tx()), - Box::new( - self.build_load_cell(&script_group.input_indices, &script_group.output_indices), - ), + Box::new(self.build_load_cell( + &script_group.input_indices, + &script_group.output_indices, + allow_cell_data_hash_in_txpool, + )), Box::new(self.build_load_input(&script_group.input_indices)), Box::new(self.build_load_header(&script_group.input_indices)), Box::new( diff --git a/spec/src/hardfork.rs b/spec/src/hardfork.rs index c3f05e51d5..731d0714a6 100644 --- a/spec/src/hardfork.rs +++ b/spec/src/hardfork.rs @@ -21,6 +21,8 @@ pub struct HardForkConfig { /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) pub rfc_pr_0224: Option, /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) + pub rfc_pr_0228: Option, + /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) pub rfc_pr_0230: Option, } @@ -69,6 +71,7 @@ impl HardForkConfig { .rfc_pr_0222(check_default!(self, rfc_pr_0222, ckb2021)) .rfc_pr_0223(check_default!(self, rfc_pr_0223, ckb2021)) .rfc_pr_0224(check_default!(self, rfc_pr_0224, ckb2021)) + .rfc_pr_0228(check_default!(self, rfc_pr_0228, ckb2021)) .rfc_pr_0230(check_default!(self, rfc_pr_0230, ckb2021)); Ok(builder) } @@ -82,6 +85,7 @@ impl HardForkConfig { .rfc_pr_0222(self.rfc_pr_0222.unwrap_or(default)) .rfc_pr_0223(self.rfc_pr_0223.unwrap_or(default)) .rfc_pr_0224(self.rfc_pr_0224.unwrap_or(default)) + .rfc_pr_0228(self.rfc_pr_0228.unwrap_or(default)) .rfc_pr_0230(self.rfc_pr_0230.unwrap_or(default)) .build() } diff --git a/store/src/store.rs b/store/src/store.rs index dcc7e08d6a..18729aa7e9 100644 --- a/store/src/store.rs +++ b/store/src/store.rs @@ -530,7 +530,7 @@ impl<'a, S> CellProvider for CellProviderWrapper<'a, S> where S: ChainStore<'a>, { - fn cell(&self, out_point: &OutPoint, with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, with_data: bool, _allow_in_txpool: bool) -> CellStatus { match self.0.get_cell(out_point) { Some(mut cell_meta) => { if with_data { diff --git a/sync/src/synchronizer/mod.rs b/sync/src/synchronizer/mod.rs index bc665402d7..62ec74a7c7 100644 --- a/sync/src/synchronizer/mod.rs +++ b/sync/src/synchronizer/mod.rs @@ -935,9 +935,14 @@ mod tests { let cellbase = create_cellbase(shared, parent_header, number); let dao = { let snapshot: &Snapshot = &shared.snapshot(); - let resolved_cellbase = - resolve_transaction(cellbase.clone(), &mut HashSet::new(), snapshot, snapshot) - .unwrap(); + let resolved_cellbase = resolve_transaction( + cellbase.clone(), + &mut HashSet::new(), + snapshot, + snapshot, + false, + ) + .unwrap(); let data_loader = shared.store().as_data_provider(); DaoCalculator::new(shared.consensus(), &data_loader) .dao_field(&[resolved_cellbase], parent_header) diff --git a/sync/src/tests/synchronizer.rs b/sync/src/tests/synchronizer.rs index 1d47bbb4c4..c0c3856e23 100644 --- a/sync/src/tests/synchronizer.rs +++ b/sync/src/tests/synchronizer.rs @@ -146,6 +146,7 @@ fn setup_node(height: u64) -> (TestNode, Shared) { &mut HashSet::new(), snapshot.as_ref(), snapshot.as_ref(), + false, ) .unwrap(); let data_loader = snapshot.as_data_provider(); diff --git a/sync/src/tests/util.rs b/sync/src/tests/util.rs index 7ca67116ce..b551e78ca0 100644 --- a/sync/src/tests/util.rs +++ b/sync/src/tests/util.rs @@ -63,6 +63,7 @@ pub fn inherit_block(shared: &Shared, parent_hash: &Byte32) -> BlockBuilder { &mut HashSet::new(), snapshot.as_ref(), snapshot.as_ref(), + false, ) .unwrap(); let data_loader = snapshot.as_data_provider(); diff --git a/test/template/specs/integration.toml b/test/template/specs/integration.toml index d674cbea64..84a5a3a407 100644 --- a/test/template/specs/integration.toml +++ b/test/template/specs/integration.toml @@ -73,6 +73,7 @@ rfc_pr_0221 = 9_223_372_036_854_775_807 rfc_pr_0222 = 9_223_372_036_854_775_807 rfc_pr_0223 = 9_223_372_036_854_775_807 rfc_pr_0224 = 9_223_372_036_854_775_807 +rfc_pr_0228 = 9_223_372_036_854_775_807 rfc_pr_0230 = 9_223_372_036_854_775_807 [pow] diff --git a/tx-pool/src/component/pending.rs b/tx-pool/src/component/pending.rs index 0e76fc6348..4c5a576176 100644 --- a/tx-pool/src/component/pending.rs +++ b/tx-pool/src/component/pending.rs @@ -73,7 +73,7 @@ impl PendingQueue { } impl CellProvider for PendingQueue { - fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { let tx_hash = out_point.tx_hash(); if let Some(entry) = self.inner.get(&ProposalShortId::from_tx_hash(&tx_hash)) { match entry @@ -81,9 +81,12 @@ impl CellProvider for PendingQueue { .output_with_data(out_point.index().unpack()) { Some((output, data)) => { - let cell_meta = CellMetaBuilder::from_cell_output(output, data) + let mut cell_meta = CellMetaBuilder::from_cell_output(output, data) .out_point(out_point.to_owned()) .build(); + if !allow_in_txpool && !with_data { + cell_meta.mem_cell_data_hash = None; + } CellStatus::live_cell(cell_meta) } None => CellStatus::Unknown, diff --git a/tx-pool/src/component/proposed.rs b/tx-pool/src/component/proposed.rs index c365ccadf9..2929d18004 100644 --- a/tx-pool/src/component/proposed.rs +++ b/tx-pool/src/component/proposed.rs @@ -97,16 +97,19 @@ pub struct ProposedPool { } impl CellProvider for ProposedPool { - fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { if let Some(x) = self.edges.get_output_ref(out_point) { // output consumed if x.is_some() { CellStatus::Dead } else { let (output, data) = self.get_output_with_data(out_point).expect("output"); - let cell_meta = CellMetaBuilder::from_cell_output(output, data) + let mut cell_meta = CellMetaBuilder::from_cell_output(output, data) .out_point(out_point.to_owned()) .build(); + if !allow_in_txpool && !with_data { + cell_meta.mem_cell_data_hash = None; + } CellStatus::live_cell(cell_meta) } } else if self.edges.get_input_ref(out_point).is_some() { diff --git a/tx-pool/src/pool.rs b/tx-pool/src/pool.rs index 2aa4a726af..14cad81f19 100644 --- a/tx-pool/src/pool.rs +++ b/tx-pool/src/pool.rs @@ -302,11 +302,22 @@ impl TxPool { let pending_and_proposed_provider = OverlayCellProvider::new(&self.pending, &gap_and_proposed_provider); let mut seen_inputs = HashSet::new(); + let allow_in_txpool = { + let tip_header = snapshot.tip_header(); + let consensus = snapshot.consensus(); + let proposal_window = consensus.tx_proposal_window(); + let tx_env = TxVerifyEnv::new_submit(tip_header); + let epoch_number = tx_env.epoch_number(proposal_window); + consensus + .hardfork_switch() + .is_allow_cell_data_hash_in_txpool_enabled(epoch_number) + }; resolve_transaction( tx, &mut seen_inputs, &pending_and_proposed_provider, snapshot, + allow_in_txpool, ) .map_err(Reject::Resolve) } @@ -332,7 +343,24 @@ impl TxPool { let snapshot = self.snapshot(); let cell_provider = OverlayCellProvider::new(&self.proposed, snapshot); let mut seen_inputs = HashSet::new(); - resolve_transaction(tx, &mut seen_inputs, &cell_provider, snapshot).map_err(Reject::Resolve) + let allow_in_txpool = { + let tip_header = snapshot.tip_header(); + let consensus = snapshot.consensus(); + let proposal_window = consensus.tx_proposal_window(); + let tx_env = TxVerifyEnv::new_proposed(tip_header, 1); + let epoch_number = tx_env.epoch_number(proposal_window); + consensus + .hardfork_switch() + .is_allow_cell_data_hash_in_txpool_enabled(epoch_number) + }; + resolve_transaction( + tx, + &mut seen_inputs, + &cell_provider, + snapshot, + allow_in_txpool, + ) + .map_err(Reject::Resolve) } pub(crate) fn check_rtx_from_proposed(&self, rtx: &ResolvedTransaction) -> Result<(), Reject> { diff --git a/util/snapshot/src/lib.rs b/util/snapshot/src/lib.rs index 2fc1745061..8cea796808 100644 --- a/util/snapshot/src/lib.rs +++ b/util/snapshot/src/lib.rs @@ -176,8 +176,10 @@ impl<'a> ChainStore<'a> for Snapshot { } impl CellProvider for Snapshot { - fn cell(&self, out_point: &OutPoint, with_data: bool) -> CellStatus { - self.store.cell_provider().cell(out_point, with_data) + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { + self.store + .cell_provider() + .cell(out_point, with_data, allow_in_txpool) } } diff --git a/util/test-chain-utils/src/mock_store.rs b/util/test-chain-utils/src/mock_store.rs index 896e456b19..466aa4f943 100644 --- a/util/test-chain-utils/src/mock_store.rs +++ b/util/test-chain-utils/src/mock_store.rs @@ -71,7 +71,7 @@ impl MockStore { } impl CellProvider for MockStore { - fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { match self.0.get_transaction(&out_point.tx_hash()) { Some((tx, _)) => tx .outputs() @@ -82,9 +82,12 @@ impl CellProvider for MockStore { .get(out_point.index().unpack()) .expect("output data"); - let cell_meta = CellMetaBuilder::from_cell_output(cell, data.unpack()) + let mut cell_meta = CellMetaBuilder::from_cell_output(cell, data.unpack()) .out_point(out_point.to_owned()) .build(); + if !allow_in_txpool && !with_data { + cell_meta.mem_cell_data_hash = None; + } CellStatus::live_cell(cell_meta) }) diff --git a/util/types/src/core/cell.rs b/util/types/src/core/cell.rs index bdf0278df3..0850383c0a 100644 --- a/util/types/src/core/cell.rs +++ b/util/types/src/core/cell.rs @@ -372,7 +372,7 @@ where /// TODO(doc): @quake pub trait CellProvider { /// TODO(doc): @quake - fn cell(&self, out_point: &OutPoint, with_data: bool) -> CellStatus; + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus; } /// TODO(doc): @quake @@ -400,11 +400,13 @@ where A: CellProvider, B: CellProvider, { - fn cell(&self, out_point: &OutPoint, with_data: bool) -> CellStatus { - match self.overlay.cell(out_point, with_data) { + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { + match self.overlay.cell(out_point, with_data, allow_in_txpool) { CellStatus::Live(cell_meta) => CellStatus::Live(cell_meta), CellStatus::Dead => CellStatus::Dead, - CellStatus::Unknown => self.cell_provider.cell(out_point, with_data), + CellStatus::Unknown => self + .cell_provider + .cell(out_point, with_data, allow_in_txpool), } } } @@ -452,7 +454,7 @@ impl<'a> BlockCellProvider<'a> { } impl<'a> CellProvider for BlockCellProvider<'a> { - fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, _with_data: bool, _allow_in_txpool: bool) -> CellStatus { self.output_indices .get(&out_point.tx_hash()) .and_then(|i| { @@ -532,7 +534,7 @@ impl<'a> TransactionsProvider<'a> { } impl<'a> CellProvider for TransactionsProvider<'a> { - fn cell(&self, out_point: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, out_point: &OutPoint, with_data: bool, allow_in_txpool: bool) -> CellStatus { match self.transactions.get(&out_point.tx_hash()) { Some(tx) => tx .outputs() @@ -543,7 +545,10 @@ impl<'a> CellProvider for TransactionsProvider<'a> { .get(out_point.index().unpack()) .expect("output data") .raw_data(); - let cell_meta = CellMetaBuilder::from_cell_output(cell, data).build(); + let mut cell_meta = CellMetaBuilder::from_cell_output(cell, data).build(); + if !allow_in_txpool && !with_data { + cell_meta.mem_cell_data_hash = None; + } CellStatus::live_cell(cell_meta) }) .unwrap_or(CellStatus::Unknown), @@ -622,6 +627,7 @@ pub fn resolve_transaction( seen_inputs: &mut HashSet, cell_provider: &CP, header_checker: &HC, + allow_in_txpool: bool, ) -> Result { let (mut resolved_inputs, mut resolved_cell_deps, mut resolved_dep_groups) = ( Vec::with_capacity(transaction.inputs().len()), @@ -635,7 +641,7 @@ pub fn resolve_transaction( return Err(OutPointError::Dead(out_point.clone())); } - let cell_status = cell_provider.cell(out_point, with_data); + let cell_status = cell_provider.cell(out_point, with_data, allow_in_txpool); match cell_status { CellStatus::Dead => Err(OutPointError::Dead(out_point.clone())), CellStatus::Unknown => Err(OutPointError::Unknown(out_point.clone())), @@ -737,7 +743,7 @@ fn build_cell_meta_from_out_point( cell_provider: &CP, out_point: &OutPoint, ) -> Result { - let cell_status = cell_provider.cell(out_point, true); + let cell_status = cell_provider.cell(out_point, true, false); match cell_status { CellStatus::Dead => Err(OutPointError::Dead(out_point.clone())), CellStatus::Unknown => Err(OutPointError::Unknown(out_point.clone())), @@ -855,7 +861,7 @@ mod tests { cells: HashMap>, } impl CellProvider for CellMemoryDb { - fn cell(&self, o: &OutPoint, _with_data: bool) -> CellStatus { + fn cell(&self, o: &OutPoint, _with_data: bool, _allow_in_txpool: bool) -> CellStatus { match self.cells.get(o) { Some(&Some(ref cell_meta)) => CellStatus::live_cell(cell_meta.clone()), Some(&None) => CellStatus::Dead, @@ -912,9 +918,9 @@ mod tests { db.cells.insert(p1.clone(), Some(o.clone())); db.cells.insert(p2.clone(), None); - assert_eq!(CellStatus::Live(o), db.cell(&p1, false)); - assert_eq!(CellStatus::Dead, db.cell(&p2, false)); - assert_eq!(CellStatus::Unknown, db.cell(&p3, false)); + assert_eq!(CellStatus::Live(o), db.cell(&p1, false, false)); + assert_eq!(CellStatus::Dead, db.cell(&p2, false, false)); + assert_eq!(CellStatus::Unknown, db.cell(&p3, false, false)); } #[test] @@ -953,6 +959,7 @@ mod tests { &mut seen_inputs, &cell_provider, &header_checker, + false, ) .unwrap(); @@ -986,6 +993,7 @@ mod tests { &mut seen_inputs, &cell_provider, &header_checker, + false, ); assert_error_eq!(result.unwrap_err(), OutPointError::InvalidDepGroup(op_dep)); } @@ -1015,6 +1023,7 @@ mod tests { &mut seen_inputs, &cell_provider, &header_checker, + false, ); assert_error_eq!(result.unwrap_err(), OutPointError::Unknown(op_unknown),); } @@ -1041,6 +1050,7 @@ mod tests { &mut seen_inputs, &cell_provider, &header_checker, + false, ); assert!(result.is_ok()); @@ -1067,6 +1077,7 @@ mod tests { &mut seen_inputs, &cell_provider, &header_checker, + false, ); assert_error_eq!( @@ -1159,8 +1170,8 @@ mod tests { .build(); let mut seen_inputs = HashSet::new(); - let rtx = - resolve_transaction(tx, &mut seen_inputs, &cell_provider, &header_checker).unwrap(); + let rtx = resolve_transaction(tx, &mut seen_inputs, &cell_provider, &header_checker, false) + .unwrap(); assert_eq!(rtx.resolved_cell_deps[0], dummy_cell_meta,); } @@ -1187,12 +1198,22 @@ mod tests { .build(); let mut seen_inputs = HashSet::new(); - let result1 = - resolve_transaction(tx1, &mut seen_inputs, &cell_provider, &header_checker); + let result1 = resolve_transaction( + tx1, + &mut seen_inputs, + &cell_provider, + &header_checker, + false, + ); assert!(result1.is_ok()); - let result2 = - resolve_transaction(tx2, &mut seen_inputs, &cell_provider, &header_checker); + let result2 = resolve_transaction( + tx2, + &mut seen_inputs, + &cell_provider, + &header_checker, + false, + ); assert!(result2.is_ok()); } @@ -1208,13 +1229,23 @@ mod tests { let tx2 = TransactionBuilder::default().cell_dep(dep).build(); let mut seen_inputs = HashSet::new(); - let result1 = - resolve_transaction(tx1, &mut seen_inputs, &cell_provider, &header_checker); + let result1 = resolve_transaction( + tx1, + &mut seen_inputs, + &cell_provider, + &header_checker, + false, + ); assert!(result1.is_ok()); - let result2 = - resolve_transaction(tx2, &mut seen_inputs, &cell_provider, &header_checker); + let result2 = resolve_transaction( + tx2, + &mut seen_inputs, + &cell_provider, + &header_checker, + false, + ); assert_error_eq!(result2.unwrap_err(), OutPointError::Dead(out_point)); } diff --git a/util/types/src/core/hardfork.rs b/util/types/src/core/hardfork.rs index 0049b79404..bb50c01c67 100644 --- a/util/types/src/core/hardfork.rs +++ b/util/types/src/core/hardfork.rs @@ -98,6 +98,7 @@ pub struct HardForkSwitch { rfc_pr_0222: EpochNumber, rfc_pr_0223: EpochNumber, rfc_pr_0224: EpochNumber, + rfc_pr_0228: EpochNumber, rfc_pr_0230: EpochNumber, } @@ -125,6 +126,11 @@ pub struct HardForkSwitchBuilder { /// /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) pub rfc_pr_0224: Option, + /// Let the syscall `load_cell_data_hash` return correct data hash + /// for cells which are still in the tx pool and not committed yet. + /// + /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) + pub rfc_pr_0228: Option, /// Allow unknown block versions and transactions versions. /// /// Ref: [CKB RFC xxxx](https://github.com/nervosnetwork/rfcs/tree/master/rfcs/xxxx-rfc-title) @@ -144,6 +150,7 @@ impl HardForkSwitch { .rfc_pr_0222(self.rfc_pr_0222()) .rfc_pr_0223(self.rfc_pr_0223()) .rfc_pr_0224(self.rfc_pr_0224()) + .rfc_pr_0228(self.rfc_pr_0228()) .rfc_pr_0230(self.rfc_pr_0230()) } @@ -155,6 +162,7 @@ impl HardForkSwitch { .disable_rfc_pr_0222() .disable_rfc_pr_0223() .disable_rfc_pr_0224() + .disable_rfc_pr_0228() .disable_rfc_pr_0230() .build() .unwrap() @@ -189,6 +197,13 @@ define_methods!( disable_rfc_pr_0224, "RFC PR 0224" ); +define_methods!( + rfc_pr_0228, + allow_cell_data_hash_in_txpool, + is_allow_cell_data_hash_in_txpool_enabled, + disable_rfc_pr_0228, + "RFC PR 0228" +); define_methods!( rfc_pr_0230, allow_unknown_versions, @@ -216,12 +231,14 @@ impl HardForkSwitchBuilder { let rfc_pr_0222 = try_find!(rfc_pr_0222); let rfc_pr_0223 = try_find!(rfc_pr_0223); let rfc_pr_0224 = try_find!(rfc_pr_0224); + let rfc_pr_0228 = try_find!(rfc_pr_0228); let rfc_pr_0230 = try_find!(rfc_pr_0230); Ok(HardForkSwitch { rfc_pr_0221, rfc_pr_0222, rfc_pr_0223, rfc_pr_0224, + rfc_pr_0228, rfc_pr_0230, }) }