diff --git a/Cargo.lock b/Cargo.lock index 9fac0de7a1..b5fa5d0235 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10485,6 +10485,7 @@ dependencies = [ "forkable-jellyfish-merkle", "serde 1.0.152", "starcoin-crypto", + "starcoin-vm-types", ] [[package]] diff --git a/network-rpc/Cargo.toml b/network-rpc/Cargo.toml index 38fd461af9..7b98b70caf 100644 --- a/network-rpc/Cargo.toml +++ b/network-rpc/Cargo.toml @@ -28,6 +28,7 @@ starcoin-txpool-api = { workspace = true } starcoin-storage = { package = "starcoin-storage", workspace = true } starcoin-types = { package = "starcoin-types", workspace = true } starcoin-state-tree = { workspace = true } +starcoin-vm-types = { workspace = true } [dev-dependencies] starcoin-account-api = { package = "starcoin-account-api", workspace = true } diff --git a/network-rpc/api/src/lib.rs b/network-rpc/api/src/lib.rs index 8188b72826..dd4b3a909c 100644 --- a/network-rpc/api/src/lib.rs +++ b/network-rpc/api/src/lib.rs @@ -19,6 +19,7 @@ use starcoin_types::account_address::AccountAddress; use starcoin_types::account_state::AccountState; use starcoin_types::block::{Block, BlockHeader, BlockInfo, BlockNumber}; use starcoin_types::transaction::{SignedUserTransaction, Transaction, TransactionInfo}; +use starcoin_vm_types::state_store::table::TableInfo; mod remote_chain_state; @@ -286,6 +287,12 @@ pub trait NetworkRpc: Sized + Send + Sync + 'static { peer_id: PeerId, request: GetStateWithTableItemProof, ) -> BoxFuture>; + + fn get_state_table_info( + &self, + peer_id: PeerId, + request: GetTableInfo, + ) -> BoxFuture>>; } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -294,3 +301,5 @@ pub struct GetStateWithTableItemProof { pub handle: TableHandle, pub key: Vec, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct GetTableInfo(pub AccountAddress); diff --git a/network-rpc/api/src/remote_chain_state.rs b/network-rpc/api/src/remote_chain_state.rs index e0ab971e48..3f625043b9 100644 --- a/network-rpc/api/src/remote_chain_state.rs +++ b/network-rpc/api/src/remote_chain_state.rs @@ -3,6 +3,7 @@ use crate::{ gen_client::NetworkRpcClient, GetAccountState, GetStateWithProof, GetStateWithTableItemProof, + GetTableInfo, }; use anyhow::{anyhow, Result}; use network_p2p_types::peer_id::PeerId; @@ -14,7 +15,7 @@ use starcoin_types::account_address::AccountAddress; use starcoin_types::account_state::AccountState; use starcoin_types::state_set::{AccountStateSet, ChainStateSet}; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; #[derive(Clone)] pub struct RemoteChainStateReader { @@ -121,6 +122,18 @@ impl ChainStateReader for RemoteChainStateReader { state_table_item_proof.verify(handle, key)?; Ok(state_table_item_proof) } + + fn get_table_info(&self, address: AccountAddress) -> Result> { + let peer_id = self + .peer_id + .clone() + .ok_or_else(|| anyhow!("peer id not set"))?; + let req = GetTableInfo(address); + let client = self.client.clone(); + let table_info: Option = + futures::executor::block_on(client.get_state_table_info(peer_id, req))?; + Ok(table_info) + } } impl StateView for RemoteChainStateReader { diff --git a/network-rpc/src/rpc.rs b/network-rpc/src/rpc.rs index e91c2f760d..c333341a44 100644 --- a/network-rpc/src/rpc.rs +++ b/network-rpc/src/rpc.rs @@ -11,8 +11,8 @@ use starcoin_chain_service::{ChainAsyncService, ChainReaderService}; use starcoin_crypto::HashValue; use starcoin_network_rpc_api::{ gen_server, BlockBody, GetAccountState, GetAccumulatorNodeByNodeHash, GetBlockHeadersByNumber, - GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, GetTxnsWithHash, GetTxnsWithSize, - Ping, RpcRequest, MAX_BLOCK_HEADER_REQUEST_SIZE, MAX_BLOCK_INFO_REQUEST_SIZE, + GetBlockIds, GetStateWithProof, GetStateWithTableItemProof, GetTableInfo, GetTxnsWithHash, + GetTxnsWithSize, Ping, RpcRequest, MAX_BLOCK_HEADER_REQUEST_SIZE, MAX_BLOCK_INFO_REQUEST_SIZE, MAX_BLOCK_REQUEST_SIZE, MAX_TXN_REQUEST_SIZE, }; use starcoin_service_registry::ServiceRef; @@ -28,6 +28,7 @@ use starcoin_types::{ block::{BlockHeader, BlockInfo, BlockNumber}, transaction::{SignedUserTransaction, Transaction, TransactionInfo}, }; +use starcoin_vm_types::state_store::table::TableInfo; use std::sync::Arc; pub struct NetworkRpcImpl { @@ -251,6 +252,16 @@ impl gen_server::NetworkRpc for NetworkRpcImpl { Box::pin(fut) } + fn get_state_table_info( + &self, + _peer_id: PeerId, + request: GetTableInfo, + ) -> BoxFuture>> { + let state_service = self.state_service.clone(); + let fut = async move { state_service.get_table_info(request.0).await }; + Box::pin(fut) + } + fn get_account_state( &self, _peer_id: PeerId, diff --git a/rpc/api/generated_rpc_schema/state.json b/rpc/api/generated_rpc_schema/state.json index 5cef5f41b0..7d94fcc4a7 100644 --- a/rpc/api/generated_rpc_schema/state.json +++ b/rpc/api/generated_rpc_schema/state.json @@ -985,6 +985,43 @@ } } }, + { + "name": "state.get_table_info", + "params": [ + { + "name": "address", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountAddress", + "type": "string", + "format": "AccountAddress" + } + } + ], + "result": { + "name": "Option < TableInfoView >", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_table_info", + "type": [ + "object", + "null" + ], + "required": [ + "key_type", + "value_type" + ], + "properties": { + "key_type": { + "type": "string" + }, + "value_type": { + "type": "string" + } + } + } + } + }, { "name": "state.get_with_table_item_proof", "params": [ diff --git a/rpc/api/src/state/mod.rs b/rpc/api/src/state/mod.rs index 0c4b9fc0be..e59f21e9b6 100644 --- a/rpc/api/src/state/mod.rs +++ b/rpc/api/src/state/mod.rs @@ -4,7 +4,7 @@ pub use self::gen_client::Client as StateClient; use crate::types::{ AccountStateSetView, CodeView, ListCodeView, ListResourceView, ResourceView, - StateWithProofView, StateWithTableItemProofView, StrView, StructTagView, + StateWithProofView, StateWithTableItemProofView, StrView, StructTagView, TableInfoView, }; use crate::FutureResult; use openrpc_derive::openrpc; @@ -63,6 +63,10 @@ pub trait StateApi { state_root: HashValue, ) -> FutureResult>>; + /// Return the TableInfo according to queried AccountAddress + #[rpc(name = "state.get_table_info")] + fn get_table_info(&self, address: AccountAddress) -> FutureResult>; + /// Return the TableItem value and provide a State Proof at `state_root` #[rpc(name = "state.get_with_table_item_proof")] fn get_with_table_item_proof( diff --git a/rpc/api/src/types.rs b/rpc/api/src/types.rs index 9d0dbc27dd..532a140998 100644 --- a/rpc/api/src/types.rs +++ b/rpc/api/src/types.rs @@ -1180,7 +1180,7 @@ use starcoin_chain_api::{EventWithProof, TransactionInfoWithProof}; use starcoin_types::account_address::AccountAddress; use starcoin_vm_types::move_resource::MoveResource; use starcoin_vm_types::state_store::state_key::{StateKey, TableItem}; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; pub use vm_status_translator::VmStatusExplainView; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] @@ -1903,15 +1903,14 @@ impl From for BlockInfoView { #[serde(rename = "table_item")] pub struct TableItemView { handle: TableHandle, - #[schemars(with = "String")] - key: Vec, + key: StrView>, } impl From for TableItemView { fn from(table_item: TableItem) -> Self { Self { handle: table_item.handle, - key: table_item.key, + key: table_item.key.into(), } } } @@ -1930,12 +1929,37 @@ impl From for StateKeyView { StateKey::AccessPath(access_path) => Self::AccessPath(access_path), StateKey::TableItem(table_item) => Self::TableItem(TableItemView { handle: table_item.handle, - key: table_item.key, + key: table_item.key.into(), }), } } } +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename = "table_info")] +pub struct TableInfoView { + key_type: TypeTagView, + value_type: TypeTagView, +} + +impl From for TableInfoView { + fn from(value: TableInfo) -> Self { + Self { + key_type: value.key_type.into(), + value_type: value.value_type.into(), + } + } +} + +impl From for TableInfo { + fn from(value: TableInfoView) -> Self { + Self { + key_type: value.key_type.0, + value_type: value.value_type.0, + } + } +} + #[cfg(test)] mod tests { use crate::types::{ByteCodeOrScriptFunction, FunctionId, StrView}; diff --git a/rpc/client/src/lib.rs b/rpc/client/src/lib.rs index e3bcec87eb..6f3d748c59 100644 --- a/rpc/client/src/lib.rs +++ b/rpc/client/src/lib.rs @@ -37,8 +37,8 @@ use starcoin_rpc_api::types::{ DryRunTransactionRequest, FactoryAction, FunctionIdView, ListCodeView, ListResourceView, MintedBlockView, ModuleIdView, PeerInfoView, ResourceView, SignedMessageView, SignedUserTransactionView, StateWithProofView, StateWithTableItemProofView, StrView, - StructTagView, TransactionEventResponse, TransactionInfoView, TransactionInfoWithProofView, - TransactionRequest, TransactionView, + StructTagView, TableInfoView, TransactionEventResponse, TransactionInfoView, + TransactionInfoWithProofView, TransactionRequest, TransactionView, }; use starcoin_rpc_api::{ account::AccountClient, chain::ChainClient, contract_api::ContractClient, debug::DebugClient, @@ -656,6 +656,14 @@ impl RpcClient { .map_err(map_err) } + pub fn state_get_table_info( + &self, + address: AccountAddress, + ) -> anyhow::Result> { + self.call_rpc_blocking(|inner| inner.state_client.get_table_info(address)) + .map_err(map_err) + } + pub fn get_state_node_by_node_hash( &self, key_hash: HashValue, diff --git a/rpc/client/src/remote_state_reader.rs b/rpc/client/src/remote_state_reader.rs index 8680f6f7d1..796d71cd9e 100644 --- a/rpc/client/src/remote_state_reader.rs +++ b/rpc/client/src/remote_state_reader.rs @@ -12,7 +12,7 @@ use starcoin_types::account_state::AccountState; use starcoin_types::block::BlockNumber; use starcoin_types::state_set::{AccountStateSet, ChainStateSet}; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use std::str::FromStr; #[derive(Debug, Clone, Copy)] @@ -109,6 +109,11 @@ impl<'a> ChainStateReader for RemoteStateReader<'a> { .state_get_with_table_item_proof_by_root(*handle, key.to_vec(), self.state_root) .map(Into::into) } + fn get_table_info(&self, address: AccountAddress) -> Result> { + self.client + .state_get_table_info(address) + .map(|v| v.map(Into::into)) + } } impl<'a> StateView for RemoteStateReader<'a> { diff --git a/rpc/server/src/module/state_rpc.rs b/rpc/server/src/module/state_rpc.rs index e7f1acc62f..2426982c7c 100644 --- a/rpc/server/src/module/state_rpc.rs +++ b/rpc/server/src/module/state_rpc.rs @@ -15,6 +15,7 @@ use starcoin_rpc_api::state::{ use starcoin_rpc_api::types::{ AccountStateSetView, AnnotatedMoveStructView, CodeView, ListCodeView, ListResourceView, ResourceView, StateWithProofView, StateWithTableItemProofView, StrView, StructTagView, + TableInfoView, }; use starcoin_rpc_api::FutureResult; use starcoin_state_api::{ChainStateAsyncService, StateView}; @@ -185,6 +186,16 @@ where Box::pin(fut) } + fn get_table_info(&self, address: AccountAddress) -> FutureResult> { + let fut = self + .service + .clone() + .get_table_info(address) + .map_ok(|v| v.map(Into::into)) + .map_err(map_err); + Box::pin(fut) + } + fn get_with_table_item_proof( &self, handle: TableHandle, diff --git a/state/api/src/chain_state.rs b/state/api/src/chain_state.rs index 23ef850f42..ca7f1328e0 100644 --- a/state/api/src/chain_state.rs +++ b/state/api/src/chain_state.rs @@ -19,7 +19,7 @@ use starcoin_types::{ use starcoin_vm_types::account_config::TABLE_HANDLE_ADDRESS_LIST; use starcoin_vm_types::genesis_config::ChainId; use starcoin_vm_types::on_chain_resource::{Epoch, EpochInfo, GlobalTimeOnChain}; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use starcoin_vm_types::token::token_code::TokenCode; use starcoin_vm_types::{ move_resource::MoveResource, on_chain_config::OnChainConfig, state_view::StateView, @@ -135,6 +135,8 @@ pub trait ChainStateReader: StateView { handle: &TableHandle, key: &[u8], ) -> Result; + + fn get_table_info(&self, address: AccountAddress) -> Result>; } pub trait ChainStateWriter { diff --git a/state/api/src/lib.rs b/state/api/src/lib.rs index 4c04fac7c6..05e69c80c6 100644 --- a/state/api/src/lib.rs +++ b/state/api/src/lib.rs @@ -21,7 +21,7 @@ use starcoin_types::state_set::AccountStateSet; use starcoin_vm_types::access_path::DataPath; use starcoin_vm_types::account_config::TABLE_HANDLE_ADDRESS_LIST; use starcoin_vm_types::move_resource::MoveResource; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; pub use starcoin_vm_types::state_view::{StateReaderExt, StateView}; mod chain_state; @@ -92,6 +92,8 @@ pub trait ChainStateAsyncService: Clone + std::marker::Unpin + Send + Sync { key: Vec, state_root: HashValue, ) -> Result; + + async fn get_table_info(self, address: AccountAddress) -> Result>; } #[async_trait::async_trait] @@ -216,6 +218,15 @@ where panic!("Unexpect response type.") } } + + async fn get_table_info(self, address: AccountAddress) -> Result> { + let response = self.send(StateRequest::GetTableInfo(address)).await??; + if let StateResponse::TableInfo(state) = response { + Ok(state) + } else { + panic!("Unexpect response type.") + } + } } #[cfg(test)] diff --git a/state/api/src/message.rs b/state/api/src/message.rs index dd6ccf5f0d..331e10b463 100644 --- a/state/api/src/message.rs +++ b/state/api/src/message.rs @@ -9,7 +9,7 @@ use starcoin_types::state_set::AccountStateSet; use starcoin_types::{ access_path::AccessPath, account_address::AccountAddress, account_state::AccountState, }; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; #[derive(Debug, Clone)] pub enum StateRequest { @@ -25,6 +25,7 @@ pub enum StateRequest { StateRoot(), GetWithTableItemProof(TableHandle, Vec), GetWithTableItemProofByRoot(TableHandle, Vec, HashValue), + GetTableInfo(AccountAddress), } impl ServiceRequest for StateRequest { @@ -40,4 +41,5 @@ pub enum StateResponse { AccountStateSet(Option), None, StateWithTableItemProof(Box), + TableInfo(Option), } diff --git a/state/api/src/mock/mock_chain_state_service.rs b/state/api/src/mock/mock_chain_state_service.rs index b70fa51d36..545a17027e 100644 --- a/state/api/src/mock/mock_chain_state_service.rs +++ b/state/api/src/mock/mock_chain_state_service.rs @@ -8,7 +8,7 @@ use starcoin_types::access_path::AccessPath; use starcoin_types::account_address::AccountAddress; use starcoin_types::account_state::AccountState; use starcoin_types::state_set::AccountStateSet; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; //TODO implement Mock service #[derive(Clone, Default)] @@ -78,4 +78,8 @@ impl ChainStateAsyncService for MockChainStateService { ) -> Result { unimplemented!() } + + async fn get_table_info(self, _address: AccountAddress) -> Result> { + Ok(None) + } } diff --git a/state/service/src/service.rs b/state/service/src/service.rs index f9f883b88b..f54738a1e8 100644 --- a/state/service/src/service.rs +++ b/state/service/src/service.rs @@ -23,7 +23,7 @@ use starcoin_types::{ state_set::ChainStateSet, }; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use std::sync::Arc; pub struct ChainStateService { @@ -121,6 +121,9 @@ impl ServiceHandler for ChainStateService { .get_with_table_item_proof_by_root(handle, key, state_root)?, )) } + StateRequest::GetTableInfo(address) => { + StateResponse::TableInfo(self.service.get_table_info(address)?) + } }; Ok(response) } @@ -244,6 +247,10 @@ impl ChainStateReader for Inner { ) -> Result { self.state_db.get_with_table_item_proof(handle, key) } + + fn get_table_info(&self, address: AccountAddress) -> Result> { + self.state_db.get_table_info(address) + } } impl StateView for Inner { diff --git a/state/state-store-api/Cargo.toml b/state/state-store-api/Cargo.toml index 845f67b4f7..39898eab35 100644 --- a/state/state-store-api/Cargo.toml +++ b/state/state-store-api/Cargo.toml @@ -3,6 +3,7 @@ anyhow = { workspace = true } forkable-jellyfish-merkle = { workspace = true } serde = { workspace = true } starcoin-crypto = { workspace = true } +starcoin-vm-types = { workspace = true } [package] authors = { workspace = true } diff --git a/state/state-store-api/src/lib.rs b/state/state-store-api/src/lib.rs index dc236fcc9d..2e192b4f69 100644 --- a/state/state-store-api/src/lib.rs +++ b/state/state-store-api/src/lib.rs @@ -3,6 +3,8 @@ use forkable_jellyfish_merkle::node_type::Node; use forkable_jellyfish_merkle::RawKey; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use starcoin_crypto::hash::HashValue; +use starcoin_vm_types::account_address::AccountAddress; +use starcoin_vm_types::state_store::table::TableInfo; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; @@ -54,4 +56,5 @@ pub trait StateNodeStore: std::marker::Send + std::marker::Sync { fn get(&self, hash: &HashValue) -> Result>; fn put(&self, key: HashValue, node: StateNode) -> Result<()>; fn write_nodes(&self, nodes: BTreeMap) -> Result<()>; + fn get_table_info(&self, address: AccountAddress) -> Result>; } diff --git a/state/state-tree/src/mock/mod.rs b/state/state-tree/src/mock/mod.rs index 3682540dac..88efa7e2e9 100644 --- a/state/state-tree/src/mock/mod.rs +++ b/state/state-tree/src/mock/mod.rs @@ -5,6 +5,8 @@ use anyhow::{Error, Result}; use parking_lot::RwLock; use starcoin_crypto::HashValue; use starcoin_state_store_api::{StateNode, StateNodeStore}; +use starcoin_types::account_address::AccountAddress; +use starcoin_vm_types::state_store::table::TableInfo; use std::collections::{BTreeMap, HashMap}; #[derive(Default)] @@ -41,4 +43,8 @@ impl StateNodeStore for MockStateNodeStore { store_nodes.extend(nodes.into_iter()); Ok(()) } + + fn get_table_info(&self, _address: AccountAddress) -> Result> { + Ok(None) + } } diff --git a/state/statedb/src/lib.rs b/state/statedb/src/lib.rs index d301cdb366..752903de90 100644 --- a/state/statedb/src/lib.rs +++ b/state/statedb/src/lib.rs @@ -28,6 +28,7 @@ use starcoin_vm_types::access_path::{DataPath, ModuleName}; use starcoin_vm_types::account_config::TABLE_ADDRESS_LIST_LEN; use starcoin_vm_types::account_config::TABLE_HANDLE_ADDRESS_LIST; use starcoin_vm_types::language_storage::StructTag; +use starcoin_vm_types::state_store::table::TableInfo; use starcoin_vm_types::state_store::{state_key::StateKey, table::TableHandle}; use starcoin_vm_types::state_view::StateView; use std::collections::HashSet; @@ -564,6 +565,10 @@ impl ChainStateReader for ChainStateDB { ), )) } + + fn get_table_info(&self, address: AccountAddress) -> Result> { + self.store.get_table_info(address) + } } impl ChainStateWriter for ChainStateDB { diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 989356b8d9..0246b6e7f4 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -29,6 +29,7 @@ use starcoin_types::{ startup_info::StartupInfo, }; //use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; +use starcoin_types::account_address::AccountAddress; use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use std::collections::BTreeMap; use std::fmt::{Debug, Display, Formatter}; @@ -325,6 +326,11 @@ impl StateNodeStore for Storage { let batch = CodecWriteBatch::new_puts(nodes.into_iter().collect()); self.state_node_storage.write_batch(batch) } + + fn get_table_info(&self, address: AccountAddress) -> Result> { + let handle = TableHandle(address); + self.table_info_storage.get(handle) + } } impl Display for Storage { diff --git a/vm/starcoin-transactional-test-harness/src/fork_state.rs b/vm/starcoin-transactional-test-harness/src/fork_state.rs index 6266115f3b..dec9571d5d 100644 --- a/vm/starcoin-transactional-test-harness/src/fork_state.rs +++ b/vm/starcoin-transactional-test-harness/src/fork_state.rs @@ -21,7 +21,7 @@ use starcoin_types::access_path::AccessPath; use starcoin_types::account_state::AccountState; use starcoin_types::state_set::AccountStateSet; use starcoin_vm_types::state_store::state_key::StateKey; -use starcoin_vm_types::state_store::table::TableHandle; +use starcoin_vm_types::state_store::table::{TableHandle, TableInfo}; use tokio::runtime::Runtime; pub struct MockStateNodeStore { @@ -72,6 +72,10 @@ impl StateNodeStore for MockStateNodeStore { let batch = CodecWriteBatch::new_puts(nodes.into_iter().collect()); self.local_storage.write_batch(batch) } + + fn get_table_info(&self, _address: AccountAddress) -> Result> { + Ok(None) + } } #[derive(Clone)] @@ -158,4 +162,9 @@ impl ChainStateAsyncService for MockChainStateAsyncService { let reader = self.state_db().fork_at(state_root); reader.get_with_table_item_proof(&handle, &key) } + + async fn get_table_info(self, address: AccountAddress) -> Result> { + let reader = self.state_db().fork(); + reader.get_table_info(address) + } }