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>;
}