Skip to content

Commit

Permalink
Add initialization for the genesis base asset contract (#2304)
Browse files Browse the repository at this point in the history
This match initializes storage slots for the genesis base asset
contract(because we forgot to add it).
In the next release we will remove it form our codebase=)

Side change:
- Moves `ChangesIterator` to the `fuel-core-storage` crate to allow
usage in the tests.

## Checklist
- [x] New behavior is reflected in tests

### Before requesting review
- [x] I have reviewed the code myself
  • Loading branch information
xgreenx authored Oct 6, 2024
1 parent d4ffb2e commit 8fa66f2
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 108 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions crates/fuel-core/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -401,7 +401,7 @@ fn commit_changes_with_height_update<Description>(
database: &mut Database<Description>,
changes: Changes,
heights_lookup: impl Fn(
&ChangesIterator<Description>,
&ChangesIterator<Description::Column>,
) -> StorageResult<Vec<Description::Height>>,
) -> StorageResult<()>
where
Expand All @@ -411,7 +411,7 @@ where
StorageMutate<MetadataTable<Description>, Error = StorageError>,
{
// Gets the all new heights from the `changes`
let iterator = ChangesIterator::<Description>::new(&changes);
let iterator = ChangesIterator::<Description::Column>::new(&changes);
let new_heights = heights_lookup(&iterator)?;

// Changes for each block should be committed separately.
Expand Down
21 changes: 11 additions & 10 deletions crates/fuel-core/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -3255,7 +3256,7 @@ mod tests {
let (result, changes) = producer.produce_without_commit(block.into())?.into();

// Then
let view = ChangesIterator::<OnChain>::new(&changes);
let view = ChangesIterator::<Column>::new(&changes);
assert_eq!(
view.iter_all::<Messages>(None).count() as u64,
block_da_height - genesis_da_height
Expand Down Expand Up @@ -3864,7 +3865,7 @@ mod tests {
.into();

// Then
let view = ChangesIterator::<OnChain>::new(&changes);
let view = ChangesIterator::<Column>::new(&changes);
assert!(result.skipped_transactions.is_empty());
assert_eq!(view.iter_all::<Messages>(None).count() as u64, 0);
}
Expand Down Expand Up @@ -3906,7 +3907,7 @@ mod tests {
.into();

// Then
let view = ChangesIterator::<OnChain>::new(&changes);
let view = ChangesIterator::<Column>::new(&changes);
assert!(result.skipped_transactions.is_empty());
assert_eq!(view.iter_all::<Messages>(None).count() as u64, 0);
assert_eq!(result.events.len(), 2);
Expand Down
95 changes: 1 addition & 94 deletions crates/fuel-core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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<Description>,
}

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<Option<Value>> {
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<KVItem> {
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<fuel_core_storage::kv_store::KeyItem> {
// 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()
}
}
}
3 changes: 3 additions & 0 deletions crates/services/executor/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
133 changes: 133 additions & 0 deletions crates/services/executor/src/hacks.rs
Original file line number Diff line number Diff line change
@@ -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<S>(
inputs: &[Input],
storage: &mut StorageTransaction<S>,
) -> ExecutorResult<()>
where
S: KeyValueInspect<Column = Column>,
{
for input in inputs {
if let Input::Contract(contract) = input {
maybe_fix_contract(&contract.contract_id, storage)?;
}
}
Ok(())
}

pub(crate) fn maybe_fix_contract<S>(
contract_id: &ContractId,
storage: &mut StorageTransaction<S>,
) -> ExecutorResult<()>
where
S: KeyValueInspect<Column = Column>,
{
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::<ContractsState>()
.contains_key(&key)?;

if contains {
continue;
}

let value: Bytes32 = slot.value.parse().unwrap();

storage
.storage_as_mut::<ContractsState>()
.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::<ContractsState>(None)
.map(|r| r.unwrap())
.collect::<Vec<_>>();
assert_eq!(entries.len(), STATE.len());
}
}
1 change: 1 addition & 0 deletions crates/services/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
extern crate alloc;

pub mod executor;
mod hacks;
pub mod ports;
pub mod refs;

Expand Down
2 changes: 2 additions & 0 deletions crates/storage/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Loading

0 comments on commit 8fa66f2

Please sign in to comment.