diff --git a/Cargo.toml b/Cargo.toml index e7e5f03a6..bec19f522 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", @@ -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" diff --git a/adapters/risc0/Cargo.toml b/adapters/risc0/Cargo.toml index f50dbdb12..e30c457e2 100644 --- a/adapters/risc0/Cargo.toml +++ b/adapters/risc0/Cargo.toml @@ -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] diff --git a/full-node/db/sov-db/Cargo.toml b/full-node/db/sov-db/Cargo.toml index b7db76cd1..8caaa2733 100644 --- a/full-node/db/sov-db/Cargo.toml +++ b/full-node/db/sov-db/Cargo.toml @@ -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] diff --git a/module-system/module-implementations/sov-blob-storage/Cargo.toml b/module-system/module-implementations/sov-blob-storage/Cargo.toml new file mode 100644 index 000000000..bf998174e --- /dev/null +++ b/module-system/module-implementations/sov-blob-storage/Cargo.toml @@ -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"] diff --git a/module-system/module-implementations/sov-blob-storage/src/README.md b/module-system/module-implementations/sov-blob-storage/src/README.md new file mode 100644 index 000000000..daeb9d5f2 --- /dev/null +++ b/module-system/module-implementations/sov-blob-storage/src/README.md @@ -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. \ No newline at end of file diff --git a/module-system/module-implementations/sov-blob-storage/src/lib.rs b/module-system/module-implementations/sov-blob-storage/src/lib.rs new file mode 100644 index 000000000..71fd61ebb --- /dev/null +++ b/module-system/module-implementations/sov-blob-storage/src/lib.rs @@ -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 { + /// 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>>, +} + +/// Non standard methods for blob storage +impl BlobStorage { + /// Store blobs for given block number, overwrite if already exists + pub fn store_blobs( + &self, + block_number: u64, + blobs: &[B], + working_set: &mut WorkingSet, + ) -> anyhow::Result<()> { + let mut raw_blobs: Vec> = 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( + &self, + block_number: u64, + working_set: &mut WorkingSet, + ) -> Vec { + 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 Module for BlobStorage { + type Context = C; + type Config = (); +} diff --git a/module-system/module-implementations/sov-blob-storage/tests/blob_storage_tests.rs b/module-system/module-implementations/sov-blob-storage/tests/blob_storage_tests.rs new file mode 100644 index 000000000..6d8509de4 --- /dev/null +++ b/module-system/module-implementations/sov-blob-storage/tests/blob_storage_tests.rs @@ -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; + +#[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::::default(); + + blob_storage.genesis(&(), &mut working_set).unwrap(); + + let blobs: Vec = 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::::default(); + + blob_storage.genesis(&(), &mut working_set).unwrap(); + + assert!(blob_storage + .take_blobs_for_block_number::(1, &mut working_set) + .is_empty()); + assert!(blob_storage + .take_blobs_for_block_number::(2, &mut working_set) + .is_empty()); + assert!(blob_storage + .take_blobs_for_block_number::(3, &mut working_set) + .is_empty()); + assert!(blob_storage + .take_blobs_for_block_number::(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::(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::(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::(4, &mut working_set) + .is_empty()); +} diff --git a/module-system/module-implementations/sov-prover-incentives/Cargo.toml b/module-system/module-implementations/sov-prover-incentives/Cargo.toml index 68e7bd384..08331aea2 100644 --- a/module-system/module-implementations/sov-prover-incentives/Cargo.toml +++ b/module-system/module-implementations/sov-prover-incentives/Cargo.toml @@ -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] diff --git a/packages_to_publish.txt b/packages_to_publish.txt index 6ce2311b7..206c86fe2 100644 --- a/packages_to_publish.txt +++ b/packages_to_publish.txt @@ -11,3 +11,4 @@ sov-accounts sov-bank sov-sequencer-registry sov-prover-incentives +sov-blob-storage \ No newline at end of file diff --git a/rollup-interface/src/state_machine/mocks.rs b/rollup-interface/src/state_machine/mocks.rs index 811da34b9..95e4cd867 100644 --- a/rollup-interface/src/state_machine/mocks.rs +++ b/rollup-interface/src/state_machine/mocks.rs @@ -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], } @@ -144,6 +144,7 @@ impl AddressTrait for MockAddress {} #[derive( Debug, Clone, + PartialEq, borsh::BorshDeserialize, borsh::BorshSerialize, serde::Serialize,