diff --git a/CHANGELOG.md b/CHANGELOG.md index 820b1db0264..a4f2a885e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Fixed +- [2304](https://github.com/FuelLabs/fuel-core/pull/2304): Add initialization for the genesis base asset contract. + ## [Version 0.37.0] ### Added diff --git a/crates/fuel-core/src/database.rs b/crates/fuel-core/src/database.rs index 851ebd08b2a..ca3607275b6 100644 --- a/crates/fuel-core/src/database.rs +++ b/crates/fuel-core/src/database.rs @@ -19,17 +19,18 @@ use crate::{ }, generic_database::GenericDatabase, in_memory::memory_store::MemoryStore, - ChangesIterator, ColumnType, IterableKeyValueView, KeyValueView, }, }; use fuel_core_chain_config::TableEntry; +use fuel_core_gas_price_service::common::fuel_core_storage_adapter::storage::GasPriceMetadata; use fuel_core_services::SharedMutex; use fuel_core_storage::{ self, iter::{ + changes_iterator::ChangesIterator, IterDirection, IterableTable, IteratorOverTable, @@ -75,7 +76,6 @@ use crate::state::{ }, rocks_db::RocksDb, }; -use fuel_core_gas_price_service::common::fuel_core_storage_adapter::storage::GasPriceMetadata; #[cfg(feature = "rocksdb")] use std::path::Path; @@ -401,7 +401,7 @@ fn commit_changes_with_height_update( database: &mut Database, changes: Changes, heights_lookup: impl Fn( - &ChangesIterator, + &ChangesIterator, ) -> StorageResult>, ) -> StorageResult<()> where @@ -411,7 +411,7 @@ where StorageMutate, Error = StorageError>, { // Gets the all new heights from the `changes` - let iterator = ChangesIterator::::new(&changes); + let iterator = ChangesIterator::::new(&changes); let new_heights = heights_lookup(&iterator)?; // Changes for each block should be committed separately. diff --git a/crates/fuel-core/src/executor.rs b/crates/fuel-core/src/executor.rs index 86e899452c7..34947e425a7 100644 --- a/crates/fuel-core/src/executor.rs +++ b/crates/fuel-core/src/executor.rs @@ -3101,16 +3101,17 @@ mod tests { #[cfg(feature = "relayer")] mod relayer { use super::*; - use crate::{ - database::database_description::{ - on_chain::OnChain, - relayer::Relayer, - }, - state::ChangesIterator, + use crate::database::database_description::{ + on_chain::OnChain, + relayer::Relayer, }; use fuel_core_relayer::storage::EventsHistory; use fuel_core_storage::{ - iter::IteratorOverTable, + column::Column, + iter::{ + changes_iterator::ChangesIterator, + IteratorOverTable, + }, tables::FuelBlocks, StorageAsMut, }; @@ -3255,7 +3256,7 @@ mod tests { let (result, changes) = producer.produce_without_commit(block.into())?.into(); // Then - let view = ChangesIterator::::new(&changes); + let view = ChangesIterator::::new(&changes); assert_eq!( view.iter_all::(None).count() as u64, block_da_height - genesis_da_height @@ -3864,7 +3865,7 @@ mod tests { .into(); // Then - let view = ChangesIterator::::new(&changes); + let view = ChangesIterator::::new(&changes); assert!(result.skipped_transactions.is_empty()); assert_eq!(view.iter_all::(None).count() as u64, 0); } @@ -3906,7 +3907,7 @@ mod tests { .into(); // Then - let view = ChangesIterator::::new(&changes); + let view = ChangesIterator::::new(&changes); assert!(result.skipped_transactions.is_empty()); assert_eq!(view.iter_all::(None).count() as u64, 0); assert_eq!(result.events.len(), 2); diff --git a/crates/fuel-core/src/state.rs b/crates/fuel-core/src/state.rs index 5f1626e827d..06ee176c652 100644 --- a/crates/fuel-core/src/state.rs +++ b/crates/fuel-core/src/state.rs @@ -8,18 +8,10 @@ use crate::{ }; use fuel_core_storage::{ iter::{ - BoxedIter, - IntoBoxedIter, IterDirection, IterableStore, }, - kv_store::{ - KVItem, - KeyValueInspect, - StorageColumn, - Value, - WriteOperation, - }, + kv_store::StorageColumn, transactional::Changes, Result as StorageResult, }; @@ -99,88 +91,3 @@ where unimplemented!() } } - -/// A type that allows to iterate over the `Changes`. -pub struct ChangesIterator<'a, Description> { - changes: &'a Changes, - _marker: core::marker::PhantomData, -} - -impl<'a, Description> ChangesIterator<'a, Description> { - /// Creates a new instance of the `ChangesIterator`. - pub fn new(changes: &'a Changes) -> Self { - Self { - changes, - _marker: Default::default(), - } - } -} - -impl<'a, Description> KeyValueInspect for ChangesIterator<'a, Description> -where - Description: DatabaseDescription, -{ - type Column = Description::Column; - - fn get(&self, key: &[u8], column: Self::Column) -> StorageResult> { - Ok(self - .changes - .get(&column.id()) - .and_then(|tree| tree.get(key)) - .and_then(|operation| match operation { - WriteOperation::Insert(value) => Some(value.clone()), - WriteOperation::Remove => None, - })) - } -} - -impl<'a, Description> IterableStore for ChangesIterator<'a, Description> -where - Description: DatabaseDescription, -{ - fn iter_store( - &self, - column: Self::Column, - prefix: Option<&[u8]>, - start: Option<&[u8]>, - direction: IterDirection, - ) -> BoxedIter { - if let Some(tree) = self.changes.get(&column.id()) { - fuel_core_storage::iter::iterator(tree, prefix, start, direction) - .filter_map(|(key, value)| match value { - WriteOperation::Insert(value) => { - Some((key.clone().into(), value.clone())) - } - WriteOperation::Remove => None, - }) - .map(Ok) - .into_boxed() - } else { - core::iter::empty().into_boxed() - } - } - - fn iter_store_keys( - &self, - column: Self::Column, - prefix: Option<&[u8]>, - start: Option<&[u8]>, - direction: IterDirection, - ) -> BoxedIter { - // We cannot define iter_store_keys appropriately for the `ChangesIterator`, - // because we have to filter out the keys that were removed, which are - // marked as `WriteOperation::Remove` in the value - // copied as-is from the above function, but only to return keys - if let Some(tree) = self.changes.get(&column.id()) { - fuel_core_storage::iter::iterator(tree, prefix, start, direction) - .filter_map(|(key, value)| match value { - WriteOperation::Insert(_) => Some(key.clone().into()), - WriteOperation::Remove => None, - }) - .map(Ok) - .into_boxed() - } else { - core::iter::empty().into_boxed() - } - } -} diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index c8d6e3b7363..89eaacea35e 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -1593,6 +1593,9 @@ where .read_transaction() .with_policy(ConflictPolicy::Overwrite); + let inputs = checked_tx.transaction().inputs(); + crate::hacks::maybe_fix_storage(inputs, &mut sub_block_db_commit)?; + let vm_db = VmStorage::new( &mut sub_block_db_commit, &header.consensus, diff --git a/crates/services/executor/src/hacks.rs b/crates/services/executor/src/hacks.rs new file mode 100644 index 00000000000..a3fb52827e8 --- /dev/null +++ b/crates/services/executor/src/hacks.rs @@ -0,0 +1,133 @@ +use fuel_core_storage::{ + column::Column, + kv_store::KeyValueInspect, + tables::ContractsState, + transactional::StorageTransaction, + ContractsStateKey, + StorageAsMut, +}; +use fuel_core_types::{ + fuel_tx::Input, + fuel_types::{ + Bytes32, + ContractId, + }, + services::executor::Result as ExecutorResult, +}; + +struct Slot { + key: &'static str, + value: &'static str, +} + +const CONTRACT_ID_WITH_BAD_STATE: &str = + "0x7e2becd64cd598da59b4d1064b711661898656c6b1f4918a787156b8965dc83c"; + +const STATE: [Slot; 4] = [ + Slot { + key: "35fa5b7532d53cf687e13e3db014eaf208c5b8c534ab693dd7090d5e02675f3e", + value: "0000000000000000000000000000000000000000000000000000000000000000", + }, + Slot { + key: "35fa5b7532d53cf687e13e3db014eaf208c5b8c534ab693dd7090d5e02675f3f", + value: "0000000000000000000000000000000000000000000000000000000000000000", + }, + Slot { + key: "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd55", + value: "0000000000000000000000000000000000000000000000000000000000000000", + }, + Slot { + key: "7bb458adc1d118713319a5baa00a2d049dd64d2916477d2688d76970c898cd56", + value: "0000000000000000000000000000000000000000000000000000000000000000", + }, +]; + +pub fn maybe_fix_storage( + inputs: &[Input], + storage: &mut StorageTransaction, +) -> ExecutorResult<()> +where + S: KeyValueInspect, +{ + for input in inputs { + if let Input::Contract(contract) = input { + maybe_fix_contract(&contract.contract_id, storage)?; + } + } + Ok(()) +} + +pub(crate) fn maybe_fix_contract( + contract_id: &ContractId, + storage: &mut StorageTransaction, +) -> ExecutorResult<()> +where + S: KeyValueInspect, +{ + let bad_contract_id: ContractId = CONTRACT_ID_WITH_BAD_STATE.parse().unwrap(); + + if contract_id == &bad_contract_id { + for slot in STATE.iter() { + let storage_key: Bytes32 = slot.key.parse().unwrap(); + + let key = ContractsStateKey::new(contract_id, &storage_key); + let contains = storage + .storage_as_mut::() + .contains_key(&key)?; + + if contains { + continue; + } + + let value: Bytes32 = slot.value.parse().unwrap(); + + storage + .storage_as_mut::() + .insert(&key, value.as_ref())?; + } + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + use fuel_core_storage::{ + iter::{ + changes_iterator::ChangesIterator, + IteratorOverTable, + }, + structured_storage::test::InMemoryStorage, + transactional::IntoTransaction, + }; + + #[test] + fn dummy() { + // Given + let contract_id: ContractId = + "0x7e2becd64cd598da59b4d1064b711661898656c6b1f4918a787156b8965dc83c" + .parse() + .unwrap(); + let input = Input::contract( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + contract_id, + ); + let mut storage = InMemoryStorage::default().into_transaction(); + + // When + maybe_fix_storage(&[input], &mut storage).unwrap(); + + // Then + let changes = storage.into_changes(); + let view = ChangesIterator::new(&changes); + let entries = view + .iter_all::(None) + .map(|r| r.unwrap()) + .collect::>(); + assert_eq!(entries.len(), STATE.len()); + } +} diff --git a/crates/services/executor/src/lib.rs b/crates/services/executor/src/lib.rs index d28e5dbb612..74ee7293d0f 100644 --- a/crates/services/executor/src/lib.rs +++ b/crates/services/executor/src/lib.rs @@ -8,6 +8,7 @@ extern crate alloc; pub mod executor; +mod hacks; pub mod ports; pub mod refs; diff --git a/crates/storage/src/iter.rs b/crates/storage/src/iter.rs index a00232876df..37c38463cc8 100644 --- a/crates/storage/src/iter.rs +++ b/crates/storage/src/iter.rs @@ -24,6 +24,8 @@ use alloc::{ vec::Vec, }; +pub mod changes_iterator; + // TODO: BoxedIter to be used until RPITIT lands in stable rust. /// A boxed variant of the iterator that can be used as a return type of the traits. pub struct BoxedIter<'a, T> { diff --git a/crates/storage/src/iter/changes_iterator.rs b/crates/storage/src/iter/changes_iterator.rs new file mode 100644 index 00000000000..9a3e566bdf3 --- /dev/null +++ b/crates/storage/src/iter/changes_iterator.rs @@ -0,0 +1,103 @@ +//! A type that allows to iterate over the `Changes`. + +use crate::{ + iter::{ + BoxedIter, + IntoBoxedIter, + IterDirection, + IterableStore, + }, + kv_store::{ + KVItem, + KeyValueInspect, + StorageColumn, + Value, + WriteOperation, + }, + transactional::Changes, +}; + +/// A type that allows to iterate over the `Changes`. +pub struct ChangesIterator<'a, Column> { + changes: &'a Changes, + _marker: core::marker::PhantomData, +} + +impl<'a, Description> ChangesIterator<'a, Description> { + /// Creates a new instance of the `ChangesIterator`. + pub fn new(changes: &'a Changes) -> Self { + Self { + changes, + _marker: Default::default(), + } + } +} + +impl<'a, Column> KeyValueInspect for ChangesIterator<'a, Column> +where + Column: StorageColumn, +{ + type Column = Column; + + fn get(&self, key: &[u8], column: Self::Column) -> crate::Result> { + Ok(self + .changes + .get(&column.id()) + .and_then(|tree| tree.get(key)) + .and_then(|operation| match operation { + WriteOperation::Insert(value) => Some(value.clone()), + WriteOperation::Remove => None, + })) + } +} + +impl<'a, Column> IterableStore for ChangesIterator<'a, Column> +where + Column: StorageColumn, +{ + fn iter_store( + &self, + column: Self::Column, + prefix: Option<&[u8]>, + start: Option<&[u8]>, + direction: IterDirection, + ) -> BoxedIter { + if let Some(tree) = self.changes.get(&column.id()) { + crate::iter::iterator(tree, prefix, start, direction) + .filter_map(|(key, value)| match value { + WriteOperation::Insert(value) => { + Some((key.clone().into(), value.clone())) + } + WriteOperation::Remove => None, + }) + .map(Ok) + .into_boxed() + } else { + core::iter::empty().into_boxed() + } + } + + fn iter_store_keys( + &self, + column: Self::Column, + prefix: Option<&[u8]>, + start: Option<&[u8]>, + direction: IterDirection, + ) -> BoxedIter { + // We cannot define iter_store_keys appropriately for the `ChangesIterator`, + // because we have to filter out the keys that were removed, which are + // marked as `WriteOperation::Remove` in the value + // copied as-is from the above function, but only to return keys + if let Some(tree) = self.changes.get(&column.id()) { + crate::iter::iterator(tree, prefix, start, direction) + .filter_map(|(key, value)| match value { + WriteOperation::Insert(_) => Some(key.clone().into()), + WriteOperation::Remove => None, + }) + .map(Ok) + .into_boxed() + } else { + core::iter::empty().into_boxed() + } + } +}