From d54d957315941fea7d9022583a8e951ae27e7eaa Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Thu, 29 Feb 2024 15:04:35 -0500 Subject: [PATCH] feat: Impl StorageRead and StorageWrite (#1713) Related issues: - https://github.com/FuelLabs/fuel-vm/issues/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 --- CHANGELOG.md | 1 + crates/storage/src/structured_storage.rs | 89 ++++++++++++++++++- .../src/structured_storage/contracts.rs | 29 +----- 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa5a491336..e0ed5c8221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/crates/storage/src/structured_storage.rs b/crates/storage/src/structured_storage.rs index 615bff7ae2..691b0962a4 100644 --- a/crates/storage/src/structured_storage.rs +++ b/crates/storage/src/structured_storage.rs @@ -7,6 +7,11 @@ use crate::{ SupportsBatching, SupportsMerkle, }, + codec::{ + raw::Raw, + Encode, + Encoder, + }, kv_store::{ BatchOperations, KeyValueStore, @@ -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; @@ -173,6 +183,83 @@ where } } +impl StorageRead for StructuredStorage +where + S: KeyValueStore, + M: Mappable + TableWithBlueprint, + M::Blueprint: Blueprint, +{ + fn read( + &self, + key: &::Key, + buf: &mut [u8], + ) -> Result, Self::Error> { + let key_encoder = >::KeyCodec::encode(key); + let key_bytes = key_encoder.as_bytes(); + self.storage + .read(key_bytes.as_ref(), ::column(), buf) + } + + fn read_alloc( + &self, + key: &::Key, + ) -> Result>, Self::Error> { + let key_encoder = >::KeyCodec::encode(key); + let key_bytes = key_encoder.as_bytes(); + self.storage + .get(key_bytes.as_ref(), ::column()) + // TODO: Return `Value` instead of cloned `Vec`. + .map(|value| value.map(|value| value.deref().clone())) + } +} + +impl StorageWrite for StructuredStorage +where + S: KeyValueStore, + M: TableWithBlueprint, + M::Blueprint: Blueprint, + // TODO: Add new methods to the `Blueprint` that allows work with bytes directly + // without deserialization into `OwnedValue`. + M::OwnedValue: Into>, +{ + fn write(&mut self, key: &M::Key, buf: Vec) -> Result { + ::Blueprint::put( + &mut self.storage, + key, + M::column(), + buf.as_slice(), + ) + .map(|_| buf.len()) + } + + fn replace( + &mut self, + key: &M::Key, + buf: Vec, + ) -> Result<(usize, Option>), Self::Error> { + let bytes_written = buf.len(); + let prev = ::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>, Self::Error> { + let take = ::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 { diff --git a/crates/storage/src/structured_storage/contracts.rs b/crates/storage/src/structured_storage/contracts.rs index 8a9b4fd57b..2189caa6d9 100644 --- a/crates/storage/src/structured_storage/contracts.rs +++ b/crates/storage/src/structured_storage/contracts.rs @@ -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 @@ -35,26 +28,6 @@ impl TableWithBlueprint for ContractsRawCode { } } -impl StorageRead for StructuredStorage -where - S: KeyValueStore, -{ - fn read( - &self, - key: &ContractId, - buf: &mut [u8], - ) -> Result, Self::Error> { - self.storage - .read(key.as_ref(), Column::ContractsRawCode, buf) - } - - fn read_alloc(&self, key: &ContractId) -> Result>, 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; type Column = Column;