From de44cf52c0b7daebc3d6a45375c715f0bf9a26b7 Mon Sep 17 00:00:00 2001 From: Eason Gao Date: Wed, 2 Aug 2023 15:11:43 +0800 Subject: [PATCH] refactor(executor): thread local instead of global variable (#1280) --- core/api/src/adapter.rs | 50 ++++-- core/api/src/jsonrpc/impl/ckb_light_client.rs | 36 +++-- core/api/src/jsonrpc/impl/web3.rs | 144 +++++++++--------- core/api/src/jsonrpc/mod.rs | 2 +- core/consensus/src/adapter.rs | 36 +++-- core/consensus/src/engine.rs | 6 +- core/consensus/src/synchronization.rs | 3 +- core/executor/src/adapter/mod.rs | 12 ++ core/executor/src/lib.rs | 30 +++- core/executor/src/precompiles/call_ckb_vm.rs | 3 +- core/executor/src/precompiles/get_cell.rs | 5 +- core/executor/src/precompiles/get_header.rs | 5 +- core/executor/src/precompiles/metadata.rs | 7 +- .../src/precompiles/verify_by_ckb_vm.rs | 3 +- .../system_contract/ckb_light_client/mod.rs | 22 +-- .../system_contract/ckb_light_client/store.rs | 24 +-- .../src/system_contract/image_cell/mod.rs | 28 ++-- .../src/system_contract/image_cell/store.rs | 25 +-- .../src/system_contract/metadata/handle.rs | 20 ++- .../src/system_contract/metadata/mod.rs | 21 +-- .../src/system_contract/metadata/store.rs | 25 +-- core/executor/src/system_contract/mod.rs | 102 ++++++++----- .../src/system_contract/native_token.rs | 4 +- core/executor/src/system_contract/utils.rs | 9 +- .../tests/system_script/ckb_light_client.rs | 31 ++-- .../src/tests/system_script/image_cell.rs | 32 ++-- core/mempool/src/adapter/mod.rs | 49 +++--- core/run/src/lib.rs | 37 +++-- protocol/src/lazy.rs | 4 +- protocol/src/traits/api.rs | 6 +- 30 files changed, 459 insertions(+), 322 deletions(-) diff --git a/core/api/src/adapter.rs b/core/api/src/adapter.rs index f0f012b7f..711584a11 100644 --- a/core/api/src/adapter.rs +++ b/core/api/src/adapter.rs @@ -1,16 +1,17 @@ use std::sync::Arc; -use core_executor::{ - system_contract::metadata::MetadataHandle, AxonExecutor, AxonExecutorAdapter, MPTTrie, -}; use protocol::traits::{APIAdapter, Context, Executor, ExecutorAdapter, MemPool, Network, Storage}; use protocol::types::{ Account, BigEndianHash, Block, BlockNumber, Bytes, CkbRelatedInfo, ExecutorContext, Hash, - Header, Metadata, Proposal, Receipt, SignedTransaction, TxResp, H160, MAX_BLOCK_GAS_LIMIT, - NIL_DATA, RLP_NULL, U256, + Header, Metadata, Proposal, Receipt, SignedTransaction, TxResp, H160, H256, + MAX_BLOCK_GAS_LIMIT, NIL_DATA, RLP_NULL, U256, }; use protocol::{async_trait, codec::ProtocolCodec, trie, ProtocolResult}; +use core_executor::{ + system_contract::metadata::MetadataHandle, AxonExecutor, AxonExecutorAdapter, MPTTrie, +}; + use crate::APIError; #[derive(Clone)] @@ -237,14 +238,43 @@ where block_number: Option, ) -> ProtocolResult { if let Some(num) = block_number { - return MetadataHandle::default().get_metadata_by_block_number(num); + return MetadataHandle::new(self.get_metadata_root(ctx).await?) + .get_metadata_by_block_number(num); } - let num = self.storage.get_latest_block_header(ctx).await?.number; - MetadataHandle::default().get_metadata_by_block_number(num) + let num = self + .storage + .get_latest_block_header(ctx.clone()) + .await? + .number; + MetadataHandle::new(self.get_metadata_root(ctx).await?).get_metadata_by_block_number(num) + } + + async fn get_ckb_related_info(&self, ctx: Context) -> ProtocolResult { + MetadataHandle::new(self.get_metadata_root(ctx).await?).get_ckb_related_info() } - async fn get_ckb_related_info(&self, _ctx: Context) -> ProtocolResult { - MetadataHandle::default().get_ckb_related_info() + async fn get_image_cell_root(&self, ctx: Context) -> ProtocolResult { + let state_root = self.storage.get_latest_block_header(ctx).await?.state_root; + + Ok(AxonExecutorAdapter::from_root( + state_root, + Arc::clone(&self.trie_db), + Arc::clone(&self.storage), + Default::default(), + )? + .get_image_cell_root()) + } + + async fn get_metadata_root(&self, ctx: Context) -> ProtocolResult { + let state_root = self.storage.get_latest_block_header(ctx).await?.state_root; + + Ok(AxonExecutorAdapter::from_root( + state_root, + Arc::clone(&self.trie_db), + Arc::clone(&self.storage), + Default::default(), + )? + .get_metadata_root()) } } diff --git a/core/api/src/jsonrpc/impl/ckb_light_client.rs b/core/api/src/jsonrpc/impl/ckb_light_client.rs index 920facf79..8236dea32 100644 --- a/core/api/src/jsonrpc/impl/ckb_light_client.rs +++ b/core/api/src/jsonrpc/impl/ckb_light_client.rs @@ -1,24 +1,31 @@ +use std::sync::Arc; + use ckb_jsonrpc_types::{CellData, CellInfo, HeaderView as CkbHeaderView, JsonBytes, OutPoint}; use ckb_traits::HeaderProvider; use ckb_types::core::cell::{CellProvider, CellStatus}; use ckb_types::{packed, prelude::Pack}; -use core_executor::system_contract::DataProvider; - +use core_executor::DataProvider; +use jsonrpsee::core::Error; +use protocol::traits::{APIAdapter, Context}; use protocol::{async_trait, ckb_blake2b_256, types::H256}; use crate::jsonrpc::{CkbLightClientRpcServer, RpcResult}; -#[derive(Default, Clone, Debug)] -pub struct CkbLightClientRpcImpl { - data_provider: DataProvider, +#[derive(Clone, Debug)] +pub struct CkbLightClientRpcImpl { + adapter: Arc, } #[async_trait] -impl CkbLightClientRpcServer for CkbLightClientRpcImpl { +impl CkbLightClientRpcServer for CkbLightClientRpcImpl { async fn get_block_header_by_hash(&self, hash: H256) -> RpcResult> { - Ok(self - .data_provider + let root = self + .adapter + .get_image_cell_root(Context::new()) + .await + .map_err(|e| Error::Custom(e.to_string()))?; + Ok(DataProvider::new(root) .get_header(&(hash.0.pack())) .map(Into::into)) } @@ -29,8 +36,13 @@ impl CkbLightClientRpcServer for CkbLightClientRpcImpl { with_data: bool, ) -> RpcResult> { let out_point: packed::OutPoint = out_point.into(); + let root = self + .adapter + .get_image_cell_root(Context::new()) + .await + .map_err(|e| Error::Custom(e.to_string()))?; - match self.data_provider.cell(&out_point, false) { + match DataProvider::new(root).cell(&out_point, false) { CellStatus::Live(c) => { let data = with_data.then_some(c.mem_cell_data).flatten(); Ok(Some(CellInfo { @@ -45,3 +57,9 @@ impl CkbLightClientRpcServer for CkbLightClientRpcImpl { } } } + +impl CkbLightClientRpcImpl { + pub fn new(adapter: Arc) -> Self { + Self { adapter } + } +} diff --git a/core/api/src/jsonrpc/impl/web3.rs b/core/api/src/jsonrpc/impl/web3.rs index 50df9af0d..9b4170405 100644 --- a/core/api/src/jsonrpc/impl/web3.rs +++ b/core/api/src/jsonrpc/impl/web3.rs @@ -190,6 +190,80 @@ impl Web3RpcImpl { reward, )) } + + async fn extract_interoperation_tx_sender( + &self, + utx: &UnverifiedTransaction, + signature: &SignatureComponents, + ) -> RpcResult { + // Call CKB-VM mode + if signature.r[0] == 0 { + let r = rlp::decode::(&signature.r[1..]) + .map_err(|e| Error::Custom(e.to_string()))?; + + return Ok(Hasher::digest(&r.pub_key).into()); + } + + // Verify by CKB-VM mode + let r = SignatureR::decode(&signature.r).map_err(|e| Error::Custom(e.to_string()))?; + let s = SignatureS::decode(&signature.s).map_err(|e| Error::Custom(e.to_string()))?; + let address_source = r.address_source(); + + let ckb_tx_view = + InteroperationImpl::dummy_transaction(r.clone(), s, Some(utx.signature_hash(true).0)); + let dummy_input = r.dummy_input(); + + let input = ckb_tx_view + .inputs() + .get(address_source.index as usize) + .ok_or(Error::Custom("Invalid address source".to_string()))?; + + log::debug!("[mempool]: verify interoperation tx sender \ntx view \n{:?}\ndummy input\n {:?}\naddress source\n{:?}\n", ckb_tx_view, dummy_input, address_source); + + // Dummy input mode + if is_dummy_out_point(&input.previous_output()) { + log::debug!("[mempool]: verify interoperation tx dummy input mode."); + + if let Some(cell) = dummy_input { + if address_source.type_ == 1 && cell.type_script.is_none() { + return Err(Error::Custom( + "Invalid address source in dummy input mode".to_string(), + )); + } + + let script_hash = if address_source.type_ == 0 { + cell.lock_script_hash() + } else { + cell.type_script_hash().unwrap() + }; + + return Ok(Hasher::digest(script_hash).into()); + } + + return Err(Error::Custom("No dummy input cell".to_string())); + } + + // Reality input mode + let root = self + .adapter + .get_image_cell_root(Context::new()) + .await + .map_err(|e| Error::Custom(e.to_string()))?; + match DataProvider::new(root).cell(&input.previous_output(), true) { + CellStatus::Live(cell) => { + let script_hash = if address_source.type_ == 0 { + ckb_blake2b_256(cell.cell_output.lock().as_slice()) + } else if let Some(type_script) = cell.cell_output.type_().to_opt() { + ckb_blake2b_256(type_script.as_slice()) + } else { + return Err(Error::Custom("Invalid address source".to_string())); + }; + + Ok(Hasher::digest(script_hash).into()) + } + _ => Err(Error::Custom("Cannot find input cell in ICSC".to_string())), + } + } } #[async_trait] @@ -231,7 +305,7 @@ impl Web3RpcServer for Web3RpcImpl { if sig.is_eth_sig() { None } else { - Some(extract_interoperation_tx_sender(&utx, sig)?) + Some(self.extract_interoperation_tx_sender(&utx, sig).await?) } } else { return Err(Error::Custom("The transaction is not signed".to_string())); @@ -1161,71 +1235,3 @@ pub fn from_receipt_to_web3_log( } } } - -fn extract_interoperation_tx_sender( - utx: &UnverifiedTransaction, - signature: &SignatureComponents, -) -> RpcResult { - // Call CKB-VM mode - if signature.r[0] == 0 { - let r = rlp::decode::(&signature.r[1..]) - .map_err(|e| Error::Custom(e.to_string()))?; - - return Ok(Hasher::digest(&r.pub_key).into()); - } - - // Verify by CKB-VM mode - let r = SignatureR::decode(&signature.r).map_err(|e| Error::Custom(e.to_string()))?; - let s = SignatureS::decode(&signature.s).map_err(|e| Error::Custom(e.to_string()))?; - let address_source = r.address_source(); - - let ckb_tx_view = - InteroperationImpl::dummy_transaction(r.clone(), s, Some(utx.signature_hash(true).0)); - let dummy_input = r.dummy_input(); - - let input = ckb_tx_view - .inputs() - .get(address_source.index as usize) - .ok_or(Error::Custom("Invalid address source".to_string()))?; - - log::debug!("[mempool]: verify interoperation tx sender \ntx view \n{:?}\ndummy input\n {:?}\naddress source\n{:?}\n", ckb_tx_view, dummy_input, address_source); - - // Dummy input mode - if is_dummy_out_point(&input.previous_output()) { - log::debug!("[mempool]: verify interoperation tx dummy input mode."); - - if let Some(cell) = dummy_input { - if address_source.type_ == 1 && cell.type_script.is_none() { - return Err(Error::Custom( - "Invalid address source in dummy input mode".to_string(), - )); - } - - let script_hash = if address_source.type_ == 0 { - cell.lock_script_hash() - } else { - cell.type_script_hash().unwrap() - }; - - return Ok(Hasher::digest(script_hash).into()); - } - - return Err(Error::Custom("No dummy input cell".to_string())); - } - - // Reality input mode - match DataProvider.cell(&input.previous_output(), true) { - CellStatus::Live(cell) => { - let script_hash = if address_source.type_ == 0 { - ckb_blake2b_256(cell.cell_output.lock().as_slice()) - } else if let Some(type_script) = cell.cell_output.type_().to_opt() { - ckb_blake2b_256(type_script.as_slice()) - } else { - return Err(Error::Custom("Invalid address source".to_string())); - }; - - Ok(Hasher::digest(script_hash).into()) - } - _ => Err(Error::Custom("Cannot find input cell in ICSC".to_string())), - } -} diff --git a/core/api/src/jsonrpc/mod.rs b/core/api/src/jsonrpc/mod.rs index 10994c05e..06e1003b8 100644 --- a/core/api/src/jsonrpc/mod.rs +++ b/core/api/src/jsonrpc/mod.rs @@ -259,7 +259,7 @@ pub async fn run_jsonrpc_server( let filter = r#impl::filter_module(Arc::clone(&adapter), config.web3.log_filter_max_block_range) .into_rpc(); - let ckb_light_client_rpc = r#impl::CkbLightClientRpcImpl::default().into_rpc(); + let ckb_light_client_rpc = r#impl::CkbLightClientRpcImpl::new(Arc::clone(&adapter)).into_rpc(); rpc.merge(node_rpc).unwrap(); rpc.merge(axon_rpc).unwrap(); diff --git a/core/consensus/src/adapter.rs b/core/consensus/src/adapter.rs index 2241c812d..6e430b87f 100644 --- a/core/consensus/src/adapter.rs +++ b/core/consensus/src/adapter.rs @@ -41,7 +41,6 @@ pub struct OverlordConsensusAdapter< storage: Arc, trie_db: Arc, - metadata: Arc, overlord_handler: RwLock>>, crypto: Arc, } @@ -339,9 +338,10 @@ where Arc::clone(&self.storage), proposal.clone().into(), )?; + let root = backend.get_metadata_root(); + let metadata_handle = MetadataHandle::new(root); - let verifier_list = self - .metadata + let verifier_list = metadata_handle .get_metadata_by_block_number(proposal.number)? .verifier_list; @@ -357,15 +357,21 @@ where } async fn is_last_block_in_current_epoch(&self, block_number: u64) -> ProtocolResult { - self.metadata.is_last_block_in_current_epoch(block_number) + self.get_metadata_handle(Context::new()) + .await? + .is_last_block_in_current_epoch(block_number) } async fn get_metadata_by_block_number(&self, block_number: u64) -> ProtocolResult { - self.metadata.get_metadata_by_block_number(block_number) + self.get_metadata_handle(Context::new()) + .await? + .get_metadata_by_block_number(block_number) } async fn get_metadata_by_epoch(&self, epoch: u64) -> ProtocolResult { - self.metadata.get_metadata_by_epoch(epoch) + self.get_metadata_handle(Context::new()) + .await? + .get_metadata_by_epoch(epoch) } #[trace_span(kind = "consensus.adapter")] @@ -460,7 +466,8 @@ where // the auth_list for the target should comes from previous number let metadata = self - .metadata + .get_metadata_handle(ctx.clone()) + .await? .get_metadata_by_block_number(block.header.number)?; if !metadata.version.contains(block.header.number) { @@ -620,14 +627,12 @@ where mempool: Arc, storage: Arc, trie_db: Arc, - metadata: Arc, crypto: Arc, ) -> ProtocolResult { Ok(OverlordConsensusAdapter { network, mempool, storage, - metadata, trie_db, overlord_handler: RwLock::new(None), crypto, @@ -637,4 +642,17 @@ where pub fn set_overlord_handler(&self, handler: OverlordHandler) { *self.overlord_handler.write() = Some(handler) } + + async fn get_metadata_handle(&self, ctx: Context) -> ProtocolResult { + let current_state_root = self.storage.get_latest_block_header(ctx).await?.state_root; + let root = AxonExecutorAdapter::from_root( + current_state_root, + Arc::clone(&self.trie_db), + Arc::clone(&self.storage), + Default::default(), + )? + .get_metadata_root(); + + Ok(MetadataHandle::new(root)) + } } diff --git a/core/consensus/src/engine.rs b/core/consensus/src/engine.rs index b3a82fc3b..53a479527 100644 --- a/core/consensus/src/engine.rs +++ b/core/consensus/src/engine.rs @@ -21,10 +21,7 @@ use protocol::types::{ Block, Bytes, ExecResp, Hash, Hasher, Hex, Log, MerkleRoot, Metadata, Proof, Proposal, Receipt, SignedTransaction, ValidatorExtend, BASE_FEE_PER_GAS, MAX_BLOCK_GAS_LIMIT, RLP_NULL, U256, }; -use protocol::{ - async_trait, lazy::CURRENT_STATE_ROOT, tokio::sync::Mutex as AsyncMutex, ProtocolError, - ProtocolResult, -}; +use protocol::{async_trait, tokio::sync::Mutex as AsyncMutex, ProtocolError, ProtocolResult}; use core_executor::logs_bloom; @@ -639,7 +636,6 @@ impl ConsensusEngine { proof: proof.clone(), }; - CURRENT_STATE_ROOT.swap(Arc::new(resp.state_root)); self.status.swap(new_status); // update timeout_gap of mempool diff --git a/core/consensus/src/synchronization.rs b/core/consensus/src/synchronization.rs index d589d1cda..cad1374f6 100644 --- a/core/consensus/src/synchronization.rs +++ b/core/consensus/src/synchronization.rs @@ -8,7 +8,7 @@ use common_apm_derive::trace_span; use protocol::tokio::{sync::Mutex, time::sleep}; use protocol::traits::{Context, Synchronization, SynchronizationAdapter}; use protocol::types::{Block, Proof, Proposal, Receipt, SignedTransaction, U256}; -use protocol::{async_trait, lazy::CURRENT_STATE_ROOT, ProtocolResult}; +use protocol::{async_trait, ProtocolResult}; use crate::status::{CurrentStatus, StatusAgent}; use crate::util::digest_signed_transactions; @@ -375,7 +375,6 @@ impl OverlordSynchronization { ) .await?; - CURRENT_STATE_ROOT.swap(Arc::new(resp.state_root)); status_agent.swap(new_status); // If there are transactions in the transaction pool that have been on chain diff --git a/core/executor/src/adapter/mod.rs b/core/executor/src/adapter/mod.rs index ddf4902c8..aad387afe 100644 --- a/core/executor/src/adapter/mod.rs +++ b/core/executor/src/adapter/mod.rs @@ -14,6 +14,10 @@ use protocol::types::{ }; use protocol::{codec::ProtocolCodec, trie, ProtocolResult}; +use crate::system_contract::{ + HEADER_CELL_ROOT_KEY, IMAGE_CELL_CONTRACT_ADDRESS, METADATA_CONTRACT_ADDRESS, METADATA_ROOT_KEY, +}; + const GET_BLOCK_HASH_NUMBER_RANGE: u64 = 256; macro_rules! blocking_async { @@ -280,6 +284,14 @@ where }) } + pub fn get_metadata_root(&self) -> H256 { + self.storage(METADATA_CONTRACT_ADDRESS, *METADATA_ROOT_KEY) + } + + pub fn get_image_cell_root(&self) -> H256 { + self.storage(IMAGE_CELL_CONTRACT_ADDRESS, *HEADER_CELL_ROOT_KEY) + } + fn apply>( &mut self, address: H160, diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs index 2acc870c2..963d3a4b8 100644 --- a/core/executor/src/lib.rs +++ b/core/executor/src/lib.rs @@ -10,10 +10,12 @@ mod tests; mod utils; pub use crate::adapter::{AxonExecutorAdapter, MPTTrie, RocksTrieDB}; +pub use crate::system_contract::{metadata::MetadataHandle, DataProvider}; pub use crate::utils::{ code_address, decode_revert_msg, logs_bloom, DefaultFeeAllocator, FeeInlet, }; +use std::cell::RefCell; use std::collections::BTreeMap; use std::iter::FromIterator; @@ -26,19 +28,27 @@ use protocol::codec::ProtocolCodec; use protocol::traits::{Backend, Executor, ExecutorAdapter}; use protocol::types::{ data_gas_cost, Account, Config, ExecResp, Hasher, SignedTransaction, TransactionAction, TxResp, - ValidatorExtend, GAS_CALL_TRANSACTION, GAS_CREATE_TRANSACTION, H160, NIL_DATA, RLP_NULL, U256, + ValidatorExtend, GAS_CALL_TRANSACTION, GAS_CREATE_TRANSACTION, H160, H256, NIL_DATA, RLP_NULL, + U256, }; use crate::precompiles::build_precompile_set; use crate::system_contract::{ after_block_hook, before_block_hook, system_contract_dispatch, CkbLightClientContract, ImageCellContract, MetadataContract, NativeTokenContract, SystemContract, + CKB_LIGHT_CLIENT_CONTRACT_ADDRESS, HEADER_CELL_ROOT_KEY, METADATA_CONTRACT_ADDRESS, + METADATA_ROOT_KEY, }; lazy_static::lazy_static! { pub static ref FEE_ALLOCATOR: ArcSwap> = ArcSwap::from_pointee(Box::new(DefaultFeeAllocator::default())); } +thread_local! { + pub(crate) static CURRENT_HEADER_CELL_ROOT: RefCell = RefCell::new(H256::default()); + pub(crate) static CURRENT_METADATA_ROOT: RefCell = RefCell::new(H256::default()); +} + pub trait FeeAllocate: Sync + Send { fn allocate( &self, @@ -130,6 +140,8 @@ impl Executor for AxonExecutor { let precompiles = build_precompile_set(); let config = Config::london(); + self.init_local_system_contract_roots(adapter); + // Execute system contracts before block hook. before_block_hook(adapter); @@ -172,6 +184,8 @@ impl Executor for AxonExecutor { // commit changes by all txs included in this block only once let new_state_root = adapter.commit(); + // self.update_system_contract_roots_for_external_module(); + ExecResp { state_root: new_state_root, receipt_root: TrieMerkle::from_iter(hashes.iter().enumerate()) @@ -293,6 +307,20 @@ impl AxonExecutor { } } + /// The `exec()` function is run in `tokio::task::block_in_place()` and all + /// the read or write operations are in the scope of exec function. The + /// thread context is not switched during exec function. + fn init_local_system_contract_roots(&self, adapter: &mut Adapter) { + CURRENT_HEADER_CELL_ROOT.with(|root| { + *root.borrow_mut() = + adapter.storage(CKB_LIGHT_CLIENT_CONTRACT_ADDRESS, *HEADER_CELL_ROOT_KEY); + }); + + CURRENT_METADATA_ROOT.with(|root| { + *root.borrow_mut() = adapter.storage(METADATA_CONTRACT_ADDRESS, *METADATA_ROOT_KEY); + }); + } + #[cfg(test)] fn test_exec( &self, diff --git a/core/executor/src/precompiles/call_ckb_vm.rs b/core/executor/src/precompiles/call_ckb_vm.rs index feadc6b3e..64495255f 100644 --- a/core/executor/src/precompiles/call_ckb_vm.rs +++ b/core/executor/src/precompiles/call_ckb_vm.rs @@ -8,6 +8,7 @@ use protocol::types::{CellDep, H160, H256}; use core_interoperation::{cycle_to_gas, gas_to_cycle, InteroperationImpl}; use crate::precompiles::{axon_precompile_address, PrecompileContract}; +use crate::CURRENT_HEADER_CELL_ROOT; use crate::{err, system_contract::DataProvider}; macro_rules! try_rlp { @@ -33,7 +34,7 @@ impl PrecompileContract for CkbVM { let rlp = Rlp::new(input); let res = ::call_ckb_vm( Default::default(), - &DataProvider::default(), + &DataProvider::new(CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow())), get_cell_dep(&rlp)?, &try_rlp!(rlp, list_at, 3), gas_to_cycle(gas), diff --git a/core/executor/src/precompiles/get_cell.rs b/core/executor/src/precompiles/get_cell.rs index e85b48b90..403886423 100644 --- a/core/executor/src/precompiles/get_cell.rs +++ b/core/executor/src/precompiles/get_cell.rs @@ -9,7 +9,7 @@ use protocol::types::{H160, H256}; use crate::precompiles::{axon_precompile_address, PrecompileContract}; use crate::system_contract::image_cell::{image_cell_abi, CellKey}; -use crate::{err, system_contract::image_cell::ImageCellContract}; +use crate::{err, system_contract::image_cell::ImageCellContract, CURRENT_HEADER_CELL_ROOT}; #[derive(Default, Clone)] pub struct GetCell; @@ -33,8 +33,9 @@ impl PrecompileContract for GetCell { let (tx_hash, index) = parse_input(input)?; + let root = CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow()); let cell = ImageCellContract::default() - .get_cell(&CellKey { tx_hash, index }) + .get_cell(root, &CellKey { tx_hash, index }) .map_err(|_| err!(_, "get cell"))? .map(|c| Cell { cell_output: packed::CellOutput::new_unchecked(c.cell_output).into(), diff --git a/core/executor/src/precompiles/get_header.rs b/core/executor/src/precompiles/get_header.rs index 1ec2697bb..1efa20611 100644 --- a/core/executor/src/precompiles/get_header.rs +++ b/core/executor/src/precompiles/get_header.rs @@ -5,7 +5,7 @@ use evm::{Context, ExitError, ExitSucceed}; use protocol::types::{H160, H256}; use crate::precompiles::{axon_precompile_address, PrecompileContract}; -use crate::{err, system_contract::CkbLightClientContract}; +use crate::{err, system_contract::CkbLightClientContract, CURRENT_HEADER_CELL_ROOT}; #[derive(Default, Clone)] pub struct GetHeader; @@ -30,8 +30,9 @@ impl PrecompileContract for GetHeader { let block_hash = H256(<[u8; 32] as AbiDecode>::decode(input).map_err(|_| err!(_, "decode input"))?); + let root = CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow()); let raw = CkbLightClientContract::default() - .get_raw(&block_hash.0) + .get_raw(root, &block_hash.0) .map_err(|_| err!(_, "get header"))?; Ok(( diff --git a/core/executor/src/precompiles/metadata.rs b/core/executor/src/precompiles/metadata.rs index 99cb01ae8..95d46b53a 100644 --- a/core/executor/src/precompiles/metadata.rs +++ b/core/executor/src/precompiles/metadata.rs @@ -4,7 +4,7 @@ use evm::{Context, ExitError, ExitSucceed}; use protocol::types::H160; use crate::precompiles::{axon_precompile_address, PrecompileContract}; -use crate::{err, system_contract::metadata::MetadataHandle}; +use crate::{err, system_contract::metadata::MetadataHandle, CURRENT_METADATA_ROOT}; const INPUT_LEN: usize = 1 + 8; @@ -34,12 +34,13 @@ impl PrecompileContract for Metadata { } let (ty, number) = parse_input(input)?; + let root = CURRENT_METADATA_ROOT.with(|r| *r.borrow()); let metadata = match ty { - 0u8 => MetadataHandle::default() + 0u8 => MetadataHandle::new(root) .get_metadata_by_block_number(number) .map_err(|e| err!(_, e.to_string()))?, - 1u8 => MetadataHandle::default() + 1u8 => MetadataHandle::new(root) .get_metadata_by_epoch(number) .map_err(|e| err!(_, e.to_string()))?, _ => return err!("Invalid call type"), diff --git a/core/executor/src/precompiles/verify_by_ckb_vm.rs b/core/executor/src/precompiles/verify_by_ckb_vm.rs index bef764a40..07fb3bb59 100644 --- a/core/executor/src/precompiles/verify_by_ckb_vm.rs +++ b/core/executor/src/precompiles/verify_by_ckb_vm.rs @@ -8,6 +8,7 @@ use protocol::types::{CellDep, OutPoint, SignatureR, SignatureS, Witness, H160, use core_interoperation::{cycle_to_gas, gas_to_cycle, InteroperationImpl}; use crate::precompiles::{axon_precompile_address, PrecompileContract}; +use crate::CURRENT_HEADER_CELL_ROOT; use crate::{err, system_contract::DataProvider}; macro_rules! try_rlp { @@ -38,7 +39,7 @@ impl PrecompileContract for CkbVM { if let Some(gas) = gas_limit { let res = InteroperationImpl::verify_by_ckb_vm( Default::default(), - &DataProvider::default(), + &DataProvider::new(CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow())), &InteroperationImpl::dummy_transaction( SignatureR::new_by_ref(cell_deps, header_deps, inputs, Default::default()), SignatureS::new(witnesses), diff --git a/core/executor/src/system_contract/ckb_light_client/mod.rs b/core/executor/src/system_contract/ckb_light_client/mod.rs index f5fb29c35..004b035a1 100644 --- a/core/executor/src/system_contract/ckb_light_client/mod.rs +++ b/core/executor/src/system_contract/ckb_light_client/mod.rs @@ -10,18 +10,19 @@ use ethers::abi::AbiDecode; use protocol::types::{SignedTransaction, TxResp, H160, H256}; use protocol::{traits::ExecutorAdapter, ProtocolResult}; -use crate::exec_try; use crate::system_contract::ckb_light_client::store::CkbLightClientStore; use crate::system_contract::utils::{succeed_resp, update_states}; -use crate::system_contract::{system_contract_address, SystemContract, CURRENT_HEADER_CELL_ROOT}; +use crate::system_contract::{system_contract_address, SystemContract}; +use crate::{exec_try, CURRENT_HEADER_CELL_ROOT}; +pub const CKB_LIGHT_CLIENT_CONTRACT_ADDRESS: H160 = system_contract_address(0x2); static ALLOW_READ: AtomicBool = AtomicBool::new(false); #[derive(Default)] pub struct CkbLightClientContract; impl SystemContract for CkbLightClientContract { - const ADDRESS: H160 = system_contract_address(0x2); + const ADDRESS: H160 = CKB_LIGHT_CLIENT_CONTRACT_ADDRESS; fn exec_( &self, @@ -33,8 +34,9 @@ impl SystemContract for CkbLightClientContract { let tx_data = tx.data(); let gas_limit = *tx.gas_limit(); + let root = CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow()); let mut store = exec_try!( - CkbLightClientStore::new(), + CkbLightClientStore::new(root), gas_limit, "[ckb light client] init ckb light client mpt" ); @@ -70,21 +72,19 @@ impl SystemContract for CkbLightClientContract { } } +/// These methods are provide for interoperation module to get CKB headers. impl CkbLightClientContract { - pub fn get_root(&self) -> H256 { - **CURRENT_HEADER_CELL_ROOT.load() - } - pub fn get_header_by_block_hash( &self, + root: H256, block_hash: &H256, ) -> ProtocolResult> { - let store = CkbLightClientStore::new()?; + let store = CkbLightClientStore::new(root)?; store.get_header(&block_hash.0) } - pub fn get_raw(&self, key: &[u8]) -> ProtocolResult>> { - let store = CkbLightClientStore::new()?; + pub fn get_raw(&self, root: H256, key: &[u8]) -> ProtocolResult>> { + let store = CkbLightClientStore::new(root)?; let ret = store.trie.get(key)?.map(Into::into); Ok(ret) } diff --git a/core/executor/src/system_contract/ckb_light_client/store.rs b/core/executor/src/system_contract/ckb_light_client/store.rs index d29ed96d1..3ebff59cd 100644 --- a/core/executor/src/system_contract/ckb_light_client/store.rs +++ b/core/executor/src/system_contract/ckb_light_client/store.rs @@ -5,28 +5,28 @@ use ethers::abi::{AbiDecode, AbiEncode}; use protocol::{types::H256, ProtocolResult}; use crate::system_contract::{ - ckb_light_client::ckb_light_client_abi, error::SystemScriptError, CURRENT_HEADER_CELL_ROOT, - HEADER_CELL_DB, + ckb_light_client::ckb_light_client_abi, error::SystemScriptError, HEADER_CELL_DB, }; -use crate::{adapter::RocksTrieDB, MPTTrie}; +use crate::{adapter::RocksTrieDB, MPTTrie, CURRENT_HEADER_CELL_ROOT}; pub struct CkbLightClientStore { pub trie: MPTTrie, } impl CkbLightClientStore { - pub fn new() -> ProtocolResult { - let trie_db = match HEADER_CELL_DB.get() { - Some(db) => db, - None => return Err(SystemScriptError::TrieDbNotInit.into()), + pub fn new(root: H256) -> ProtocolResult { + let trie_db = { + let lock = HEADER_CELL_DB.read(); + match lock.clone() { + Some(db) => db, + None => return Err(SystemScriptError::TrieDbNotInit.into()), + } }; - let root = **CURRENT_HEADER_CELL_ROOT.load(); - let trie = if root == H256::default() { - MPTTrie::new(Arc::clone(trie_db)) + MPTTrie::new(Arc::clone(&trie_db)) } else { - match MPTTrie::from_root(root, Arc::clone(trie_db)) { + match MPTTrie::from_root(root, Arc::clone(&trie_db)) { Ok(m) => m, Err(e) => return Err(SystemScriptError::RestoreMpt(e.to_string()).into()), } @@ -84,7 +84,7 @@ impl CkbLightClientStore { pub fn commit(&mut self) -> ProtocolResult<()> { match self.trie.commit() { Ok(new_root) => { - CURRENT_HEADER_CELL_ROOT.swap(Arc::new(new_root)); + CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow_mut() = new_root); Ok(()) } Err(e) => Err(SystemScriptError::CommitError(e.to_string()).into()), diff --git a/core/executor/src/system_contract/image_cell/mod.rs b/core/executor/src/system_contract/image_cell/mod.rs index b529f0c22..49f2ec478 100644 --- a/core/executor/src/system_contract/image_cell/mod.rs +++ b/core/executor/src/system_contract/image_cell/mod.rs @@ -15,16 +15,17 @@ use protocol::ProtocolResult; use crate::system_contract::image_cell::store::ImageCellStore; use crate::system_contract::utils::{succeed_resp, update_states}; -use crate::system_contract::{system_contract_address, SystemContract, CURRENT_HEADER_CELL_ROOT}; -use crate::{exec_try, MPTTrie}; +use crate::system_contract::{system_contract_address, SystemContract}; +use crate::{exec_try, MPTTrie, CURRENT_HEADER_CELL_ROOT}; +pub const IMAGE_CELL_CONTRACT_ADDRESS: H160 = system_contract_address(0x3); static ALLOW_READ: AtomicBool = AtomicBool::new(false); #[derive(Default)] pub struct ImageCellContract; impl SystemContract for ImageCellContract { - const ADDRESS: H160 = system_contract_address(0x3); + const ADDRESS: H160 = IMAGE_CELL_CONTRACT_ADDRESS; fn exec_( &self, @@ -36,8 +37,9 @@ impl SystemContract for ImageCellContract { let tx_data = tx.data(); let gas_limit = *tx.gas_limit(); + let root = CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow()); let mut store = exec_try!( - ImageCellStore::new(), + ImageCellStore::new(root), gas_limit, "[image cell] init image cell mpt" ); @@ -69,24 +71,26 @@ impl SystemContract for ImageCellContract { } } +/// These methods are provide for interoperation module to get CKB cells. impl ImageCellContract { - pub fn get_root(&self) -> H256 { - **CURRENT_HEADER_CELL_ROOT.load() - } - - pub fn get_cell(&self, key: &CellKey) -> ProtocolResult> { - ImageCellStore::new()?.get_cell(key) + pub fn get_cell(&self, root: H256, key: &CellKey) -> ProtocolResult> { + ImageCellStore::new(root)?.get_cell(key) } pub fn allow_read(&self) -> bool { ALLOW_READ.load(Ordering::Relaxed) } +} - pub fn save_cells( +impl ImageCellContract { + /// This method is only use in init. It insert the always success script + /// deployed cell. + pub(super) fn save_cells( &self, + root: H256, cells: Vec, created_number: u64, ) -> ProtocolResult<()> { - ImageCellStore::new()?.save_cells(cells, created_number) + ImageCellStore::new(root)?.save_cells(cells, created_number) } } diff --git a/core/executor/src/system_contract/image_cell/store.rs b/core/executor/src/system_contract/image_cell/store.rs index 19ea97623..32e995cbc 100644 --- a/core/executor/src/system_contract/image_cell/store.rs +++ b/core/executor/src/system_contract/image_cell/store.rs @@ -8,8 +8,10 @@ use protocol::types::H256; use protocol::ProtocolResult; use crate::system_contract::image_cell::{image_cell_abi, MPTTrie}; -use crate::system_contract::{CURRENT_HEADER_CELL_ROOT, HEADER_CELL_DB}; -use crate::{adapter::RocksTrieDB, system_contract::error::SystemScriptError}; +use crate::system_contract::HEADER_CELL_DB; +use crate::{ + adapter::RocksTrieDB, system_contract::error::SystemScriptError, CURRENT_HEADER_CELL_ROOT, +}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CellKey { @@ -22,18 +24,19 @@ pub struct ImageCellStore { } impl ImageCellStore { - pub fn new() -> ProtocolResult { - let trie_db = match HEADER_CELL_DB.get() { - Some(db) => db, - None => return Err(SystemScriptError::TrieDbNotInit.into()), + pub fn new(root: H256) -> ProtocolResult { + let trie_db = { + let lock = HEADER_CELL_DB.read(); + match lock.clone() { + Some(db) => db, + None => return Err(SystemScriptError::TrieDbNotInit.into()), + } }; - let root = **CURRENT_HEADER_CELL_ROOT.load(); - let trie = if root == H256::default() { - MPTTrie::new(Arc::clone(trie_db)) + MPTTrie::new(Arc::clone(&trie_db)) } else { - match MPTTrie::from_root(root, Arc::clone(trie_db)) { + match MPTTrie::from_root(root, Arc::clone(&trie_db)) { Ok(m) => m, Err(e) => return Err(SystemScriptError::RestoreMpt(e.to_string()).into()), } @@ -174,7 +177,7 @@ impl ImageCellStore { pub fn commit(&mut self) -> ProtocolResult<()> { match self.trie.commit() { Ok(new_root) => { - CURRENT_HEADER_CELL_ROOT.swap(Arc::new(new_root)); + CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow_mut() = new_root); Ok(()) } Err(e) => Err(SystemScriptError::CommitError(e.to_string()).into()), diff --git a/core/executor/src/system_contract/metadata/handle.rs b/core/executor/src/system_contract/metadata/handle.rs index 996794c5b..c32e2cd01 100644 --- a/core/executor/src/system_contract/metadata/handle.rs +++ b/core/executor/src/system_contract/metadata/handle.rs @@ -1,16 +1,22 @@ -use protocol::types::{CkbRelatedInfo, Metadata, H160}; +use protocol::types::{CkbRelatedInfo, Metadata, H160, H256}; use protocol::ProtocolResult; use crate::system_contract::metadata::MetadataStore; /// The MetadataHandle is used to expose apis that can be accessed from outside /// of the system contract. -#[derive(Default)] -pub struct MetadataHandle; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MetadataHandle { + root: H256, +} impl MetadataHandle { + pub fn new(root: H256) -> Self { + MetadataHandle { root } + } + pub fn get_metadata_by_block_number(&self, block_number: u64) -> ProtocolResult { - let store = MetadataStore::new()?; + let store = MetadataStore::new(self.root)?; // Should retrieve the first metadata for the genesis block if block_number == 0 { @@ -23,11 +29,11 @@ impl MetadataHandle { } pub fn get_metadata_by_epoch(&self, epoch: u64) -> ProtocolResult { - MetadataStore::new()?.get_metadata(epoch) + MetadataStore::new(self.root)?.get_metadata(epoch) } pub fn is_last_block_in_current_epoch(&self, block_number: u64) -> ProtocolResult { - let store = MetadataStore::new()?; + let store = MetadataStore::new(self.root)?; let segment = store.get_epoch_segment()?; let is_last_block = segment.is_last_block_in_epoch(block_number); Ok(is_last_block) @@ -39,6 +45,6 @@ impl MetadataHandle { } pub fn get_ckb_related_info(&self) -> ProtocolResult { - MetadataStore::new()?.get_ckb_related_info() + MetadataStore::new(self.root)?.get_ckb_related_info() } } diff --git a/core/executor/src/system_contract/metadata/mod.rs b/core/executor/src/system_contract/metadata/mod.rs index 77f772960..c85ac43a0 100644 --- a/core/executor/src/system_contract/metadata/mod.rs +++ b/core/executor/src/system_contract/metadata/mod.rs @@ -1,5 +1,5 @@ mod abi; -mod handle; +pub(crate) mod handle; mod segment; mod store; @@ -16,14 +16,15 @@ use parking_lot::RwLock; use protocol::traits::ExecutorAdapter; use protocol::types::{Hasher, Metadata, SignedTransaction, TxResp, H160, H256}; -use crate::exec_try; use crate::system_contract::utils::{ generate_mpt_root_changes, revert_resp, succeed_resp, update_states, }; -use crate::system_contract::{system_contract_address, SystemContract, CURRENT_METADATA_ROOT}; +use crate::system_contract::{system_contract_address, SystemContract}; +use crate::{exec_try, CURRENT_METADATA_ROOT}; type Epoch = u64; +pub const METADATA_CONTRACT_ADDRESS: H160 = system_contract_address(0x1); const METADATA_CACHE_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) }; lazy_static::lazy_static! { @@ -36,7 +37,7 @@ lazy_static::lazy_static! { pub struct MetadataContract; impl SystemContract for MetadataContract { - const ADDRESS: H160 = system_contract_address(0x1); + const ADDRESS: H160 = METADATA_CONTRACT_ADDRESS; fn exec_( &self, @@ -48,15 +49,16 @@ impl SystemContract for MetadataContract { let tx_data = tx.data(); let gas_limit = *tx.gas_limit(); let block_number = adapter.block_number().as_u64(); + let root = CURRENT_METADATA_ROOT.with(|r| *r.borrow()); let mut store = exec_try!( - MetadataStore::new(), + MetadataStore::new(root), gas_limit, "[metadata] init metadata mpt" ); if block_number != 0 { - let handle = MetadataHandle::default(); + let handle = MetadataHandle::new(CURRENT_METADATA_ROOT.with(|r| *r.borrow())); if !exec_try!( handle.is_validator(block_number, sender), @@ -110,7 +112,8 @@ impl SystemContract for MetadataContract { return; } - if let Err(e) = MetadataStore::new() + let root = CURRENT_METADATA_ROOT.with(|r| *r.borrow()); + if let Err(e) = MetadataStore::new(root) .unwrap() .update_propose_count(block_number.as_u64(), &adapter.origin()) { @@ -122,6 +125,6 @@ impl SystemContract for MetadataContract { } } -pub fn check_ckb_related_info_exist() -> bool { - MetadataHandle::default().get_ckb_related_info().is_ok() +pub fn check_ckb_related_info_exist(root: H256) -> bool { + MetadataHandle::new(root).get_ckb_related_info().is_ok() } diff --git a/core/executor/src/system_contract/metadata/store.rs b/core/executor/src/system_contract/metadata/store.rs index 656de66ff..b0980f318 100644 --- a/core/executor/src/system_contract/metadata/store.rs +++ b/core/executor/src/system_contract/metadata/store.rs @@ -5,33 +5,34 @@ use protocol::types::{CkbRelatedInfo, Metadata, H160, H256}; use protocol::{codec::ProtocolCodec, ProtocolResult}; use crate::system_contract::metadata::{ - segment::EpochSegment, CKB_RELATED_INFO_KEY, CURRENT_METADATA_ROOT, EPOCH_SEGMENT_KEY, + segment::EpochSegment, CKB_RELATED_INFO_KEY, EPOCH_SEGMENT_KEY, }; use crate::system_contract::{error::SystemScriptError, METADATA_DB}; -use crate::{adapter::RocksTrieDB, MPTTrie}; +use crate::{adapter::RocksTrieDB, MPTTrie, CURRENT_METADATA_ROOT}; pub struct MetadataStore { pub trie: MPTTrie, } impl MetadataStore { - pub fn new() -> ProtocolResult { - let trie_db = match METADATA_DB.get() { - Some(db) => db, - None => return Err(SystemScriptError::TrieDbNotInit.into()), + pub fn new(root: H256) -> ProtocolResult { + let trie_db = { + let lock = METADATA_DB.read().clone(); + match lock { + Some(db) => db, + None => return Err(SystemScriptError::TrieDbNotInit.into()), + } }; - let root = **CURRENT_METADATA_ROOT.load(); - let trie = if root == H256::default() { - let mut m = MPTTrie::new(Arc::clone(trie_db)); + let mut m = MPTTrie::new(Arc::clone(&trie_db)); m.insert( EPOCH_SEGMENT_KEY.as_bytes(), &EpochSegment::new().as_bytes(), )?; m } else { - match MPTTrie::from_root(root, Arc::clone(trie_db)) { + match MPTTrie::from_root(root, Arc::clone(&trie_db)) { Ok(m) => m, Err(e) => return Err(SystemScriptError::RestoreMpt(e.to_string()).into()), } @@ -83,7 +84,7 @@ impl MetadataStore { self.trie .insert(&metadata.epoch.to_be_bytes(), &metadata.encode()?)?; let new_root = self.trie.commit()?; - CURRENT_METADATA_ROOT.swap(Arc::new(new_root)); + CURRENT_METADATA_ROOT.with(|r| *r.borrow_mut() = new_root); Ok(()) } @@ -105,7 +106,7 @@ impl MetadataStore { self.trie .insert(&metadata.epoch.to_be_bytes(), &metadata.encode()?)?; let new_root = self.trie.commit()?; - CURRENT_METADATA_ROOT.swap(Arc::new(new_root)); + CURRENT_METADATA_ROOT.with(|r| *r.borrow_mut() = new_root); Ok(()) } diff --git a/core/executor/src/system_contract/mod.rs b/core/executor/src/system_contract/mod.rs index b776a1ab7..36331eaaf 100644 --- a/core/executor/src/system_contract/mod.rs +++ b/core/executor/src/system_contract/mod.rs @@ -2,24 +2,29 @@ mod error; mod native_token; mod utils; -pub mod ckb_light_client; -pub mod image_cell; +pub(crate) mod ckb_light_client; +pub(crate) mod image_cell; pub mod metadata; -pub use crate::system_contract::ckb_light_client::CkbLightClientContract; -pub use crate::system_contract::image_cell::ImageCellContract; -pub use crate::system_contract::metadata::{check_ckb_related_info_exist, MetadataContract}; -pub use crate::system_contract::native_token::NativeTokenContract; +pub use crate::system_contract::ckb_light_client::{ + CkbLightClientContract, CKB_LIGHT_CLIENT_CONTRACT_ADDRESS, +}; +pub use crate::system_contract::image_cell::{ImageCellContract, IMAGE_CELL_CONTRACT_ADDRESS}; +pub use crate::system_contract::metadata::{ + check_ckb_related_info_exist, MetadataContract, METADATA_CONTRACT_ADDRESS, +}; +pub use crate::system_contract::native_token::{ + NativeTokenContract, NATIVE_TOKEN_CONTRACT_ADDRESS, +}; use std::path::Path; use std::sync::Arc; -use std::sync::OnceLock; -use arc_swap::ArcSwap; use ckb_traits::{CellDataProvider, HeaderProvider}; use ckb_types::core::cell::{CellProvider, CellStatus}; use ckb_types::core::{HeaderBuilder, HeaderView}; use ckb_types::{packed, prelude::*}; +use parking_lot::RwLock; use common_config_parser::types::ConfigRocksDB; use protocol::types::{Bytes, Hasher, SignedTransaction, TxResp, H160, H256}; @@ -38,16 +43,11 @@ pub const fn system_contract_address(addr: u8) -> H160 { const HEADER_CELL_DB_CACHE_SIZE: usize = 200; const METADATA_DB_CACHE_SIZE: usize = 10; -/// System contract init section. It needs to initialize two databases, one for -/// Metadata and one for CkbLightClient&ImageCell -static HEADER_CELL_DB: OnceLock> = OnceLock::new(); -static METADATA_DB: OnceLock> = OnceLock::new(); - lazy_static::lazy_static! { pub static ref HEADER_CELL_ROOT_KEY: H256 = Hasher::digest("header_cell_mpt_root"); - static ref CURRENT_HEADER_CELL_ROOT: ArcSwap = ArcSwap::from_pointee(H256::default()); - static ref METADATA_ROOT_KEY: H256 = Hasher::digest("metadata_root"); - static ref CURRENT_METADATA_ROOT: ArcSwap = ArcSwap::from_pointee(H256::default()); + pub static ref METADATA_ROOT_KEY: H256 = Hasher::digest("metadata_root"); + pub(crate) static ref METADATA_DB: RwLock>> = RwLock::new(None); + pub(crate) static ref HEADER_CELL_DB: RwLock>> = RwLock::new(None); } #[macro_export] @@ -77,48 +77,58 @@ pub trait SystemContract { fn after_block_hook(&self, _adapter: &mut Adapter) {} } +pub fn swap_metadata_db(new_db: Arc) -> Arc { + METADATA_DB + .write() + .replace(new_db) + .unwrap_or_else(|| panic!("metadata db is not initialized")) +} + +pub fn swap_header_cell_db(new_db: Arc) -> Arc { + HEADER_CELL_DB + .write() + .replace(new_db) + .unwrap_or_else(|| panic!("header cell db is not initialized")) +} + pub fn init, Adapter: ExecutorAdapter>( path: P, config: ConfigRocksDB, adapter: &mut Adapter, -) { - // Init metadata db +) -> (H256, H256) { let current_metadata_root = adapter.storage(MetadataContract::ADDRESS, *METADATA_ROOT_KEY); - CURRENT_METADATA_ROOT.store(Arc::new(current_metadata_root)); + // Init metadata db. let metadata_db_path = path.as_ref().join("metadata"); - METADATA_DB.get_or_init(|| { - Arc::new( + { + let mut db = METADATA_DB.write(); + db.replace(Arc::new( RocksTrieDB::new(metadata_db_path, config.clone(), METADATA_DB_CACHE_SIZE) .expect("[system contract] metadata new rocksdb error"), - ) - }); + )); + } - // Init header cell db let header_cell_db_path = path.as_ref().join("header_cell"); - HEADER_CELL_DB.get_or_init(|| { - Arc::new( - RocksTrieDB::new( - header_cell_db_path, - config.clone(), - HEADER_CELL_DB_CACHE_SIZE, - ) - .expect("[system contract] header&cell new rocksdb error"), - ) - }); + { + let mut db = HEADER_CELL_DB.write(); + db.replace(Arc::new( + RocksTrieDB::new(header_cell_db_path, config, HEADER_CELL_DB_CACHE_SIZE) + .expect("[system contract] header&cell new rocksdb error"), + )); + } let current_cell_root = adapter.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); if current_cell_root.is_zero() { // todo need refactoring ImageCellContract::default() - .save_cells(vec![always_success_script_deploy_cell()], 0) + .save_cells(H256::zero(), vec![always_success_script_deploy_cell()], 0) .unwrap(); let changes = generate_mpt_root_changes(adapter, ImageCellContract::ADDRESS); - return adapter.apply(changes, vec![], false); + adapter.apply(changes, vec![], false); } - CURRENT_HEADER_CELL_ROOT.store(Arc::new(current_cell_root)); + (current_metadata_root, current_cell_root) } pub fn before_block_hook(adapter: &mut Adapter) { @@ -155,13 +165,15 @@ pub fn system_contract_dispatch( None } -#[derive(Default, Clone, Debug)] -pub struct DataProvider; +#[derive(Clone, Debug)] +pub struct DataProvider { + root: H256, +} impl CellProvider for DataProvider { fn cell(&self, out_point: &packed::OutPoint, _eager_load: bool) -> CellStatus { if let Some(c) = ImageCellContract::default() - .get_cell(&(out_point).into()) + .get_cell(self.root, &(out_point).into()) .ok() .flatten() { @@ -175,7 +187,7 @@ impl CellProvider for DataProvider { impl CellDataProvider for DataProvider { fn get_cell_data(&self, out_point: &packed::OutPoint) -> Option { ImageCellContract::default() - .get_cell(&(out_point.into())) + .get_cell(self.root, &(out_point.into())) .ok() .flatten() .map(|info| info.cell_data) @@ -196,7 +208,7 @@ impl HeaderProvider for DataProvider { fn get_header(&self, hash: &packed::Byte32) -> Option { let block_hash = hash.unpack(); CkbLightClientContract::default() - .get_header_by_block_hash(&H256(block_hash.0)) + .get_header_by_block_hash(self.root, &H256(block_hash.0)) .ok() .flatten() .map(|h| { @@ -216,3 +228,9 @@ impl HeaderProvider for DataProvider { }) } } + +impl DataProvider { + pub fn new(root: H256) -> Self { + DataProvider { root } + } +} diff --git a/core/executor/src/system_contract/native_token.rs b/core/executor/src/system_contract/native_token.rs index c5207d698..77a333dcc 100644 --- a/core/executor/src/system_contract/native_token.rs +++ b/core/executor/src/system_contract/native_token.rs @@ -4,11 +4,13 @@ use protocol::types::{Apply, Basic, SignedTransaction, TxResp, H160, U256}; use crate::system_contract::utils::{revert_resp, succeed_resp}; use crate::system_contract::{system_contract_address, SystemContract}; +pub const NATIVE_TOKEN_CONTRACT_ADDRESS: H160 = system_contract_address(0x0); + #[derive(Default)] pub struct NativeTokenContract; impl SystemContract for NativeTokenContract { - const ADDRESS: H160 = system_contract_address(0x0); + const ADDRESS: H160 = NATIVE_TOKEN_CONTRACT_ADDRESS; fn exec_(&self, backend: &mut B, tx: &SignedTransaction) -> TxResp { let tx = &tx.transaction.unsigned; diff --git a/core/executor/src/system_contract/utils.rs b/core/executor/src/system_contract/utils.rs index 3b3faaf23..9cac50c13 100644 --- a/core/executor/src/system_contract/utils.rs +++ b/core/executor/src/system_contract/utils.rs @@ -6,8 +6,9 @@ use protocol::types::{ use crate::system_contract::{ CkbLightClientContract, ImageCellContract, MetadataContract, SystemContract, - CURRENT_HEADER_CELL_ROOT, CURRENT_METADATA_ROOT, HEADER_CELL_ROOT_KEY, METADATA_ROOT_KEY, + HEADER_CELL_ROOT_KEY, METADATA_ROOT_KEY, }; +use crate::{CURRENT_HEADER_CELL_ROOT, CURRENT_METADATA_ROOT}; pub fn revert_resp(gas_limit: U256) -> TxResp { TxResp { @@ -67,7 +68,8 @@ pub fn generate_mpt_root_changes( if contract_address == CkbLightClientContract::ADDRESS || contract_address == ImageCellContract::ADDRESS { - let storage_changes = vec![(*HEADER_CELL_ROOT_KEY, **CURRENT_HEADER_CELL_ROOT.load())]; + let current_header_cell_root = CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow()); + let storage_changes = vec![(*HEADER_CELL_ROOT_KEY, current_header_cell_root)]; vec![ Apply::Modify { address: CkbLightClientContract::ADDRESS, @@ -85,11 +87,12 @@ pub fn generate_mpt_root_changes( }, ] } else if contract_address == MetadataContract::ADDRESS { + let current_metadata_root = CURRENT_METADATA_ROOT.with(|r| *r.borrow()); vec![Apply::Modify { address: MetadataContract::ADDRESS, basic: backend.basic(MetadataContract::ADDRESS), code: None, - storage: vec![(*METADATA_ROOT_KEY, **CURRENT_METADATA_ROOT.load())], + storage: vec![(*METADATA_ROOT_KEY, current_metadata_root)], reset_storage: false, }] } else { diff --git a/core/executor/src/tests/system_script/ckb_light_client.rs b/core/executor/src/tests/system_script/ckb_light_client.rs index bfbc529f9..59d2c8256 100644 --- a/core/executor/src/tests/system_script/ckb_light_client.rs +++ b/core/executor/src/tests/system_script/ckb_light_client.rs @@ -60,11 +60,11 @@ fn test_update_first(backend: &mut MemoryBackend, executor: &CkbLightClientContr let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); check_nonce(backend, 1); + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let queried_header = executor - .get_header_by_block_hash(&H256::default()) + .get_header_by_block_hash(root, &H256::default()) .unwrap() .unwrap(); @@ -80,11 +80,11 @@ fn test_update_second(backend: &mut MemoryBackend, executor: &CkbLightClientCont let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); check_nonce(backend, 2); + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let queried_header = executor - .get_header_by_block_hash(&H256::from_slice(&header.block_hash)) + .get_header_by_block_hash(root, &H256::from_slice(&header.block_hash)) .unwrap() .unwrap(); @@ -99,10 +99,9 @@ fn test_roll_back_first(backend: &mut MemoryBackend, executor: &CkbLightClientCo let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); - + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let queried_header = executor - .get_header_by_block_hash(&H256::default()) + .get_header_by_block_hash(root, &H256::default()) .unwrap() .unwrap(); @@ -117,9 +116,10 @@ fn test_roll_back_second(backend: &mut MemoryBackend, executor: &CkbLightClientC let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); - - let queried_header = executor.get_header_by_block_hash(&H256::default()).unwrap(); + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); + let queried_header = executor + .get_header_by_block_hash(root, &H256::default()) + .unwrap(); assert!(queried_header.is_none()); } @@ -140,17 +140,6 @@ fn exec(backend: &mut MemoryBackend, executor: &CkbLightClientContract, data: Ve executor.exec_(backend, &tx) } -fn check_root(backend: &MemoryBackend, executor: &CkbLightClientContract) { - let account = backend - .state() - .get(&CkbLightClientContract::ADDRESS) - .unwrap(); - assert_eq!( - &executor.get_root(), - account.storage.get(&HEADER_CELL_ROOT_KEY).unwrap(), - ); -} - fn check_nonce(backend: &mut MemoryBackend, nonce: u64) { assert_eq!( backend.basic(CkbLightClientContract::ADDRESS).nonce, diff --git a/core/executor/src/tests/system_script/image_cell.rs b/core/executor/src/tests/system_script/image_cell.rs index e0dbf4536..3b9485eee 100644 --- a/core/executor/src/tests/system_script/image_cell.rs +++ b/core/executor/src/tests/system_script/image_cell.rs @@ -10,6 +10,7 @@ use protocol::types::{Backend, MemoryBackend, TxResp, H160, U256}; use crate::system_contract::image_cell::{image_cell_abi, CellInfo, CellKey, ImageCellContract}; use crate::system_contract::{init, CkbLightClientContract, SystemContract, HEADER_CELL_ROOT_KEY}; use crate::tests::{gen_tx, gen_vicinity}; +use crate::{CURRENT_HEADER_CELL_ROOT, CURRENT_METADATA_ROOT}; static ROCKSDB_PATH: &str = "./free-space/system-contract/image-cell"; @@ -18,7 +19,10 @@ pub fn test_write_functions() { let mut backend = MemoryBackend::new(&vicinity, BTreeMap::new()); let executor = ImageCellContract::default(); - init(ROCKSDB_PATH, ConfigRocksDB::default(), &mut backend); + let (m_root, h_root) = init(ROCKSDB_PATH, ConfigRocksDB::default(), &mut backend); + + CURRENT_METADATA_ROOT.with(|r| *r.borrow_mut() = m_root); + CURRENT_HEADER_CELL_ROOT.with(|r| *r.borrow_mut() = h_root); test_update_first(&mut backend, &executor); test_update_second(&mut backend, &executor); @@ -41,11 +45,11 @@ fn test_update_first(backend: &mut MemoryBackend, executor: &ImageCellContract) let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); check_nonce(backend, 1); + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let cell_key = CellKey::new([7u8; 32], 0x0); - let get_cell = executor.get_cell(&cell_key).unwrap().unwrap(); + let get_cell = executor.get_cell(root, &cell_key).unwrap().unwrap(); check_cell(&get_cell, 0x1, None); } @@ -64,11 +68,11 @@ fn test_update_second(backend: &mut MemoryBackend, executor: &ImageCellContract) let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); check_nonce(backend, 2); + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let cell_key = CellKey::new([7u8; 32], 0x0); - let get_cell = executor.get_cell(&cell_key).unwrap().unwrap(); + let get_cell = executor.get_cell(root, &cell_key).unwrap().unwrap(); check_cell(&get_cell, 0x1, Some(0x2)); } @@ -86,10 +90,9 @@ fn test_rollback_first(backend: &mut MemoryBackend, executor: &ImageCellContract let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); - + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let cell_key = CellKey::new([7u8; 32], 0x0); - let get_cell = executor.get_cell(&cell_key).unwrap().unwrap(); + let get_cell = executor.get_cell(root, &cell_key).unwrap().unwrap(); check_cell(&get_cell, 0x1, None); } @@ -107,10 +110,9 @@ fn test_rollback_second(backend: &mut MemoryBackend, executor: &ImageCellContrac let r = exec(backend, executor, data.encode()); assert!(r.exit_reason.is_succeed()); - check_root(backend, executor); - + let root = backend.storage(CkbLightClientContract::ADDRESS, *HEADER_CELL_ROOT_KEY); let cell_key = CellKey::new([7u8; 32], 0x0); - let get_cell = executor.get_cell(&cell_key).unwrap(); + let get_cell = executor.get_cell(root, &cell_key).unwrap(); assert!(get_cell.is_none()); } @@ -131,14 +133,6 @@ fn exec(backend: &mut MemoryBackend, executor: &ImageCellContract, data: Vec executor.exec_(backend, &tx) } -fn check_root(backend: &MemoryBackend, executor: &ImageCellContract) { - let account = backend.state().get(&ImageCellContract::ADDRESS).unwrap(); - assert_eq!( - &executor.get_root(), - account.storage.get(&HEADER_CELL_ROOT_KEY).unwrap(), - ); -} - fn check_cell(get_cell: &CellInfo, created_number: u64, consumed_number: Option) { let cell = &prepare_outputs()[0]; diff --git a/core/mempool/src/adapter/mod.rs b/core/mempool/src/adapter/mod.rs index d2be579f5..593af09d7 100644 --- a/core/mempool/src/adapter/mod.rs +++ b/core/mempool/src/adapter/mod.rs @@ -20,14 +20,13 @@ use protocol::types::{ SignatureR, SignatureS, SignedTransaction, H160, U256, }; use protocol::{ - async_trait, codec::ProtocolCodec, lazy::CURRENT_STATE_ROOT, tokio, trie, Display, - ProtocolError, ProtocolErrorKind, ProtocolResult, + async_trait, codec::ProtocolCodec, tokio, trie, Display, ProtocolError, ProtocolErrorKind, + ProtocolResult, }; use common_apm_derive::trace_span; use common_crypto::{Crypto, Secp256k1Recoverable}; -use core_executor::system_contract::{metadata::MetadataHandle, DataProvider}; -use core_executor::{is_call_system_script, AxonExecutorAdapter}; +use core_executor::{is_call_system_script, AxonExecutorAdapter, DataProvider, MetadataHandle}; use core_interoperation::InteroperationImpl; use crate::adapter::message::{MsgPullTxs, END_GOSSIP_NEW_TXS, RPC_PULL_TXS}; @@ -115,10 +114,9 @@ impl IntervalTxsBroadcaster { } pub struct DefaultMemPoolAdapter { - network: N, - storage: Arc, - trie_db: Arc, - metadata: Arc, + network: N, + storage: Arc, + trie_db: Arc, addr_nonce: DashMap, gas_limit: AtomicU64, @@ -144,7 +142,6 @@ where network: N, storage: Arc, trie_db: Arc, - metadata: Arc, chain_id: u64, gas_limit: u64, max_tx_size: usize, @@ -166,7 +163,6 @@ where network, storage, trie_db, - metadata, addr_nonce: DashMap::new(), gas_limit: AtomicU64::new(gas_limit), @@ -188,8 +184,9 @@ where ) -> ProtocolResult { let addr = &stx.sender; let block = self.storage.get_latest_block(ctx.clone()).await?; + let root = self.executor_backend(ctx).await?.get_metadata_root(); - if self.metadata.is_validator(block.header.number + 1, *addr)? { + if MetadataHandle::new(root).is_validator(block.header.number + 1, *addr)? { return Ok(U256::zero()); } @@ -274,7 +271,7 @@ where Ok(()) } - fn verify_signature(&self, stx: &SignedTransaction) -> ProtocolResult<()> { + async fn verify_signature(&self, ctx: Context, stx: &SignedTransaction) -> ProtocolResult<()> { let signature = stx.transaction.signature.clone().unwrap(); if signature.is_eth_sig() { // Verify secp256k1 signature @@ -288,6 +285,8 @@ where return Ok(()); } + let root = self.executor_backend(ctx).await?.get_image_cell_root(); + // Verify interoperation signature match signature.r[0] { 0u8 => { @@ -297,7 +296,7 @@ where InteroperationImpl::call_ckb_vm( Default::default(), - &DataProvider::default(), + &DataProvider::new(root), r.cell_dep, &[r.pub_key, signature.s], u64::MAX, @@ -318,7 +317,7 @@ where InteroperationImpl::verify_by_ckb_vm( Default::default(), - &DataProvider::default(), + &DataProvider::new(root), &InteroperationImpl::dummy_transaction( r.clone(), s, @@ -332,6 +331,16 @@ where } Ok(()) } + + async fn executor_backend(&self, ctx: Context) -> ProtocolResult> { + let current_state_root = self.storage.get_latest_block_header(ctx).await?.state_root; + AxonExecutorAdapter::from_root( + current_state_root, + Arc::clone(&self.trie_db), + Arc::clone(&self.storage), + Default::default(), + ) + } } #[async_trait] @@ -413,13 +422,7 @@ where } } - let backend = AxonExecutorAdapter::from_root( - **CURRENT_STATE_ROOT.load(), - Arc::clone(&self.trie_db), - Arc::clone(&self.storage), - Default::default(), - )?; - + let backend = self.executor_backend(ctx).await?; let account = backend.basic(*addr); self.addr_nonce .insert(*addr, (account.nonce, account.balance)); @@ -456,8 +459,8 @@ where self.verify_chain_id(ctx.clone(), stx)?; self.verify_tx_size(ctx.clone(), stx)?; self.verify_gas_price(stx)?; - self.verify_gas_limit(ctx, stx)?; - self.verify_signature(stx)?; + self.verify_gas_limit(ctx.clone(), stx)?; + self.verify_signature(ctx, stx).await?; Ok(()) } diff --git a/core/run/src/lib.rs b/core/run/src/lib.rs index 826dcfccd..9cd2ca168 100644 --- a/core/run/src/lib.rs +++ b/core/run/src/lib.rs @@ -22,7 +22,7 @@ use common_crypto::{ }; use protocol::codec::{hex_decode, ProtocolCodec}; -use protocol::lazy::{CHAIN_ID, CURRENT_STATE_ROOT}; +use protocol::lazy::CHAIN_ID; #[cfg(unix)] use protocol::tokio::signal::unix as os_impl; use protocol::tokio::{ @@ -249,6 +249,8 @@ impl Axon { // Init Block db and get the current block let storage = self.init_storage(); let current_block = storage.get_latest_block(Context::new()).await?; + let current_state_root = current_block.header.state_root; + log::info!("At block number {}", current_block.header.number + 1); // Init network @@ -266,30 +268,29 @@ impl Axon { .to_string(); let txs_wal = Arc::new(SignedTxsWAL::new(txs_wal_path)); - // Init system contract when the chain is not first initiated - if current_block.header.state_root != H256::default() { + // Init system contract + if current_block.header.number != 0 { self.init_system_contract(&trie_db, ¤t_block, &storage); } - // Init metadata handle which will be used in mempool - let metadata_handle = Arc::new(MetadataHandle::default()); - // Init mempool and recover signed transactions with the current block number let current_stxs = txs_wal.load_by_number(current_block.header.number + 1); log::info!("Recover {} txs from wal", current_stxs.len()); let mempool = self - .init_mempool( - &trie_db, - &network_service.handle(), - &storage, - &metadata_handle, - ¤t_stxs, - ) + .init_mempool(&trie_db, &network_service.handle(), &storage, ¤t_stxs) .await; // Get the validator list from current metadata for consensus initialization - let metadata = metadata_handle.get_metadata_by_block_number(current_block.header.number)?; + let metadata_root = AxonExecutorAdapter::from_root( + current_state_root, + Arc::clone(&trie_db), + Arc::clone(&storage), + Proposal::new_without_state_root(&self.genesis.block.header).into(), + )? + .get_metadata_root(); + let metadata = MetadataHandle::new(metadata_root) + .get_metadata_by_block_number(current_block.header.number)?; let validators: Vec = metadata .verifier_list .iter() @@ -316,7 +317,6 @@ impl Axon { Arc::clone(&mempool), Arc::clone(&storage), Arc::clone(&trie_db), - Arc::clone(&metadata_handle), Arc::clone(&crypto), )?; let consensus_adapter = Arc::new(consensus_adapter); @@ -441,7 +441,6 @@ impl Axon { trie_db: &Arc, network_service: &N, storage: &Arc, - metadata_handle: &Arc, signed_txs: &[SignedTransaction], ) -> Arc>> where @@ -453,7 +452,6 @@ impl Axon { network_service.clone(), Arc::clone(storage), Arc::clone(trie_db), - Arc::clone(metadata_handle), self.genesis.block.header.chain_id, self.genesis.block.header.gas_limit.as_u64(), self.config.mempool.pool_size as usize, @@ -489,7 +487,7 @@ impl Axon { trie_db: &Arc, current_block: &Block, storage: &Arc>, - ) { + ) -> (H256, H256) { let path_system_contract = self.config.data_path_for_system_contract(); let mut backend = AxonExecutorAdapter::from_root( current_block.header.state_root, @@ -502,7 +500,7 @@ impl Axon { path_system_contract, self.config.rocksdb.clone(), &mut backend, - ); + ) } fn get_my_pubkey(&self, hex_privkey: Vec) -> Secp256k1PublicKey { @@ -568,7 +566,6 @@ impl Axon { }, }; - CURRENT_STATE_ROOT.swap(Arc::new(current_consensus_status.last_state_root)); CHAIN_ID.swap(Arc::new(header.chain_id)); StatusAgent::new(current_consensus_status) diff --git a/protocol/src/lazy.rs b/protocol/src/lazy.rs index d89cd0c2f..c19a31e34 100644 --- a/protocol/src/lazy.rs +++ b/protocol/src/lazy.rs @@ -2,11 +2,9 @@ use arc_swap::ArcSwap; use ckb_always_success_script::ALWAYS_SUCCESS; use ckb_types::{core::ScriptHashType, packed, prelude::*}; -use crate::ckb_blake2b_256; -use crate::types::{Hex, MerkleRoot}; +use crate::{ckb_blake2b_256, types::Hex}; lazy_static::lazy_static! { - pub static ref CURRENT_STATE_ROOT: ArcSwap = ArcSwap::from_pointee(Default::default()); pub static ref CHAIN_ID: ArcSwap = ArcSwap::from_pointee(Default::default()); pub static ref PROTOCOL_VERSION: ArcSwap = ArcSwap::from_pointee(Default::default()); diff --git a/protocol/src/traits/api.rs b/protocol/src/traits/api.rs index 8092b36d4..e7ea0e58b 100644 --- a/protocol/src/traits/api.rs +++ b/protocol/src/traits/api.rs @@ -1,6 +1,6 @@ use crate::types::{ Account, Block, BlockNumber, Bytes, CkbRelatedInfo, Hash, Header, Metadata, Proposal, Receipt, - SignedTransaction, TxResp, H160, U256, + SignedTransaction, TxResp, H160, H256, U256, }; use crate::{async_trait, traits::Context, ProtocolResult}; @@ -99,4 +99,8 @@ pub trait APIAdapter: Send + Sync { ) -> ProtocolResult; async fn get_ckb_related_info(&self, ctx: Context) -> ProtocolResult; + + async fn get_image_cell_root(&self, ctx: Context) -> ProtocolResult; + + async fn get_metadata_root(&self, ctx: Context) -> ProtocolResult; }