Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use Vec of Bytes for Contract State #671

Merged
merged 35 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
739f5eb
Change ContractsState::Value to Vec<u8>
bvrooman Feb 1, 2024
29c5a48
WIP
bvrooman Feb 2, 2024
b174897
WIP
bvrooman Feb 5, 2024
6566120
WIP
bvrooman Feb 5, 2024
c2a7644
WIP
bvrooman Feb 6, 2024
f9fe1cf
Update
bvrooman Feb 6, 2024
46388b3
Update CHANGELOG.md
bvrooman Feb 7, 2024
0775951
Fix test
bvrooman Feb 7, 2024
4fc8288
Update blockchain.rs
bvrooman Feb 7, 2024
9f6e7bb
no_std vec
bvrooman Feb 7, 2024
6150b1d
Merge branch 'master' into bvrooman/feat/dynamic-contract-state
Feb 12, 2024
db04050
WIP
bvrooman Feb 16, 2024
99c5541
Test Fix
bvrooman Feb 16, 2024
e5cc6f4
WIP
bvrooman Feb 16, 2024
ce7bbd8
Maintain serialization/deserialization format
bvrooman Feb 19, 2024
0d814ed
Remove dbg
bvrooman Feb 19, 2024
2450866
Merge branch 'master' into bvrooman/feat/dynamic-contract-state
bvrooman Feb 19, 2024
8ff5159
Fix includes for no_std
bvrooman Feb 19, 2024
1c0439d
Merge branch 'master' into bvrooman/feat/dynamic-contract-state
bvrooman Feb 22, 2024
ecfcdc3
Use StorageRead/StorageWrite
bvrooman Feb 23, 2024
a9149f1
Clippeehee
bvrooman Feb 23, 2024
8eda94e
Uncomment take
bvrooman Feb 23, 2024
873b518
Update tests
bvrooman Feb 26, 2024
87b707f
Fix test
bvrooman Feb 26, 2024
df88364
Rename StorageData to ContractsStateData for clarity
bvrooman Feb 26, 2024
b7c1f90
Revert changes to StorageSlot
bvrooman Feb 26, 2024
59bb82b
Merge branch 'master' into bvrooman/feat/dynamic-contract-state
Feb 26, 2024
15a069a
Refactor ContractsState into separate file
bvrooman Feb 26, 2024
65ac84f
Minor refactor
bvrooman Feb 26, 2024
6d7d036
Merge branch 'bvrooman/feat/dynamic-contract-state' of https://github…
bvrooman Feb 26, 2024
f6f3dfd
Update use statements
bvrooman Feb 26, 2024
d444d20
Remove commented out code
bvrooman Feb 26, 2024
bc0a256
Fix feature flag
bvrooman Feb 26, 2024
6dde649
Merge branch 'master' into bvrooman/feat/dynamic-contract-state
bvrooman Feb 26, 2024
d784d40
Use iterator instead of the vector
xgreenx Feb 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed
#### Breaking

- [#671](https://github.com/FuelLabs/fuel-vm/pull/671): Support dynamically sized values in the ContractsState table by using a vector data type (`Vec<u8>`).
- [#682](https://github.com/FuelLabs/fuel-vm/pull/682): Include `Tip` policy in fee calculation
- [#683](https://github.com/FuelLabs/fuel-vm/pull/683): Simplify `InterpreterStorage` by removing dependency on `MerkleRootStorage` and removing `merkle_` prefix from method names.
- [#678](https://github.com/FuelLabs/fuel-vm/pull/678): Zero malleable fields before execution. Remove some now-obsolete GTF getters. Don't update `tx.receiptsRoot` after pushing receipts, and do it after execution instead.
Expand Down
24 changes: 22 additions & 2 deletions fuel-storage/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ impl<'a, T: StorageRead<Type> + StorageSize<Type> + ?Sized, Type: Mappable>
}
}

impl<'a, T: StorageWrite<Type> + ?Sized, Type: Mappable> StorageWrite<Type>
for &'a mut T
{
fn write(&mut self, key: &Type::Key, buf: &[u8]) -> Result<usize, Self::Error> {
<T as StorageWrite<Type>>::write(self, key, buf)
}

fn replace(
&mut self,
key: &Type::Key,
buf: &[u8],
) -> Result<(usize, Option<Vec<u8>>), Self::Error> {
<T as StorageWrite<Type>>::replace(self, key, buf)
}

fn take(&mut self, key: &Type::Key) -> Result<Option<Vec<u8>>, Self::Error> {
<T as StorageWrite<Type>>::take(self, key)
}
}

impl<'a, T: MerkleRootStorage<Key, Type> + ?Sized, Key, Type: Mappable>
MerkleRootStorage<Key, Type> for &'a mut T
{
Expand Down Expand Up @@ -221,15 +241,15 @@ impl<'a, T, Type: Mappable> StorageMut<'a, T, Type> {

impl<'a, T: StorageWrite<Type>, Type: Mappable> StorageMut<'a, T, Type> {
#[inline(always)]
pub fn write(&mut self, key: &Type::Key, buf: Vec<u8>) -> Result<usize, T::Error> {
pub fn write(&mut self, key: &Type::Key, buf: &[u8]) -> Result<usize, T::Error> {
self.0.write(key, buf)
}

#[inline(always)]
pub fn replace(
&mut self,
key: &Type::Key,
buf: Vec<u8>,
buf: &[u8],
) -> Result<(usize, Option<Vec<u8>>), T::Error>
where
T: StorageSize<Type>,
Expand Down
8 changes: 3 additions & 5 deletions fuel-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub trait StorageWrite<Type: Mappable>: StorageMutate<Type> {
/// Does not perform any serialization.
///
/// Returns the number of bytes written.
fn write(&mut self, key: &Type::Key, buf: Vec<u8>) -> Result<usize, Self::Error>;
fn write(&mut self, key: &Type::Key, buf: &[u8]) -> Result<usize, Self::Error>;

/// Write the value to the given key from the provided buffer and
/// return the previous value if it existed.
Expand All @@ -143,10 +143,8 @@ pub trait StorageWrite<Type: Mappable>: StorageMutate<Type> {
fn replace(
&mut self,
key: &Type::Key,
buf: Vec<u8>,
) -> Result<(usize, Option<Vec<u8>>), Self::Error>
where
Self: StorageSize<Type>;
buf: &[u8],
) -> Result<(usize, Option<Vec<u8>>), Self::Error>;

/// Removes a value from the storage and returning it without deserializing it.
fn take(&mut self, key: &Type::Key) -> Result<Option<Vec<u8>>, Self::Error>;
Expand Down
16 changes: 5 additions & 11 deletions fuel-tx/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ use fuel_types::{
};

use alloc::vec::Vec;
use core::{
iter,
ops::Deref,
};
use core::iter;

/// The target size of Merkle tree leaves in bytes. Contract code will will be divided
/// into chunks of this size and pushed to the Merkle tree.
Expand Down Expand Up @@ -105,13 +102,10 @@ impl Contract {
where
I: Iterator<Item = &'a StorageSlot>,
{
let root = SparseMerkleTree::root_from_set(storage_slots.map(|slot| {
(
MerkleTreeKey::new(*slot.key().deref()),
*slot.value().deref(),
)
}));

let storage_slots = storage_slots
.map(|slot| (*slot.key(), slot.value()))
.map(|(key, data)| (MerkleTreeKey::new(key), data));
let root = SparseMerkleTree::root_from_set(storage_slots);
root.into()
}

Expand Down
5 changes: 1 addition & 4 deletions fuel-tx/src/transaction/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,7 @@ mod tests {
}

fn invert_storage_slot(storage_slot: &mut StorageSlot) {
let mut data = [0u8; 64];
storage_slot
.encode(&mut &mut data[..])
.expect("Failed to encode storage slot");
let mut data = storage_slot.to_bytes();
invert(&mut data);
*storage_slot =
StorageSlot::from_bytes(&data).expect("Failed to decode storage slot");
Expand Down
1 change: 0 additions & 1 deletion fuel-tx/src/transaction/types/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use fuel_types::{
Bytes32,
Bytes64,
};

#[cfg(feature = "random")]
use rand::{
distributions::{
Expand Down
28 changes: 13 additions & 15 deletions fuel-vm/src/interpreter/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use crate::{
storage::{
ContractsAssetsStorage,
ContractsRawCode,
ContractsStateData,
InterpreterStorage,
},
};
Expand Down Expand Up @@ -1013,7 +1014,7 @@ pub(crate) fn state_read_word<S: InterpreterStorage>(
.map_err(RuntimeError::Storage)?
.map(|bytes| {
Word::from_be_bytes(
bytes[..8]
bytes.as_ref().as_ref()[..8]
.try_into()
.expect("8 bytes can be converted to a Word"),
)
Expand Down Expand Up @@ -1065,17 +1066,16 @@ pub(crate) fn state_write_word<S: InterpreterStorage>(
let contract = ContractId::from_bytes_ref(contract.read(memory));
let key = Bytes32::from_bytes_ref(key.read(memory));

let mut value = Bytes32::default();
let mut value = Bytes32::zeroed();
value.as_mut()[..WORD_SIZE].copy_from_slice(&c.to_be_bytes());

value[..WORD_SIZE].copy_from_slice(&c.to_be_bytes());

let result = storage
.contract_state_insert(contract, key, &value)
let (_size, prev) = storage
.contract_state_insert(contract, key, value.as_ref())
.map_err(RuntimeError::Storage)?;

*created_new = result.is_none() as Word;
*created_new = prev.is_none() as Word;

if result.is_none() {
if prev.is_none() {
// New data was written, charge gas for it
let profiler = ProfileGas {
pc: pc.as_ref(),
Expand Down Expand Up @@ -1239,10 +1239,10 @@ fn state_read_qword<S: InterpreterStorage>(
.map_err(RuntimeError::Storage)?
.into_iter()
.flat_map(|bytes| match bytes {
Some(bytes) => **bytes,
Some(bytes) => bytes.into_owned(),
None => {
all_set = false;
*Bytes32::zeroed()
ContractsStateData::from(Bytes32::zeroed().as_ref())
}
})
.collect();
Expand Down Expand Up @@ -1304,13 +1304,11 @@ fn state_write_qword<'vm, S: InterpreterStorage>(
let destination_key =
Bytes32::from_bytes_ref(input.starting_storage_key_memory_range.read(memory));

let values: Vec<_> = memory[input.source_address_memory_range.usizes()]
.chunks_exact(Bytes32::LEN)
.flat_map(|chunk| Some(Bytes32::from(<[u8; 32]>::try_from(chunk).ok()?)))
.collect();
let values =
memory[input.source_address_memory_range.usizes()].chunks_exact(Bytes32::LEN);

let unset_count = storage
.contract_state_insert_range(contract_id, destination_key, &values)
.contract_state_insert_range(contract_id, destination_key, values)
.map_err(RuntimeError::Storage)?;
*result_register = unset_count as Word;

Expand Down
2 changes: 1 addition & 1 deletion fuel-vm/src/interpreter/blockchain/other_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ fn test_code_size() {
let mut memory: Memory<MEM_SIZE> = vec![1u8; MEM_SIZE].try_into().unwrap();
memory[0..ContractId::LEN].copy_from_slice(contract_id.as_slice());
StorageAsMut::storage::<ContractsRawCode>(&mut storage)
.write(&ContractId::from([3u8; 32]), vec![1u8; 100])
.write(&ContractId::from([3u8; 32]), &[1u8; 100])
.unwrap();
let mut pc = 4;
let is = 0;
Expand Down
9 changes: 8 additions & 1 deletion fuel-vm/src/interpreter/blockchain/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use core::{
use crate::{
context::Context,
interpreter::memory::Memory,
storage::MemoryStorage,
storage::{
ContractsStateData,
MemoryStorage,
},
};
use test_case::test_case;

Expand All @@ -34,6 +37,10 @@ const fn key(k: u8) -> [u8; 32] {
]
}

fn data(value: &[u8]) -> ContractsStateData {
ContractsStateData::from(value)
}

impl OwnershipRegisters {
pub fn test(stack: Range<u64>, heap: Range<u64>, context: Context) -> Self {
Self {
Expand Down
23 changes: 13 additions & 10 deletions fuel-vm/src/interpreter/blockchain/test/scwq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use alloc::{

use crate::storage::{
ContractsState,
ContractsStateData,
MemoryStorage,
};

Expand All @@ -16,22 +17,22 @@ use test_case::test_case;

struct SCWQInput {
input: StateClearQWord,
storage_slots: Vec<([u8; 32], [u8; 32])>,
storage_slots: Vec<([u8; 32], ContractsStateData)>,
memory: Memory<MEM_SIZE>,
}

#[test_case(
SCWQInput{
input: StateClearQWord::new(0, 1).unwrap(),
storage_slots: vec![(key(27), [8; 32])],
storage_slots: vec![(key(27), data(&[8; 32]))],
memory: mem(&[&key(27)]),
} => (vec![], true)
; "Clear single storage slot"
)]
#[test_case(
SCWQInput{
input: StateClearQWord::new(0, 2).unwrap(),
storage_slots: vec![(key(27), [8; 32]), (key(28), [9; 32])],
storage_slots: vec![(key(27), data(&[8; 32])), (key(28), data(&[9; 32]))],
memory: mem(&[&key(27)]),
} => (vec![], true)
; "Clear multiple existing storage slots"
Expand All @@ -55,20 +56,22 @@ struct SCWQInput {
#[test_case(
SCWQInput{
input: StateClearQWord::new(0, 2).unwrap(),
storage_slots: vec![(key(27), [8; 32]), (key(29), [8; 32])],
storage_slots: vec![(key(27), data(&[8; 32])), (key(29), data(&[8; 32]))],
memory: mem(&[&key(27)]),
} => (vec![(key(29), [8; 32])], false)
} => (vec![(key(29), vec![8; 32].into())], false)
; "Clear storage slots with some previously set"
)]
#[test_case(
SCWQInput{
input: StateClearQWord::new(0, 2).unwrap(),
storage_slots: vec![(key(27), [8; 32]), (key(26), [8; 32])],
storage_slots: vec![(key(27), data(&[8; 32])), (key(26), data(&[8; 32]))],
memory: mem(&[&key(27)]),
} => (vec![(key(26), [8; 32])], false)
} => (vec![(key(26), vec![8; 32].into())], false)
; "Clear storage slots with some previously set before the key"
)]
fn test_state_clear_qword(input: SCWQInput) -> (Vec<([u8; 32], [u8; 32])>, bool) {
fn test_state_clear_qword(
input: SCWQInput,
) -> (Vec<([u8; 32], ContractsStateData)>, bool) {
let SCWQInput {
input,
storage_slots,
Expand All @@ -81,7 +84,7 @@ fn test_state_clear_qword(input: SCWQInput) -> (Vec<([u8; 32], [u8; 32])>, bool)
.storage::<ContractsState>()
.insert(
&(&ContractId::default(), &Bytes32::new(k)).into(),
&Bytes32::new(v),
v.as_ref(),
)
.unwrap();
}
Expand All @@ -100,7 +103,7 @@ fn test_state_clear_qword(input: SCWQInput) -> (Vec<([u8; 32], [u8; 32])>, bool)

let results = storage
.all_contract_state()
.map(|(key, v)| (**key.state_key(), **v))
.map(|(key, v)| (**key.state_key(), v.clone()))
.collect();
(results, result_register != 0)
}
Expand Down
21 changes: 11 additions & 10 deletions 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::{
context::Context,
storage::{
ContractsState,
ContractsStateData,
MemoryStorage,
},
};
Expand All @@ -12,7 +13,7 @@ use test_case::test_case;

struct SRWQInput {
input: StateReadQWord,
storage_slots: Vec<([u8; 32], [u8; 32])>,
storage_slots: Vec<([u8; 32], ContractsStateData)>,
memory: Memory<MEM_SIZE>,
}

Expand Down Expand Up @@ -43,56 +44,56 @@ impl StateReadQWord {
#[test_case(
SRWQInput{
input: StateReadQWord::test(1, 2, 1).unwrap(),
storage_slots: vec![(key(27), [5; 32])],
storage_slots: vec![(key(27), data(&[5; 32]))],
memory: mem(&[&[0; 2], &key(27)]),
} => (mem(&[&[0], &[5; 32], &[27]]), true)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 2).unwrap(),
storage_slots: vec![(key(27), [5; 32]), (key(28), [6; 32])],
storage_slots: vec![(key(27), data(&[5; 32])), (key(28), data(&[6; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[5; 32], &[6; 32]]), true)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 3).unwrap(),
storage_slots: vec![(key(27), [5; 32]), (key(28), [6; 32]), (key(29), [7; 32])],
storage_slots: vec![(key(27), data(&[5; 32])), (key(28), data(&[6; 32])), (key(29), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[5; 32], &[6; 32], &[7; 32]]), true)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 2).unwrap(),
storage_slots: vec![(key(27), [5; 32]), (key(28), [6; 32]), (key(29), [7; 32])],
storage_slots: vec![(key(27), data(&[5; 32])), (key(28), data(&[6; 32])), (key(29), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[5; 32], &[6; 32]]), true)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 3).unwrap(),
storage_slots: vec![(key(27), [5; 32]), (key(30), [6; 32]), (key(29), [7; 32])],
storage_slots: vec![(key(27), data(&[5; 32])), (key(30), data(&[6; 32])), (key(29), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[5; 32], &[0; 32], &[7; 32]]), false)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 3).unwrap(),
storage_slots: vec![(key(27), [5; 32]), (key(28), [7; 32])],
storage_slots: vec![(key(27), data(&[5; 32])), (key(28), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[5; 32], &[7; 32], &[0; 32]]), false)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 3).unwrap(),
storage_slots: vec![(key(26), [5; 32]), (key(28), [6; 32]), (key(29), [7; 32])],
storage_slots: vec![(key(26), data(&[5; 32])), (key(28), data(&[6; 32])), (key(29), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[0; 32], &[6; 32], &[7; 32]]), false)
)]
#[test_case(
SRWQInput{
input: StateReadQWord::test(0, 0, 3).unwrap(),
storage_slots: vec![(key(28), [6; 32]), (key(29), [7; 32])],
storage_slots: vec![(key(28), data(&[6; 32])), (key(29), data(&[7; 32]))],
memory: mem(&[&key(27)]),
} => (mem(&[&[0; 32], &[6; 32], &[7; 32]]), false)
)]
Expand All @@ -108,7 +109,7 @@ fn test_state_read_qword(input: SRWQInput) -> (Memory<MEM_SIZE>, bool) {
.storage::<ContractsState>()
.insert(
&(&ContractId::default(), &Bytes32::new(k)).into(),
&Bytes32::new(v),
v.as_ref(),
)
.unwrap();
}
Expand Down
Loading
Loading