sov_db/schema/
tables.rs

1//! This module defines the following tables:
2//!
3//!
4//! Slot Tables:
5//! - `SlotNumber -> StoredSlot`
6//! - `SlotNumber -> Vec<BatchNumber>`
7//!
8//! Batch Tables:
9//! - `BatchNumber -> StoredBatch`
10//! - `BatchHash -> BatchNumber`
11//!
12//! Tx Tables:
13//! - `TxNumber -> (TxHash,Tx)`
14//! - `TxHash -> TxNumber`
15//!
16//! Event Tables:
17//! - `(EventKey, TxNumber) -> EventNumber`
18//! - `EventNumber -> (EventKey, EventValue)`
19//!
20//! JMT Tables:
21//! - `KeyHash -> Key`
22//! - `(Key, Version) -> JmtValue`
23//! - `NodeKey -> Node`
24//!
25//! Module Accessory State Table:
26//! - `(ModuleAddress, Key) -> Value`
27
28use borsh::{maybestd, BorshDeserialize, BorshSerialize};
29use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
30use jmt::storage::{Node, NodeKey};
31use jmt::Version;
32use sov_rollup_interface::stf::{Event, EventKey};
33use sov_schema_db::schema::{KeyDecoder, KeyEncoder, ValueCodec};
34use sov_schema_db::{CodecError, SeekKeyEncoder};
35
36use super::types::{
37    AccessoryKey, AccessoryStateValue, BatchNumber, DbHash, EventNumber, JmtValue, SlotNumber,
38    StateKey, StoredBatch, StoredSlot, StoredTransaction, TxNumber,
39};
40
41/// A list of all tables used by the StateDB. These tables store rollup state - meaning
42/// account balances, nonces, etc.
43pub const STATE_TABLES: &[&str] = &[
44    KeyHashToKey::table_name(),
45    JmtValues::table_name(),
46    JmtNodes::table_name(),
47];
48
49/// A list of all tables used by the LedgerDB. These tables store rollup "history" - meaning
50/// transaction, events, receipts, etc.
51pub const LEDGER_TABLES: &[&str] = &[
52    SlotByNumber::table_name(),
53    SlotByHash::table_name(),
54    BatchByHash::table_name(),
55    BatchByNumber::table_name(),
56    TxByHash::table_name(),
57    TxByNumber::table_name(),
58    EventByKey::table_name(),
59    EventByNumber::table_name(),
60];
61
62/// A list of all tables used by the NativeDB. These tables store
63/// "accessory" state only accessible from a native execution context, to be
64/// used for JSON-RPC and other tooling.
65pub const NATIVE_TABLES: &[&str] = &[ModuleAccessoryState::table_name()];
66
67/// Macro to define a table that implements [`sov_schema_db::Schema`].
68/// KeyCodec<Schema> and ValueCodec<Schema> must be implemented separately.
69///
70/// ```ignore
71/// define_table_without_codec!(
72///  /// A table storing keys and value
73///  (MyTable) MyKey => MyValue
74/// )
75///
76/// // This impl must be written by hand
77/// impl KeyCodec<MyTable> for MyKey {
78/// // ...
79/// }
80///
81/// // This impl must be written by hand
82/// impl ValueCodec<MyTable> for MyValue {
83/// // ...
84/// }
85/// ```
86macro_rules! define_table_without_codec {
87    ($(#[$docs:meta])+ ( $table_name:ident ) $key:ty => $value:ty) => {
88        $(#[$docs])+
89        ///
90        #[doc = concat!("Takes [`", stringify!($key), "`] as a key and returns [`", stringify!($value), "`]")]
91        #[derive(Clone, Copy, Debug, Default)]
92        pub(crate) struct $table_name;
93
94        impl ::sov_schema_db::schema::Schema for $table_name {
95            const COLUMN_FAMILY_NAME: &'static str = $table_name::table_name();
96            type Key = $key;
97            type Value = $value;
98        }
99
100        impl $table_name {
101            #[doc=concat!("Return ", stringify!($table_name), " as it is present inside the database.")]
102            pub const fn table_name() -> &'static str {
103                ::core::stringify!($table_name)
104            }
105        }
106
107        impl ::std::fmt::Display for $table_name {
108            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
109                ::core::write!(f, "{}", stringify!($table_name))
110            }
111        }
112    };
113}
114
115macro_rules! impl_borsh_value_codec {
116    ($table_name:ident, $value:ty) => {
117        impl ::sov_schema_db::schema::ValueCodec<$table_name> for $value {
118            fn encode_value(
119                &self,
120            ) -> ::std::result::Result<
121                ::sov_rollup_interface::maybestd::vec::Vec<u8>,
122                ::sov_schema_db::CodecError,
123            > {
124                ::borsh::BorshSerialize::try_to_vec(self).map_err(Into::into)
125            }
126
127            fn decode_value(
128                data: &[u8],
129            ) -> ::std::result::Result<Self, ::sov_schema_db::CodecError> {
130                ::borsh::BorshDeserialize::deserialize_reader(&mut &data[..]).map_err(Into::into)
131            }
132        }
133    };
134}
135
136/// Macro to define a table that implements [`sov_schema_db::schema::Schema`].
137/// Automatically generates KeyCodec<...> and ValueCodec<...> implementations
138/// using the Encode and Decode traits from sov_rollup_interface
139///
140/// ```ignore
141/// define_table_with_default_codec!(
142///  /// A table storing keys and value
143///  (MyTable) MyKey => MyValue
144/// )
145/// ```
146macro_rules! define_table_with_default_codec {
147    ($(#[$docs:meta])+ ($table_name:ident) $key:ty => $value:ty) => {
148        define_table_without_codec!($(#[$docs])+ ( $table_name ) $key => $value);
149
150        impl ::sov_schema_db::schema::KeyEncoder<$table_name> for $key {
151            fn encode_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_schema_db::CodecError> {
152                ::borsh::BorshSerialize::try_to_vec(self).map_err(Into::into)
153            }
154        }
155
156        impl ::sov_schema_db::schema::KeyDecoder<$table_name> for $key {
157            fn decode_key(data: &[u8]) -> ::std::result::Result<Self, ::sov_schema_db::CodecError> {
158                ::borsh::BorshDeserialize::deserialize_reader(&mut &data[..]).map_err(Into::into)
159            }
160        }
161
162        impl_borsh_value_codec!($table_name, $value);
163    };
164}
165
166/// Macro similar to [`define_table_with_default_codec`], but to be used when
167/// your key type should be [`SeekKeyEncoder`]. Borsh serializes integers as
168/// little-endian, but RocksDB uses lexicographic ordering which is only
169/// compatible with big-endian, so we use [`bincode`] with the big-endian option
170/// here.
171macro_rules! define_table_with_seek_key_codec {
172    ($(#[$docs:meta])+ ($table_name:ident) $key:ty => $value:ty) => {
173        define_table_without_codec!($(#[$docs])+ ( $table_name ) $key => $value);
174
175        impl ::sov_schema_db::schema::KeyEncoder<$table_name> for $key {
176            fn encode_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_schema_db::CodecError> {
177                use ::anyhow::Context as _;
178                use ::bincode::Options as _;
179
180                let bincode_options = ::bincode::options()
181                    .with_fixint_encoding()
182                    .with_big_endian();
183
184                bincode_options.serialize(self).context("Failed to serialize key").map_err(Into::into)
185            }
186        }
187
188        impl ::sov_schema_db::schema::KeyDecoder<$table_name> for $key {
189            fn decode_key(data: &[u8]) -> ::std::result::Result<Self, ::sov_schema_db::CodecError> {
190                use ::anyhow::Context as _;
191                use ::bincode::Options as _;
192
193                let bincode_options = ::bincode::options()
194                    .with_fixint_encoding()
195                    .with_big_endian();
196
197                bincode_options.deserialize_from(&mut &data[..]).context("Failed to deserialize key").map_err(Into::into)
198            }
199        }
200
201        impl ::sov_schema_db::SeekKeyEncoder<$table_name> for $key {
202            fn encode_seek_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_schema_db::CodecError> {
203                <Self as ::sov_schema_db::schema::KeyEncoder<$table_name>>::encode_key(self)
204            }
205        }
206
207        impl_borsh_value_codec!($table_name, $value);
208    };
209}
210
211// fn deser(target: &mut &[u8]) -> Result<Self, DeserializationError>;
212define_table_with_seek_key_codec!(
213    /// The primary source for slot data
214    (SlotByNumber) SlotNumber => StoredSlot
215);
216
217define_table_with_default_codec!(
218    /// A "secondary index" for slot data by hash
219    (SlotByHash) DbHash => SlotNumber
220);
221
222define_table_with_default_codec!(
223    /// Non-JMT state stored by a module for JSON-RPC use.
224    (ModuleAccessoryState) AccessoryKey => AccessoryStateValue
225);
226
227define_table_with_seek_key_codec!(
228    /// The primary source for batch data
229    (BatchByNumber) BatchNumber => StoredBatch
230);
231
232define_table_with_default_codec!(
233    /// A "secondary index" for batch data by hash
234    (BatchByHash) DbHash => BatchNumber
235);
236
237define_table_with_seek_key_codec!(
238    /// The primary source for transaction data
239    (TxByNumber) TxNumber => StoredTransaction
240);
241
242define_table_with_default_codec!(
243    /// A "secondary index" for transaction data by hash
244    (TxByHash) DbHash => TxNumber
245);
246
247define_table_with_seek_key_codec!(
248    /// The primary store for event data
249    (EventByNumber) EventNumber => Event
250);
251
252define_table_with_default_codec!(
253    /// A "secondary index" for event data by key
254    (EventByKey) (EventKey, TxNumber, EventNumber) => ()
255);
256
257define_table_without_codec!(
258    /// The source of truth for JMT nodes
259    (JmtNodes) NodeKey => Node
260);
261
262impl KeyEncoder<JmtNodes> for NodeKey {
263    fn encode_key(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
264        self.try_to_vec().map_err(CodecError::from)
265    }
266}
267impl KeyDecoder<JmtNodes> for NodeKey {
268    fn decode_key(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
269        Ok(Self::deserialize_reader(&mut &data[..])?)
270    }
271}
272
273impl ValueCodec<JmtNodes> for Node {
274    fn encode_value(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
275        self.try_to_vec().map_err(CodecError::from)
276    }
277
278    fn decode_value(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
279        Ok(Self::deserialize_reader(&mut &data[..])?)
280    }
281}
282
283define_table_without_codec!(
284    /// The source of truth for JMT values by version
285    (JmtValues) (StateKey, Version) => JmtValue
286);
287
288impl<T: AsRef<[u8]> + PartialEq + core::fmt::Debug> KeyEncoder<JmtValues> for (T, Version) {
289    fn encode_key(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
290        let mut out =
291            Vec::with_capacity(self.0.as_ref().len() + std::mem::size_of::<Version>() + 8);
292        self.0
293            .as_ref()
294            .serialize(&mut out)
295            .map_err(CodecError::from)?;
296        // Write the version in big-endian order so that sorting order is based on the most-significant bytes of the key
297        out.write_u64::<BigEndian>(self.1)
298            .expect("serialization to vec is infallible");
299        Ok(out)
300    }
301}
302
303impl<T: AsRef<[u8]> + PartialEq + core::fmt::Debug> SeekKeyEncoder<JmtValues> for (T, Version) {
304    fn encode_seek_key(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
305        self.encode_key()
306    }
307}
308
309impl KeyDecoder<JmtValues> for (StateKey, Version) {
310    fn decode_key(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
311        let mut cursor = maybestd::io::Cursor::new(data);
312        let key = Vec::<u8>::deserialize_reader(&mut cursor)?;
313        let version = cursor.read_u64::<BigEndian>()?;
314        Ok((key, version))
315    }
316}
317
318impl ValueCodec<JmtValues> for JmtValue {
319    fn encode_value(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
320        self.try_to_vec().map_err(CodecError::from)
321    }
322
323    fn decode_value(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
324        Ok(Self::deserialize_reader(&mut &data[..])?)
325    }
326}
327
328define_table_with_default_codec!(
329    /// A mapping from key-hashes to their preimages and latest version. Since we store raw
330    /// key-value pairs instead of keyHash->value pairs,
331    /// this table is required to implement the `jmt::TreeReader` trait,
332    /// which requires the ability to fetch values by hash.
333    (KeyHashToKey) [u8;32] => StateKey
334);