Skip to content

Commit

Permalink
Add the possibility to specify an offset for the read value in Storag…
Browse files Browse the repository at this point in the history
…eRead::read (#863)

* WIP: Change Storage Read trait

* Modify StorageRead trait to specify offset as input

* Add test for that new StroageRead::read behaves as expected for the Memory implementation

* Add changelog

* Simplify test

* Change implementation to return Some(0) when contract is read at contract boundary

* Make StorageRead::read fallible for MemoryStorage

* StorageRef::read now takes an offset in input

* Better use statements

* Newline between versions in changelog
  • Loading branch information
acerone85 authored Nov 11, 2024
1 parent 13d23c1 commit 486d06f
Show file tree
Hide file tree
Showing 18 changed files with 270 additions and 105 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- [860](https://github.com/FuelLabs/fuel-vm/pull/860): Fixed missing fuzzing coverage report in CI.

### Breaking
- [863](https://github.com/FuelLabs/fuel-vm/pull/863): Changed StorageRead::read to load a serialized value starting from a offset. The function returns an optional value equal to the number of bytes read when defined, or none if the offset specified in input is outside the boundaries of the serialized value read.

## [Version 0.58.2]

### Fixed
Expand Down
9 changes: 6 additions & 3 deletions fuel-storage/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,10 @@ impl<'a, T: StorageRead<Type> + StorageSize<Type> + ?Sized, Type: Mappable>
fn read(
&self,
key: &<Type as Mappable>::Key,
offset: usize,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error> {
<T as StorageRead<Type>>::read(self, key, buf)
<T as StorageRead<Type>>::read(self, key, offset, buf)
}

fn read_alloc(
Expand All @@ -120,9 +121,10 @@ impl<'a, T: StorageRead<Type> + StorageSize<Type> + ?Sized, Type: Mappable>
fn read(
&self,
key: &<Type as Mappable>::Key,
offset: usize,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error> {
<T as StorageRead<Type>>::read(self, key, buf)
<T as StorageRead<Type>>::read(self, key, offset, buf)
}

fn read_alloc(
Expand Down Expand Up @@ -199,9 +201,10 @@ impl<'a, T: StorageRead<Type>, Type: Mappable> StorageRef<'a, T, Type> {
pub fn read(
&self,
key: &<Type as Mappable>::Key,
offset: usize,
buf: &mut [u8],
) -> Result<Option<usize>, T::Error> {
self.0.read(key, buf)
self.0.read(key, offset, buf)
}

#[inline(always)]
Expand Down
8 changes: 6 additions & 2 deletions fuel-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,12 @@ pub trait StorageRead<Type: Mappable>: StorageInspect<Type> + StorageSize<Type>
///
/// Returns None if the value does not exist.
/// Otherwise, returns the number of bytes read.
fn read(&self, key: &Type::Key, buf: &mut [u8])
-> Result<Option<usize>, Self::Error>;
fn read(
&self,
key: &Type::Key,
offset: usize,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error>;

/// Same as `read` but allocates a new buffer and returns it.
///
Expand Down
12 changes: 7 additions & 5 deletions fuel-vm/src/interpreter/blockchain/code_tests.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#![allow(clippy::cast_possible_truncation)]
use core::convert::Infallible;

use alloc::vec;

use super::*;
use crate::{
interpreter::PanicContext,
storage::MemoryStorage,
storage::{
MemoryStorage,
MemoryStorageError,
},
};
use fuel_tx::Contract;

#[test]
fn test_load_contract_in_script() -> IoResult<(), Infallible> {
fn test_load_contract_in_script() -> IoResult<(), MemoryStorageError> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
let mut pc = 4;
Expand Down Expand Up @@ -70,7 +72,7 @@ fn test_load_contract_in_script() -> IoResult<(), Infallible> {
Ok(())
}
#[test]
fn test_load_contract_in_call() -> IoResult<(), Infallible> {
fn test_load_contract_in_call() -> IoResult<(), MemoryStorageError> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
let mut pc = 4;
Expand Down Expand Up @@ -130,7 +132,7 @@ fn test_load_contract_in_call() -> IoResult<(), Infallible> {
}

#[test]
fn test_code_copy() -> IoResult<(), Infallible> {
fn test_code_copy() -> IoResult<(), MemoryStorageError> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
let mut cgas = 1000;
Expand Down
10 changes: 6 additions & 4 deletions fuel-vm/src/interpreter/blockchain/other_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

use alloc::vec;

use crate::storage::MemoryStorage;
use core::convert::Infallible;
use crate::storage::{
MemoryStorage,
MemoryStorageError,
};

use super::*;
use crate::interpreter::PanicContext;
Expand All @@ -25,7 +27,7 @@ fn test_burn(
initialize: impl Into<Option<Word>>,
amount: Word,
sub_id: [u8; 32],
) -> IoResult<(), Infallible> {
) -> IoResult<(), MemoryStorageError> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
let contract_id = ContractId::from([3u8; 32]);
Expand Down Expand Up @@ -103,7 +105,7 @@ fn test_mint(
initialize: impl Into<Option<Word>>,
amount: Word,
sub_id: [u8; 32],
) -> IoResult<(), Infallible> {
) -> IoResult<(), MemoryStorageError> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
let contract_id = ContractId::from([3u8; 32]);
Expand Down
9 changes: 5 additions & 4 deletions fuel-vm/src/interpreter/blockchain/smo_tests.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#![allow(clippy::arithmetic_side_effects, clippy::cast_possible_truncation)]

use core::convert::Infallible;

use alloc::{
vec,
vec::Vec,
};

use crate::{
interpreter::contract::balance as contract_balance,
storage::MemoryStorage,
storage::{
MemoryStorage,
MemoryStorageError,
},
};

use super::*;
Expand Down Expand Up @@ -206,7 +207,7 @@ fn test_smo(
max_message_data_length,
initial_balance,
}: Input,
) -> Result<Output, RuntimeError<Infallible>> {
) -> Result<Output, RuntimeError<MemoryStorageError>> {
let mut rng = StdRng::seed_from_u64(100);
let base_asset_id = rng.gen();

Expand Down
10 changes: 4 additions & 6 deletions fuel-vm/src/interpreter/blockchain/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ use alloc::{
vec::Vec,
};

use core::{
convert::Infallible,
ops::Range,
};
use core::ops::Range;

use crate::{
context::Context,
storage::{
ContractsStateData,
MemoryStorage,
MemoryStorageError,
},
};
use test_case::test_case;
Expand Down Expand Up @@ -64,7 +62,7 @@ fn test_state_read_word(
fp: Word,
insert: impl Into<Option<Word>>,
key: Word,
) -> Result<(Word, Word), RuntimeError<Infallible>> {
) -> Result<(Word, Word), RuntimeError<MemoryStorageError>> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]);
Expand Down Expand Up @@ -134,7 +132,7 @@ fn test_state_write_word(
fp: Word,
insert: bool,
key: Word,
) -> Result<Word, RuntimeError<Infallible>> {
) -> Result<Word, RuntimeError<MemoryStorageError>> {
let mut storage = MemoryStorage::default();
let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap();
memory[0..ContractId::LEN].copy_from_slice(&[3u8; ContractId::LEN][..]);
Expand Down
4 changes: 3 additions & 1 deletion fuel-vm/src/interpreter/blockchain/test/scwq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::storage::{
ContractsState,
ContractsStateData,
MemoryStorage,
MemoryStorageError,
};

use super::*;
Expand Down Expand Up @@ -95,7 +96,8 @@ struct SCWQInput {
)]
fn test_state_clear_qword(
input: SCWQInput,
) -> Result<(Vec<([u8; 32], ContractsStateData)>, bool), RuntimeError<Infallible>> {
) -> Result<(Vec<([u8; 32], ContractsStateData)>, bool), RuntimeError<MemoryStorageError>>
{
let SCWQInput {
input,
storage_slots,
Expand Down
3 changes: 2 additions & 1 deletion fuel-vm/src/interpreter/blockchain/test/srwq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::storage::{
ContractsState,
ContractsStateData,
MemoryStorage,
MemoryStorageError,
};

use super::*;
Expand Down Expand Up @@ -163,7 +164,7 @@ struct SRWQInput {
)]
fn test_state_read_qword(
input: SRWQInput,
) -> Result<(MemoryInstance, bool), RuntimeError<Infallible>> {
) -> Result<(MemoryInstance, bool), RuntimeError<MemoryStorageError>> {
let SRWQInput {
storage_slots,
context,
Expand Down
4 changes: 3 additions & 1 deletion fuel-vm/src/interpreter/blockchain/test/swwq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::storage::{
ContractsState,
ContractsStateData,
MemoryStorage,
MemoryStorageError,
};

use super::*;
Expand Down Expand Up @@ -215,7 +216,8 @@ struct SWWQInput {
)]
fn test_state_write_qword(
input: SWWQInput,
) -> Result<(Vec<([u8; 32], ContractsStateData)>, u64), RuntimeError<Infallible>> {
) -> Result<(Vec<([u8; 32], ContractsStateData)>, u64), RuntimeError<MemoryStorageError>>
{
let SWWQInput {
input,
storage_slots,
Expand Down
3 changes: 2 additions & 1 deletion fuel-vm/src/interpreter/diff/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,10 @@ where
fn read(
&self,
key: &<Type as Mappable>::Key,
offset: usize,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error> {
<S as StorageRead<Type>>::read(&self.0, key, buf)
<S as StorageRead<Type>>::read(&self.0, key, offset, buf)
}

fn read_alloc(
Expand Down
2 changes: 1 addition & 1 deletion fuel-vm/src/interpreter/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ where
{
let bytes_read = storage
.storage::<ContractsRawCode>()
.read(contract, dst)
.read(contract, 0, dst)
.map_err(RuntimeError::Storage)?
.ok_or(PanicReason::ContractNotFound)?;
if bytes_read != dst.len() {
Expand Down
13 changes: 7 additions & 6 deletions fuel-vm/src/interpreter/flow/tests.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![allow(clippy::arithmetic_side_effects, clippy::cast_possible_truncation)]

use core::convert::Infallible;

use alloc::{
vec,
vec::Vec,
};

use crate::storage::MemoryStorage;
use crate::storage::{
MemoryStorage,
MemoryStorageError,
};

use super::*;
use crate::crypto;
Expand Down Expand Up @@ -325,7 +326,7 @@ fn mem(set: &[(usize, Vec<u8>)]) -> MemoryInstance {
..Default::default()
} => using check_output(Err(RuntimeError::Recoverable(PanicReason::NotEnoughBalance))); "Transfer too many coins internally"
)]
fn test_prepare_call(input: Input) -> Result<Output, RuntimeError<Infallible>> {
fn test_prepare_call(input: Input) -> Result<Output, RuntimeError<MemoryStorageError>> {
let Input {
params,
mut reg,
Expand Down Expand Up @@ -395,8 +396,8 @@ fn test_prepare_call(input: Input) -> Result<Output, RuntimeError<Infallible>> {
}

fn check_output(
expected: Result<Output, RuntimeError<Infallible>>,
) -> impl FnOnce(Result<Output, RuntimeError<Infallible>>) {
expected: Result<Output, RuntimeError<MemoryStorageError>>,
) -> impl FnOnce(Result<Output, RuntimeError<MemoryStorageError>>) {
move |result| match (expected, result) {
(Ok(e), Ok(r)) => {
assert_eq!(e.reg, r.reg);
Expand Down
10 changes: 6 additions & 4 deletions fuel-vm/src/memory_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ use crate::{
NotSupportedEcal,
},
state::StateTransitionRef,
storage::MemoryStorage,
storage::{
MemoryStorage,
MemoryStorageError,
},
transactor::Transactor,
};
use core::convert::Infallible;
use fuel_tx::{
Blob,
Create,
Expand Down Expand Up @@ -103,15 +105,15 @@ where
pub fn deploy(
&mut self,
tx: Checked<Create>,
) -> Result<Create, InterpreterError<Infallible>> {
) -> Result<Create, InterpreterError<MemoryStorageError>> {
self.transactor.deploy(tx)
}

/// Executes `Upgrade` transaction.
pub fn upgrade(
&mut self,
tx: Checked<Upgrade>,
) -> Result<Upgrade, InterpreterError<Infallible>> {
) -> Result<Upgrade, InterpreterError<MemoryStorageError>> {
self.transactor.upgrade(tx)
}

Expand Down
5 changes: 4 additions & 1 deletion fuel-vm/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ pub use interpreter::{
InterpreterStorage,
};
#[cfg(feature = "test-helpers")]
pub use memory::MemoryStorage;
pub use memory::{
MemoryStorage,
MemoryStorageError,
};

#[cfg(feature = "alloc")]
use alloc::vec::Vec;
Expand Down
9 changes: 7 additions & 2 deletions fuel-vm/src/storage/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,13 @@ pub trait InterpreterStorage:
fn read_contract(
&self,
id: &ContractId,
offset: usize,
writer: &mut [u8],
) -> Result<Option<Word>, Self::DataError> {
Ok(StorageRead::<ContractsRawCode>::read(self, id, writer)?.map(|r| r as Word))
Ok(
StorageRead::<ContractsRawCode>::read(self, id, offset, writer)?
.map(|r| r as Word),
)
}

/// Append a contract to the chain, provided its identifier.
Expand Down Expand Up @@ -370,9 +374,10 @@ where
fn read_contract(
&self,
id: &ContractId,
offset: usize,
writer: &mut [u8],
) -> Result<Option<Word>, Self::DataError> {
<S as InterpreterStorage>::read_contract(self.deref(), id, writer)
<S as InterpreterStorage>::read_contract(self.deref(), id, offset, writer)
}

fn contract_state_range(
Expand Down
Loading

0 comments on commit 486d06f

Please sign in to comment.