fuel_core_storage/blueprint.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
//! The module defines structures for the [`Mappable`] tables.
//! Each table may have its blueprint that defines how it works with the storage.
//! The table may have a plain blueprint that simply works in CRUD mode, or it may be an SMT-based
//! blueprint that maintains a valid Merkle tree over the storage entries.
use crate::{
codec::{
Decode,
Encode,
Encoder,
},
kv_store::{
BatchOperations,
KeyValueInspect,
KeyValueMutate,
},
Mappable,
Result as StorageResult,
};
use fuel_vm_private::prelude::MerkleRoot;
pub mod merklized;
pub mod plain;
pub mod sparse;
/// This trait allows defining the agnostic implementation for all storage
/// traits(`StorageInspect,` `StorageMutate,` etc) while the main logic is
/// hidden inside the blueprint. It allows quickly adding support for new
/// structures only by implementing the trait and reusing the existing
/// infrastructure in other places. It allows changing the blueprint on the
/// fly in the definition of the table without affecting other areas of the codebase.
///
/// The blueprint is responsible for encoding/decoding(usually it is done via `KeyCodec` and `ValueCodec`)
/// the key and value and putting/extracting it to/from the storage.
pub trait BlueprintInspect<M, S>
where
M: Mappable,
S: KeyValueInspect,
{
/// The codec used to encode and decode storage key.
type KeyCodec: Encode<M::Key> + Decode<M::OwnedKey>;
/// The codec used to encode and decode storage value.
type ValueCodec: Encode<M::Value> + Decode<M::OwnedValue>;
/// Checks if the value exists in the storage.
fn exists(storage: &S, key: &M::Key, column: S::Column) -> StorageResult<bool> {
let key_encoder = Self::KeyCodec::encode(key);
let key_bytes = key_encoder.as_bytes();
storage.exists(key_bytes.as_ref(), column)
}
/// Returns the size of the value in the storage.
fn size_of_value(
storage: &S,
key: &M::Key,
column: S::Column,
) -> StorageResult<Option<usize>> {
let key_encoder = Self::KeyCodec::encode(key);
let key_bytes = key_encoder.as_bytes();
storage.size_of_value(key_bytes.as_ref(), column)
}
/// Returns the value from the storage.
fn get(
storage: &S,
key: &M::Key,
column: S::Column,
) -> StorageResult<Option<M::OwnedValue>> {
let key_encoder = Self::KeyCodec::encode(key);
let key_bytes = key_encoder.as_bytes();
storage
.get(key_bytes.as_ref(), column)?
.map(|value| {
Self::ValueCodec::decode_from_value(value).map_err(crate::Error::Codec)
})
.transpose()
}
}
/// It is an extension of the [`BlueprintInspect`] that allows mutating the storage.
pub trait BlueprintMutate<M, S>: BlueprintInspect<M, S>
where
M: Mappable,
S: KeyValueMutate,
{
/// Puts the key-value pair into the storage.
fn put(
storage: &mut S,
key: &M::Key,
column: S::Column,
value: &M::Value,
) -> StorageResult<()>;
/// Puts the key-value pair into the storage and returns the old value.
fn replace(
storage: &mut S,
key: &M::Key,
column: S::Column,
value: &M::Value,
) -> StorageResult<Option<M::OwnedValue>>;
/// Takes the value from the storage and returns it.
/// The value is removed from the storage.
fn take(
storage: &mut S,
key: &M::Key,
column: S::Column,
) -> StorageResult<Option<M::OwnedValue>>;
/// Removes the value from the storage.
fn delete(storage: &mut S, key: &M::Key, column: S::Column) -> StorageResult<()>;
}
/// It is an extension of the blueprint that allows supporting batch operations.
/// Usually, they are more performant than initializing/inserting/removing values one by one.
pub trait SupportsBatching<M, S>: BlueprintMutate<M, S>
where
M: Mappable,
S: BatchOperations,
{
/// Initializes the storage with a bunch of key-value pairs.
/// In some cases, this method may be more performant than [`Self::insert`].
fn init<'a, Iter>(storage: &mut S, column: S::Column, set: Iter) -> StorageResult<()>
where
Iter: 'a + Iterator<Item = (&'a M::Key, &'a M::Value)>,
M::Key: 'a,
M::Value: 'a;
/// Inserts the batch of key-value pairs into the storage.
fn insert<'a, Iter>(
storage: &mut S,
column: S::Column,
set: Iter,
) -> StorageResult<()>
where
Iter: 'a + Iterator<Item = (&'a M::Key, &'a M::Value)>,
M::Key: 'a,
M::Value: 'a;
/// Removes the batch of key-value pairs from the storage.
fn remove<'a, Iter>(
storage: &mut S,
column: S::Column,
set: Iter,
) -> StorageResult<()>
where
Iter: 'a + Iterator<Item = &'a M::Key>,
M::Key: 'a;
}
/// It is an extension of the blueprint that supporting creation of the Merkle tree over the storage.
pub trait SupportsMerkle<Key, M, S>: BlueprintInspect<M, S>
where
Key: ?Sized,
M: Mappable,
S: KeyValueInspect,
{
/// Returns the root of the Merkle tree.
fn root(storage: &S, key: &Key) -> StorageResult<MerkleRoot>;
}