diff --git a/CHANGELOG.md b/CHANGELOG.md index 2362c8085b5..7be9e546dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - [1983](https://github.com/FuelLabs/fuel-core/pull/1983): Add adapters for gas price service for accessing database values -### Breaking +#### Breaking +- [2048](https://github.com/FuelLabs/fuel-core/pull/2048): Disable SMT for `ContractsAssets` and `ContractsState` for the production mode of the `fuel-core`. The SMT still is used in benchmarks and tests. - [#1988](https://github.com/FuelLabs/fuel-core/pull/1988): Updated `fuel-vm` to `0.56.0` ([release notes](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.55.0)). Adds Blob transaction support. - [2025](https://github.com/FuelLabs/fuel-core/pull/2025): Add new V0 algorithm for gas price to services. This change includes new flags for the CLI: @@ -18,10 +19,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - "gas-price-threshold-percent" - the threshold percent for determining if the gas price will be increase or decreased And the following CLI flags are serving a new purpose - "min-gas-price" - the minimum gas price that the gas price algorithm will return - -### Fixed - -#### Breaking - [2045](https://github.com/FuelLabs/fuel-core/pull/2045): Include withdrawal message only if transaction is executed successfully. - [2041](https://github.com/FuelLabs/fuel-core/pull/2041): Add code for startup of the gas price algorithm updater so the gas price db on startup is always in sync with the on chain db diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 65f7958ea37..9f768bd1d70 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -18,6 +18,7 @@ ctrlc = "3.2.3" ed25519-dalek = { version = "2.0", features = ["rand_core"] } ethnum = "1.3" fuel-core = { path = "../crates/fuel-core", default-features = false, features = [ + "smt", "rocksdb-production", ] } fuel-core-chain-config = { workspace = true } diff --git a/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm b/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm index 00fac70f7f7..23b272e83dd 100755 Binary files a/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm and b/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm differ diff --git a/crates/fuel-core/Cargo.toml b/crates/fuel-core/Cargo.toml index de9512eea7f..0997bca0178 100644 --- a/crates/fuel-core/Cargo.toml +++ b/crates/fuel-core/Cargo.toml @@ -66,7 +66,7 @@ uuid = { version = "1.1", features = ["v4"] } [dev-dependencies] assert_matches = "1.5" -fuel-core = { path = ".", features = ["test-helpers"] } +fuel-core = { path = ".", features = ["smt", "test-helpers"] } fuel-core-executor = { workspace = true, features = ["std", "test-helpers"] } fuel-core-services = { path = "./../services", features = ["test-helpers"] } fuel-core-storage = { path = "./../storage", features = ["test-helpers"] } @@ -82,6 +82,11 @@ test-strategy = { workspace = true } [features] default = ["rocksdb"] +smt = [ + "fuel-core-storage/smt", + "fuel-core-executor/smt", + "fuel-core-upgradable-executor/smt", +] p2p = ["dep:fuel-core-p2p", "dep:fuel-core-sync"] relayer = ["dep:fuel-core-relayer"] rocksdb = ["dep:rocksdb", "dep:tempfile", "dep:num_cpus"] diff --git a/crates/services/executor/Cargo.toml b/crates/services/executor/Cargo.toml index 4655e50ad61..7d7e23259bf 100644 --- a/crates/services/executor/Cargo.toml +++ b/crates/services/executor/Cargo.toml @@ -26,6 +26,7 @@ fuel-core-types = { workspace = true, features = ["test-helpers"] } [features] default = ["std"] std = ["fuel-core-types/default"] +smt = ["fuel-core-storage/smt"] test-helpers = [ "fuel-core-types/test-helpers", "fuel-core-storage/test-helpers", diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index 11589e97122..c550387472d 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -9,7 +9,6 @@ use crate::{ use fuel_core_storage::{ column::Column, kv_store::KeyValueInspect, - structured_storage::StructuredStorage, tables::{ Coins, ConsensusParametersVersions, @@ -1769,8 +1768,7 @@ where ref contract_id, .. }) => { - let contract = - ContractRef::new(StructuredStorage::new(db), *contract_id); + let contract = ContractRef::new(db, *contract_id); let utxo_info = contract.validated_utxo(self.options.extra_tx_checks)?; *utxo_id = *utxo_info.utxo_id(); @@ -1814,7 +1812,7 @@ where }) }; - let contract = ContractRef::new(StructuredStorage::new(db), *contract_id); + let contract = ContractRef::new(db, *contract_id); contract_output.balance_root = contract.balance_root()?; contract_output.state_root = contract.state_root()?; } diff --git a/crates/services/executor/src/refs/contract.rs b/crates/services/executor/src/refs/contract.rs index bd11b94506b..e82b5083843 100644 --- a/crates/services/executor/src/refs/contract.rs +++ b/crates/services/executor/src/refs/contract.rs @@ -1,15 +1,10 @@ use core::fmt; use fuel_core_storage::{ not_found, - tables::{ - ContractsAssets, - ContractsLatestUtxo, - ContractsState, - }, + tables::ContractsLatestUtxo, Error as StorageError, Mappable, MerkleRoot, - MerkleRootStorage, StorageAsRef, StorageInspect, }; @@ -26,6 +21,12 @@ use fuel_core_types::{ }; use std::borrow::Cow; +#[cfg(feature = "smt")] +pub use smt::*; + +#[cfg(not(feature = "smt"))] +pub use not_smt::*; + /// The wrapper around `contract_id` to simplify work with `Contract` in the database. pub struct ContractRef { database: Database, @@ -87,49 +88,98 @@ where } } -impl ContractRef -where - Database: MerkleRootStorage, -{ - pub fn balance_root( - &self, - ) -> Result>::Error> { - self.database.root(&self.contract_id).map(Into::into) +#[cfg(feature = "smt")] +mod smt { + use super::*; + use fuel_core_storage::{ + tables::{ + ContractsAssets, + ContractsState, + }, + MerkleRootStorage, + }; + + impl ContractRef + where + Database: ContractStorageTrait, + { + pub fn balance_root( + &self, + ) -> Result>::Error> + { + >::root( + &self.database, + &self.contract_id, + ) + .map(Into::into) + } + + pub fn state_root( + &self, + ) -> Result>::Error> + { + >::root( + &self.database, + &self.contract_id, + ) + .map(Into::into) + } } -} -impl ContractRef -where - Database: MerkleRootStorage, -{ - pub fn state_root( - &self, - ) -> Result>::Error> { - self.database.root(&self.contract_id).map(Into::into) + pub trait ContractStorageTrait: + StorageInspect + + MerkleRootStorage + + MerkleRootStorage + { + type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static; } -} -pub trait ContractStorageTrait: - StorageInspect - + MerkleRootStorage - + MerkleRootStorage -{ - type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static; + impl ContractStorageTrait for D + where + D: StorageInspect + + MerkleRootStorage + + MerkleRootStorage, + E: fmt::Debug + fmt::Display + Send + Sync + 'static, + { + type InnerError = E; + } } -impl ContractStorageTrait for D -where - D: StorageInspect - + MerkleRootStorage - + MerkleRootStorage, -{ - type InnerError = StorageError; +#[cfg(not(feature = "smt"))] +mod not_smt { + use super::*; + use fuel_core_storage::Error as StorageError; + + impl ContractRef { + pub fn balance_root(&self) -> Result { + Ok(Bytes32::zeroed()) + } + } + + impl ContractRef { + pub fn state_root(&self) -> Result { + Ok(Bytes32::zeroed()) + } + } + + pub trait ContractStorageTrait: + StorageInspect + { + type InnerError: fmt::Debug + fmt::Display + Send + Sync + 'static; + } + + impl ContractStorageTrait for D + where + D: StorageInspect, + E: fmt::Debug + fmt::Display + Send + Sync + 'static, + { + type InnerError = E; + } } impl<'a, Database> ContractRef<&'a Database> where - Database: ContractStorageTrait, - anyhow::Error: From, + &'a Database: ContractStorageTrait, { /// Returns the state root of the whole contract. pub fn root(&self) -> anyhow::Result { @@ -142,15 +192,9 @@ where .into_owned() .utxo_id(); - let state_root = self - .database - .storage::() - .root(&contract_id)?; + let state_root = self.state_root()?; - let balance_root = self - .database - .storage::() - .root(&contract_id)?; + let balance_root = self.balance_root()?; let contract_hash = *Hasher::default() // `ContractId` already is based on contract's code and salt so we don't need it. diff --git a/crates/services/upgradable-executor/Cargo.toml b/crates/services/upgradable-executor/Cargo.toml index 136b707999a..49444880c23 100644 --- a/crates/services/upgradable-executor/Cargo.toml +++ b/crates/services/upgradable-executor/Cargo.toml @@ -39,6 +39,11 @@ fuel-core-wasm-executor = { workspace = true, optional = true, default-features [features] default = ["std"] std = ["fuel-core-executor/std", "fuel-core-storage/std", "fuel-core-types/std"] +smt = [ + "fuel-core-storage/smt", + "fuel-core-executor/smt", + "fuel-core-wasm-executor?/smt", +] wasm-executor = [ "dep:anyhow", "dep:parking_lot", diff --git a/crates/services/upgradable-executor/build.rs b/crates/services/upgradable-executor/build.rs index 13cfd3f13f3..40e652fa53a 100644 --- a/crates/services/upgradable-executor/build.rs +++ b/crates/services/upgradable-executor/build.rs @@ -45,6 +45,9 @@ fn build_wasm() { bin_dir, ]; + #[cfg(feature = "smt")] + args.extend(["--features".to_owned(), "smt".to_owned()]); + let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").expect("The manifest directory is not set"); let manifest_path = Path::new(&manifest_dir); diff --git a/crates/services/upgradable-executor/wasm-executor/Cargo.toml b/crates/services/upgradable-executor/wasm-executor/Cargo.toml index 9af66134104..6fbbd6f71c9 100644 --- a/crates/services/upgradable-executor/wasm-executor/Cargo.toml +++ b/crates/services/upgradable-executor/wasm-executor/Cargo.toml @@ -30,3 +30,4 @@ proptest = { workspace = true } [features] default = ["std"] std = [] +smt = ["fuel-core-storage/smt", "fuel-core-executor/smt"] diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 655ab988248..078b139e461 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -48,4 +48,5 @@ test-case = { workspace = true } [features] default = ["std"] std = ["fuel-core-types/std"] +smt = [] test-helpers = ["dep:mockall", "dep:rand"] diff --git a/crates/storage/src/structured_storage/balances.rs b/crates/storage/src/structured_storage/balances.rs index 89be009b1a1..6bb7aa94332 100644 --- a/crates/storage/src/structured_storage/balances.rs +++ b/crates/storage/src/structured_storage/balances.rs @@ -1,91 +1,149 @@ //! The module contains implementations and tests for the `ContractsAssets` table. -use crate::{ - blueprint::sparse::{ - PrimaryKey, - Sparse, - }, - codec::{ - primitive::Primitive, - raw::Raw, - }, - column::Column, - structured_storage::TableWithBlueprint, - tables::{ - merkle::{ - ContractsAssetsMerkleData, - ContractsAssetsMerkleMetadata, +#[cfg(feature = "smt")] +mod smt { + use crate::{ + blueprint::sparse::{ + PrimaryKey, + Sparse, + }, + codec::{ + primitive::Primitive, + raw::Raw, }, - ContractsAssets, - }, - Mappable, -}; + column::Column, + structured_storage::TableWithBlueprint, + tables::{ + merkle::{ + ContractsAssetsMerkleData, + ContractsAssetsMerkleMetadata, + }, + ContractsAssets, + }, + Mappable, + }; -/// The key convertor used to convert the key from the `ContractsAssets` table -/// to the key of the `ContractsAssetsMerkleMetadata` table. -pub struct KeyConverter; + /// The key convertor used to convert the key from the `ContractsAssets` table + /// to the key of the `ContractsAssetsMerkleMetadata` table. + pub struct KeyConverter; -impl PrimaryKey for KeyConverter { - type InputKey = ::Key; - type OutputKey = ::Key; + impl PrimaryKey for KeyConverter { + type InputKey = ::Key; + type OutputKey = ::Key; - fn primary_key(key: &Self::InputKey) -> &Self::OutputKey { - key.contract_id() + fn primary_key(key: &Self::InputKey) -> &Self::OutputKey { + key.contract_id() + } } -} -impl TableWithBlueprint for ContractsAssets { - type Blueprint = Sparse< - Raw, - Primitive<8>, - ContractsAssetsMerkleMetadata, - ContractsAssetsMerkleData, - KeyConverter, - >; - type Column = Column; - - fn column() -> Column { - Column::ContractsAssets - } -} + impl TableWithBlueprint for ContractsAssets { + type Blueprint = Sparse< + Raw, + Primitive<8>, + ContractsAssetsMerkleMetadata, + ContractsAssetsMerkleData, + KeyConverter, + >; + type Column = Column; -#[cfg(test)] -mod test { - use super::*; - - fn generate_key( - primary_key: &::Key, - rng: &mut impl rand::Rng, - ) -> ::Key { - let mut bytes = [0u8; 32]; - rng.fill(bytes.as_mut()); - ::Key::new(primary_key, &bytes.into()) + fn column() -> Column { + Column::ContractsAssets + } } - fn generate_key_for_same_contract( - rng: &mut impl rand::Rng, - ) -> ::Key { - generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + #[cfg(test)] + mod test { + use super::*; + + fn generate_key( + primary_key: &::Key, + rng: &mut impl rand::Rng, + ) -> ::Key { + let mut bytes = [0u8; 32]; + rng.fill(bytes.as_mut()); + ::Key::new(primary_key, &bytes.into()) + } + + fn generate_key_for_same_contract( + rng: &mut impl rand::Rng, + ) -> ::Key { + generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + } + + crate::basic_storage_tests!( + ContractsAssets, + ::Key::default(), + ::Value::default(), + ::Value::default(), + generate_key_for_same_contract + ); + + fn generate_value( + rng: &mut impl rand::Rng, + ) -> ::Value { + rng.gen() + } + + crate::root_storage_tests!( + ContractsAssets, + ContractsAssetsMerkleMetadata, + ::Key::from([1u8; 32]), + ::Key::from([2u8; 32]), + generate_key, + generate_value + ); } +} - crate::basic_storage_tests!( - ContractsAssets, - ::Key::default(), - ::Value::default(), - ::Value::default(), - generate_key_for_same_contract - ); +#[cfg(not(feature = "smt"))] +mod plain { + use crate::{ + blueprint::plain::Plain, + codec::{ + primitive::Primitive, + raw::Raw, + }, + column::Column, + structured_storage::TableWithBlueprint, + tables::ContractsAssets, + }; - fn generate_value(rng: &mut impl rand::Rng) -> ::Value { - rng.gen() + impl TableWithBlueprint for ContractsAssets { + type Blueprint = Plain>; + type Column = Column; + + fn column() -> Column { + Column::ContractsAssets + } } - crate::root_storage_tests!( - ContractsAssets, - ContractsAssetsMerkleMetadata, - ::Key::from([1u8; 32]), - ::Key::from([2u8; 32]), - generate_key, - generate_value - ); + #[cfg(test)] + mod test { + use super::*; + use crate::Mappable; + use fuel_core_types::fuel_tx::ContractId; + + fn generate_key( + primary_key: &ContractId, + rng: &mut impl rand::Rng, + ) -> ::Key { + let mut bytes = [0u8; 32]; + rng.fill(bytes.as_mut()); + ::Key::new(primary_key, &bytes.into()) + } + + fn generate_key_for_same_contract( + rng: &mut impl rand::Rng, + ) -> ::Key { + generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + } + + crate::basic_storage_tests!( + ContractsAssets, + ::Key::default(), + ::Value::default(), + ::Value::default(), + generate_key_for_same_contract + ); + } } diff --git a/crates/storage/src/structured_storage/state.rs b/crates/storage/src/structured_storage/state.rs index 755e880311e..8f8e294aea3 100644 --- a/crates/storage/src/structured_storage/state.rs +++ b/crates/storage/src/structured_storage/state.rs @@ -1,198 +1,267 @@ //! The module contains implementations and tests for the `ContractsState` table. -use crate::{ - blueprint::sparse::{ - PrimaryKey, - Sparse, - }, - codec::raw::Raw, - column::Column, - structured_storage::TableWithBlueprint, - tables::{ - merkle::{ - ContractsStateMerkleData, - ContractsStateMerkleMetadata, +#[cfg(feature = "smt")] +mod smt { + use crate::{ + blueprint::sparse::{ + PrimaryKey, + Sparse, }, - ContractsState, - }, - Mappable, -}; + codec::raw::Raw, + column::Column, + structured_storage::TableWithBlueprint, + tables::{ + merkle::{ + ContractsStateMerkleData, + ContractsStateMerkleMetadata, + }, + ContractsState, + }, + Mappable, + }; -/// The key convertor used to convert the key from the `ContractsState` table -/// to the key of the `ContractsStateMerkleMetadata` table. -pub struct KeyConverter; + /// The key convertor used to convert the key from the `ContractsState` table + /// to the key of the `ContractsStateMerkleMetadata` table. + pub struct KeyConverter; -impl PrimaryKey for KeyConverter { - type InputKey = ::Key; - type OutputKey = ::Key; + impl PrimaryKey for KeyConverter { + type InputKey = ::Key; + type OutputKey = ::Key; - fn primary_key(key: &Self::InputKey) -> &Self::OutputKey { - key.contract_id() - } -} - -impl TableWithBlueprint for ContractsState { - type Blueprint = Sparse< - Raw, - Raw, - ContractsStateMerkleMetadata, - ContractsStateMerkleData, - KeyConverter, - >; - type Column = Column; - - fn column() -> Column { - Column::ContractsState + fn primary_key(key: &Self::InputKey) -> &Self::OutputKey { + key.contract_id() + } } -} -#[cfg(test)] -mod test { - use super::*; - - fn generate_key( - primary_key: &::Key, - rng: &mut impl rand::Rng, - ) -> ::Key { - let mut bytes = [0u8; 32]; - rng.fill(bytes.as_mut()); - ::Key::new(primary_key, &bytes.into()) - } + impl TableWithBlueprint for ContractsState { + type Blueprint = Sparse< + Raw, + Raw, + ContractsStateMerkleMetadata, + ContractsStateMerkleData, + KeyConverter, + >; + type Column = Column; - fn generate_key_for_same_contract( - rng: &mut impl rand::Rng, - ) -> ::Key { - generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + fn column() -> Column { + Column::ContractsState + } } - crate::basic_storage_tests!( - ContractsState, - ::Key::default(), - [0u8; 32], - vec![0u8; 32].into(), - generate_key_for_same_contract - ); - - fn generate_value(rng: &mut impl rand::Rng) -> Vec { - let mut bytes = [0u8; 32]; - rng.fill(bytes.as_mut()); - bytes.to_vec() - } + #[cfg(test)] + mod test { + use super::*; + + fn generate_key( + primary_key: &::Key, + rng: &mut impl rand::Rng, + ) -> ::Key { + let mut bytes = [0u8; 32]; + rng.fill(bytes.as_mut()); + ::Key::new(primary_key, &bytes.into()) + } - crate::root_storage_tests!( - ContractsState, - ContractsStateMerkleMetadata, - ::Key::from([1u8; 32]), - ::Key::from([2u8; 32]), - generate_key, - generate_value - ); -} + fn generate_key_for_same_contract( + rng: &mut impl rand::Rng, + ) -> ::Key { + generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + } -#[cfg(test)] -#[allow(non_snake_case)] -mod structured_storage_tests { - use crate::{ - column::Column, - structured_storage::test::InMemoryStorage, - transactional::ReadTransaction, - StorageAsMut, - StorageMutate, - StorageWrite, - }; - use fuel_vm_private::{ - prelude::{ - Bytes32, - ContractId, - }, - storage::{ + crate::basic_storage_tests!( ContractsState, - ContractsStateKey, - }, - }; - use rand::{ - prelude::StdRng, - Rng, - SeedableRng, - }; - - #[test] - fn storage_write__write__generates_the_same_merkle_root_as_storage_insert() { - type Storage = InMemoryStorage; + ::Key::default(), + [0u8; 32], + vec![0u8; 32].into(), + generate_key_for_same_contract + ); + + fn generate_value(rng: &mut impl rand::Rng) -> Vec { + let mut bytes = [0u8; 32]; + rng.fill(bytes.as_mut()); + bytes.to_vec() + } - let mut rng = StdRng::seed_from_u64(1234); + crate::root_storage_tests!( + ContractsState, + ContractsStateMerkleMetadata, + ::Key::from([1u8; 32]), + ::Key::from([2u8; 32]), + generate_key, + generate_value + ); + } - // Given - let contract_id = ContractId::default(); - let keys = std::iter::from_fn(|| Some(rng.gen::())) - .take(10) - .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) - .collect::>(); - let value = vec![0u8; 32]; + #[cfg(test)] + #[allow(non_snake_case)] + mod structured_storage_tests { + use crate::{ + column::Column, + structured_storage::test::InMemoryStorage, + transactional::ReadTransaction, + StorageAsMut, + StorageMutate, + StorageWrite, + }; + use fuel_vm_private::{ + prelude::{ + Bytes32, + ContractId, + }, + storage::{ + ContractsState, + ContractsStateKey, + }, + }; + use rand::{ + prelude::StdRng, + Rng, + SeedableRng, + }; - // When - let merkle_root_write = { - let storage = Storage::default(); - let mut structure = storage.read_transaction(); - let mut merkle_root = structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root"); - for key in keys.iter() { - <_ as StorageWrite>::write_bytes( - &mut structure, - key, - &value, - ) - .expect("Unable to write storage"); - let new_merkle_root = structure + #[test] + fn storage_write__write__generates_the_same_merkle_root_as_storage_insert() { + type Storage = InMemoryStorage; + + let mut rng = StdRng::seed_from_u64(1234); + + // Given + let contract_id = ContractId::default(); + let keys = std::iter::from_fn(|| Some(rng.gen::())) + .take(10) + .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) + .collect::>(); + let value = vec![0u8; 32]; + + // When + let merkle_root_write = { + let storage = Storage::default(); + let mut structure = storage.read_transaction(); + let mut merkle_root = structure .storage::() .root(&contract_id) .expect("Unable to retrieve Merkle root"); - assert_ne!(merkle_root, new_merkle_root); - merkle_root = new_merkle_root; - } + for key in keys.iter() { + <_ as StorageWrite>::write_bytes( + &mut structure, + key, + &value, + ) + .expect("Unable to write storage"); + let new_merkle_root = structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root"); + assert_ne!(merkle_root, new_merkle_root); + merkle_root = new_merkle_root; + } + + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; + + // Then + let merkle_root_insert = { + let storage = Storage::default(); + let mut structure = storage.read_transaction(); + for key in keys.iter() { + <_ as StorageMutate>::insert( + &mut structure, + key, + &value, + ) + .expect("Unable to write storage"); + } - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; - // Then - let merkle_root_insert = { - let storage = Storage::default(); - let mut structure = storage.read_transaction(); - for key in keys.iter() { - <_ as StorageMutate>::insert(&mut structure, key, &value) + assert_eq!(merkle_root_write, merkle_root_insert); + } + + #[test] + fn storage_write__replace__generates_the_same_merkle_root_as_storage_insert() { + type Storage = InMemoryStorage; + + let mut rng = StdRng::seed_from_u64(1234); + + // Given + let contract_id = ContractId::default(); + let keys = std::iter::from_fn(|| Some(rng.gen::())) + .take(10) + .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) + .collect::>(); + let value = vec![0u8; 32]; + + // When + let merkle_root_replace = { + let storage = Storage::default(); + let mut structure = storage.read_transaction(); + let mut merkle_root = structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root"); + for key in keys.iter() { + <_ as StorageWrite>::replace_bytes( + &mut structure, + key, + &value, + ) .expect("Unable to write storage"); - } + let new_merkle_root = structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root"); + assert_ne!(merkle_root, new_merkle_root); + merkle_root = new_merkle_root; + } + + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; + + // Then + let merkle_root_insert = { + let storage = Storage::default(); + let mut structure = storage.read_transaction(); + for key in keys.iter() { + <_ as StorageMutate>::insert( + &mut structure, + key, + &value, + ) + .expect("Unable to write storage"); + } - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; - assert_eq!(merkle_root_write, merkle_root_insert); - } + assert_eq!(merkle_root_replace, merkle_root_insert); + } - #[test] - fn storage_write__replace__generates_the_same_merkle_root_as_storage_insert() { - type Storage = InMemoryStorage; + #[test] + fn storage_write__take__generates_the_same_merkle_root_as_storage_remove() { + type Storage = InMemoryStorage; - let mut rng = StdRng::seed_from_u64(1234); + let mut rng = StdRng::seed_from_u64(1234); - // Given - let contract_id = ContractId::default(); - let keys = std::iter::from_fn(|| Some(rng.gen::())) - .take(10) - .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) - .collect::>(); - let value = vec![0u8; 32]; + // Given + let contract_id = ContractId::default(); + let keys = std::iter::from_fn(|| Some(rng.gen::())) + .take(10) + .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) + .collect::>(); + let value = vec![0u8; 32]; - // When - let merkle_root_replace = { let storage = Storage::default(); let mut structure = storage.read_transaction(); let mut merkle_root = structure @@ -206,6 +275,7 @@ mod structured_storage_tests { &value, ) .expect("Unable to write storage"); + let new_merkle_root = structure .storage::() .root(&contract_id) @@ -214,107 +284,98 @@ mod structured_storage_tests { merkle_root = new_merkle_root; } - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + // When + let state_key = rng.gen::(); + let key = ContractsStateKey::from((&contract_id, &state_key)); - // Then - let merkle_root_insert = { - let storage = Storage::default(); - let mut structure = storage.read_transaction(); - for key in keys.iter() { - <_ as StorageMutate>::insert(&mut structure, key, &value) - .expect("Unable to write storage"); - } + let merkle_root_replace = { + <_ as StorageWrite>::write_bytes( + &mut structure, + &key, + &value, + ) + .expect("Unable to write storage"); - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + <_ as StorageWrite>::take_bytes(&mut structure, &key) + .expect("Unable to take value from storage"); - assert_eq!(merkle_root_replace, merkle_root_insert); - } + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; - #[test] - fn storage_write__take__generates_the_same_merkle_root_as_storage_remove() { - type Storage = InMemoryStorage; - - let mut rng = StdRng::seed_from_u64(1234); - - // Given - let contract_id = ContractId::default(); - let keys = std::iter::from_fn(|| Some(rng.gen::())) - .take(10) - .map(|state_key| ContractsStateKey::from((&contract_id, &state_key))) - .collect::>(); - let value = vec![0u8; 32]; - - let storage = Storage::default(); - let mut structure = storage.read_transaction(); - let mut merkle_root = structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root"); - for key in keys.iter() { - <_ as StorageWrite>::replace_bytes( - &mut structure, - key, - &value, - ) - .expect("Unable to write storage"); - - let new_merkle_root = structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root"); - assert_ne!(merkle_root, new_merkle_root); - merkle_root = new_merkle_root; - } + // Then + let merkle_root_remove = { + <_ as StorageWrite>::write_bytes( + &mut structure, + &key, + &value, + ) + .expect("Unable to write storage"); + + structure + .storage::() + .remove(&key) + .expect("Unable to take value from storage"); - // When - let state_key = rng.gen::(); - let key = ContractsStateKey::from((&contract_id, &state_key)); + structure + .storage::() + .root(&contract_id) + .expect("Unable to retrieve Merkle root") + }; - let merkle_root_replace = { - <_ as StorageWrite>::write_bytes( - &mut structure, - &key, - &value, - ) - .expect("Unable to write storage"); + assert_eq!(merkle_root_replace, merkle_root_remove); + } + } +} - <_ as StorageWrite>::take_bytes(&mut structure, &key) - .expect("Unable to take value from storage"); +#[cfg(not(feature = "smt"))] +mod plain { + use crate::{ + blueprint::plain::Plain, + codec::raw::Raw, + column::Column, + structured_storage::TableWithBlueprint, + tables::ContractsState, + }; - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + impl TableWithBlueprint for ContractsState { + type Blueprint = Plain; + type Column = Column; - // Then - let merkle_root_remove = { - <_ as StorageWrite>::write_bytes( - &mut structure, - &key, - &value, - ) - .expect("Unable to write storage"); + fn column() -> Column { + Column::ContractsState + } + } - structure - .storage::() - .remove(&key) - .expect("Unable to take value from storage"); + #[cfg(test)] + mod test { + use super::*; + use crate::Mappable; + use fuel_core_types::fuel_tx::ContractId; + + fn generate_key( + primary_key: &ContractId, + rng: &mut impl rand::Rng, + ) -> ::Key { + let mut bytes = [0u8; 32]; + rng.fill(bytes.as_mut()); + ::Key::new(primary_key, &bytes.into()) + } - structure - .storage::() - .root(&contract_id) - .expect("Unable to retrieve Merkle root") - }; + fn generate_key_for_same_contract( + rng: &mut impl rand::Rng, + ) -> ::Key { + generate_key(&fuel_core_types::fuel_tx::ContractId::zeroed(), rng) + } - assert_eq!(merkle_root_replace, merkle_root_remove); + crate::basic_storage_tests!( + ContractsState, + ::Key::default(), + [0u8; 32], + vec![0u8; 32].into(), + generate_key_for_same_contract + ); } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index b752e9e2539..8109de76f5c 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -23,6 +23,8 @@ clap = { workspace = true } cynic = { workspace = true } ethers = "2" fuel-core = { path = "../crates/fuel-core", default-features = false, features = [ + "smt", + "wasm-executor", "test-helpers", ] } fuel-core-benches = { path = "../benches" } @@ -42,9 +44,7 @@ fuel-core-txpool = { path = "../crates/services/txpool", features = [ "test-helpers", ] } fuel-core-types = { path = "../crates/types", features = ["test-helpers"] } -fuel-core-upgradable-executor = { path = "../crates/services/upgradable-executor", features = [ - "wasm-executor", -] } +fuel-core-upgradable-executor = { path = "../crates/services/upgradable-executor" } futures = "0.3" hyper = { workspace = true, features = ["server"] } insta = { workspace = true }