diff --git a/crates/rooch-executor/src/actor/executor.rs b/crates/rooch-executor/src/actor/executor.rs index d867cc0fad..e16997d480 100644 --- a/crates/rooch-executor/src/actor/executor.rs +++ b/crates/rooch-executor/src/actor/executor.rs @@ -28,9 +28,6 @@ use moveos_types::transaction::FunctionCall; use moveos_types::transaction::TransactionExecutionInfo; use moveos_types::transaction::VerifiedMoveOSTransaction; use moveos_types::tx_context::TxContext; -// use raw_store::rocks::RocksDB; -// use raw_store::StoreInstance; -// use rooch_config::store_config::StoreConfig; use rooch_framework::bindings::address_mapping::AddressMapping; use rooch_framework::bindings::auth_validator::AuthValidatorCaller; use rooch_framework::bindings::transaction_validator::TransactionValidator; diff --git a/crates/rooch-framework-tests/src/binding_test.rs b/crates/rooch-framework-tests/src/binding_test.rs index 032c6b1c29..c68b0a1b02 100644 --- a/crates/rooch-framework-tests/src/binding_test.rs +++ b/crates/rooch-framework-tests/src/binding_test.rs @@ -13,8 +13,7 @@ pub struct RustBindingTest { impl RustBindingTest { pub fn new() -> Result { - // let moveos_store = MoveOSDB::new_with_memory_store(); - let moveos_store = MoveOSStore::mock().unwrap(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); let genesis: &RoochGenesis = &rooch_genesis::ROOCH_GENESIS; let mut moveos = MoveOS::new(moveos_store, genesis.all_natives(), genesis.config.clone())?; diff --git a/crates/rooch-genesis/src/lib.rs b/crates/rooch-genesis/src/lib.rs index de4c869ad8..ca764c02ef 100644 --- a/crates/rooch-genesis/src/lib.rs +++ b/crates/rooch-genesis/src/lib.rs @@ -191,7 +191,7 @@ mod tests { fn test_genesis_init() { let genesis = super::RoochGenesis::build().expect("build rooch framework failed"); // let db = moveos_store::MoveOSStore::new_with_memory_store(); - let moveos_store = MoveOSStore::mock().unwrap(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); let mut moveos = MoveOS::new( moveos_store, all_natives(genesis.gas_params), diff --git a/crates/rooch-integration-test-runner/src/lib.rs b/crates/rooch-integration-test-runner/src/lib.rs index 9dfd83e876..fa9e48d36a 100644 --- a/crates/rooch-integration-test-runner/src/lib.rs +++ b/crates/rooch-integration-test-runner/src/lib.rs @@ -87,7 +87,7 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { None => BTreeMap::new(), }; - let moveos_store = MoveOSStore::mock().unwrap(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); let genesis: &RoochGenesis = &rooch_genesis::ROOCH_GENESIS; let mut moveos = MoveOS::new( diff --git a/crates/rooch-rpc-server/src/lib.rs b/crates/rooch-rpc-server/src/lib.rs index e33ff7d8c1..f4004effd5 100644 --- a/crates/rooch-rpc-server/src/lib.rs +++ b/crates/rooch-rpc-server/src/lib.rs @@ -12,7 +12,8 @@ use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::ServerBuilder; use jsonrpsee::RpcModule; use moveos_config::store_config::RocksdbConfig; -use moveos_store::MoveOSStore; +use moveos_store::config_store::ConfigStore; +use moveos_store::{MoveOSDB, MoveOSStore}; use raw_store::rocks::RocksDB; use raw_store::StoreInstance; use rooch_config::rpc::server_config::ServerConfig; @@ -72,8 +73,8 @@ impl Service { Self { handle: None } } - pub async fn start(&mut self, is_mock: bool) -> Result<()> { - self.handle = Some(start_server(is_mock).await?); + pub async fn start(&mut self, is_mock_storage: bool) -> Result<()> { + self.handle = Some(start_server(is_mock_storage).await?); Ok(()) } @@ -108,7 +109,7 @@ impl RpcModuleBuilder { } // Start json-rpc server -pub async fn start_server(is_mock: bool) -> Result { +pub async fn start_server(is_mock_storage: bool) -> Result { tracing_subscriber::fmt::init(); let config = ServerConfig::default(); @@ -116,39 +117,8 @@ pub async fn start_server(is_mock: bool) -> Result { let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?; let actor_system = ActorSystem::global_system(); - let (rooch_db_path, moveos_db_path) = if !is_mock { - ( - StoreConfig::get_rooch_store_dir(), - StoreConfig::get_moveos_store_dir(), - ) - } else { - ( - moveos_config::temp_dir().path().to_path_buf(), - moveos_config::temp_dir().path().to_path_buf(), - ) - }; - //Init store - let moveos_store = MoveOSStore::new(StoreInstance::new_db_instance( - RocksDB::new( - moveos_db_path, - moveos_store::StoreMeta::get_column_family_names().to_vec(), - RocksdbConfig::default(), - None, - ) - .unwrap(), - )) - .unwrap(); - let rooch_store = RoochStore::new(StoreInstance::new_db_instance( - RocksDB::new( - rooch_db_path, - rooch_store::StoreMeta::get_column_family_names().to_vec(), - RocksdbConfig::default(), - None, - ) - .unwrap(), - )) - .unwrap(); + let (moveos_store, rooch_store) = init_stroage(is_mock_storage)?; // Init executor let executor = ExecutorActor::new(moveos_store, rooch_store.clone())? @@ -220,3 +190,44 @@ fn _build_rpc_api(mut rpc_module: RpcModule) -> Rpc rpc_module } + +fn init_stroage(is_mock_storage: bool) -> Result<(MoveOSStore, RoochStore)> { + let (rooch_db_path, moveos_db_path) = if !is_mock_storage { + ( + StoreConfig::get_rooch_store_dir(), + StoreConfig::get_moveos_store_dir(), + ) + } else { + ( + moveos_config::temp_dir().path().to_path_buf(), + moveos_config::temp_dir().path().to_path_buf(), + ) + }; + + //Init store + let moveosdb = MoveOSDB::new(StoreInstance::new_db_instance( + RocksDB::new( + moveos_db_path, + moveos_store::StoreMeta::get_column_family_names().to_vec(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + ))?; + let lastest_state_root = moveosdb + .config_store + .get_startup_info()? + .map(|info| info.state_root_hash); + let moveos_store = MoveOSStore::new_with_root(moveosdb, lastest_state_root).unwrap(); + + let rooch_store = RoochStore::new(StoreInstance::new_db_instance( + RocksDB::new( + rooch_db_path, + rooch_store::StoreMeta::get_column_family_names().to_vec(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + ))?; + Ok((moveos_store, rooch_store)) +} diff --git a/crates/rooch/src/commands/move_cli/commands/unit_test.rs b/crates/rooch/src/commands/move_cli/commands/unit_test.rs index e36f466b9d..ea2aa25e8f 100644 --- a/crates/rooch/src/commands/move_cli/commands/unit_test.rs +++ b/crates/rooch/src/commands/move_cli/commands/unit_test.rs @@ -11,7 +11,7 @@ use move_package::BuildConfig; use move_unit_test::extensions::set_extension_hook; use move_vm_runtime::native_extensions::NativeContextExtensions; use moveos_stdlib::natives::moveos_stdlib::raw_table::NativeTableContext; -use moveos_store::state_store::StateDBStore; +use moveos_store::state_store::statedb::StateDBStore; use moveos_store::MoveOSStore; use moveos_verifier::build::build_model_with_test_attr; use moveos_verifier::metadata::run_extended_checks; @@ -88,7 +88,7 @@ impl Test { } static STATEDBSTORE: Lazy> = - Lazy::new(|| Box::new(MoveOSStore::mock().unwrap().state_store)); + Lazy::new(|| Box::new(MoveOSStore::mock_moveos_store().unwrap().statedb)); fn new_moveos_natives_runtime(ext: &mut NativeContextExtensions) { let statedb_store = Lazy::force(&STATEDBSTORE).as_ref(); diff --git a/moveos/moveos-store/Cargo.toml b/moveos/moveos-store/Cargo.toml index 20a4d3ffaa..bd4344ea45 100644 --- a/moveos/moveos-store/Cargo.toml +++ b/moveos/moveos-store/Cargo.toml @@ -12,8 +12,6 @@ repository = { workspace = true } rust-version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -#[lib] -#proc-macro = true [dependencies] anyhow = { workspace = true } @@ -23,7 +21,6 @@ serde = { workspace = true } serde_bytes = { workspace = true } hex = { workspace = true } parking_lot = { workspace = true } -#proc-macro2 = { workspace = true } quote = { workspace = true } num_enum = { workspace = true } once_cell = { workspace = true } diff --git a/moveos/moveos-store/src/config_store/mod.rs b/moveos/moveos-store/src/config_store/mod.rs new file mode 100644 index 0000000000..da14f7d430 --- /dev/null +++ b/moveos/moveos-store/src/config_store/mod.rs @@ -0,0 +1,28 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::CONFIG_PREFIX_NAME; +use anyhow::Result; +use moveos_types::startup_info::StartupInfo; +use raw_store::{derive_store, CodecKVStore}; +use std::string::ToString; + +pub const STARTUP_INFO_KEY: &str = "startup_info"; + +derive_store!(StartupInfoDBStore, String, StartupInfo, CONFIG_PREFIX_NAME); + +pub trait ConfigStore { + fn get_startup_info(&self) -> Result>; + + fn save_startup_info(&self, startup_info: StartupInfo) -> Result<()>; +} + +impl ConfigStore for StartupInfoDBStore { + fn get_startup_info(&self) -> Result> { + self.kv_get(STARTUP_INFO_KEY.to_string()) + } + + fn save_startup_info(&self, startup_info: StartupInfo) -> Result<()> { + self.put_sync(STARTUP_INFO_KEY.to_string(), startup_info) + } +} diff --git a/moveos/moveos-store/src/lib.rs b/moveos/moveos-store/src/lib.rs index 417e6ff5a1..a1d07f38e9 100644 --- a/moveos/moveos-store/src/lib.rs +++ b/moveos/moveos-store/src/lib.rs @@ -4,15 +4,17 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use anyhow::Result; +use anyhow::{Error, Result}; use once_cell::sync::Lazy; use raw_store::{ColumnFamilyName, StoreInstance}; use std::collections::BTreeMap; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; +use crate::config_store::{ConfigStore, StartupInfoDBStore}; use crate::event_store::{EventDBStore, EventStore}; -use crate::state_store::StateDBStore; +use crate::state_store::statedb::StateDBStore; +use crate::state_store::NodeDBStore; use crate::transaction_store::{TransactionDBStore, TransactionStore}; use move_core_types::language_storage::TypeTag; use moveos_config::store_config::RocksdbConfig; @@ -20,10 +22,14 @@ use moveos_types::event::{Event, EventID}; use moveos_types::event_filter::EventFilter; use moveos_types::h256::H256; use moveos_types::object::ObjectID; +use moveos_types::startup_info::StartupInfo; +use moveos_types::state::State; +use moveos_types::state_resolver::StateResolver; use moveos_types::transaction::TransactionExecutionInfo; use raw_store::rocks::RocksDB; use smt::NodeStore; +pub mod config_store; pub mod event_store; pub mod state_store; #[cfg(test)] @@ -35,6 +41,7 @@ pub const STATE_NODE_PREFIX_NAME: ColumnFamilyName = "state_node"; pub const TRANSACTION_PREFIX_NAME: ColumnFamilyName = "transaction"; pub const EVENT_PREFIX_NAME: ColumnFamilyName = "event"; pub const EVENT_INDEX_PREFIX_NAME: ColumnFamilyName = "event_index"; +pub const CONFIG_PREFIX_NAME: ColumnFamilyName = "config"; ///db store use prefix_name vec to init /// Please note that adding a prefix needs to be added in vec simultaneously, remember!! @@ -44,6 +51,7 @@ static VEC_PREFIX_NAME: Lazy> = Lazy::new(|| { TRANSACTION_PREFIX_NAME, EVENT_PREFIX_NAME, EVENT_INDEX_PREFIX_NAME, + CONFIG_PREFIX_NAME, ] }); @@ -56,18 +64,17 @@ impl StoreMeta { } } -// #[derive(Clone)] -pub struct MoveOSStore { - pub state_store: StateDBStore, +pub struct MoveOSDB { + pub node_store: NodeDBStore, pub event_store: EventDBStore, pub transaction_store: TransactionDBStore, + pub config_store: StartupInfoDBStore, } -// // TODO: remove Arc, we can clone Store directly. -impl MoveOSStore { - pub fn mock() -> Result { +impl MoveOSDB { + pub fn mock_store_instance() -> StoreInstance { let tmpdir = moveos_config::temp_dir(); - let mock_instance = StoreInstance::new_db_instance( + StoreInstance::new_db_instance( RocksDB::new( tmpdir.path(), StoreMeta::get_column_family_names().to_vec(), @@ -75,40 +82,78 @@ impl MoveOSStore { None, ) .unwrap(), - ); + ) + } - Self::new(mock_instance) + pub fn mock_moveosdb() -> Result { + Self::new(Self::mock_store_instance()) } pub fn new(instance: StoreInstance) -> Result { let store = Self { - state_store: StateDBStore::new(instance.clone()), + node_store: NodeDBStore::new(instance.clone()), event_store: EventDBStore::new(instance.clone()), - transaction_store: TransactionDBStore::new(instance), + transaction_store: TransactionDBStore::new(instance.clone()), + config_store: StartupInfoDBStore::new(instance), + }; + Ok(store) + } +} + +pub struct MoveOSStore { + pub statedb: StateDBStore, + pub moveosdb: MoveOSDB, +} + +impl MoveOSStore { + pub fn mock_moveos_store() -> Result { + let moveosdb = MoveOSDB::mock_moveosdb()?; + Self::new(moveosdb) + } + + pub fn new(moveosdb: MoveOSDB) -> Result { + let store = Self { + statedb: StateDBStore::new(moveosdb.node_store.clone()), + moveosdb, + }; + Ok(store) + } + + pub fn new_with_root(moveosdb: MoveOSDB, state_root: Option) -> Result { + let store = Self { + statedb: StateDBStore::new_with_root(moveosdb.node_store.clone(), state_root), + moveosdb, }; Ok(store) } pub fn get_event_store(&self) -> &EventDBStore { - &self.event_store + &self.moveosdb.event_store } pub fn get_transaction_store(&self) -> &TransactionDBStore { - &self.transaction_store + &self.moveosdb.transaction_store + } + + pub fn get_state_node_store(&self) -> &NodeDBStore { + &self.moveosdb.node_store + } + + pub fn get_config_store(&self) -> &StartupInfoDBStore { + &self.moveosdb.config_store } pub fn get_state_store(&self) -> &StateDBStore { - &self.state_store + &self.statedb } } impl Display for MoveOSStore { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - // write!(f, "{}", self.clone()) - - write!(f, "state_store")?; + write!(f, "statedb")?; write!(f, "event_store")?; write!(f, "transaction_store")?; + write!(f, "node_store")?; Ok(()) } } @@ -121,33 +166,33 @@ impl Debug for MoveOSStore { impl NodeStore for MoveOSStore { fn get(&self, hash: &H256) -> Result>> { - self.state_store.node_store.get(hash) + self.get_state_node_store().get(hash) } fn put(&self, key: H256, node: Vec) -> Result<()> { - self.state_store.node_store.put(key, node) + self.get_state_node_store().put(key, node) } fn write_nodes(&self, nodes: BTreeMap>) -> Result<()> { - self.state_store.node_store.write_nodes(nodes) + self.get_state_node_store().write_nodes(nodes) } } impl EventStore for MoveOSStore { fn save_event(&self, event: Event) -> Result<()> { - self.event_store.save_event(event) + self.get_event_store().save_event(event) } fn save_events(&self, events: Vec) -> Result<()> { - self.event_store.save_events(events) + self.get_event_store().save_events(events) } fn get_event(&self, event_id: EventID) -> Result> { - self.event_store.get_event(event_id) + self.get_event_store().get_event(event_id) } fn get_events_by_tx_hash(&self, tx_hash: &H256) -> Result> { - self.event_store.get_events_by_tx_hash(tx_hash) + self.get_event_store().get_events_by_tx_hash(tx_hash) } fn get_events_by_event_handle_id( @@ -156,39 +201,53 @@ impl EventStore for MoveOSStore { cursor: Option, limit: u64, ) -> Result> { - self.event_store + self.get_event_store() .get_events_by_event_handle_id(event_handle_id, cursor, limit) } fn get_events_by_event_handle_type(&self, event_handle_type: &TypeTag) -> Result> { - self.event_store + self.get_event_store() .get_events_by_event_handle_type(event_handle_type) } fn get_events_with_filter(&self, filter: EventFilter) -> Result> { - self.event_store.get_events_with_filter(filter) + self.get_event_store().get_events_with_filter(filter) } } impl TransactionStore for MoveOSStore { fn save_tx_exec_info(&self, tx_exec_info: TransactionExecutionInfo) -> Result<()> { - self.transaction_store.save_tx_exec_info(tx_exec_info) + self.get_transaction_store().save_tx_exec_info(tx_exec_info) } fn get_tx_exec_info(&self, tx_hash: H256) -> Result> { - self.transaction_store.get_tx_exec_info(tx_hash) + self.get_transaction_store().get_tx_exec_info(tx_hash) } fn multi_get_tx_exec_infos( &self, tx_hashes: Vec, ) -> Result>> { - self.transaction_store.multi_get_tx_exec_infos(tx_hashes) + self.get_transaction_store() + .multi_get_tx_exec_infos(tx_hashes) + } +} + +impl ConfigStore for MoveOSStore { + fn get_startup_info(&self) -> Result> { + self.get_config_store().get_startup_info() + } + + fn save_startup_info(&self, startup_info: StartupInfo) -> Result<()> { + self.get_config_store().save_startup_info(startup_info) } } /// Moveos store define -pub trait Store: NodeStore + TransactionStore + EventStore + IntoSuper {} +pub trait Store: + NodeStore + TransactionStore + EventStore + ConfigStore + IntoSuper +{ +} pub trait IntoSuper { fn as_super(&self) -> &Super; @@ -213,3 +272,13 @@ impl<'a, T: 'a + NodeStore> IntoSuper for T { } impl Store for MoveOSStore {} + +impl StateResolver for MoveOSStore { + fn resolve_state( + &self, + handle: &ObjectID, + key: &[u8], + ) -> std::result::Result, Error> { + self.statedb.resolve_state(handle, key) + } +} diff --git a/moveos/moveos-store/src/state_store/mod.rs b/moveos/moveos-store/src/state_store/mod.rs index 3096b7ac6e..e685fe8061 100644 --- a/moveos/moveos-store/src/state_store/mod.rs +++ b/moveos/moveos-store/src/state_store/mod.rs @@ -1,36 +1,16 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::MoveOSStore; -use anyhow::{Error, Result}; -use move_core_types::{ - account_address::AccountAddress, - effects::{ChangeSet, Op}, - identifier::Identifier, - language_storage::{StructTag, TypeTag}, -}; -use moveos_types::{ - h256::H256, - move_module::MoveModule, - state::{MoveStructState, State}, -}; -use moveos_types::{ - object::{AccountStorage, Object, ObjectID, RawObject, TableInfo}, - storage_context, -}; -use moveos_types::{ - state::StateChangeSet, - state_resolver::{self, module_name_to_key, resource_tag_to_key, StateResolver}, -}; +pub mod statedb; + +use anyhow::Result; +use moveos_types::h256::H256; use raw_store::{CodecKVStore, CodecWriteBatch}; -use smt::{NodeStore, SMTree, UpdateSet}; +use smt::NodeStore; use std::collections::BTreeMap; -#[cfg(test)] -mod tests; - use crate::STATE_NODE_PREFIX_NAME; -use raw_store::{derive_store, StoreInstance}; +use raw_store::derive_store; derive_store!(NodeDBStore, H256, Vec, STATE_NODE_PREFIX_NAME); @@ -48,268 +28,3 @@ impl NodeStore for NodeDBStore { self.write_batch(batch) } } - -struct AccountStorageTables { - pub resources: (Object, TreeTable), - pub modules: (Object, TreeTable), -} - -pub struct TreeTable { - smt: SMTree, State, NS>, -} - -impl TreeTable -where - NS: NodeStore, -{ - pub fn new(node_store: NS) -> Self { - Self::new_with_root(node_store, None) - } - - pub fn new_with_root(node_store: NS, state_root: Option) -> Self { - Self { - smt: SMTree::new(node_store, state_root), - } - } - - pub fn get(&self, key: Vec) -> Result> { - self.smt.get(key) - } - - pub fn puts(&self, update_set: I) -> Result - where - I: Into, State>>, - { - self.smt.puts(update_set) - } - - pub fn state_root(&self) -> H256 { - self.smt.root_hash() - } - - pub fn put_modules(&self, modules: BTreeMap>>) -> Result { - //We wrap the modules to `MoveModule` - //For distinguish `vector` and MoveModule in Move. - self.put_changes(modules.into_iter().map(|(k, v)| { - ( - module_name_to_key(k.as_ident_str()), - v.map(|v| MoveModule::new(v).into()), - ) - })) - } - - pub fn put_resources(&self, modules: BTreeMap>>) -> Result { - self.put_changes(modules.into_iter().map(|(k, v)| { - ( - resource_tag_to_key(&k), - v.map(|v| State::new(v, TypeTag::Struct(Box::new(k)))), - ) - })) - } - - pub fn put_changes, Op)>>( - &self, - changes: I, - ) -> Result { - let mut update_set = UpdateSet::new(); - for (key, op) in changes { - match op { - Op::Modify(value) => { - update_set.put(key, value); - } - Op::Delete => { - update_set.remove(key); - } - Op::New(value) => { - update_set.put(key, value); - } - } - } - self.puts(update_set) - } -} - -/// StateDB provide state storage and state proof -pub struct StateDBStore { - pub node_store: NodeDBStore, - global_table: TreeTable, -} - -impl StateDBStore { - pub fn new(instance: StoreInstance) -> Self { - let store = NodeDBStore::new(instance); - Self { - node_store: store.clone(), - global_table: TreeTable::new(store), - } - } - - pub fn get(&self, id: ObjectID) -> Result> { - self.global_table.get(id.to_bytes()) - } - - fn get_as_object(&self, id: ObjectID) -> Result>> { - self.get(id)? - .map(|state| state.as_object::()) - .transpose() - .map_err(Into::into) - } - - pub fn get_as_raw_object(&self, id: ObjectID) -> Result> { - self.get(id)? - .map(|state| state.as_raw_object()) - .transpose() - .map_err(Into::into) - } - - fn get_as_account_storage( - &self, - account: AccountAddress, - ) -> Result>> { - self.get_as_object::(account.into()) - } - - fn get_as_account_storage_or_create( - &self, - account: AccountAddress, - ) -> Result<(Object, AccountStorageTables)> { - let account_storage = self - .get_as_account_storage(account)? - .unwrap_or_else(|| Object::new_account_storage_object(account)); - let storage_tables = AccountStorageTables { - resources: self.get_as_table_or_create(account_storage.value.resources)?, - modules: self.get_as_table_or_create(account_storage.value.modules)?, - }; - Ok((account_storage, storage_tables)) - } - - fn get_as_table( - &self, - id: ObjectID, - ) -> Result, TreeTable)>> { - let object = self.get_as_object::(id)?; - match object { - Some(object) => { - let state_root = object.value.state_root; - Ok(Some(( - object, - TreeTable::new_with_root( - self.node_store.clone(), - Some(H256(state_root.into())), - ), - ))) - } - None => Ok(None), - } - } - - fn get_as_table_or_create( - &self, - id: ObjectID, - ) -> Result<(Object, TreeTable)> { - Ok(self.get_as_table(id)?.unwrap_or_else(|| { - let table = TreeTable::new(self.node_store.clone()); - let table_info = TableInfo::new(AccountAddress::new(table.state_root().into())); - let object = Object::new_table_object(id, table_info); - (object, table) - })) - } - - pub fn get_with_key(&self, id: ObjectID, key: Vec) -> Result> { - self.get_as_table(id) - .and_then(|res| res.map(|(_, table)| table.get(key)).unwrap_or(Ok(None))) - } - - pub fn apply_change_set( - &self, - change_set: ChangeSet, - state_change_set: StateChangeSet, - ) -> Result { - let mut changed_objects = UpdateSet::new(); - //TODO - //We want deprecate the global storage instructions https://github.com/rooch-network/rooch/issues/248 - //So the ChangeSet should be empty, but the module publish still need it - //We need to figure out a way to make the module publish use raw table's StateChangeSet - for (account, account_change_set) in change_set.into_inner() { - let (account_storage, storage_tables) = - self.get_as_account_storage_or_create(account)?; - - let (modules, resources) = account_change_set.into_inner(); - if !modules.is_empty() { - let (mut object, module_table) = storage_tables.modules; - let new_state_root = module_table.put_modules(modules)?; - object.value.state_root = AccountAddress::new(new_state_root.into()); - changed_objects.put(account_storage.value.modules.to_bytes(), object.into()); - } - if !resources.is_empty() { - let (mut object, resource_table) = storage_tables.resources; - let new_state_root = resource_table.put_resources(resources)?; - object.value.state_root = AccountAddress::new(new_state_root.into()); - changed_objects.put(account_storage.value.resources.to_bytes(), object.into()); - } - //TODO check if the account_storage and table is changed, if not changed, don't put it - changed_objects.put(ObjectID::from(account).to_bytes(), account_storage.into()) - } - - for (table_handle, table_change) in state_change_set.changes { - // handle global object - if table_handle == storage_context::GLOBAL_OBJECT_STORAGE_HANDLE { - self.global_table - .put_changes(table_change.entries.into_iter())?; - } else { - let (mut object, table) = self.get_as_table_or_create(table_handle)?; - let new_state_root = table.put_changes(table_change.entries.into_iter())?; - object.value.state_root = AccountAddress::new(new_state_root.into()); - changed_objects.put(table_handle.to_bytes(), object.into()); - } - } - - for table_handle in state_change_set.removed_tables { - changed_objects.remove(table_handle.to_bytes()); - } - - self.global_table.puts(changed_objects) - } - - pub fn is_genesis(&self) -> bool { - self.global_table.smt.is_genesis() - } - - //Only for unit test and integration test runner - pub fn create_account_storage(&self, account: AccountAddress) -> Result<()> { - let account_storage = Object::new_account_storage_object(account); - self.global_table.puts(( - ObjectID::from(account).to_bytes(), - State::from(account_storage), - ))?; - Ok(()) - } - - pub fn resolve_state(&self, handle: &ObjectID, key: &[u8]) -> Result, Error> { - if handle == &state_resolver::GLOBAL_OBJECT_STORAGE_HANDLE { - self.global_table.get(key.to_vec()) - } else { - self.get_with_key(*handle, key.to_vec()) - } - } -} - -impl StateResolver for MoveOSStore { - fn resolve_state( - &self, - handle: &ObjectID, - key: &[u8], - ) -> std::result::Result, Error> { - self.state_store.resolve_state(handle, key) - } -} - -impl StateResolver for StateDBStore { - fn resolve_state( - &self, - handle: &ObjectID, - key: &[u8], - ) -> std::result::Result, Error> { - self.resolve_state(handle, key) - } -} diff --git a/moveos/moveos-store/src/state_store/statedb.rs b/moveos/moveos-store/src/state_store/statedb.rs new file mode 100644 index 0000000000..86250b73eb --- /dev/null +++ b/moveos/moveos-store/src/state_store/statedb.rs @@ -0,0 +1,288 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{Error, Result}; +use move_core_types::{ + account_address::AccountAddress, + effects::{ChangeSet, Op}, + identifier::Identifier, + language_storage::{StructTag, TypeTag}, +}; +use moveos_types::{ + h256::H256, + move_module::MoveModule, + state::{MoveStructState, State}, +}; +use moveos_types::{ + object::{AccountStorage, Object, ObjectID, RawObject, TableInfo}, + storage_context, +}; +use moveos_types::{ + state::StateChangeSet, + state_resolver::{self, module_name_to_key, resource_tag_to_key, StateResolver}, +}; +use smt::{NodeStore, SMTree, UpdateSet}; +use std::collections::BTreeMap; + +use crate::state_store::NodeDBStore; + +struct AccountStorageTables { + pub resources: (Object, TreeTable), + pub modules: (Object, TreeTable), +} + +pub struct TreeTable { + smt: SMTree, State, NS>, +} + +impl TreeTable +where + NS: NodeStore, +{ + pub fn new(node_store: NS) -> Self { + Self::new_with_root(node_store, None) + } + + pub fn new_with_root(node_store: NS, state_root: Option) -> Self { + Self { + smt: SMTree::new(node_store, state_root), + } + } + + pub fn get(&self, key: Vec) -> Result> { + self.smt.get(key) + } + + pub fn puts(&self, update_set: I) -> Result + where + I: Into, State>>, + { + self.smt.puts(update_set) + } + + pub fn state_root(&self) -> H256 { + self.smt.root_hash() + } + + pub fn put_modules(&self, modules: BTreeMap>>) -> Result { + //We wrap the modules to `MoveModule` + //For distinguish `vector` and MoveModule in Move. + self.put_changes(modules.into_iter().map(|(k, v)| { + ( + module_name_to_key(k.as_ident_str()), + v.map(|v| MoveModule::new(v).into()), + ) + })) + } + + pub fn put_resources(&self, modules: BTreeMap>>) -> Result { + self.put_changes(modules.into_iter().map(|(k, v)| { + ( + resource_tag_to_key(&k), + v.map(|v| State::new(v, TypeTag::Struct(Box::new(k)))), + ) + })) + } + + pub fn put_changes, Op)>>( + &self, + changes: I, + ) -> Result { + let mut update_set = UpdateSet::new(); + for (key, op) in changes { + match op { + Op::Modify(value) => { + update_set.put(key, value); + } + Op::Delete => { + update_set.remove(key); + } + Op::New(value) => { + update_set.put(key, value); + } + } + } + self.puts(update_set) + } +} + +/// StateDB provide state storage and state proof +pub struct StateDBStore { + pub node_store: NodeDBStore, + global_table: TreeTable, +} + +impl StateDBStore { + pub fn new(node_store: NodeDBStore) -> Self { + Self { + node_store: node_store.clone(), + global_table: TreeTable::new(node_store), + } + } + + pub fn new_with_root(node_store: NodeDBStore, state_root: Option) -> Self { + Self { + node_store: node_store.clone(), + global_table: TreeTable::new_with_root(node_store, state_root), + } + } + + pub fn get(&self, id: ObjectID) -> Result> { + self.global_table.get(id.to_bytes()) + } + + fn get_as_object(&self, id: ObjectID) -> Result>> { + self.get(id)? + .map(|state| state.as_object::()) + .transpose() + .map_err(Into::into) + } + + pub fn get_as_raw_object(&self, id: ObjectID) -> Result> { + self.get(id)? + .map(|state| state.as_raw_object()) + .transpose() + .map_err(Into::into) + } + + fn get_as_account_storage( + &self, + account: AccountAddress, + ) -> Result>> { + self.get_as_object::(account.into()) + } + + fn get_as_account_storage_or_create( + &self, + account: AccountAddress, + ) -> Result<(Object, AccountStorageTables)> { + let account_storage = self + .get_as_account_storage(account)? + .unwrap_or_else(|| Object::new_account_storage_object(account)); + let storage_tables = AccountStorageTables { + resources: self.get_as_table_or_create(account_storage.value.resources)?, + modules: self.get_as_table_or_create(account_storage.value.modules)?, + }; + Ok((account_storage, storage_tables)) + } + + fn get_as_table( + &self, + id: ObjectID, + ) -> Result, TreeTable)>> { + let object = self.get_as_object::(id)?; + match object { + Some(object) => { + let state_root = object.value.state_root; + Ok(Some(( + object, + TreeTable::new_with_root( + self.node_store.clone(), + Some(H256(state_root.into())), + ), + ))) + } + None => Ok(None), + } + } + + fn get_as_table_or_create( + &self, + id: ObjectID, + ) -> Result<(Object, TreeTable)> { + Ok(self.get_as_table(id)?.unwrap_or_else(|| { + let table = TreeTable::new(self.node_store.clone()); + let table_info = TableInfo::new(AccountAddress::new(table.state_root().into())); + let object = Object::new_table_object(id, table_info); + (object, table) + })) + } + + pub fn get_with_key(&self, id: ObjectID, key: Vec) -> Result> { + self.get_as_table(id) + .and_then(|res| res.map(|(_, table)| table.get(key)).unwrap_or(Ok(None))) + } + + pub fn apply_change_set( + &self, + change_set: ChangeSet, + state_change_set: StateChangeSet, + ) -> Result { + let mut changed_objects = UpdateSet::new(); + //TODO + //We want deprecate the global storage instructions https://github.com/rooch-network/rooch/issues/248 + //So the ChangeSet should be empty, but the module publish still need it + //We need to figure out a way to make the module publish use raw table's StateChangeSet + for (account, account_change_set) in change_set.into_inner() { + let (account_storage, storage_tables) = + self.get_as_account_storage_or_create(account)?; + + let (modules, resources) = account_change_set.into_inner(); + if !modules.is_empty() { + let (mut object, module_table) = storage_tables.modules; + let new_state_root = module_table.put_modules(modules)?; + object.value.state_root = AccountAddress::new(new_state_root.into()); + changed_objects.put(account_storage.value.modules.to_bytes(), object.into()); + } + if !resources.is_empty() { + let (mut object, resource_table) = storage_tables.resources; + let new_state_root = resource_table.put_resources(resources)?; + object.value.state_root = AccountAddress::new(new_state_root.into()); + changed_objects.put(account_storage.value.resources.to_bytes(), object.into()); + } + //TODO check if the account_storage and table is changed, if not changed, don't put it + changed_objects.put(ObjectID::from(account).to_bytes(), account_storage.into()) + } + + for (table_handle, table_change) in state_change_set.changes { + // handle global object + if table_handle == storage_context::GLOBAL_OBJECT_STORAGE_HANDLE { + self.global_table + .put_changes(table_change.entries.into_iter())?; + } else { + let (mut object, table) = self.get_as_table_or_create(table_handle)?; + let new_state_root = table.put_changes(table_change.entries.into_iter())?; + object.value.state_root = AccountAddress::new(new_state_root.into()); + changed_objects.put(table_handle.to_bytes(), object.into()); + } + } + + for table_handle in state_change_set.removed_tables { + changed_objects.remove(table_handle.to_bytes()); + } + + self.global_table.puts(changed_objects) + } + + pub fn is_genesis(&self) -> bool { + self.global_table.smt.is_genesis() + } + + //Only for unit test and integration test runner + pub fn create_account_storage(&self, account: AccountAddress) -> Result<()> { + let account_storage = Object::new_account_storage_object(account); + self.global_table.puts(( + ObjectID::from(account).to_bytes(), + State::from(account_storage), + ))?; + Ok(()) + } + + pub fn resolve_state(&self, handle: &ObjectID, key: &[u8]) -> Result, Error> { + if handle == &state_resolver::GLOBAL_OBJECT_STORAGE_HANDLE { + self.global_table.get(key.to_vec()) + } else { + self.get_with_key(*handle, key.to_vec()) + } + } +} + +impl StateResolver for StateDBStore { + fn resolve_state( + &self, + handle: &ObjectID, + key: &[u8], + ) -> std::result::Result, Error> { + self.resolve_state(handle, key) + } +} diff --git a/moveos/moveos-store/src/tests/mod.rs b/moveos/moveos-store/src/tests/mod.rs index ad1c8830b7..d82bfd8559 100644 --- a/moveos/moveos-store/src/tests/mod.rs +++ b/moveos/moveos-store/src/tests/mod.rs @@ -1,4 +1,5 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 +mod test_state_store; mod test_store; diff --git a/moveos/moveos-store/src/state_store/tests.rs b/moveos/moveos-store/src/tests/test_state_store.rs similarity index 54% rename from moveos/moveos-store/src/state_store/tests.rs rename to moveos/moveos-store/src/tests/test_state_store.rs index 8994ca57a3..a82b60834a 100644 --- a/moveos/moveos-store/src/state_store/tests.rs +++ b/moveos/moveos-store/src/tests/test_state_store.rs @@ -1,16 +1,19 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; - +use move_core_types::effects::{ChangeSet, Op}; +use moveos_types::h256::H256; +use moveos_types::object::ObjectID; +use moveos_types::state::{MoveState, StateChangeSet}; use moveos_types::{move_string::MoveString, state::TableChange}; +use smt::NodeStore; +use std::str::FromStr; -use super::*; use crate::MoveOSStore; #[test] fn test_statedb() { - let moveos_store = MoveOSStore::mock().unwrap(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); let change_set = ChangeSet::new(); @@ -26,7 +29,7 @@ fn test_statedb() { table_change_set.changes.insert(table_handle, table_change); moveos_store - .state_store + .get_state_store() .apply_change_set(change_set, table_change_set) .unwrap(); @@ -37,3 +40,22 @@ fn test_statedb() { assert!(state.is_some()); assert_eq!(state.unwrap(), value.into()); } + +#[test] +fn test_reopen() { + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); + let node_store = moveos_store.get_state_node_store(); + + let key = H256::random(); + let node = b"testnode".to_vec(); + { + node_store + .put(key, node.clone()) + .map_err(|e| anyhow::anyhow!("test_state_store test_reopen error: {:?}", e)) + .ok(); + assert_eq!(node_store.get(&key).unwrap(), Some(node.clone())); + } + { + assert_eq!(node_store.get(&key).unwrap(), Some(node)); + } +} diff --git a/moveos/moveos-store/src/tests/test_store.rs b/moveos/moveos-store/src/tests/test_store.rs index 5a00c2c1c5..f4d3157250 100644 --- a/moveos/moveos-store/src/tests/test_store.rs +++ b/moveos/moveos-store/src/tests/test_store.rs @@ -4,7 +4,7 @@ extern crate chrono; use crate::event_store::EventStore; -use crate::{MoveOSStore, StoreMeta}; +use crate::MoveOSStore; use move_core_types::account_address::AccountAddress; use move_core_types::identifier::Identifier; use move_core_types::language_storage::{StructTag, TypeTag}; @@ -15,7 +15,7 @@ use moveos_types::h256::H256; use moveos_types::transaction::TransactionExecutionInfo; use raw_store::rocks::{RocksDB, DEFAULT_PREFIX_NAME}; use raw_store::traits::DBStore; -use raw_store::{CodecKVStore, StoreInstance}; +use raw_store::CodecKVStore; #[test] fn test_reopen() { @@ -60,7 +60,6 @@ fn test_open_read_only() { bcs::to_bytes(&value).unwrap(), ); assert!(result.is_ok()); - // let path = tmpdir.as_ref().join("roochdb"); let path = tmpdir.as_ref(); let db = RocksDB::open_with_cfs(path, cfs, true, RocksdbConfig::default(), None).unwrap(); let result = db.put( @@ -77,12 +76,7 @@ fn test_open_read_only() { #[test] fn test_store() { - let tmpdir = moveos_config::temp_dir(); - let cfs = StoreMeta::get_column_family_names().to_vec(); - let store = MoveOSStore::new(StoreInstance::new_db_instance( - RocksDB::new(tmpdir.path(), cfs, RocksdbConfig::default(), None).unwrap(), - )) - .unwrap(); + let store = MoveOSStore::mock_moveos_store().unwrap(); let transaction_info1 = TransactionExecutionInfo::new( H256::random(), @@ -93,22 +87,17 @@ fn test_store() { ); let id = transaction_info1.tx_hash; store - .transaction_store + .get_transaction_store() .kv_put(id, transaction_info1.clone()) .unwrap(); - let transaction_info2 = store.transaction_store.kv_get(id).unwrap(); + let transaction_info2 = store.get_transaction_store().kv_get(id).unwrap(); assert!(transaction_info2.is_some()); assert_eq!(transaction_info1, transaction_info2.unwrap()); } #[test] fn test_event_store() { - let tmpdir = moveos_config::temp_dir(); - let cfs = StoreMeta::get_column_family_names().to_vec(); - let store = MoveOSStore::new(StoreInstance::new_db_instance( - RocksDB::new(tmpdir.path(), cfs, RocksdbConfig::default(), None).unwrap(), - )) - .unwrap(); + let store = MoveOSStore::mock_moveos_store().unwrap(); let test_struct_tag = StructTag { address: AccountAddress::random(), @@ -134,12 +123,7 @@ fn test_event_store() { #[test] fn test_iter() { - let tmpdir = moveos_config::temp_dir(); - let cfs = StoreMeta::get_column_family_names().to_vec(); - let store = MoveOSStore::new(StoreInstance::new_db_instance( - RocksDB::new(tmpdir.path(), cfs, RocksdbConfig::default(), None).unwrap(), - )) - .unwrap(); + let store = MoveOSStore::mock_moveos_store().unwrap(); let transaction_info1 = TransactionExecutionInfo::new( H256::random(), H256::random(), @@ -149,10 +133,10 @@ fn test_iter() { ); let id = transaction_info1.tx_hash; store - .transaction_store + .get_transaction_store() .kv_put(id, transaction_info1.clone()) .unwrap(); - let mut iter = store.transaction_store.iter().unwrap(); + let mut iter = store.get_transaction_store().iter().unwrap(); iter.seek_to_first(); let item2 = iter.next().and_then(|item| item.ok()); assert!(item2.is_some()); diff --git a/moveos/moveos-types/src/lib.rs b/moveos/moveos-types/src/lib.rs index 5e3e33a4f7..64d1d7d7f0 100644 --- a/moveos/moveos-types/src/lib.rs +++ b/moveos/moveos-types/src/lib.rs @@ -17,6 +17,7 @@ pub mod move_string; pub mod move_types; pub mod object; pub mod serde; +pub mod startup_info; pub mod state; pub mod state_resolver; pub mod storage_context; diff --git a/moveos/moveos-types/src/startup_info.rs b/moveos/moveos-types/src/startup_info.rs new file mode 100644 index 0000000000..22a12cd5d2 --- /dev/null +++ b/moveos/moveos-types/src/startup_info.rs @@ -0,0 +1,36 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use primitive_types::H256; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Debug; + +#[derive(Eq, PartialEq, Hash, Deserialize, Serialize, Clone, Debug)] +pub struct StartupInfo { + /// lastest state root hash + pub state_root_hash: H256, +} + +impl fmt::Display for StartupInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StartupInfo {{")?; + write!(f, "state_root_hash: {:?},", self.state_root_hash)?; + write!(f, "}}")?; + Ok(()) + } +} + +impl StartupInfo { + pub fn new(state_root_hash: H256) -> Self { + Self { state_root_hash } + } + + pub fn update_state_root_hash(&mut self, new_state_root_hash: H256) { + self.state_root_hash = new_state_root_hash; + } + + pub fn get_state_root_hash(&self) -> &H256 { + &self.state_root_hash + } +} diff --git a/moveos/moveos/src/moveos.rs b/moveos/moveos/src/moveos.rs index 60997aa9aa..860e00590b 100644 --- a/moveos/moveos/src/moveos.rs +++ b/moveos/moveos/src/moveos.rs @@ -12,12 +12,14 @@ use move_core_types::{ use move_vm_runtime::config::VMConfig; use move_vm_runtime::native_functions::NativeFunction; use move_vm_types::gas::UnmeteredGasMeter; +use moveos_store::config_store::ConfigStore; use moveos_store::event_store::EventDBStore; -use moveos_store::state_store::StateDBStore; +use moveos_store::state_store::statedb::StateDBStore; use moveos_store::transaction_store::TransactionDBStore; use moveos_store::MoveOSStore; use moveos_types::function_return_value::FunctionReturnValue; use moveos_types::module_binding::MoveFunctionCaller; +use moveos_types::startup_info::StartupInfo; use moveos_types::state_resolver::MoveOSResolverProxy; use moveos_types::transaction::{MoveOSTransaction, TransactionOutput, VerifiedMoveOSTransaction}; use moveos_types::tx_context::TxContext; @@ -210,6 +212,15 @@ impl MoveOS { .with_message(e.to_string()) .finish(Location::Undefined) })?; + self.db + .0 + .get_config_store() + .save_startup_info(StartupInfo::new(new_state_root)) + .map_err(|e| { + PartialVMError::new(StatusCode::STORAGE_ERROR) + .with_message(e.to_string()) + .finish(Location::Undefined) + })?; Ok(new_state_root) } diff --git a/moveos/raw-store/src/errors.rs b/moveos/raw-store/src/errors.rs index 9c53ed61ea..a113361bfe 100644 --- a/moveos/raw-store/src/errors.rs +++ b/moveos/raw-store/src/errors.rs @@ -2,30 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::Error; -// use serde::{Deserialize, Serialize}; -// use thiserror::Error; -// #[derive(Debug, Error)] -// pub enum RawStoreError { -// #[error("Store check error {0:?}.")] -// StoreCheckError(Error), -// } - -// #[derive(Error, Debug, Serialize, Deserialize)] #[derive(thiserror::Error, Debug)] pub enum RawStoreError { #[error("Store check error {0:?}.")] StoreCheckError(Error), - // #[error("rocksdb error: {0}")] - // RocksDBError(String), - // #[error("(de)serialization error: {0}")] - // SerializationError(String), - // #[error("the column family {0} was not registered with the database")] - // UnregisteredColumn(String), - // #[error("a batch operation can't operate across databases")] - // CrossDBBatch, - // #[error("Metric reporting thread failed with error")] - // MetricsReporting, - // #[error("Transaction should be retried")] - // RetryableTransactionError, } diff --git a/moveos/raw-store/src/lib.rs b/moveos/raw-store/src/lib.rs index 74a431f1b1..3ddd94b8ad 100644 --- a/moveos/raw-store/src/lib.rs +++ b/moveos/raw-store/src/lib.rs @@ -294,6 +294,8 @@ where fn kv_put(&self, key: K, value: V) -> Result<()>; + fn put_sync(&self, key: K, value: V) -> Result<()>; + fn contains_key(&self, key: K) -> Result; fn remove(&self, key: K) -> Result<()>; @@ -352,6 +354,10 @@ where KVStore::put(self.get_store(), to_bytes(&key)?, to_bytes(&value)?) } + fn put_sync(&self, key: K, value: V) -> Result<()> { + KVStore::put_sync(self.get_store(), to_bytes(&key)?, to_bytes(&value)?) + } + fn contains_key(&self, key: K) -> Result { KVStore::contains_key(self.get_store(), to_bytes(&key)?) } diff --git a/moveos/smt/src/lib.rs b/moveos/smt/src/lib.rs index a3cca5df34..18c9bbce5d 100644 --- a/moveos/smt/src/lib.rs +++ b/moveos/smt/src/lib.rs @@ -177,7 +177,7 @@ where self.node_store.write_nodes(node_map)?; let new_state_root: H256 = new_state_root.into(); - //TODO handle change_set's stale_node_index + //TODO handle change_set's state_node_index *self.root_hash.write() = new_state_root; Ok(new_state_root)