Skip to content

Commit

Permalink
feat: Impl StorageRead and StorageWrite (#1713)
Browse files Browse the repository at this point in the history
Related issues:
- FuelLabs/fuel-vm#655

This PR removes the specific implementation of `StorageRead` for
`ContractsRawCode`, and replaces it with a generic implementation over
`Blueprint` tables and `StructuredStorage`. This means that other
implementations of `TableWithBlueprint` will automatically receive this
implementation, including `ContractsStorage`, which uses the `Sparse`
Blueprint.

---------

Co-authored-by: xgreenx <xgreenx9999@gmail.com>
  • Loading branch information
Brandon Vrooman and xgreenx committed Feb 29, 2024
1 parent 5bb98b4 commit d54d957
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- [#1713](https://github.com/FuelLabs/fuel-core/pull/1713): Added automatic `impl` of traits `StorageWrite` and `StorageRead` for `StructuredStorage`. Tables that use a `Blueprint` can be read and written using these interfaces provided by structured storage types.
- [#1671](https://github.com/FuelLabs/fuel-core/pull/1671): Added a new `Merklized` blueprint that maintains the binary Merkle tree over the storage data. It supports only the insertion of the objects without removing them.
- [#1657](https://github.com/FuelLabs/fuel-core/pull/1657): Moved `ContractsInfo` table from `fuel-vm` to on-chain tables, and created version-able `ContractsInfoType` to act as the table's data type.

Expand Down
89 changes: 88 additions & 1 deletion crates/storage/src/structured_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use crate::{
SupportsBatching,
SupportsMerkle,
},
codec::{
raw::Raw,
Encode,
Encoder,
},
kv_store::{
BatchOperations,
KeyValueStore,
Expand All @@ -19,9 +24,14 @@ use crate::{
StorageBatchMutate,
StorageInspect,
StorageMutate,
StorageRead,
StorageSize,
StorageWrite,
};
use std::{
borrow::Cow,
ops::Deref,
};
use std::borrow::Cow;

pub mod balances;
pub mod blocks;
Expand Down Expand Up @@ -173,6 +183,83 @@ where
}
}

impl<Column, S, M> StorageRead<M> for StructuredStorage<S>
where
S: KeyValueStore<Column = Column>,
M: Mappable + TableWithBlueprint<Column = Column, Value = [u8]>,
M::Blueprint: Blueprint<M, S, ValueCodec = Raw>,
{
fn read(
&self,
key: &<M as Mappable>::Key,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error> {
let key_encoder = <M::Blueprint as Blueprint<M, S>>::KeyCodec::encode(key);
let key_bytes = key_encoder.as_bytes();
self.storage
.read(key_bytes.as_ref(), <M as TableWithBlueprint>::column(), buf)
}

fn read_alloc(
&self,
key: &<M as Mappable>::Key,
) -> Result<Option<Vec<u8>>, Self::Error> {
let key_encoder = <M::Blueprint as Blueprint<M, S>>::KeyCodec::encode(key);
let key_bytes = key_encoder.as_bytes();
self.storage
.get(key_bytes.as_ref(), <M as TableWithBlueprint>::column())
// TODO: Return `Value` instead of cloned `Vec<u8>`.
.map(|value| value.map(|value| value.deref().clone()))
}
}

impl<Column, S, M> StorageWrite<M> for StructuredStorage<S>
where
S: KeyValueStore<Column = Column>,
M: TableWithBlueprint<Column = Column, Value = [u8]>,
M::Blueprint: Blueprint<M, S, ValueCodec = Raw>,
// TODO: Add new methods to the `Blueprint` that allows work with bytes directly
// without deserialization into `OwnedValue`.
M::OwnedValue: Into<Vec<u8>>,
{
fn write(&mut self, key: &M::Key, buf: Vec<u8>) -> Result<usize, Self::Error> {
<M as TableWithBlueprint>::Blueprint::put(
&mut self.storage,
key,
M::column(),
buf.as_slice(),
)
.map(|_| buf.len())
}

fn replace(
&mut self,
key: &M::Key,
buf: Vec<u8>,
) -> Result<(usize, Option<Vec<u8>>), Self::Error> {
let bytes_written = buf.len();
let prev = <M as TableWithBlueprint>::Blueprint::replace(
&mut self.storage,
key,
M::column(),
buf.as_slice(),
)?
.map(|prev| prev.into());
let result = (bytes_written, prev);
Ok(result)
}

fn take(&mut self, key: &M::Key) -> Result<Option<Vec<u8>>, Self::Error> {
let take = <M as TableWithBlueprint>::Blueprint::take(
&mut self.storage,
key,
M::column(),
)?
.map(|value| value.into());
Ok(take)
}
}

/// The module that provides helper macros for testing the structured storage.
#[cfg(feature = "test-helpers")]
pub mod test {
Expand Down
29 changes: 1 addition & 28 deletions crates/storage/src/structured_storage/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@ use crate::{
raw::Raw,
},
column::Column,
kv_store::KeyValueStore,
structured_storage::{
StructuredStorage,
TableWithBlueprint,
},
structured_storage::TableWithBlueprint,
tables::{
ContractsInfo,
ContractsLatestUtxo,
ContractsRawCode,
},
StorageRead,
};
use core::ops::Deref;
use fuel_core_types::fuel_tx::ContractId;

// # Dev-note: The value of the `ContractsRawCode` has a unique implementation of serialization
// and deserialization and uses `Raw` codec. Because the value is a contract byte code represented
Expand All @@ -35,26 +28,6 @@ impl TableWithBlueprint for ContractsRawCode {
}
}

impl<S> StorageRead<ContractsRawCode> for StructuredStorage<S>
where
S: KeyValueStore<Column = Column>,
{
fn read(
&self,
key: &ContractId,
buf: &mut [u8],
) -> Result<Option<usize>, Self::Error> {
self.storage
.read(key.as_ref(), Column::ContractsRawCode, buf)
}

fn read_alloc(&self, key: &ContractId) -> Result<Option<Vec<u8>>, Self::Error> {
self.storage
.get(key.as_ref(), Column::ContractsRawCode)
.map(|value| value.map(|value| value.deref().clone()))
}
}

impl TableWithBlueprint for ContractsInfo {
type Blueprint = Plain<Raw, Postcard>;
type Column = Column;
Expand Down

0 comments on commit d54d957

Please sign in to comment.