-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'nightly' into preston/da_interface
- Loading branch information
Showing
28 changed files
with
1,265 additions
and
225 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use std::path::Path; | ||
use std::sync::Arc; | ||
|
||
use sov_schema_db::{SchemaBatch, DB}; | ||
|
||
use crate::rocks_db_config::gen_rocksdb_options; | ||
use crate::schema::tables::{ModuleAccessoryState, NATIVE_TABLES}; | ||
use crate::schema::types::StateKey; | ||
|
||
/// A typed wrapper around RocksDB for storing native-only accessory state. | ||
/// Internally, this is roughly just an [`Arc<SchemaDB>`]. | ||
#[derive(Clone)] | ||
pub struct NativeDB { | ||
/// The underlying RocksDB instance, wrapped in an [`Arc`] for convenience | ||
/// and [`DB`] for type safety. | ||
db: Arc<DB>, | ||
} | ||
|
||
impl NativeDB { | ||
const DB_PATH_SUFFIX: &str = "native"; | ||
const DB_NAME: &str = "native-db"; | ||
|
||
/// Opens a [`NativeDB`] (backed by RocksDB) at the specified path. | ||
/// The returned instance will be at the path `{path}/native-db`. | ||
pub fn with_path(path: impl AsRef<Path>) -> Result<Self, anyhow::Error> { | ||
let path = path.as_ref().join(Self::DB_PATH_SUFFIX); | ||
let inner = DB::open( | ||
path, | ||
Self::DB_NAME, | ||
NATIVE_TABLES.iter().copied(), | ||
&gen_rocksdb_options(&Default::default(), false), | ||
)?; | ||
|
||
Ok(Self { | ||
db: Arc::new(inner), | ||
}) | ||
} | ||
|
||
/// Queries for a value in the [`NativeDB`], given a key. | ||
pub fn get_value_option(&self, key: &StateKey) -> anyhow::Result<Option<Vec<u8>>> { | ||
self.db | ||
.get::<ModuleAccessoryState>(key) | ||
.map(Option::flatten) | ||
} | ||
|
||
/// Sets a key-value pair in the [`NativeDB`]. | ||
pub fn set_value(&self, key: Vec<u8>, value: Option<Vec<u8>>) -> anyhow::Result<()> { | ||
self.set_values(vec![(key, value)]) | ||
} | ||
|
||
/// Sets a sequence of key-value pairs in the [`NativeDB`]. The write is atomic. | ||
pub fn set_values( | ||
&self, | ||
key_value_pairs: Vec<(Vec<u8>, Option<Vec<u8>>)>, | ||
) -> anyhow::Result<()> { | ||
let batch = SchemaBatch::default(); | ||
for (key, value) in key_value_pairs { | ||
batch.put::<ModuleAccessoryState>(&key, &value)?; | ||
} | ||
self.db.write_schemas(batch) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn get_after_set() { | ||
let tmpdir = tempfile::tempdir().unwrap(); | ||
let db = NativeDB::with_path(tmpdir.path()).unwrap(); | ||
|
||
let key = b"foo".to_vec(); | ||
let value = b"bar".to_vec(); | ||
db.set_values(vec![(key.clone(), Some(value.clone()))]) | ||
.unwrap(); | ||
assert_eq!(db.get_value_option(&key).unwrap(), Some(value)); | ||
} | ||
|
||
#[test] | ||
fn get_after_delete() { | ||
let tmpdir = tempfile::tempdir().unwrap(); | ||
let db = NativeDB::with_path(tmpdir.path()).unwrap(); | ||
|
||
let key = b"deleted".to_vec(); | ||
db.set_value(key.clone(), None).unwrap(); | ||
assert_eq!(db.get_value_option(&key).unwrap(), None); | ||
} | ||
|
||
#[test] | ||
fn get_nonexistent() { | ||
let tmpdir = tempfile::tempdir().unwrap(); | ||
let db = NativeDB::with_path(tmpdir.path()).unwrap(); | ||
|
||
let key = b"spam".to_vec(); | ||
assert_eq!(db.get_value_option(&key).unwrap(), None); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
module-system/module-implementations/examples/sov-accessory-state/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
[package] | ||
name = "sov-accessory-state" | ||
authors = { workspace = true } | ||
edition = { workspace = true } | ||
homepage = { workspace = true } | ||
license = { workspace = true } | ||
repository = { workspace = true } | ||
rust-version = { workspace = true } | ||
version = { workspace = true } | ||
readme = "README.md" | ||
publish = false | ||
resolver = "2" | ||
|
||
[dependencies] | ||
jsonrpsee = { workspace = true, features = ["macros", "client-core", "server"], optional = true } | ||
sov-modules-api = { path = "../../../sov-modules-api" } | ||
sov-state = { path = "../../../sov-state" } | ||
serde = { workspace = true, optional = true } | ||
borsh = { workspace = true, features = ["rc"] } | ||
|
||
[dev-dependencies] | ||
tempfile = { workspace = true } | ||
|
||
[features] | ||
default = [] | ||
native = ["serde", "sov-modules-api/native", "dep:jsonrpsee"] |
5 changes: 5 additions & 0 deletions
5
module-system/module-implementations/examples/sov-accessory-state/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# The `sov-accessory-state` module | ||
|
||
This module has no useful functionality, but it illustrates the ability to store data outside of the JMT, called "accessory state". | ||
|
||
Accessory state does not contribute to the state root hash and can be written during zkVM execution, but reads are only allowed in a native execution context. Accessory state data can be used for tooling, serving JSON-RPC data, debugging, and any other purpose that is not core to the core functioning of the module without incurring in major performance penalties. |
68 changes: 68 additions & 0 deletions
68
module-system/module-implementations/examples/sov-accessory-state/src/lib.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#![deny(missing_docs)] | ||
#![doc = include_str!("../README.md")] | ||
|
||
#[cfg(feature = "native")] | ||
pub mod query; | ||
|
||
use sov_modules_api::{CallResponse, Context, Error, Module, ModuleInfo}; | ||
use sov_state::{AccessoryStateValue, StateValue, WorkingSet}; | ||
|
||
/// [`AccessorySetter`] is a module that stores data both *inside* the JMT and | ||
/// *outside* the JMT. | ||
/// | ||
/// Data stored inside the JMT contributes to the state root hash, and is always | ||
/// accessible. This costs significant compute, and should be avoided for all | ||
/// data that is not necessary to the core functioning of the rollup. Other data | ||
/// that facilitates serving queries over JSON-RPC or only accessed by tooling | ||
/// doesn't need to be verifiable, and can thus be stored outside the JMT much | ||
/// more cheaply. Since accessory data is not included in the state root hash, | ||
/// it is not accessible inside the zkVM and can only be accessed with | ||
/// `#[cfg(feature = "native")]`. | ||
#[derive(ModuleInfo)] | ||
pub struct AccessorySetter<C: sov_modules_api::Context> { | ||
/// The address of the module. | ||
#[address] | ||
pub address: C::Address, | ||
/// Some arbitrary value stored in the JMT to demonstrate the difference | ||
/// between the JMT and accessory state. | ||
#[state] | ||
pub state_value: StateValue<String>, | ||
/// A non-JMT value stored in the accessory state. | ||
#[state] | ||
pub accessory_value: AccessoryStateValue<String>, | ||
} | ||
|
||
/// The [`Module::CallMessage`] for [`AccessorySetter`]. | ||
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq)] | ||
pub enum CallMessage { | ||
/// Sets the value of [`AccessorySetter::state_value`]. | ||
SetValue(String), | ||
/// Stores some arbitrary value in the accessory state. | ||
SetValueAccessory(String), | ||
} | ||
|
||
impl<C: Context> Module for AccessorySetter<C> { | ||
type Context = C; | ||
|
||
type Config = (); | ||
|
||
type CallMessage = CallMessage; | ||
|
||
fn call( | ||
&self, | ||
msg: Self::CallMessage, | ||
_context: &Self::Context, | ||
working_set: &mut WorkingSet<C::Storage>, | ||
) -> Result<sov_modules_api::CallResponse, Error> { | ||
match msg { | ||
CallMessage::SetValueAccessory(new_value) => { | ||
self.accessory_value | ||
.set(&new_value, &mut working_set.accessory_state()); | ||
} | ||
CallMessage::SetValue(new_value) => { | ||
self.state_value.set(&new_value, working_set); | ||
} | ||
}; | ||
Ok(CallResponse::default()) | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
module-system/module-implementations/examples/sov-accessory-state/src/query.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
//! JSON-RPC server implementation for the [`AccessorySetter`] module. | ||
use jsonrpsee::core::RpcResult; | ||
use sov_modules_api::macros::rpc_gen; | ||
use sov_state::WorkingSet; | ||
|
||
use super::AccessorySetter; | ||
|
||
/// Response type to the `accessorySetter_value` endpoint. | ||
#[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone)] | ||
pub struct ValueResponse { | ||
/// The value stored in the accessory state. | ||
pub value: Option<String>, | ||
} | ||
|
||
#[rpc_gen(client, server, namespace = "accessorySetter")] | ||
impl<C: sov_modules_api::Context> AccessorySetter<C> { | ||
/// Returns the latest value set in the accessory state via | ||
/// [`CallMessage::SetValueAccessory`](crate::CallMessage::SetValueAccessory). | ||
#[rpc_method(name = "value")] | ||
pub fn query_value( | ||
&self, | ||
working_set: &mut WorkingSet<C::Storage>, | ||
) -> RpcResult<ValueResponse> { | ||
Ok(ValueResponse { | ||
value: self.accessory_value.get(&mut working_set.accessory_state()), | ||
}) | ||
} | ||
} |
Oops, something went wrong.