Skip to content

Commit

Permalink
Blob storage module (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
citizen-stig authored and preston-evans98 committed Sep 14, 2023
1 parent 79f9852 commit 14cfe8e
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 10 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ members = [
"module-system/utils/sov-first-read-last-write-cache",
"module-system/module-implementations/sov-accounts",
"module-system/module-implementations/sov-bank",
"module-system/module-implementations/sov-blob-storage",
"module-system/module-implementations/sov-evm",
"module-system/module-implementations/sov-prover-incentives",
"module-system/module-implementations/sov-sequencer-registry",
Expand Down Expand Up @@ -51,6 +52,9 @@ jmt = "0.6.0"
async-trait = "0.1.71"
anyhow = "1.0.68"
borsh = { version = "0.10.3", features = ["rc", "bytes"] }
# TODO: Consider replacing this serialization format
# https://github.com/Sovereign-Labs/sovereign-sdk/issues/283
bincode = "1.3.3"
byteorder = "1.4.3"
bytes = "1.2.1"
hex = "0.4.3"
Expand Down
8 changes: 3 additions & 5 deletions adapters/risc0/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sov-rollup-interface = { path = "../../rollup-interface" }
anyhow = { workspace = true }
bincode = { workspace = true }
risc0-zkvm = { version = "0.16", default-features = false, features = ['std'] }
risc0-zkp = { version = "0.16", optional = true }
risc0-circuit-rv32im = { version = "0.16", optional = true }
serde = { workspace = true }
anyhow = { workspace = true }

# TODO: Consider replacing this serialization format
# https://github.com/Sovereign-Labs/sovereign-sdk/issues/283
bincode = "1.3.3"
sov-rollup-interface = { path = "../../rollup-interface" }


[features]
Expand Down
2 changes: 1 addition & 1 deletion full-node/db/sov-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ byteorder = { workspace = true }
borsh = { workspace = true }
serde = { workspace = true, features = ["derive"] }
rocksdb = { workspace = true }
bincode = "1.3.3"
bincode = { workspace = true }


[dev-dependencies]
Expand Down
30 changes: 30 additions & 0 deletions module-system/module-implementations/sov-blob-storage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "sov-blob-storage"
description = "A Sovereign SDK module for holding blobs from Data Availability Layer"
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"

resolver = "2"


[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
sov-modules-api = { path = "../../sov-modules-api", version = "0.1", default-features = false }
sov-modules-macros = { path = "../../sov-modules-macros", version = "0.1" }
sov-state = { path = "../../sov-state", version = "0.1", default-features = false }
sov-rollup-interface = { path = "../../../rollup-interface", version = "0.1" }

[dev-dependencies]
sov-modules-api = { path = "../../sov-modules-api", version = "0.1" }
tempfile = { workspace = true }

[features]
default = ["native"]
native = ["sov-modules-api/native", "sov-state/native"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# sov-blob-storage

Blob storage module allows to store arbitrary blobs in state of the module

Caller is responsible for correct match between block number and actual blob, as well as for order of blobs.
62 changes: 62 additions & 0 deletions module-system/module-implementations/sov-blob-storage/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#![deny(missing_docs)]

//! Blob storage module allows to save DA blobs in the state
use sov_modules_api::Module;
use sov_modules_macros::ModuleInfo;
use sov_rollup_interface::da::BlobTransactionTrait;
use sov_state::{StateMap, WorkingSet};

/// Blob storage contains only address and vector of blobs
#[derive(ModuleInfo, Clone)]
pub struct BlobStorage<C: sov_modules_api::Context> {
/// The address of blob storage module
/// Note: this is address is generated by the module framework and the corresponding private key is unknown.
#[address]
pub(crate) address: C::Address,

/// Actual storage of blobs
/// DA block number => vector of blobs
/// Caller controls the order of blobs in the vector
#[state]
pub(crate) blobs: StateMap<u64, Vec<Vec<u8>>>,
}

/// Non standard methods for blob storage
impl<C: sov_modules_api::Context> BlobStorage<C> {
/// Store blobs for given block number, overwrite if already exists
pub fn store_blobs<B: BlobTransactionTrait>(
&self,
block_number: u64,
blobs: &[B],
working_set: &mut WorkingSet<C::Storage>,
) -> anyhow::Result<()> {
let mut raw_blobs: Vec<Vec<u8>> = Vec::with_capacity(blobs.len());
for blob in blobs {
raw_blobs.push(bincode::serialize(blob)?);
}
self.blobs.set(&block_number, &raw_blobs, working_set);
Ok(())
}

/// Take all blobs for given block number, return empty vector if not exists
/// Returned blobs are removed from the storage
pub fn take_blobs_for_block_number<B: BlobTransactionTrait>(
&self,
block_number: u64,
working_set: &mut WorkingSet<C::Storage>,
) -> Vec<B> {
self.blobs
.remove(&block_number, working_set)
.unwrap_or_default()
.iter()
.map(|b| bincode::deserialize(b).expect("malformed blob was stored previously"))
.collect()
}
}

/// Empty module implementation
impl<C: sov_modules_api::Context> Module for BlobStorage<C> {
type Context = C;
type Config = ();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use sov_blob_storage::BlobStorage;
use sov_modules_api::default_context::DefaultContext;
use sov_modules_api::Genesis;
use sov_rollup_interface::mocks::{MockAddress, TestBlob};
use sov_state::{ProverStorage, WorkingSet};

type C = DefaultContext;
type B = TestBlob<MockAddress>;

#[test]
fn empty_test() {
let tmpdir = tempfile::tempdir().unwrap();
let mut working_set = WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap());
let blob_storage = BlobStorage::<C>::default();

blob_storage.genesis(&(), &mut working_set).unwrap();

let blobs: Vec<B> = blob_storage.take_blobs_for_block_number(1, &mut working_set);

assert!(blobs.is_empty());
}

#[test]
fn store_and_retrieve_standard() {
let tmpdir = tempfile::tempdir().unwrap();
let mut working_set = WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap());
let blob_storage = BlobStorage::<C>::default();

blob_storage.genesis(&(), &mut working_set).unwrap();

assert!(blob_storage
.take_blobs_for_block_number::<B>(1, &mut working_set)
.is_empty());
assert!(blob_storage
.take_blobs_for_block_number::<B>(2, &mut working_set)
.is_empty());
assert!(blob_storage
.take_blobs_for_block_number::<B>(3, &mut working_set)
.is_empty());
assert!(blob_storage
.take_blobs_for_block_number::<B>(4, &mut working_set)
.is_empty());

let sender = MockAddress::from([1u8; 32]);
let dummy_hash = [2u8; 32];

let blob_1 = B::new(vec![1, 2, 3], sender, dummy_hash);
let blob_2 = B::new(vec![3, 4, 5], sender, dummy_hash);
let blob_3 = B::new(vec![6, 7, 8], sender, dummy_hash);
let blob_4 = B::new(vec![9, 9, 9], sender, dummy_hash);
let blob_5 = B::new(vec![0, 1, 0], sender, dummy_hash);

let block_2_blobs = vec![blob_1, blob_2, blob_3];
let block_3_blobs = vec![blob_4];
let block_4_blobs = vec![blob_5];

blob_storage
.store_blobs(2, &block_2_blobs, &mut working_set)
.unwrap();
blob_storage
.store_blobs(3, &block_3_blobs, &mut working_set)
.unwrap();
blob_storage
.store_blobs(4, &block_4_blobs, &mut working_set)
.unwrap();

assert_eq!(
block_2_blobs,
blob_storage.take_blobs_for_block_number(2, &mut working_set)
);
assert!(blob_storage
.take_blobs_for_block_number::<B>(2, &mut working_set)
.is_empty());

assert_eq!(
block_3_blobs,
blob_storage.take_blobs_for_block_number(3, &mut working_set)
);
assert!(blob_storage
.take_blobs_for_block_number::<B>(3, &mut working_set)
.is_empty());

assert_eq!(
block_4_blobs,
blob_storage.take_blobs_for_block_number(4, &mut working_set)
);
assert!(blob_storage
.take_blobs_for_block_number::<B>(4, &mut working_set)
.is_empty());
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ schemars = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true, optional = true }
borsh = { workspace = true, features = ["rc"] }
# TODO: Replace with serde-compatible borsh implementation when it becomes availabile
# see https://github.com/Sovereign-Labs/sovereign-sdk/issues/215
bincode = "1.3.3"
bincode = { workspace = true }


[features]
Expand Down
1 change: 1 addition & 0 deletions packages_to_publish.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ sov-accounts
sov-bank
sov-sequencer-registry
sov-prover-incentives
sov-blob-storage
3 changes: 2 additions & 1 deletion rollup-interface/src/state_machine/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn test_mock_proof_roundtrip() {
}

/// A mock address type used for testing. Internally, this type is standard 32 byte array.
#[derive(Debug, PartialEq, Clone, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, Clone, Eq, Copy, serde::Serialize, serde::Deserialize)]
pub struct MockAddress {
addr: [u8; 32],
}
Expand Down Expand Up @@ -144,6 +144,7 @@ impl AddressTrait for MockAddress {}
#[derive(
Debug,
Clone,
PartialEq,
borsh::BorshDeserialize,
borsh::BorshSerialize,
serde::Serialize,
Expand Down

0 comments on commit 14cfe8e

Please sign in to comment.