diff --git a/Cargo.lock b/Cargo.lock index e94df23bd2..eddc85ae63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6985,6 +6985,7 @@ dependencies = [ "borsh", "hex", "jmt", + "proptest", "risc0-zkvm", "risc0-zkvm-platform", "serde", diff --git a/module-system/sov-state/Cargo.toml b/module-system/sov-state/Cargo.toml index e881420502..97ebdb7985 100644 --- a/module-system/sov-state/Cargo.toml +++ b/module-system/sov-state/Cargo.toml @@ -32,6 +32,7 @@ risc0-zkvm-platform = { version = "0.16", optional = true } [dev-dependencies] tempfile = { workspace = true } +proptest = { workspace = true } [features] bench = ["sov-zk-cycle-macros", "risc0-zkvm", "risc0-zkvm-platform"] diff --git a/module-system/sov-state/src/codec/bcs_codec.rs b/module-system/sov-state/src/codec/bcs_codec.rs index 0635477a37..f950067612 100644 --- a/module-system/sov-state/src/codec/bcs_codec.rs +++ b/module-system/sov-state/src/codec/bcs_codec.rs @@ -1,7 +1,7 @@ use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; -/// A [`StateValueCodec`] that uses [`bcs`] for all keys and values. +/// A [`StateCodec`] that uses [`bcs`] for all keys and values. #[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] pub struct BcsCodec; @@ -32,6 +32,7 @@ where impl StateCodec for BcsCodec { type KeyCodec = Self; type ValueCodec = Self; + fn key_codec(&self) -> &Self::KeyCodec { self } diff --git a/module-system/sov-state/src/codec/borsh_codec.rs b/module-system/sov-state/src/codec/borsh_codec.rs index 92f59ca57a..3c5c99b064 100644 --- a/module-system/sov-state/src/codec/borsh_codec.rs +++ b/module-system/sov-state/src/codec/borsh_codec.rs @@ -1,7 +1,7 @@ use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; -/// A [`StateValueCodec`] that uses [`borsh`] for all values. +/// A [`StateCodec`] that uses [`borsh`] for all keys and values. #[derive(Debug, Default, PartialEq, Eq, Clone, borsh::BorshDeserialize, borsh::BorshSerialize)] pub struct BorshCodec; @@ -10,7 +10,7 @@ where K: borsh::BorshSerialize + borsh::BorshDeserialize, { fn encode_key(&self, value: &K) -> Vec { - value.try_to_vec().expect("Failed to serialize value") + value.try_to_vec().expect("Failed to serialize key") } } @@ -32,6 +32,7 @@ where impl StateCodec for BorshCodec { type KeyCodec = Self; type ValueCodec = Self; + fn key_codec(&self) -> &Self::KeyCodec { self } diff --git a/module-system/sov-state/src/codec/json_codec.rs b/module-system/sov-state/src/codec/json_codec.rs index c7d5ab35d8..4aed63349f 100644 --- a/module-system/sov-state/src/codec/json_codec.rs +++ b/module-system/sov-state/src/codec/json_codec.rs @@ -3,7 +3,7 @@ use serde_json; use super::{StateCodec, StateKeyCodec}; use crate::codec::StateValueCodec; -/// A [`StateValueCodec`] that uses [`serde_json`] for all values. +/// A [`StateCodec`] that uses [`serde_json`] for all keys and values. #[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] pub struct JsonCodec; @@ -12,7 +12,7 @@ where K: serde::Serialize, { fn encode_key(&self, key: &K) -> Vec { - serde_json::to_vec(key).expect("Failed to serialize value") + serde_json::to_vec(key).expect("Failed to serialize key") } } diff --git a/module-system/sov-state/src/codec/mod.rs b/module-system/sov-state/src/codec/mod.rs index 0a350b32e0..d081cf41a6 100644 --- a/module-system/sov-state/src/codec/mod.rs +++ b/module-system/sov-state/src/codec/mod.rs @@ -47,21 +47,43 @@ pub trait StateValueCodec { /// A trait for types that can serialize keys for storage /// access. +/// +/// Note that, unlike [`StateValueCodec`], this trait does not provide +/// deserialization logic as it's not needed nor supported. pub trait StateKeyCodec { + /// Serializes a key into a bytes vector. + /// + /// # Determinism + /// + /// All implementations of this trait method **MUST** provide deterministic + /// serialization behavior: + /// + /// 1. Equal (as defined by [`Eq`]) values **MUST** be serialized to the same + /// byte sequence. + /// 2. The serialization result **MUST NOT** depend on the compilation target + /// and other runtime environment parameters. If that were the case, zkVM + /// code and native code wouldn't produce the same keys. fn encode_key(&self, key: &K) -> Vec; } /// A trait for types that can serialize keys and values, as well /// as deserializing values for storage access. +/// +/// # Type bounds +/// There are no type bounds on [`StateCodec::KeyCodec`] and +/// [`StateCodec::ValueCodec`], so they can be any type at well. That said, +/// you'll find many APIs require these two to implement [`StateKeyCodec`] and +/// [`StateValueCodec`] respectively. pub trait StateCodec { - /// The codec used to serialize keys + /// The codec used to serialize keys. See [`StateKeyCodec`]. type KeyCodec; - /// The codec used to serialize and deserialize values + /// The codec used to serialize and deserialize values. See + /// [`StateValueCodec`]. type ValueCodec; - /// Returns a reference to the type's key codec + /// Returns a reference to the type's key codec. fn key_codec(&self) -> &Self::KeyCodec; - /// Returns a reference to the type's value codec + /// Returns a reference to the type's value codec. fn value_codec(&self) -> &Self::ValueCodec; } @@ -97,13 +119,26 @@ where } } -#[test] -fn test_borsh_slice_encode_alike() { - let codec = BorshCodec; - let slice = [1, 2, 3]; - let vec = vec![1, 2, 3]; - assert_eq!( - >>::encode_key_like(&codec, &slice), - codec.encode_value(&vec) - ); +#[cfg(test)] +mod tests { + use proptest::collection::vec; + use proptest::prelude::any; + use proptest::strategy::Strategy; + + use super::*; + + fn arb_vec_i32() -> impl Strategy> { + vec(any::(), 0..2048) + } + + proptest::proptest! { + #[test] + fn test_borsh_slice_encode_alike(vec in arb_vec_i32()) { + let codec = BorshCodec; + assert_eq!( + >>::encode_key_like(&codec, &vec[..]), + codec.encode_value(&vec) + ); + } + } } diff --git a/module-system/sov-state/src/codec/split_codec.rs b/module-system/sov-state/src/codec/split_codec.rs index 47d409c96a..2c19b667fc 100644 --- a/module-system/sov-state/src/codec/split_codec.rs +++ b/module-system/sov-state/src/codec/split_codec.rs @@ -5,7 +5,9 @@ use super::{StateCodec, StateKeyCodec, StateValueCodec}; /// A [`StateValueCodec`] that uses one pre-existing codec for keys and a different one values. #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct SplitCodec { + /// The codec to use for keys. pub key_codec: KC, + /// The codec to use for values. pub value_codec: VC, } @@ -36,6 +38,7 @@ where impl StateCodec for SplitCodec { type KeyCodec = KC; type ValueCodec = VC; + fn key_codec(&self) -> &Self::KeyCodec { &self.key_codec } diff --git a/module-system/sov-state/src/config.rs b/module-system/sov-state/src/config.rs index 3f5b6ddf9f..7bc460b4e1 100644 --- a/module-system/sov-state/src/config.rs +++ b/module-system/sov-state/src/config.rs @@ -1,7 +1,11 @@ +//! Configuration options for [`Storage`](crate::storage::Storage) types. + use std::path::PathBuf; +/// Configuration options for [`ProverStorage`](crate::ProverStorage) +/// initialization. #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Config { - /// Path to folder where storage files will be stored + /// Path to folder where storage files will be stored. pub path: PathBuf, } diff --git a/module-system/sov-state/src/containers/accessory_map.rs b/module-system/sov-state/src/containers/accessory_map.rs index e148ce651e..b55ba00a8c 100644 --- a/module-system/sov-state/src/containers/accessory_map.rs +++ b/module-system/sov-state/src/containers/accessory_map.rs @@ -190,6 +190,9 @@ where Codec::KeyCodec: StateKeyCodec, Codec::ValueCodec: StateValueCodec, { + /// Generates an arbitrary [`AccessoryStateMap`] instance. + /// + /// See the [`arbitrary`] crate for more information. pub fn arbitrary_workset( u: &mut arbitrary::Unstructured<'a>, working_set: &mut AccessoryWorkingSet, diff --git a/module-system/sov-state/src/containers/accessory_vec.rs b/module-system/sov-state/src/containers/accessory_vec.rs index 3028d83b73..279bf60158 100644 --- a/module-system/sov-state/src/containers/accessory_vec.rs +++ b/module-system/sov-state/src/containers/accessory_vec.rs @@ -6,6 +6,8 @@ use crate::{ AccessoryStateMap, AccessoryStateValue, AccessoryWorkingSet, Prefix, StateVecError, Storage, }; +/// A variant of [`StateVec`](crate::StateVec) that stores its elements as +/// "accessory" state, instead of in the JMT. #[derive( Debug, Clone, @@ -139,6 +141,7 @@ where Some(elem) } + /// Removes all values from this [`AccessoryStateVec`]. pub fn clear(&self, working_set: &mut AccessoryWorkingSet) { let len = self.len_value.remove(working_set).unwrap_or_default(); diff --git a/module-system/sov-state/src/containers/map.rs b/module-system/sov-state/src/containers/map.rs index 9eccfdbb40..7c64a748c3 100644 --- a/module-system/sov-state/src/containers/map.rs +++ b/module-system/sov-state/src/containers/map.rs @@ -31,6 +31,7 @@ pub struct StateMap { /// Error type for the [`StateMap::get`] method. #[derive(Debug, Error)] pub enum StateMapError { + /// Value not found. #[error("Value not found for prefix: {0} and: storage key {1}")] MissingValue(Prefix, StorageKey), } @@ -53,6 +54,7 @@ impl StateMap { } } + /// Returns a reference to the codec used by this [`StateMap`]. pub fn codec(&self) -> &Codec { &self.codec } @@ -206,6 +208,9 @@ where Codec::KeyCodec: StateKeyCodec, Codec::ValueCodec: StateValueCodec, { + /// Returns an arbitrary [`StateMap`] instance. + /// + /// See the [`arbitrary`] crate for more information. pub fn arbitrary_workset( u: &mut arbitrary::Unstructured<'a>, working_set: &mut WorkingSet, diff --git a/module-system/sov-state/src/containers/value.rs b/module-system/sov-state/src/containers/value.rs index 388c4e7dc7..1e3f55d674 100644 --- a/module-system/sov-state/src/containers/value.rs +++ b/module-system/sov-state/src/containers/value.rs @@ -24,6 +24,7 @@ pub struct StateValue { /// Error type for `StateValue` get method. #[derive(Debug, Error)] pub enum Error { + /// Value not found. #[error("Value not found for prefix: {0}")] MissingValue(Prefix), } diff --git a/module-system/sov-state/src/containers/vec.rs b/module-system/sov-state/src/containers/vec.rs index 1873064e3e..9c35ddab0d 100644 --- a/module-system/sov-state/src/containers/vec.rs +++ b/module-system/sov-state/src/containers/vec.rs @@ -6,6 +6,17 @@ use thiserror::Error; use crate::codec::{BorshCodec, StateCodec, StateKeyCodec, StateValueCodec}; use crate::{Prefix, StateMap, StateValue, Storage, WorkingSet}; +/// A growable array of values stored as JMT-backed state. +/// +/// # An Example +/// +/// ``` +/// use sov_state::StateVec; +/// +/// let mut state_vec = StateVec::::new(b"test".to_vec()); +/// +/// assert_eq!(state_vec.len(), 0); +/// ``` #[derive( Debug, Clone, @@ -25,8 +36,10 @@ pub struct StateVec { /// Error type for `StateVec` get method. #[derive(Debug, Error)] pub enum Error { + /// Operation failed because the index was out of bounds. #[error("Index out of bounds for index: {0}")] IndexOutOfBounds(usize), + /// Value not found. #[error("Value not found for prefix: {0} and index: {1}")] MissingValue(Prefix, usize), } @@ -141,6 +154,7 @@ where Some(elem) } + /// Removes all values from this [`StateVec`]. pub fn clear(&self, working_set: &mut WorkingSet) { let len = self.len_value.remove(working_set).unwrap_or_default(); @@ -183,7 +197,7 @@ where } } -/// An [`Iterator`] over a [`StateVec`] +/// An [`Iterator`] over a [`StateVec`]. /// /// See [`StateVec::iter`] for more details. pub struct StateVecIter<'a, 'ws, V, Codec, S> diff --git a/module-system/sov-state/src/lib.rs b/module-system/sov-state/src/lib.rs index 9be0bfc5ac..d1d5c86e63 100644 --- a/module-system/sov-state/src/lib.rs +++ b/module-system/sov-state/src/lib.rs @@ -1,3 +1,7 @@ +//! Storage and state management interfaces for Sovereign SDK modules. + +#![deny(missing_docs)] + pub mod codec; mod internal_cache; @@ -12,6 +16,7 @@ mod tree_db; mod scratchpad; +/// Trait and type definitions related to the [`Storage`] trait. pub mod storage; mod utils; @@ -36,10 +41,19 @@ use utils::AlignedVec; pub use crate::witness::{ArrayWitness, TreeWitnessReader, Witness}; -// A prefix prepended to each key before insertion and retrieval from the storage. -// All the collection types in this crate are backed by the same storage instance, this means that insertions of the same key -// to two different `StorageMaps` would collide with each other. We solve it by instantiating every collection type with a unique -// prefix that is prepended to each key. +/// A prefix prepended to each key before insertion and retrieval from the storage. +/// All the collection types in this crate are backed by the same storage instance, this means that insertions of the same key +/// to two different `StorageMaps` would collide with each other. We solve it by instantiating every collection type with a unique +/// prefix that is prepended to each key. +/// +/// # Examples +/// +/// ``` +/// use sov_state::Prefix; +/// +/// let prefix = Prefix::new(b"test".to_vec()); +/// assert_eq!(prefix.len(), "4"); +/// ``` #[derive( borsh::BorshDeserialize, borsh::BorshSerialize, @@ -70,25 +84,31 @@ impl Display for Prefix { } impl Prefix { + /// Creates a new prefix from a byte vector. pub fn new(prefix: Vec) -> Self { Self { prefix: AlignedVec::new(prefix), } } + /// Returns a reference to the internal [`AlignedVec`] used by this prefix. pub fn as_aligned_vec(&self) -> &AlignedVec { &self.prefix } + /// Returns the length in bytes of the prefix. pub fn len(&self) -> usize { self.prefix.len() } + /// Returns `true` if the prefix is empty, `false` otherwise. #[must_use] pub fn is_empty(&self) -> bool { self.prefix.is_empty() } + /// Returns a new prefix allocated on the fly, by extending the current + /// prefix with the given bytes. pub fn extended(&self, bytes: &[u8]) -> Self { let mut prefix = self.clone(); prefix.extend(bytes.iter().copied()); @@ -114,6 +134,9 @@ pub trait MerkleProofSpec { use sha2::Sha256; +/// The default [`MerkleProofSpec`] implementation. +/// +/// This type is typically found as a type parameter for [`ProverStorage`]. #[derive(Clone)] pub struct DefaultStorageSpec; diff --git a/module-system/sov-state/src/prover_storage.rs b/module-system/sov-state/src/prover_storage.rs index bed8157cc7..b8224cbad5 100644 --- a/module-system/sov-state/src/prover_storage.rs +++ b/module-system/sov-state/src/prover_storage.rs @@ -15,6 +15,8 @@ use crate::tree_db::TreeReadLogger; use crate::witness::Witness; use crate::{MerkleProofSpec, Storage}; +/// A [`Storage`] implementation to be used by the prover in a native execution +/// environment (outside of the zkVM). pub struct ProverStorage { db: StateDB, native_db: NativeDB, @@ -32,6 +34,8 @@ impl Clone for ProverStorage { } impl ProverStorage { + /// Creates a new [`ProverStorage`] instance at the specified path, opening + /// or creating the necessary RocksDB database(s) at the specified path. pub fn with_path(path: impl AsRef) -> Result { let state_db = StateDB::with_path(&path)?; let native_db = NativeDB::with_path(&path)?; @@ -217,6 +221,7 @@ impl NativeStorage for ProverStorage { } } +/// Deletes the storage at the specified path. pub fn delete_storage(path: impl AsRef) { fs::remove_dir_all(&path) .or_else(|_| fs::remove_file(&path)) @@ -230,6 +235,15 @@ mod test { use super::*; use crate::{DefaultStorageSpec, StateReaderAndWriter, WorkingSet}; + #[test] + fn delete_storage_works() { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path(); + assert!(path.exists()); + delete_storage(path); + assert!(!path.exists()); + } + #[derive(Clone)] struct TestCase { key: StorageKey, diff --git a/module-system/sov-state/src/scratchpad.rs b/module-system/sov-state/src/scratchpad.rs index 558b025a92..9c13df9fca 100644 --- a/module-system/sov-state/src/scratchpad.rs +++ b/module-system/sov-state/src/scratchpad.rs @@ -60,14 +60,19 @@ impl StateReaderAndWriter for Delta { type RevertableWrites = HashMap>; -/// This structure is responsible for storing the `read-write` set -/// and is obtained from the `WorkingSet` by using either the `commit` or `revert` method. +/// This structure is responsible for storing the `read-write` set. +/// +/// A [`StateCheckpoint`] can be obtained from a [`WorkingSet`] in two ways: +/// 1. With [`WorkingSet::checkpoint`]. +/// 2. With [`WorkingSet::revert`]. pub struct StateCheckpoint { delta: Delta, accessory_delta: AccessoryDelta, } impl StateCheckpoint { + /// Creates a new [`StateCheckpoint`] instance without any changes, backed + /// by the given [`Storage`]. pub fn new(inner: S) -> Self { Self { delta: Delta::new(inner.clone()), @@ -75,10 +80,13 @@ impl StateCheckpoint { } } + /// Fetches a value from the underlying storage. pub fn get(&mut self, key: &StorageKey) -> Option { self.delta.get(key) } + /// Creates a new [`StateCheckpoint`] instance without any changes, backed + /// by the given [`Storage`] and witness. pub fn with_witness(inner: S, witness: S::Witness) -> Self { Self { delta: Delta::with_witness(inner.clone(), witness), @@ -86,6 +94,7 @@ impl StateCheckpoint { } } + /// Transforms this [`StateCheckpoint`] back into a [`WorkingSet`]. pub fn to_revertable(self) -> WorkingSet { WorkingSet { delta: RevertableWriter::new(self.delta), @@ -94,10 +103,21 @@ impl StateCheckpoint { } } + /// Extracts ordered reads, writes, and witness from this [`StateCheckpoint`]. + /// + /// You can then use these to call [`Storage::validate_and_commit`] or some + /// of the other related [`Storage`] methods. Note that this data is moved + /// **out** of the [`StateCheckpoint`] i.e. it can't be extracted twice. pub fn freeze(&mut self) -> (OrderedReadsAndWrites, S::Witness) { self.delta.freeze() } + /// Extracts ordered reads and writes of accessory state from this + /// [`StateCheckpoint`]. + /// + /// You can then use these to call + /// [`Storage::validate_and_commit_with_accessory_update`], together with + /// the data extracted with [`StateCheckpoint::freeze`]. pub fn freeze_non_provable(&mut self) -> OrderedReadsAndWrites { self.accessory_delta.freeze() } @@ -259,18 +279,31 @@ impl StateReaderAndWriter for RevertableWriter { } impl WorkingSet { + /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`]. + /// + /// The witness value is set to [`Default::default`]. Use + /// [`WorkingSet::with_witness`] to set a custom witness value. pub fn new(inner: S) -> Self { StateCheckpoint::new(inner).to_revertable() } + /// Returns a handler for the accessory state (non-JMT state). + /// + /// You can use this method when calling getters and setters on accessory + /// state containers, like [`AccessoryStateMap`](crate::AccessoryStateMap). pub fn accessory_state(&mut self) -> AccessoryWorkingSet { AccessoryWorkingSet { ws: self } } + /// Creates a new [`WorkingSet`] instance backed by the given [`Storage`] + /// and a custom witness value. pub fn with_witness(inner: S, witness: S::Witness) -> Self { StateCheckpoint::with_witness(inner, witness).to_revertable() } + /// Turns this [`WorkingSet`] into a [`StateCheckpoint`], in preparation for + /// committing the changes to the underlying [`Storage`] via + /// [`StateCheckpoint::freeze`]. pub fn checkpoint(self) -> StateCheckpoint { StateCheckpoint { delta: self.delta.commit(), @@ -278,6 +311,8 @@ impl WorkingSet { } } + /// Reverts the most recent changes to this [`WorkingSet`], returning a pristine + /// [`StateCheckpoint`] instance. pub fn revert(self) -> StateCheckpoint { StateCheckpoint { delta: self.delta.revert(), @@ -285,18 +320,24 @@ impl WorkingSet { } } + /// Adds an event to the working set. pub fn add_event(&mut self, key: &str, value: &str) { self.events.push(Event::new(key, value)); } + /// Extracts all events from this working set. pub fn take_events(&mut self) -> Vec { std::mem::take(&mut self.events) } + /// Returns an immutable slice of all events that have been previously + /// written to this working set. pub fn events(&self) -> &[Event] { &self.events } + /// Returns an immutable reference to the [`Storage`] instance backing this + /// working set. pub fn backing(&self) -> &S { &self.delta.inner.inner } diff --git a/module-system/sov-state/src/storage.rs b/module-system/sov-state/src/storage.rs index d3c1445905..592f4190cf 100644 --- a/module-system/sov-state/src/storage.rs +++ b/module-system/sov-state/src/storage.rs @@ -14,7 +14,8 @@ use crate::utils::AlignedVec; use crate::witness::Witness; use crate::{Prefix, StateMap}; -// `Key` type for the `Storage` +/// The key type suitable for use in [`Storage::get`] and other getter methods of +/// [`Storage`]. Cheaply-clonable. #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, BorshDeserialize, BorshSerialize)] pub struct StorageKey { key: Arc>, @@ -27,16 +28,19 @@ impl From for StorageKey { } impl StorageKey { + /// Returns a new [`Arc`] reference to the bytes of this key. pub fn key(&self) -> Arc> { self.key.clone() } + /// Converts this key into a [`CacheKey`] via cloning. pub fn to_cache_key(&self) -> CacheKey { CacheKey { key: self.key.clone(), } } + /// Converts this key into a [`CacheKey`]. pub fn into_cache_key(self) -> CacheKey { CacheKey { key: self.key } } @@ -55,7 +59,7 @@ impl Display for StorageKey { } impl StorageKey { - /// Creates a new StorageKey that combines a prefix and a key. + /// Creates a new [`StorageKey`] that combines a prefix and a key. pub fn new(prefix: &Prefix, key: &Q, codec: &KC) -> Self where KC: EncodeKeyLike, @@ -74,7 +78,7 @@ impl StorageKey { } } - /// Creates a new StorageKey that combines a prefix and a key. + /// Creates a new [`StorageKey`] that combines a prefix and a key. pub fn singleton(prefix: &Prefix) -> Self { Self { key: Arc::new(prefix.as_aligned_vec().clone().into_inner()), @@ -82,7 +86,8 @@ impl StorageKey { } } -/// A serialized value suitable for storing. Internally uses an [`Arc>`] for cheap cloning. +/// A serialized value suitable for storing. Internally uses an [`Arc>`] +/// for cheap cloning. #[derive( Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize, Default, )] @@ -159,12 +164,20 @@ pub trait Storage: Clone { /// State update that will be committed to the database. type StateUpdate; + /// Creates a new instance of this [`Storage`] type, with some configuration + /// options. fn with_config(config: Self::RuntimeConfig) -> Result; /// Returns the value corresponding to the key or None if key is absent. fn get(&self, key: &StorageKey, witness: &Self::Witness) -> Option; /// Returns the value corresponding to the key or None if key is absent. + /// + /// # About accessory state + /// This method is blanket-implemented to return [`None`]. **Only native + /// execution environments** (i.e. outside of the zmVM) **SHOULD** override + /// this method to return a value. This is because accessory state **MUST + /// NOT** be readable from within the zmVM. fn get_accessory(&self, _key: &StorageKey) -> Option { None } @@ -222,6 +235,8 @@ pub trait Storage: Clone { proof: StorageProof, ) -> Result<(StorageKey, Option), anyhow::Error>; + /// Verifies the key-value pair returned by [`Storage::open_proof`] against + /// a [`StateMap`] entry. fn verify_proof( &self, state_root: [u8; 32], @@ -244,7 +259,7 @@ pub trait Storage: Clone { } /// Indicates if storage is empty or not. - /// Useful during initialization + /// Useful during initialization. fn is_empty(&self) -> bool; } @@ -268,6 +283,8 @@ impl From<&'static str> for StorageValue { } } +/// A [`Storage`] that is suitable for use in native execution environments +/// (outside of the zkVM). pub trait NativeStorage: Storage { /// Returns the value corresponding to the key or None if key is absent and a proof to /// get the value. Panics if [`get_with_proof_from_state_map`](NativeStorage::get_with_proof_from_state_map) @@ -275,6 +292,8 @@ pub trait NativeStorage: Storage { fn get_with_proof(&self, key: StorageKey, witness: &Self::Witness) -> StorageProof; + /// Same as [`get_with_proof`](NativeStorage::get_with_proof) but uses a + /// [`StateMap`] entry as a key. fn get_with_proof_from_state_map( &self, key: &Q, diff --git a/module-system/sov-state/src/zk_storage.rs b/module-system/sov-state/src/zk_storage.rs index abc0aeaa6a..1436182207 100644 --- a/module-system/sov-state/src/zk_storage.rs +++ b/module-system/sov-state/src/zk_storage.rs @@ -14,6 +14,7 @@ use crate::{MerkleProofSpec, Storage}; #[cfg(all(target_os = "zkvm", feature = "bench"))] extern crate risc0_zkvm; +/// A [`Storage`] implementation designed to be used inside the zkVM. #[derive(Default)] pub struct ZkStorage { _phantom_hasher: PhantomData, @@ -28,6 +29,7 @@ impl Clone for ZkStorage { } impl ZkStorage { + /// Creates a new [`ZkStorage`] instance. Identical to [`Default::default`]. pub fn new() -> Self { Self { _phantom_hasher: Default::default(),