From 8a56468f257b44817281a47c4205f97b9b1982fe Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 6 Jan 2025 12:22:39 +0530 Subject: [PATCH 01/14] move delta backend from execution prover to custom api --- Cargo.lock | 3 +- crates/sp-domains-fraud-proof/Cargo.toml | 5 +- .../src/execution_prover.rs | 54 +----------------- domains/client/block-builder/Cargo.toml | 1 + .../client/block-builder/src/custom_api.rs | 57 +++++++++++++++++++ domains/client/block-builder/src/lib.rs | 3 + 6 files changed, 66 insertions(+), 57 deletions(-) create mode 100644 domains/client/block-builder/src/custom_api.rs diff --git a/Cargo.lock b/Cargo.lock index 24779abeb8..6853831ce1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2506,6 +2506,7 @@ dependencies = [ name = "domain-block-builder" version = "0.1.0" dependencies = [ + "hash-db", "parity-scale-codec", "sc-client-api", "sp-api", @@ -11835,7 +11836,6 @@ dependencies = [ "pallet-ethereum", "pallet-evm", "parity-scale-codec", - "parking_lot 0.12.3", "rand", "rlp", "sc-cli", @@ -11860,7 +11860,6 @@ dependencies = [ "sp-weights", "subspace-core-primitives", "subspace-runtime-primitives", - "subspace-test-client", "subspace-test-service", "tempfile", "thiserror 2.0.0", diff --git a/crates/sp-domains-fraud-proof/Cargo.toml b/crates/sp-domains-fraud-proof/Cargo.toml index defda73a60..08a8d1c584 100644 --- a/crates/sp-domains-fraud-proof/Cargo.toml +++ b/crates/sp-domains-fraud-proof/Cargo.toml @@ -12,6 +12,7 @@ include = [ [dependencies] codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } +domain-block-builder = { version = "0.1.0", path = "../../domains/client/block-builder", optional = true, default-features = false } domain-block-preprocessor = { version = "0.1.0", default-features = false, path = "../../domains/client/block-preprocessor", optional = true } domain-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../domains/primitives/runtime" } frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } @@ -41,7 +42,6 @@ trie-db = { version = "0.29.1", default-features = false } thiserror = { version = "2.0.0", default-features = false } [dev-dependencies] -domain-block-builder = { version = "0.1.0", path = "../../domains/client/block-builder" } domain-block-preprocessor = { version = "0.1.0", path = "../../domains/client/block-preprocessor" } domain-test-service = { version = "0.1.0", path = "../../domains/test/service" } ethereum = "0.15.0" @@ -54,13 +54,11 @@ libsecp256k1 = { version = "0.7.1", features = ["static-context", "hmac"] } pallet-balances = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } pallet-ethereum = { git = "https://github.com/autonomys/frontier", rev = "f80f9e2bad338f3bf3854b256b3c4edea23e5968", features = ['default'] } pallet-evm = { version = "6.0.0-dev", git = "https://github.com/autonomys/frontier", rev = "f80f9e2bad338f3bf3854b256b3c4edea23e5968", default-features = false } -parking_lot = "0.12.2" rand = { version = "0.8.5", features = ["min_const_gen"] } rlp = "0.5.2" sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sc-cli = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305", default-features = false } sc-service = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305", default-features = false } -subspace-test-client = { version = "0.1.0", path = "../../test/subspace-test-client" } subspace-test-service = { version = "0.1.0", path = "../../test/subspace-test-service" } subspace-runtime-primitives = { version = "0.1.0", path = "../../crates/subspace-runtime-primitives" } tempfile = "3.13.0" @@ -70,6 +68,7 @@ tokio = "1.40.0" default = ["std"] std = [ "codec/std", + "domain-block-builder", "domain-block-preprocessor", "domain-runtime-primitives/std", "frame-support/std", diff --git a/crates/sp-domains-fraud-proof/src/execution_prover.rs b/crates/sp-domains-fraud-proof/src/execution_prover.rs index 3ca980f615..84fe2919a4 100644 --- a/crates/sp-domains-fraud-proof/src/execution_prover.rs +++ b/crates/sp-domains-fraud-proof/src/execution_prover.rs @@ -5,14 +5,13 @@ //! (`initialize_block` and `finalize_block`) and any specific extrinsic execution are supported. use crate::fraud_proof::ExecutionPhase; -use codec::Codec; -use hash_db::{HashDB, Hasher, Prefix}; +use domain_block_builder::create_delta_backend; +use hash_db::HashDB; use sc_client_api::backend::Backend; use sp_api::StorageProof; use sp_core::traits::CodeExecutor; use sp_runtime::traits::{Block as BlockT, HashingFor}; use sp_state_machine::backend::AsTrieBackend; -use sp_state_machine::{TrieBackend, TrieBackendBuilder, TrieBackendStorage}; use sp_trie::DBValue; use std::marker::PhantomData; use std::sync::Arc; @@ -87,52 +86,3 @@ where } } } - -/// Create a new trie backend with memory DB delta changes. -/// -/// This can be used to verify any extrinsic-specific execution on the combined state of `backend` -/// and `delta`. -fn create_delta_backend<'a, S, H, DB>( - backend: &'a TrieBackend, - delta: DB, - post_delta_root: H::Out, -) -> TrieBackend, H> -where - S: 'a + TrieBackendStorage, - H: 'a + Hasher, - H::Out: Codec, - DB: HashDB, -{ - let essence = backend.essence(); - let delta_backend = DeltaBackend { - backend: essence.backend_storage(), - delta, - _phantom: PhantomData::, - }; - TrieBackendBuilder::new(delta_backend, post_delta_root).build() -} - -struct DeltaBackend<'a, S, H, DB> -where - S: 'a + TrieBackendStorage, - H: 'a + Hasher, - DB: HashDB, -{ - backend: &'a S, - delta: DB, - _phantom: PhantomData, -} - -impl<'a, S, H, DB> TrieBackendStorage for DeltaBackend<'a, S, H, DB> -where - S: 'a + TrieBackendStorage, - H: 'a + Hasher, - DB: HashDB, -{ - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { - match HashDB::get(&self.delta, key, prefix) { - Some(v) => Ok(Some(v)), - None => Ok(self.backend.get(key, prefix)?), - } - } -} diff --git a/domains/client/block-builder/Cargo.toml b/domains/client/block-builder/Cargo.toml index 0cd58c5130..18daefca98 100644 --- a/domains/client/block-builder/Cargo.toml +++ b/domains/client/block-builder/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.12", features = ["derive"] } +hash-db = { version = "0.16.0", default-features = false } sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-blockchain = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs new file mode 100644 index 0000000000..f07aba67da --- /dev/null +++ b/domains/client/block-builder/src/custom_api.rs @@ -0,0 +1,57 @@ +//! Custom API that is efficient to collect storage roots. + +use codec::Codec; +use hash_db::{HashDB, Hasher, Prefix}; +use sp_state_machine::{DBValue, TrieBackend, TrieBackendBuilder, TrieBackendStorage}; +use std::marker::PhantomData; + +/// Create a new trie backend with memory DB delta changes. +/// +/// This can be used to verify any extrinsic-specific execution on the combined state of `backend` +/// and `delta`. +pub fn create_delta_backend<'a, S, H, DB>( + backend: &'a TrieBackend, + delta: DB, + post_delta_root: H::Out, +) -> TrieBackend, H> +where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + H::Out: Codec, + DB: HashDB, +{ + let essence = backend.essence(); + let delta_backend = DeltaBackend { + backend: essence.backend_storage(), + delta, + _phantom: PhantomData::, + }; + TrieBackendBuilder::new(delta_backend, post_delta_root).build() +} + +/// DeltaBackend provides the TrieBackend using main backend and some delta changes +/// that are not part of the main backend. +pub struct DeltaBackend<'a, S, H, DB> +where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + DB: HashDB, +{ + backend: &'a S, + delta: DB, + _phantom: PhantomData, +} + +impl<'a, S, H, DB> TrieBackendStorage for DeltaBackend<'a, S, H, DB> +where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + DB: HashDB, +{ + fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + match HashDB::get(&self.delta, key, prefix) { + Some(v) => Ok(Some(v)), + None => Ok(self.backend.get(key, prefix)?), + } + } +} diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index a3d7fb8c84..2df5c4f910 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -26,7 +26,10 @@ #![warn(missing_docs)] +mod custom_api; + use codec::Encode; +pub use custom_api::{create_delta_backend, DeltaBackend}; use sc_client_api::backend; use sp_api::{ ApiExt, ApiRef, Core, ProvideRuntimeApi, StorageChanges, StorageProof, TransactionOutcome, From 1b578e7db0e53c689a08add79e04deb0c6c4739e Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 6 Jan 2025 17:56:49 +0530 Subject: [PATCH 02/14] add trie backend api --- Cargo.lock | 1 + .../src/execution_prover.rs | 7 +- domains/client/block-builder/Cargo.toml | 1 + .../client/block-builder/src/custom_api.rs | 271 +++++++++++++++++- domains/client/block-builder/src/lib.rs | 1 + 5 files changed, 262 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6853831ce1..7a395a12e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2513,6 +2513,7 @@ dependencies = [ "sp-block-builder", "sp-blockchain", "sp-core", + "sp-externalities", "sp-inherents", "sp-runtime", "sp-state-machine", diff --git a/crates/sp-domains-fraud-proof/src/execution_prover.rs b/crates/sp-domains-fraud-proof/src/execution_prover.rs index 84fe2919a4..3414d1c282 100644 --- a/crates/sp-domains-fraud-proof/src/execution_prover.rs +++ b/crates/sp-domains-fraud-proof/src/execution_prover.rs @@ -6,13 +6,12 @@ use crate::fraud_proof::ExecutionPhase; use domain_block_builder::create_delta_backend; -use hash_db::HashDB; use sc_client_api::backend::Backend; use sp_api::StorageProof; use sp_core::traits::CodeExecutor; use sp_runtime::traits::{Block as BlockT, HashingFor}; use sp_state_machine::backend::AsTrieBackend; -use sp_trie::DBValue; +use sp_state_machine::BackendTransaction; use std::marker::PhantomData; use std::sync::Arc; @@ -40,12 +39,12 @@ where /// Returns a storage proof which can be used to reconstruct a partial state trie to re-run /// the execution by someone who does not own the whole state. - pub fn prove_execution, DBValue>>( + pub fn prove_execution( &self, at: Block::Hash, execution_phase: &ExecutionPhase, call_data: &[u8], - delta_changes: Option<(DB, Block::Hash)>, + delta_changes: Option<(BackendTransaction>, Block::Hash)>, ) -> sp_blockchain::Result { let state = self.backend.state_at(at)?; diff --git a/domains/client/block-builder/Cargo.toml b/domains/client/block-builder/Cargo.toml index 18daefca98..5798ca4122 100644 --- a/domains/client/block-builder/Cargo.toml +++ b/domains/client/block-builder/Cargo.toml @@ -20,6 +20,7 @@ sp-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89 sp-blockchain = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-block-builder = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +sp-externalities = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-inherents = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-state-machine = { git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index f07aba67da..f3c0c1e754 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -1,29 +1,64 @@ //! Custom API that is efficient to collect storage roots. -use codec::Codec; +// TODO: remove in later commits +#![allow(dead_code)] + +use codec::{Codec, Decode, Encode}; use hash_db::{HashDB, Hasher, Prefix}; -use sp_state_machine::{DBValue, TrieBackend, TrieBackendBuilder, TrieBackendStorage}; +use sc_client_api::{backend, ExecutorProvider, StateBackend}; +use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode}; +use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor}; +use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode, TransactionOutcome}; +use sp_state_machine::backend::AsTrieBackend; +use sp_state_machine::{ + BackendTransaction, DBValue, OverlayedChanges, StateMachine, StorageChanges, TrieBackend, + TrieBackendBuilder, TrieBackendStorage, +}; use std::marker::PhantomData; +use std::sync::Arc; + +type TrieBackendStorageFor = + >>::TrieBackendStorage; + +type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< + DeltaBackend<'a, TrieBackendStorageFor, HashingFor>, + HashingFor, +>; + +type TrieBackendFor = + TrieBackend, HashingFor>; /// Create a new trie backend with memory DB delta changes. /// /// This can be used to verify any extrinsic-specific execution on the combined state of `backend` /// and `delta`. -pub fn create_delta_backend<'a, S, H, DB>( +pub fn create_delta_backend<'a, S, H>( + backend: &'a TrieBackend, + delta: BackendTransaction, + post_delta_root: H::Out, +) -> TrieBackend, H> +where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, + H::Out: Codec, +{ + create_delta_backend_with_maybe_delta(backend, Some(delta), post_delta_root) +} + +fn create_delta_backend_with_maybe_delta<'a, S, H>( backend: &'a TrieBackend, - delta: DB, + maybe_delta: Option>, post_delta_root: H::Out, -) -> TrieBackend, H> +) -> TrieBackend, H> where S: 'a + TrieBackendStorage, H: 'a + Hasher, H::Out: Codec, - DB: HashDB, { let essence = backend.essence(); let delta_backend = DeltaBackend { backend: essence.backend_storage(), - delta, + delta: maybe_delta, _phantom: PhantomData::, }; TrieBackendBuilder::new(delta_backend, post_delta_root).build() @@ -31,27 +66,233 @@ where /// DeltaBackend provides the TrieBackend using main backend and some delta changes /// that are not part of the main backend. -pub struct DeltaBackend<'a, S, H, DB> +pub struct DeltaBackend<'a, S, H> where S: 'a + TrieBackendStorage, H: 'a + Hasher, - DB: HashDB, { backend: &'a S, - delta: DB, + delta: Option>, _phantom: PhantomData, } -impl<'a, S, H, DB> TrieBackendStorage for DeltaBackend<'a, S, H, DB> +impl<'a, S, H> TrieBackendStorage for DeltaBackend<'a, S, H> where S: 'a + TrieBackendStorage, H: 'a + Hasher, - DB: HashDB, { fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { - match HashDB::get(&self.delta, key, prefix) { - Some(v) => Ok(Some(v)), - None => Ok(self.backend.get(key, prefix)?), + if let Some(db) = &self.delta + && let Some(v) = HashDB::get(db, key, prefix) + { + Ok(Some(v)) + } else { + Ok(self.backend.get(key, prefix)?) } } } + +impl<'a, S, H> DeltaBackend<'a, S, H> +where + S: 'a + TrieBackendStorage, + H: 'a + Hasher, +{ + fn consolidate_delta(&mut self, db: BackendTransaction) { + let delta = if let Some(mut delta) = self.delta.take() { + delta.consolidate(db); + delta + } else { + db + }; + self.delta = Some(delta); + } +} + +pub(crate) struct TrieBackendApi, Exec> { + parent_hash: Block::Hash, + parent_number: NumberFor, + client: Arc, + state: Backend::State, + executor: Exec, + maybe_storage_changes: Option>>, +} + +impl TrieBackendApi +where + Block: BlockT, + Backend: backend::Backend, + Client: ExecutorProvider, + Exec: CodeExecutor, +{ + pub(crate) fn new( + parent_hash: Block::Hash, + parent_number: NumberFor, + client: Arc, + backend: Arc, + executor: Exec, + ) -> Result { + let state = backend.state_at(parent_hash)?; + Ok(Self { + parent_hash, + parent_number, + client, + state, + executor, + maybe_storage_changes: None, + }) + } + + fn call_function( + &self, + method: &str, + call_data: Vec, + trie_backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>, + ) -> Result { + let mut extensions = self + .client + .execution_extensions() + .extensions(self.parent_hash, self.parent_number); + + let result = StateMachine::<_, _, _>::new( + trie_backend, + overlayed_changes, + &self.executor, + method, + call_data.as_slice(), + &mut extensions, + &runtime_code, + CallContext::Onchain, + ) + .set_parent_hash(self.parent_hash) + .execute()?; + + R::decode(&mut result.as_slice()) + .map_err(|err| sp_blockchain::Error::CallResultDecode("failed to decode Result", err)) + } + + fn consolidate_storage_changes(&mut self, mut changes: StorageChanges>) { + let changes = if let Some(mut current_changes) = self.maybe_storage_changes.take() { + current_changes + .main_storage_changes + .append(&mut changes.main_storage_changes); + current_changes + .child_storage_changes + .append(&mut changes.child_storage_changes); + current_changes + .offchain_storage_changes + .append(&mut changes.offchain_storage_changes); + current_changes.transaction.consolidate(changes.transaction); + current_changes.transaction_storage_root = changes.transaction_storage_root; + current_changes + .transaction_index_changes + .append(&mut changes.transaction_index_changes); + current_changes + } else { + changes + }; + self.maybe_storage_changes = Some(changes) + } + + pub(crate) fn initialize_block( + &self, + header: Block::Header, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>, + ) -> Result { + let call_data = header.encode(); + self.call_function( + "Core_initialize_block", + call_data, + backend, + runtime_code, + overlayed_changes, + ) + } + + pub(crate) fn apply_extrinsic( + &self, + extrinsic: ::Extrinsic, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>, + ) -> Result { + let call_data = extrinsic.encode(); + self.call_function( + "BlockBuilder_apply_extrinsic", + call_data, + backend, + runtime_code, + overlayed_changes, + ) + } + + pub(crate) fn finalize_block( + &self, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>, + ) -> Result { + self.call_function( + "BlockBuilder_finalize_block", + vec![], + backend, + runtime_code, + overlayed_changes, + ) + } + + pub(crate) fn execute_in_transaction( + &mut self, + call: F, + ) -> Result + where + F: FnOnce( + &Self, + &TrieDeltaBackendFor, + RuntimeCode, + &mut OverlayedChanges>, + ) -> TransactionOutcome, + R: Decode, + { + let trie_backend = self.state.as_trie_backend(); + let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend); + let runtime_code = state_runtime_code + .runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + let mut overlayed_changes = OverlayedChanges::default(); + let (state_root, delta) = if let Some(changes) = &self.maybe_storage_changes { + ( + changes.transaction_storage_root, + Some(changes.transaction.clone()), + ) + } else { + (*trie_backend.root(), None) + }; + let trie_delta_backend = + create_delta_backend_with_maybe_delta(trie_backend, delta, state_root); + + let outcome = call( + self, + &trie_delta_backend, + runtime_code, + &mut overlayed_changes, + ); + + let result = match outcome { + TransactionOutcome::Commit(result) => { + let state_version = sp_core::storage::StateVersion::V1; + let storage_changes = overlayed_changes + .drain_storage_changes(&trie_delta_backend, state_version) + .map_err(sp_blockchain::Error::StorageChanges)?; + self.consolidate_storage_changes(storage_changes); + result + } + TransactionOutcome::Rollback(result) => result, + }; + + Ok(result) + } +} diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index 2df5c4f910..f37c51b7e3 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -25,6 +25,7 @@ //! initialize a block, to push extrinsics and to finalize a block. #![warn(missing_docs)] +#![feature(let_chains)] mod custom_api; From ac1d95c85e2587d1ecde94969d950b27bfc0ba82 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Tue, 7 Jan 2025 15:25:32 +0530 Subject: [PATCH 03/14] cleanup block builder --- domains/client/block-builder/src/lib.rs | 117 +----------------- .../src/domain_block_processor.rs | 4 +- .../client/domain-operator/src/fraud_proof.rs | 3 +- 3 files changed, 8 insertions(+), 116 deletions(-) diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index f37c51b7e3..9a3b277d54 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -32,112 +32,37 @@ mod custom_api; use codec::Encode; pub use custom_api::{create_delta_backend, DeltaBackend}; use sc_client_api::backend; -use sp_api::{ - ApiExt, ApiRef, Core, ProvideRuntimeApi, StorageChanges, StorageProof, TransactionOutcome, -}; +use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi, StorageChanges, TransactionOutcome}; pub use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ApplyExtrinsicFailed, Error}; -use sp_runtime::generic::BlockId; use sp_runtime::traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}; use sp_runtime::Digest; use std::collections::VecDeque; -/// Used as parameter to [`BlockBuilderProvider`] to express if proof recording should be enabled. -/// -/// When `RecordProof::Yes` is given, all accessed trie nodes should be saved. These recorded -/// trie nodes can be used by a third party to proof this proposal without having access to the -/// full storage. -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum RecordProof { - /// `Yes`, record a proof. - Yes, - /// `No`, don't record any proof. - No, -} - -impl RecordProof { - /// Returns if `Self` == `Yes`. - pub fn yes(&self) -> bool { - matches!(self, Self::Yes) - } -} - -/// Will return [`RecordProof::No`] as default value. -impl Default for RecordProof { - #[inline] - fn default() -> Self { - Self::No - } -} - -impl From for RecordProof { - #[inline] - fn from(val: bool) -> Self { - if val { - Self::Yes - } else { - Self::No - } - } -} - /// A block that was build by [`BlockBuilder`] plus some additional data. /// /// This additional data includes the `storage_changes`, these changes can be applied to the -/// backend to get the state of the block. Furthermore an optional `proof` is included which -/// can be used to proof that the build block contains the expected data. The `proof` will -/// only be set when proof recording was activated. +/// backend to get the state of the block. pub struct BuiltBlock { /// The actual block that was build. pub block: Block, /// The changes that need to be applied to the backend to get the state of the build block. pub storage_changes: StorageChanges, - /// An optional proof that was recorded while building the block. - pub proof: Option, } impl BuiltBlock { /// Convert into the inner values. - pub fn into_inner(self) -> (Block, StorageChanges, Option) { - (self.block, self.storage_changes, self.proof) + pub fn into_inner(self) -> (Block, StorageChanges) { + (self.block, self.storage_changes) } } -/// Block builder provider -pub trait BlockBuilderProvider -where - Block: BlockT, - B: backend::Backend, - Self: Sized, - RA: ProvideRuntimeApi, -{ - /// Create a new block, built on top of `parent`. - /// - /// When proof recording is enabled, all accessed trie nodes are saved. - /// These recorded trie nodes can be used by a third party to proof the - /// output of this block builder without having access to the full storage. - fn new_block_at>( - &self, - parent: &BlockId, - inherent_digests: Digest, - record_proof: R, - ) -> sp_blockchain::Result>; - - /// Create a new block, built on the head of the chain. - fn new_block( - &self, - inherent_digests: Digest, - ) -> sp_blockchain::Result>; -} - /// Utility for building new (valid) blocks from a stream of extrinsics. pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi, B> { extrinsics: VecDeque, api: ApiRef<'a, A::Api>, parent_hash: Block::Hash, backend: &'a B, - /// The estimated size of the block header. - estimated_header_size: usize, } impl<'a, Block, A, B> BlockBuilder<'a, Block, A, B> @@ -157,7 +82,6 @@ where api: &'a A, parent_hash: Block::Hash, parent_number: NumberFor, - record_proof: RecordProof, inherent_digests: Digest, backend: &'a B, mut extrinsics: VecDeque, @@ -171,14 +95,7 @@ where inherent_digests, ); - let estimated_header_size = header.encoded_size(); - - let mut api = api.runtime_api(); - - if record_proof.yes() { - api.record_proof(); - } - + let api = api.runtime_api(); api.initialize_block(parent_hash, &header)?; if let Some(inherent_data) = maybe_inherent_data { @@ -195,7 +112,6 @@ where extrinsics, api, backend, - estimated_header_size, }) } @@ -273,7 +189,7 @@ where /// Returns the build `Block`, the changes to the storage and an optional `StorageProof` /// supplied by `self.api`, combined as [`BuiltBlock`]. /// The storage proof will be `Some(_)` when proof recording was enabled. - pub fn build(mut self) -> Result, Error> { + pub fn build(self) -> Result, Error> { self.execute_extrinsics()?; let header = self.api.finalize_block(self.parent_hash)?; @@ -286,14 +202,11 @@ where ), ); - let proof = self.api.extract_proof(); - let storage_changes = self.collect_storage_changes()?; Ok(BuiltBlock { block: Block::new(header, self.extrinsics.into()), storage_changes, - proof, }) } @@ -314,24 +227,6 @@ where .map_err(|e| Error::Application(Box::new(e)))?; Ok(VecDeque::from(exts)) } - - /// Estimate the size of the block in the current state. - /// - /// If `include_proof` is `true`, the estimated size of the storage proof will be added - /// to the estimation. - pub fn estimate_block_size(&self, include_proof: bool) -> usize { - let size = self.estimated_header_size + self.extrinsics.encoded_size(); - - if include_proof { - size + self - .api - .proof_recorder() - .map(|pr| pr.estimate_encoded_size()) - .unwrap_or(0) - } else { - size - } - } } // TODO: Unlock this test, it got broken in https://github.com/autonomys/subspace/pull/1548 and diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 513672835e..232abe56c9 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -3,7 +3,7 @@ use crate::fraud_proof::FraudProofGenerator; use crate::utils::{DomainBlockImportNotification, DomainImportNotificationSinks}; use crate::ExecutionReceiptFor; use codec::{Decode, Encode}; -use domain_block_builder::{BlockBuilder, BuiltBlock, RecordProof}; +use domain_block_builder::{BlockBuilder, BuiltBlock}; use domain_block_preprocessor::inherents::get_inherent_data; use domain_block_preprocessor::PreprocessResult; use sc_client_api::{AuxStore, BlockBackend, Finalizer, ProofProvider}; @@ -426,7 +426,6 @@ where &*self.client, parent_hash, parent_number, - RecordProof::No, inherent_digests, &*self.backend, extrinsics, @@ -436,7 +435,6 @@ where let BuiltBlock { block, storage_changes, - proof: _, } = block_builder.build()?; let (header, body) = block.deconstruct(); diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index a15d79d486..b333709152 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -1,7 +1,7 @@ use crate::aux_schema::BundleMismatchType; use crate::ExecutionReceiptFor; use codec::{Decode, Encode}; -use domain_block_builder::{BlockBuilder, RecordProof}; +use domain_block_builder::BlockBuilder; use domain_runtime_primitives::opaque::AccountId; use domain_runtime_primitives::CheckExtrinsicsValidityError; use sc_client_api::{AuxStore, BlockBackend, ProofProvider}; @@ -231,7 +231,6 @@ where &*self.client, parent_header.hash(), *parent_header.number(), - RecordProof::No, inherent_digests.clone(), &*self.backend, extrinsics.into(), From 90a28c2a56a2d025141c61199fbeae66cabbd3d6 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Tue, 7 Jan 2025 17:01:18 +0530 Subject: [PATCH 04/14] collect intermediate roots and add missing inherent_extrinsics api call --- .../client/block-builder/src/custom_api.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index f3c0c1e754..d845a51031 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -7,6 +7,7 @@ use codec::{Codec, Decode, Encode}; use hash_db::{HashDB, Hasher, Prefix}; use sc_client_api::{backend, ExecutorProvider, StateBackend}; use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode}; +use sp_inherents::InherentData; use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor}; use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode, TransactionOutcome}; use sp_state_machine::backend::AsTrieBackend; @@ -28,6 +29,12 @@ type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< type TrieBackendFor = TrieBackend, HashingFor>; +/// Storage changes are the collected throughout the execution. +pub struct CollectedStorageChanges { + pub storage_changes: StorageChanges, + pub intermediate_roots: Vec, +} + /// Create a new trie backend with memory DB delta changes. /// /// This can be used to verify any extrinsic-specific execution on the combined state of `backend` @@ -115,6 +122,7 @@ pub(crate) struct TrieBackendApi>>, + intermediate_roots: Vec, } impl TrieBackendApi @@ -139,6 +147,7 @@ where state, executor, maybe_storage_changes: None, + intermediate_roots: vec![], }) } @@ -184,6 +193,8 @@ where .offchain_storage_changes .append(&mut changes.offchain_storage_changes); current_changes.transaction.consolidate(changes.transaction); + self.intermediate_roots + .push(current_changes.transaction_storage_root); current_changes.transaction_storage_root = changes.transaction_storage_root; current_changes .transaction_index_changes @@ -244,6 +255,44 @@ where ) } + pub(crate) fn inherent_extrinsics( + &self, + inherent: InherentData, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>, + ) -> Result::Extrinsic>, sp_blockchain::Error> { + let call_data = inherent.encode(); + self.call_function( + "BlockBuilder_inherent_extrinsics", + call_data, + backend, + runtime_code, + overlayed_changes, + ) + } + + /// Collect storage changes returns the storage changes and intermediate roots collected so far. + /// The changes are reset after this call. + /// Could return None if there were no execution done. + pub(crate) fn collect_storage_changes( + &mut self, + ) -> Option>> { + let mut intermediate_roots = self.intermediate_roots.drain(..).collect::>(); + // we did not add the last transaction storage root. + // include that and then return + let maybe_storage_changes = self.maybe_storage_changes.take(); + if let Some(storage_changes) = maybe_storage_changes { + intermediate_roots.push(storage_changes.transaction_storage_root); + Some(CollectedStorageChanges { + storage_changes, + intermediate_roots, + }) + } else { + None + } + } + pub(crate) fn execute_in_transaction( &mut self, call: F, From f7026d300f19b69ffcbf90a5b6396615aa603c54 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Tue, 7 Jan 2025 18:03:45 +0530 Subject: [PATCH 05/14] intergate TrieBackendApi into domain block builder --- .../client/block-builder/src/custom_api.rs | 19 +- domains/client/block-builder/src/lib.rs | 172 +++++++++++------- .../domain-operator/src/bundle_processor.rs | 7 +- .../src/domain_block_processor.rs | 29 ++- .../domain-operator/src/domain_worker.rs | 4 +- .../client/domain-operator/src/fraud_proof.rs | 14 +- .../client/domain-operator/src/operator.rs | 9 +- domains/client/domain-operator/src/tests.rs | 1 + 8 files changed, 156 insertions(+), 99 deletions(-) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index d845a51031..ec9533db94 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -21,7 +21,7 @@ use std::sync::Arc; type TrieBackendStorageFor = >>::TrieBackendStorage; -type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< +pub(crate) type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< DeltaBackend<'a, TrieBackendStorageFor, HashingFor>, HashingFor, >; @@ -31,7 +31,10 @@ type TrieBackendFor = /// Storage changes are the collected throughout the execution. pub struct CollectedStorageChanges { + /// Storage changes that are captured during the execution. + /// Can be used for block import. pub storage_changes: StorageChanges, + /// Intermediate storage roots in between the execution. pub intermediate_roots: Vec, } @@ -120,7 +123,7 @@ pub(crate) struct TrieBackendApi, client: Arc, state: Backend::State, - executor: Exec, + executor: Arc, maybe_storage_changes: Option>>, intermediate_roots: Vec, } @@ -137,7 +140,7 @@ where parent_number: NumberFor, client: Arc, backend: Arc, - executor: Exec, + executor: Arc, ) -> Result { let state = backend.state_at(parent_hash)?; Ok(Self { @@ -167,7 +170,7 @@ where let result = StateMachine::<_, _, _>::new( trie_backend, overlayed_changes, - &self.executor, + &*self.executor, method, call_data.as_slice(), &mut extensions, @@ -303,7 +306,7 @@ where &TrieDeltaBackendFor, RuntimeCode, &mut OverlayedChanges>, - ) -> TransactionOutcome, + ) -> TransactionOutcome>, R: Decode, { let trie_backend = self.state.as_trie_backend(); @@ -330,7 +333,7 @@ where &mut overlayed_changes, ); - let result = match outcome { + match outcome { TransactionOutcome::Commit(result) => { let state_version = sp_core::storage::StateVersion::V1; let storage_changes = overlayed_changes @@ -340,8 +343,6 @@ where result } TransactionOutcome::Rollback(result) => result, - }; - - Ok(result) + } } } diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index 9a3b277d54..dd79bba4f8 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -29,15 +29,19 @@ mod custom_api; +use crate::custom_api::{TrieBackendApi, TrieDeltaBackendFor}; use codec::Encode; -pub use custom_api::{create_delta_backend, DeltaBackend}; -use sc_client_api::backend; -use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi, StorageChanges, TransactionOutcome}; +pub use custom_api::{create_delta_backend, CollectedStorageChanges, DeltaBackend}; +use sc_client_api::{backend, ExecutorProvider}; +use sp_api::{ProvideRuntimeApi, TransactionOutcome}; pub use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ApplyExtrinsicFailed, Error}; +use sp_core::traits::{CodeExecutor, RuntimeCode}; use sp_runtime::traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}; use sp_runtime::Digest; +use sp_state_machine::OverlayedChanges; use std::collections::VecDeque; +use std::sync::Arc; /// A block that was build by [`BlockBuilder`] plus some additional data. /// @@ -47,30 +51,29 @@ pub struct BuiltBlock { /// The actual block that was build. pub block: Block, /// The changes that need to be applied to the backend to get the state of the build block. - pub storage_changes: StorageChanges, + pub storage_changes: CollectedStorageChanges>, } impl BuiltBlock { /// Convert into the inner values. - pub fn into_inner(self) -> (Block, StorageChanges) { + pub fn into_inner(self) -> (Block, CollectedStorageChanges>) { (self.block, self.storage_changes) } } /// Utility for building new (valid) blocks from a stream of extrinsics. -pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi, B> { +pub struct BlockBuilder, Exec> { extrinsics: VecDeque, - api: ApiRef<'a, A::Api>, - parent_hash: Block::Hash, - backend: &'a B, + api: TrieBackendApi, } -impl<'a, Block, A, B> BlockBuilder<'a, Block, A, B> +impl BlockBuilder where Block: BlockT, - A: ProvideRuntimeApi + 'a, - A::Api: BlockBuilderApi + ApiExt, - B: backend::Backend, + Client: ProvideRuntimeApi + ExecutorProvider, + Client::Api: BlockBuilderApi, + Backend: backend::Backend, + Exec: CodeExecutor, { /// Create a new instance of builder based on the given `parent_hash` and `parent_number`. /// @@ -79,14 +82,16 @@ where /// output of this block builder without having access to the full storage. #[allow(clippy::too_many_arguments)] pub fn new( - api: &'a A, + client: Arc, parent_hash: Block::Hash, parent_number: NumberFor, inherent_digests: Digest, - backend: &'a B, + backend: Arc, + exec: Arc, mut extrinsics: VecDeque, maybe_inherent_data: Option, ) -> Result { + let mut api = TrieBackendApi::new(parent_hash, parent_number, client, backend, exec)?; let header = ::new( parent_number + One::one(), Default::default(), @@ -95,11 +100,20 @@ where inherent_digests, ); - let api = api.runtime_api(); - api.initialize_block(parent_hash, &header)?; + api.execute_in_transaction( + |api: &TrieBackendApi, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>| { + match api.initialize_block(header, backend, runtime_code, overlayed_changes) { + Ok(_) => TransactionOutcome::Commit(Ok(())), + Err(e) => TransactionOutcome::Rollback(Err(e)), + } + }, + )?; if let Some(inherent_data) = maybe_inherent_data { - let inherent_extrinsics = Self::create_inherents(parent_hash, &api, inherent_data)?; + let inherent_extrinsics = Self::create_inherents(&mut api, inherent_data)?; // reverse and push the inherents so that order is maintained for inherent_extrinsic in inherent_extrinsics.into_iter().rev() { @@ -107,28 +121,27 @@ where } } - Ok(Self { - parent_hash, - extrinsics, - api, - backend, - }) + Ok(Self { extrinsics, api }) } /// Execute the block's list of extrinsics. - fn execute_extrinsics(&self) -> Result<(), Error> { - let parent_hash = self.parent_hash; - + fn execute_extrinsics(&mut self) -> Result<(), Error> { for (index, xt) in self.extrinsics.iter().enumerate() { - let res = self.api.execute_in_transaction(|api| { - match api.apply_extrinsic(parent_hash, xt.clone()) { - Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), - Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( - ApplyExtrinsicFailed::Validity(tx_validity).into(), - )), - Err(e) => TransactionOutcome::Rollback(Err(Error::from(e))), - } - }); + let res = self.api.execute_in_transaction( + |api: &TrieBackendApi, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>| { + match api.apply_extrinsic(xt.clone(), backend, runtime_code, overlayed_changes) + { + Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), + Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( + ApplyExtrinsicFailed::Validity(tx_validity).into(), + )), + Err(e) => TransactionOutcome::Rollback(Err(e)), + } + }, + ); if let Err(e) = res { tracing::debug!("Apply extrinsic at index {index} failed: {e}"); @@ -138,35 +151,38 @@ where Ok(()) } - fn collect_storage_changes(&self) -> Result, Error> { - let state = self.backend.state_at(self.parent_hash)?; - let parent_hash = self.parent_hash; - self.api - .into_storage_changes(&state, parent_hash) - .map_err(Error::StorageChanges) + fn collect_storage_changes(&mut self) -> Option>> { + self.api.collect_storage_changes() } /// Returns the state before executing the extrinsic at given extrinsic index. pub fn prepare_storage_changes_before( - &self, + &mut self, extrinsic_index: usize, - ) -> Result, Error> { + ) -> Result>, Error> { for (index, xt) in self.extrinsics.iter().enumerate() { if index == extrinsic_index { - return self.collect_storage_changes(); + return self + .collect_storage_changes() + .ok_or(Error::Execution(Box::new("No execution is done"))); } // TODO: rethink what to do if an error occurs when executing the transaction. - self.api.execute_in_transaction(|api| { - let res = api.apply_extrinsic(self.parent_hash, xt.clone()); - match res { - Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), - Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( - ApplyExtrinsicFailed::Validity(tx_validity).into(), - )), - Err(e) => TransactionOutcome::Rollback(Err(Error::from(e))), - } - })?; + self.api.execute_in_transaction( + |api: &TrieBackendApi, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>| { + match api.apply_extrinsic(xt.clone(), backend, runtime_code, overlayed_changes) + { + Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), + Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( + ApplyExtrinsicFailed::Validity(tx_validity).into(), + )), + Err(e) => TransactionOutcome::Rollback(Err(e)), + } + }, + )?; } Err(Error::Execution(Box::new(format!( @@ -178,10 +194,11 @@ where /// Returns the state before finalizing the block. pub fn prepare_storage_changes_before_finalize_block( - &self, - ) -> Result, Error> { + &mut self, + ) -> Result>, Error> { self.execute_extrinsics()?; self.collect_storage_changes() + .ok_or(Error::Execution(Box::new("No execution is done"))) } /// Consume the builder to build a valid `Block` containing all pushed extrinsics. @@ -189,10 +206,20 @@ where /// Returns the build `Block`, the changes to the storage and an optional `StorageProof` /// supplied by `self.api`, combined as [`BuiltBlock`]. /// The storage proof will be `Some(_)` when proof recording was enabled. - pub fn build(self) -> Result, Error> { + pub fn build(mut self) -> Result, Error> { self.execute_extrinsics()?; - let header = self.api.finalize_block(self.parent_hash)?; + let header = self.api.execute_in_transaction( + |api: &TrieBackendApi, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>| { + match api.finalize_block(backend, runtime_code, overlayed_changes) { + Ok(header) => TransactionOutcome::Commit(Ok(header)), + Err(e) => TransactionOutcome::Rollback(Err(e)), + } + }, + )?; debug_assert_eq!( header.extrinsics_root().clone(), @@ -202,7 +229,9 @@ where ), ); - let storage_changes = self.collect_storage_changes()?; + let storage_changes = self + .collect_storage_changes() + .expect("must always have the storage changes due to execution above"); Ok(BuiltBlock { block: Block::new(header, self.extrinsics.into()), @@ -213,18 +242,25 @@ where /// Create the inherents for the block. /// /// Returns the inherents created by the runtime or an error if something failed. - pub fn create_inherents( - parent_hash: Block::Hash, - api: &ApiRef, + pub(crate) fn create_inherents( + api: &mut TrieBackendApi, inherent_data: sp_inherents::InherentData, ) -> Result, Error> { - let exts = api - .execute_in_transaction(move |api| { + let exts = api.execute_in_transaction( + |api: &TrieBackendApi, + backend: &TrieDeltaBackendFor, + runtime_code: RuntimeCode, + overlayed_changes: &mut OverlayedChanges>| { // `create_inherents` should not change any state, to ensure this we always rollback // the transaction. - TransactionOutcome::Rollback(api.inherent_extrinsics(parent_hash, inherent_data)) - }) - .map_err(|e| Error::Application(Box::new(e)))?; + TransactionOutcome::Rollback(api.inherent_extrinsics( + inherent_data, + backend, + runtime_code, + overlayed_changes, + )) + }, + )?; Ok(VecDeque::from(exts)) } } diff --git a/domains/client/domain-operator/src/bundle_processor.rs b/domains/client/domain-operator/src/bundle_processor.rs index db0270386c..1a16b4f4f3 100644 --- a/domains/client/domain-operator/src/bundle_processor.rs +++ b/domains/client/domain-operator/src/bundle_processor.rs @@ -3,7 +3,7 @@ use crate::domain_block_processor::{ }; use crate::ExecutionReceiptFor; use domain_block_preprocessor::DomainBlockPreprocessor; -use sc_client_api::{AuxStore, BlockBackend, Finalizer, ProofProvider}; +use sc_client_api::{AuxStore, BlockBackend, ExecutorProvider, Finalizer, ProofProvider}; use sc_consensus::{BlockImportParams, ForkChoiceStrategy, StateAction}; use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, HeaderMetadata}; @@ -50,7 +50,7 @@ where domain_receipts_checker: DomainReceiptsChecker, domain_block_preprocessor: DomainBlockPreprocessor>, - domain_block_processor: DomainBlockProcessor, + domain_block_processor: DomainBlockProcessor, } impl Clone @@ -134,6 +134,7 @@ where + AuxStore + ProvideRuntimeApi + ProofProvider + + ExecutorProvider + Finalizer + 'static, Client::Api: DomainCoreApi @@ -160,7 +161,7 @@ where client: Arc, backend: Arc, domain_receipts_checker: DomainReceiptsChecker, - domain_block_processor: DomainBlockProcessor, + domain_block_processor: DomainBlockProcessor, ) -> Self { let domain_block_preprocessor = DomainBlockPreprocessor::new( domain_id, diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 232abe56c9..86c4d4384a 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -3,10 +3,10 @@ use crate::fraud_proof::FraudProofGenerator; use crate::utils::{DomainBlockImportNotification, DomainImportNotificationSinks}; use crate::ExecutionReceiptFor; use codec::{Decode, Encode}; -use domain_block_builder::{BlockBuilder, BuiltBlock}; +use domain_block_builder::{BlockBuilder, BuiltBlock, CollectedStorageChanges}; use domain_block_preprocessor::inherents::get_inherent_data; use domain_block_preprocessor::PreprocessResult; -use sc_client_api::{AuxStore, BlockBackend, Finalizer, ProofProvider}; +use sc_client_api::{AuxStore, BlockBackend, ExecutorProvider, Finalizer, ProofProvider}; use sc_consensus::{ BlockImportParams, BoxBlockImport, ForkChoiceStrategy, ImportResult, StateAction, StorageChanges, @@ -51,7 +51,7 @@ where } /// An abstracted domain block processor. -pub(crate) struct DomainBlockProcessor +pub(crate) struct DomainBlockProcessor where Block: BlockT, CBlock: BlockT, @@ -65,10 +65,11 @@ where pub(crate) block_import: Arc>, pub(crate) import_notification_sinks: DomainImportNotificationSinks, pub(crate) domain_sync_oracle: Arc, + pub(crate) domain_executor: Arc, } -impl Clone - for DomainBlockProcessor +impl Clone + for DomainBlockProcessor where Block: BlockT, CBlock: BlockT, @@ -84,6 +85,7 @@ where block_import: self.block_import.clone(), import_notification_sinks: self.import_notification_sinks.clone(), domain_sync_oracle: self.domain_sync_oracle.clone(), + domain_executor: self.domain_executor.clone(), } } } @@ -102,8 +104,8 @@ pub(crate) struct PendingConsensusBlocks { pub consensus_imports: Vec>, } -impl - DomainBlockProcessor +impl + DomainBlockProcessor where Block: BlockT, CBlock: BlockT, @@ -113,6 +115,7 @@ where + AuxStore + ProvideRuntimeApi + Finalizer + + ExecutorProvider + 'static, Client::Api: DomainCoreApi + sp_block_builder::BlockBuilder + sp_api::ApiExt, @@ -125,6 +128,7 @@ where + MessengerApi, CBlock::Hash> + 'static, Backend: sc_client_api::Backend + 'static, + Executor: CodeExecutor, { /// Returns a list of consensus blocks waiting to be processed if any. /// @@ -423,11 +427,12 @@ where inherent_data: sp_inherents::InherentData, ) -> Result, sp_blockchain::Error> { let block_builder = BlockBuilder::new( - &*self.client, + self.client.clone(), parent_hash, parent_number, inherent_digests, - &*self.backend, + self.backend.clone(), + self.domain_executor.clone(), extrinsics, Some(inherent_data), )?; @@ -437,6 +442,11 @@ where storage_changes, } = block_builder.build()?; + let CollectedStorageChanges { + storage_changes, + intermediate_roots: _, + } = storage_changes; + let (header, body) = block.deconstruct(); let state_root = *header.state_root(); let extrinsics_root = *header.extrinsics_root(); @@ -711,6 +721,7 @@ where + BlockBackend + ProofProvider + AuxStore + + ExecutorProvider + ProvideRuntimeApi + 'static, Client::Api: DomainCoreApi diff --git a/domains/client/domain-operator/src/domain_worker.rs b/domains/client/domain-operator/src/domain_worker.rs index 70181684da..923564ec73 100644 --- a/domains/client/domain-operator/src/domain_worker.rs +++ b/domains/client/domain-operator/src/domain_worker.rs @@ -21,7 +21,8 @@ use crate::{NewSlotNotification, OperatorStreams}; use futures::channel::mpsc; use futures::{SinkExt, Stream, StreamExt}; use sc_client_api::{ - AuxStore, BlockBackend, BlockImportNotification, BlockchainEvents, Finalizer, ProofProvider, + AuxStore, BlockBackend, BlockImportNotification, BlockchainEvents, ExecutorProvider, Finalizer, + ProofProvider, }; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_api::{ApiExt, ProvideRuntimeApi}; @@ -77,6 +78,7 @@ pub(super) async fn start_worker< + ProvideRuntimeApi + ProofProvider + Finalizer + + ExecutorProvider + 'static, Client::Api: DomainCoreApi + MessengerApi, CBlock::Hash> diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index b333709152..45d15dde24 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -4,7 +4,7 @@ use codec::{Decode, Encode}; use domain_block_builder::BlockBuilder; use domain_runtime_primitives::opaque::AccountId; use domain_runtime_primitives::CheckExtrinsicsValidityError; -use sc_client_api::{AuxStore, BlockBackend, ProofProvider}; +use sc_client_api::{AuxStore, BlockBackend, ExecutorProvider, ProofProvider}; use sc_domains::FPStorageKeyProvider; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; @@ -117,6 +117,7 @@ where + AuxStore + ProvideRuntimeApi + ProofProvider + + ExecutorProvider + 'static, Client::Api: sp_block_builder::BlockBuilder + sp_api::ApiExt @@ -227,12 +228,13 @@ where let max_extrinsic_index = extrinsics.len() - 1; let encoded_extrinsics: Vec<_> = extrinsics.iter().map(Encode::encode).collect(); - let block_builder = BlockBuilder::new( - &*self.client, + let mut block_builder = BlockBuilder::new( + self.client.clone(), parent_header.hash(), *parent_header.number(), inherent_digests.clone(), - &*self.backend, + self.backend.clone(), + self.code_executor.clone(), extrinsics.into(), // NOTE: the inherent extrinsic is already contained in the above `extrinsics`, which // is getting from the block body, thus it is okay to pass `maybe_inherent_data` as @@ -280,8 +282,8 @@ where let delta_changes = storage_changes.map(|storage_changes| { ( - storage_changes.transaction, - storage_changes.transaction_storage_root, + storage_changes.storage_changes.transaction, + storage_changes.storage_changes.transaction_storage_root, ) }); diff --git a/domains/client/domain-operator/src/operator.rs b/domains/client/domain-operator/src/operator.rs index b1057b524c..bc07e73e0b 100644 --- a/domains/client/domain-operator/src/operator.rs +++ b/domains/client/domain-operator/src/operator.rs @@ -11,7 +11,8 @@ use futures::channel::mpsc; use futures::future::pending; use futures::{FutureExt, SinkExt, Stream, StreamExt}; use sc_client_api::{ - AuxStore, BlockBackend, BlockImportNotification, BlockchainEvents, Finalizer, ProofProvider, + AuxStore, BlockBackend, BlockImportNotification, BlockchainEvents, ExecutorProvider, Finalizer, + ProofProvider, }; use sc_consensus::BlockImport; use sc_network::NetworkRequest; @@ -44,7 +45,7 @@ where backend: Arc, fraud_proof_generator: FraudProofGenerator, bundle_processor: BundleProcessor, - domain_block_processor: DomainBlockProcessor, + domain_block_processor: DomainBlockProcessor, pub keystore: KeystorePtr, } @@ -84,6 +85,7 @@ where + Finalizer + BlockImport + BlockchainEvents + + ExecutorProvider + 'static, for<'a> &'a Client: BlockImport, Client::Api: DomainCoreApi @@ -174,7 +176,7 @@ where params.client.clone(), params.consensus_client.clone(), params.backend.clone(), - params.code_executor, + params.code_executor.clone(), ); let domain_block_processor = DomainBlockProcessor { @@ -187,6 +189,7 @@ where block_import: params.block_import, import_notification_sinks: Default::default(), domain_sync_oracle: params.domain_sync_oracle.clone(), + domain_executor: params.code_executor.clone(), }; let receipts_checker = ReceiptsChecker { diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index af3ddadaf0..6bc062ff65 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -337,6 +337,7 @@ async fn test_processing_empty_consensus_block() { block_import: Arc::new(Box::new(alice.client.clone())), import_notification_sinks: Default::default(), domain_sync_oracle: ferdie.sync_service.clone(), + domain_executor: alice.code_executor.clone(), }; let domain_genesis_hash = alice.client.info().best_hash; From c34427faf3e747b81b2708e7e1c8a52f31c3f119 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Wed, 8 Jan 2025 14:48:54 +0530 Subject: [PATCH 06/14] replace runtime intermediate roots with roots derived from TrieBackendApi. These are essentially same roots as tested before the commit --- .../src/domain_block_processor.rs | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 86c4d4384a..d84d699ada 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -2,7 +2,7 @@ use crate::aux_schema::BundleMismatchType; use crate::fraud_proof::FraudProofGenerator; use crate::utils::{DomainBlockImportNotification, DomainImportNotificationSinks}; use crate::ExecutionReceiptFor; -use codec::{Decode, Encode}; +use codec::Encode; use domain_block_builder::{BlockBuilder, BuiltBlock, CollectedStorageChanges}; use domain_block_preprocessor::inherents::get_inherent_data; use domain_block_preprocessor::PreprocessResult; @@ -38,6 +38,7 @@ where header_hash: Block::Hash, state_root: Block::Hash, extrinsics_root: Block::Hash, + intermediate_roots: Vec, } pub(crate) struct DomainBlockResult @@ -308,6 +309,7 @@ where state_root, header_number, header_hash, + intermediate_roots, } = self .build_and_import_block( parent_hash, @@ -341,30 +343,20 @@ where // } // } - let runtime_api = self.client.runtime_api(); - - let mut roots = runtime_api.intermediate_roots(header_hash)?; - - let encoded_state_root = state_root - .encode() - .try_into() - .expect("State root uses the same Block hash type which must fit into [u8; 32]; qed"); - - roots.push(encoded_state_root); - + let roots: Vec<[u8; 32]> = intermediate_roots + .iter() + .map(|v| { + v.encode().try_into().expect( + "State root uses the same Block hash type which must fit into [u8; 32]; qed", + ) + }) + .collect(); let trace_root = MerkleTree::from_leaves(&roots).root().ok_or_else(|| { sp_blockchain::Error::Application(Box::from("Failed to get merkle root of trace")) })?; - let trace: Vec<::Hash> = roots - .into_iter() - .map(|r| { - Block::Hash::decode(&mut r.as_slice()) - .expect("Storage root uses the same Block hash type; qed") - }) - .collect(); tracing::trace!( - ?trace, + ?intermediate_roots, ?trace_root, "Trace root calculated for #{header_number},{header_hash}" ); @@ -391,6 +383,7 @@ where // Get the accumulated transaction fee of all transactions included in the block // and used as the operator reward + let runtime_api = self.client.runtime_api(); let block_fees = runtime_api.block_fees(header_hash)?; let transfers = runtime_api.transfers(header_hash)?; @@ -404,7 +397,7 @@ where consensus_block_hash, inboxed_bundles: bundles, final_state_root: state_root, - execution_trace: trace, + execution_trace: intermediate_roots, execution_trace_root: sp_core::H256(trace_root), block_fees, transfers, @@ -444,7 +437,7 @@ where let CollectedStorageChanges { storage_changes, - intermediate_roots: _, + intermediate_roots, } = storage_changes; let (header, body) = block.deconstruct(); @@ -475,6 +468,7 @@ where header_number, state_root, extrinsics_root, + intermediate_roots, }) } From fa4fce98b19484e671e6a71eb00f7f2c25da9099 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 9 Jan 2025 17:19:56 +0530 Subject: [PATCH 07/14] remove Intermediate roots from pallet executive --- crates/sp-domains/src/core_api.rs | 3 -- .../client/block-builder/src/custom_api.rs | 22 ----------- domains/pallets/executive/src/lib.rs | 39 +------------------ domains/runtime/auto-id/src/lib.rs | 4 -- domains/runtime/evm/src/lib.rs | 4 -- domains/test/runtime/auto-id/src/lib.rs | 4 -- domains/test/runtime/evm/src/lib.rs | 4 -- 7 files changed, 2 insertions(+), 78 deletions(-) diff --git a/crates/sp-domains/src/core_api.rs b/crates/sp-domains/src/core_api.rs index 90a07c3a76..f17219f287 100644 --- a/crates/sp-domains/src/core_api.rs +++ b/crates/sp-domains/src/core_api.rs @@ -28,9 +28,6 @@ sp_api::decl_runtime_apis! { tx_range: &U256, ) -> bool; - /// Returns the intermediate storage roots in an encoded form. - fn intermediate_roots() -> Vec<[u8; 32]>; - /// Returns the storage root after initializing the block. fn initialize_block_with_post_state_root(header: &Block::Header) -> Vec; diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index ec9533db94..ae73f735ab 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -1,8 +1,5 @@ //! Custom API that is efficient to collect storage roots. -// TODO: remove in later commits -#![allow(dead_code)] - use codec::{Codec, Decode, Encode}; use hash_db::{HashDB, Hasher, Prefix}; use sc_client_api::{backend, ExecutorProvider, StateBackend}; @@ -26,9 +23,6 @@ pub(crate) type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< HashingFor, >; -type TrieBackendFor = - TrieBackend, HashingFor>; - /// Storage changes are the collected throughout the execution. pub struct CollectedStorageChanges { /// Storage changes that are captured during the execution. @@ -102,22 +96,6 @@ where } } -impl<'a, S, H> DeltaBackend<'a, S, H> -where - S: 'a + TrieBackendStorage, - H: 'a + Hasher, -{ - fn consolidate_delta(&mut self, db: BackendTransaction) { - let delta = if let Some(mut delta) = self.delta.take() { - delta.consolidate(db); - delta - } else { - db - }; - self.delta = Some(delta); - } -} - pub(crate) struct TrieBackendApi, Exec> { parent_hash: Block::Hash, parent_number: NumberFor, diff --git a/domains/pallets/executive/src/lib.rs b/domains/pallets/executive/src/lib.rs index f4ebe82486..c7b6de62a7 100644 --- a/domains/pallets/executive/src/lib.rs +++ b/domains/pallets/executive/src/lib.rs @@ -182,26 +182,10 @@ mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_block_number: BlockNumberFor) -> Weight { - // Reset the intermediate storage roots from last block. - IntermediateRoots::::kill(); // TODO: Probably needs a different value Weight::from_parts(1, 0) } } - - /// Intermediate storage roots collected during the block execution. - #[pallet::storage] - #[pallet::getter(fn intermediate_roots)] - pub(super) type IntermediateRoots = StorageValue<_, Vec<[u8; 32]>, ValueQuery>; -} - -impl Pallet { - pub(crate) fn push_root(root: Vec) { - IntermediateRoots::::append( - TryInto::<[u8; 32]>::try_into(root) - .expect("root is a SCALE encoded hash which uses H256; qed"), - ); - } } /// Same semantics with `frame_executive::Executive`. @@ -388,16 +372,10 @@ where panic!("{}", err) } }); - - // Note the storage root before finalizing the block so that the block imported during the - // syncing process produces the same storage root with the one processed based on - // the consensus block. - Pallet::::push_root(Self::storage_root()); } /// Wrapped `frame_executive::Executive::finalize_block`. pub fn finalize_block() -> HeaderFor { - Pallet::::push_root(Self::storage_root()); frame_executive::Executive::< ExecutiveConfig, BlockOf, @@ -429,8 +407,6 @@ where /// /// Note the storage root in the beginning. pub fn apply_extrinsic(uxt: ExtrinsicOf) -> ApplyExtrinsicResult { - Pallet::::push_root(Self::storage_root()); - // apply the extrinsic within another transaction so that changes can be reverted. let res = with_storage_layer(|| { frame_executive::Executive::< @@ -455,7 +431,7 @@ where // - Pre and Post dispatch fails. Check the test `test_domain_block_builder_include_ext_with_failed_predispatch` // why this could happen. If it fail due to this, then we revert the inner storage changes // but still include extrinsic so that we can clear inconsistency between block body and trace roots. - let res = match res { + match res { Ok(dispatch_outcome) => Ok(dispatch_outcome), Err(err) => { let encoded = uxt.encode(); @@ -509,18 +485,7 @@ where >::note_applied_extrinsic(&r, dispatch_info); Ok(Err(err)) } - }; - - // TODO: Critical!!! https://github.com/paritytech/substrate/pull/10922#issuecomment-1068997467 - log::debug!( - target: "domain::runtime::executive", - "[apply_extrinsic] after: {:?}", - { - use codec::Decode; - BlockHashOf::::decode(&mut Self::storage_root().as_slice()).unwrap() - } - ); - res + } } // TODO: https://github.com/paritytech/substrate/issues/10711 diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index ade1200f0c..0559a139a4 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -758,10 +758,6 @@ impl_runtime_apis! { } } - fn intermediate_roots() -> Vec<[u8; 32]> { - ExecutivePallet::intermediate_roots() - } - fn initialize_block_with_post_state_root(header: &::Header) -> Vec { Executive::initialize_block(header); Executive::storage_root() diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 965573b918..ff73735e9a 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -1169,10 +1169,6 @@ impl_runtime_apis! { } } - fn intermediate_roots() -> Vec<[u8; 32]> { - ExecutivePallet::intermediate_roots() - } - fn initialize_block_with_post_state_root(header: &::Header) -> Vec { Executive::initialize_block(header); Executive::storage_root() diff --git a/domains/test/runtime/auto-id/src/lib.rs b/domains/test/runtime/auto-id/src/lib.rs index 0f77507876..018f347322 100644 --- a/domains/test/runtime/auto-id/src/lib.rs +++ b/domains/test/runtime/auto-id/src/lib.rs @@ -749,10 +749,6 @@ impl_runtime_apis! { } } - fn intermediate_roots() -> Vec<[u8; 32]> { - ExecutivePallet::intermediate_roots() - } - fn initialize_block_with_post_state_root(header: &::Header) -> Vec { Executive::initialize_block(header); Executive::storage_root() diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 26dce324a9..1d50a4a141 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -1124,10 +1124,6 @@ impl_runtime_apis! { } } - fn intermediate_roots() -> Vec<[u8; 32]> { - ExecutivePallet::intermediate_roots() - } - fn initialize_block_with_post_state_root(header: &::Header) -> Vec { Executive::initialize_block(header); Executive::storage_root() From dfc3827aecbbb16d56af4a0b0356fcda9d5e7ff8 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 9 Jan 2025 19:51:36 +0530 Subject: [PATCH 08/14] avoid unnecessary clone of InmemoryDB --- .../sp-domains-fraud-proof/src/execution_prover.rs | 2 +- domains/client/block-builder/src/custom_api.rs | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/sp-domains-fraud-proof/src/execution_prover.rs b/crates/sp-domains-fraud-proof/src/execution_prover.rs index 3414d1c282..7dadc50344 100644 --- a/crates/sp-domains-fraud-proof/src/execution_prover.rs +++ b/crates/sp-domains-fraud-proof/src/execution_prover.rs @@ -58,7 +58,7 @@ where // TODO: avoid using the String API specified by `execution_method()` // https://github.com/paritytech/substrate/discussions/11095 if let Some((delta, post_delta_root)) = delta_changes { - let delta_backend = create_delta_backend(trie_backend, delta, post_delta_root); + let delta_backend = create_delta_backend(trie_backend, &delta, post_delta_root); sp_state_machine::prove_execution_on_trie_backend( &delta_backend, &mut Default::default(), diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index ae73f735ab..2d9fd452a6 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -38,7 +38,7 @@ pub struct CollectedStorageChanges { /// and `delta`. pub fn create_delta_backend<'a, S, H>( backend: &'a TrieBackend, - delta: BackendTransaction, + delta: &'a BackendTransaction, post_delta_root: H::Out, ) -> TrieBackend, H> where @@ -51,7 +51,7 @@ where fn create_delta_backend_with_maybe_delta<'a, S, H>( backend: &'a TrieBackend, - maybe_delta: Option>, + maybe_delta: Option<&'a BackendTransaction>, post_delta_root: H::Out, ) -> TrieBackend, H> where @@ -76,7 +76,7 @@ where H: 'a + Hasher, { backend: &'a S, - delta: Option>, + delta: Option<&'a BackendTransaction>, _phantom: PhantomData, } @@ -87,7 +87,7 @@ where { fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { if let Some(db) = &self.delta - && let Some(v) = HashDB::get(db, key, prefix) + && let Some(v) = HashDB::get(*db, key, prefix) { Ok(Some(v)) } else { @@ -294,10 +294,7 @@ where .map_err(sp_blockchain::Error::RuntimeCode)?; let mut overlayed_changes = OverlayedChanges::default(); let (state_root, delta) = if let Some(changes) = &self.maybe_storage_changes { - ( - changes.transaction_storage_root, - Some(changes.transaction.clone()), - ) + (changes.transaction_storage_root, Some(&changes.transaction)) } else { (*trie_backend.root(), None) }; From cae4f9076cc92e582dd19a4553bd97efa6a427fc Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 9 Jan 2025 22:14:38 +0530 Subject: [PATCH 09/14] deduplicate storage changes instead of simp,y appending them --- .../client/block-builder/src/custom_api.rs | 109 ++++++++++++++---- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index 2d9fd452a6..1d944511f0 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -3,15 +3,17 @@ use codec::{Codec, Decode, Encode}; use hash_db::{HashDB, Hasher, Prefix}; use sc_client_api::{backend, ExecutorProvider, StateBackend}; +use sp_core::offchain::OffchainOverlayedChange; use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode}; use sp_inherents::InherentData; use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor}; use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode, TransactionOutcome}; use sp_state_machine::backend::AsTrieBackend; use sp_state_machine::{ - BackendTransaction, DBValue, OverlayedChanges, StateMachine, StorageChanges, TrieBackend, - TrieBackendBuilder, TrieBackendStorage, + BackendTransaction, DBValue, IndexOperation, OverlayedChanges, StateMachine, StorageChanges, + StorageKey, StorageValue, TrieBackend, TrieBackendBuilder, TrieBackendStorage, }; +use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; @@ -23,6 +25,79 @@ pub(crate) type TrieDeltaBackendFor<'a, State, Block> = TrieBackend< HashingFor, >; +struct MappedStorageChanges { + /// All changes to the main storage. + /// + /// A value of `None` means that it was deleted. + pub main_storage_changes: HashMap>, + /// All changes to the child storages. + pub child_storage_changes: HashMap>>, + /// Offchain state changes to write to the offchain database. + pub offchain_storage_changes: HashMap<(Vec, Vec), OffchainOverlayedChange>, + /// A transaction for the backend that contains all changes from + /// [`main_storage_changes`](StorageChanges::main_storage_changes) and from + /// [`child_storage_changes`](StorageChanges::child_storage_changes). + /// [`offchain_storage_changes`](StorageChanges::offchain_storage_changes). + pub transaction: BackendTransaction, + /// The storage root after applying the transaction. + pub transaction_storage_root: H::Out, + /// Changes to the transaction index, + pub transaction_index_changes: Vec, +} + +impl From> for StorageChanges { + fn from(value: MappedStorageChanges) -> Self { + StorageChanges { + main_storage_changes: value.main_storage_changes.into_iter().collect(), + child_storage_changes: value + .child_storage_changes + .into_iter() + .map(|(k, v)| (k, v.into_iter().collect())) + .collect(), + offchain_storage_changes: value.offchain_storage_changes.into_iter().collect(), + transaction: value.transaction, + transaction_storage_root: value.transaction_storage_root, + transaction_index_changes: value.transaction_index_changes, + } + } +} + +impl From> for MappedStorageChanges { + fn from(value: StorageChanges) -> Self { + MappedStorageChanges { + main_storage_changes: value.main_storage_changes.into_iter().collect(), + child_storage_changes: value + .child_storage_changes + .into_iter() + .map(|(k, v)| (k, v.into_iter().collect())) + .collect(), + offchain_storage_changes: value.offchain_storage_changes.into_iter().collect(), + transaction: value.transaction, + transaction_storage_root: value.transaction_storage_root, + transaction_index_changes: value.transaction_index_changes, + } + } +} + +impl MappedStorageChanges { + fn consolidate_storage_changes(&mut self, mut changes: StorageChanges) -> H::Out { + self.main_storage_changes + .extend(changes.main_storage_changes); + changes + .child_storage_changes + .into_iter() + .for_each(|(k, v)| self.child_storage_changes.entry(k).or_default().extend(v)); + self.offchain_storage_changes + .extend(changes.offchain_storage_changes); + self.transaction.consolidate(changes.transaction); + self.transaction_index_changes + .append(&mut changes.transaction_index_changes); + let previous_storage_root = self.transaction_storage_root; + self.transaction_storage_root = changes.transaction_storage_root; + previous_storage_root + } +} + /// Storage changes are the collected throughout the execution. pub struct CollectedStorageChanges { /// Storage changes that are captured during the execution. @@ -102,7 +177,7 @@ pub(crate) struct TrieBackendApi, state: Backend::State, executor: Arc, - maybe_storage_changes: Option>>, + maybe_storage_changes: Option>>, intermediate_roots: Vec, } @@ -162,27 +237,13 @@ where .map_err(|err| sp_blockchain::Error::CallResultDecode("failed to decode Result", err)) } - fn consolidate_storage_changes(&mut self, mut changes: StorageChanges>) { - let changes = if let Some(mut current_changes) = self.maybe_storage_changes.take() { - current_changes - .main_storage_changes - .append(&mut changes.main_storage_changes); - current_changes - .child_storage_changes - .append(&mut changes.child_storage_changes); - current_changes - .offchain_storage_changes - .append(&mut changes.offchain_storage_changes); - current_changes.transaction.consolidate(changes.transaction); - self.intermediate_roots - .push(current_changes.transaction_storage_root); - current_changes.transaction_storage_root = changes.transaction_storage_root; - current_changes - .transaction_index_changes - .append(&mut changes.transaction_index_changes); - current_changes + fn consolidate_storage_changes(&mut self, changes: StorageChanges>) { + let changes = if let Some(mut mapped_changes) = self.maybe_storage_changes.take() { + let previous_root = mapped_changes.consolidate_storage_changes(changes); + self.intermediate_roots.push(previous_root); + mapped_changes } else { - changes + changes.into() }; self.maybe_storage_changes = Some(changes) } @@ -266,7 +327,7 @@ where if let Some(storage_changes) = maybe_storage_changes { intermediate_roots.push(storage_changes.transaction_storage_root); Some(CollectedStorageChanges { - storage_changes, + storage_changes: storage_changes.into(), intermediate_roots, }) } else { From cc6f251ce8fcddbc1b30798ca3f75f6aad027323 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 10 Jan 2025 16:32:16 +0530 Subject: [PATCH 10/14] hold runtime code and some small cleanup --- .../client/block-builder/src/custom_api.rs | 137 ++++++++++++------ domains/client/block-builder/src/lib.rs | 35 +++-- 2 files changed, 114 insertions(+), 58 deletions(-) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index 1d944511f0..a6cbc1db2b 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -4,7 +4,7 @@ use codec::{Codec, Decode, Encode}; use hash_db::{HashDB, Hasher, Prefix}; use sc_client_api::{backend, ExecutorProvider, StateBackend}; use sp_core::offchain::OffchainOverlayedChange; -use sp_core::traits::{CallContext, CodeExecutor, RuntimeCode}; +use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode}; use sp_inherents::InherentData; use sp_runtime::traits::{Block as BlockT, HashingFor, NumberFor}; use sp_runtime::{ApplyExtrinsicResult, ExtrinsicInclusionMode, TransactionOutcome}; @@ -13,6 +13,7 @@ use sp_state_machine::{ BackendTransaction, DBValue, IndexOperation, OverlayedChanges, StateMachine, StorageChanges, StorageKey, StorageValue, TrieBackend, TrieBackendBuilder, TrieBackendStorage, }; +use std::borrow::Cow; use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; @@ -45,55 +46,82 @@ struct MappedStorageChanges { pub transaction_index_changes: Vec, } +#[derive(Clone)] +struct RuntimeCode { + code: Vec, + heap_pages: Option, + hash: Vec, +} + impl From> for StorageChanges { fn from(value: MappedStorageChanges) -> Self { + let MappedStorageChanges { + main_storage_changes, + child_storage_changes, + offchain_storage_changes, + transaction, + transaction_storage_root, + transaction_index_changes, + } = value; StorageChanges { - main_storage_changes: value.main_storage_changes.into_iter().collect(), - child_storage_changes: value - .child_storage_changes + main_storage_changes: main_storage_changes.into_iter().collect(), + child_storage_changes: child_storage_changes .into_iter() .map(|(k, v)| (k, v.into_iter().collect())) .collect(), - offchain_storage_changes: value.offchain_storage_changes.into_iter().collect(), - transaction: value.transaction, - transaction_storage_root: value.transaction_storage_root, - transaction_index_changes: value.transaction_index_changes, + offchain_storage_changes: offchain_storage_changes.into_iter().collect(), + transaction, + transaction_storage_root, + transaction_index_changes, } } } impl From> for MappedStorageChanges { fn from(value: StorageChanges) -> Self { + let StorageChanges { + main_storage_changes, + child_storage_changes, + offchain_storage_changes, + transaction, + transaction_storage_root, + transaction_index_changes, + } = value; MappedStorageChanges { - main_storage_changes: value.main_storage_changes.into_iter().collect(), - child_storage_changes: value - .child_storage_changes + main_storage_changes: main_storage_changes.into_iter().collect(), + child_storage_changes: child_storage_changes .into_iter() .map(|(k, v)| (k, v.into_iter().collect())) .collect(), - offchain_storage_changes: value.offchain_storage_changes.into_iter().collect(), - transaction: value.transaction, - transaction_storage_root: value.transaction_storage_root, - transaction_index_changes: value.transaction_index_changes, + offchain_storage_changes: offchain_storage_changes.into_iter().collect(), + transaction, + transaction_storage_root, + transaction_index_changes, } } } impl MappedStorageChanges { - fn consolidate_storage_changes(&mut self, mut changes: StorageChanges) -> H::Out { - self.main_storage_changes - .extend(changes.main_storage_changes); - changes - .child_storage_changes + fn consolidate_storage_changes(&mut self, changes: StorageChanges) -> H::Out { + let StorageChanges { + main_storage_changes, + child_storage_changes, + offchain_storage_changes, + transaction, + transaction_storage_root, + mut transaction_index_changes, + } = changes; + self.main_storage_changes.extend(main_storage_changes); + child_storage_changes .into_iter() .for_each(|(k, v)| self.child_storage_changes.entry(k).or_default().extend(v)); self.offchain_storage_changes - .extend(changes.offchain_storage_changes); - self.transaction.consolidate(changes.transaction); + .extend(offchain_storage_changes); + self.transaction.consolidate(transaction); self.transaction_index_changes - .append(&mut changes.transaction_index_changes); + .append(&mut transaction_index_changes); let previous_storage_root = self.transaction_storage_root; - self.transaction_storage_root = changes.transaction_storage_root; + self.transaction_storage_root = transaction_storage_root; previous_storage_root } } @@ -179,6 +207,17 @@ pub(crate) struct TrieBackendApi, maybe_storage_changes: Option>>, intermediate_roots: Vec, + runtime_code: RuntimeCode, +} + +impl FetchRuntimeCode for TrieBackendApi +where + Block: BlockT, + Backend: backend::Backend, +{ + fn fetch_runtime_code(&self) -> Option> { + Some(Cow::from(&self.runtime_code.code)) + } } impl TrieBackendApi @@ -196,6 +235,23 @@ where executor: Arc, ) -> Result { let state = backend.state_at(parent_hash)?; + let trie_backend = state.as_trie_backend(); + let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend); + let sp_core::traits::RuntimeCode { + code_fetcher, + heap_pages, + hash, + } = state_runtime_code + .runtime_code() + .map_err(sp_blockchain::Error::RuntimeCode)?; + let runtime_code = RuntimeCode { + code: code_fetcher + .fetch_runtime_code() + .map(|c| c.to_vec()) + .ok_or(sp_blockchain::Error::RuntimeCode("missing runtime code"))?, + heap_pages, + hash, + }; Ok(Self { parent_hash, parent_number, @@ -204,15 +260,23 @@ where executor, maybe_storage_changes: None, intermediate_roots: vec![], + runtime_code, }) } + fn runtime_code(&self) -> sp_core::traits::RuntimeCode { + sp_core::traits::RuntimeCode { + code_fetcher: self, + heap_pages: self.runtime_code.heap_pages, + hash: self.runtime_code.hash.clone(), + } + } + fn call_function( &self, method: &str, call_data: Vec, trie_backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>, ) -> Result { let mut extensions = self @@ -220,6 +284,8 @@ where .execution_extensions() .extensions(self.parent_hash, self.parent_number); + let runtime_code = self.runtime_code(); + let result = StateMachine::<_, _, _>::new( trie_backend, overlayed_changes, @@ -252,7 +318,6 @@ where &self, header: Block::Header, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>, ) -> Result { let call_data = header.encode(); @@ -260,7 +325,6 @@ where "Core_initialize_block", call_data, backend, - runtime_code, overlayed_changes, ) } @@ -269,7 +333,6 @@ where &self, extrinsic: ::Extrinsic, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>, ) -> Result { let call_data = extrinsic.encode(); @@ -277,7 +340,6 @@ where "BlockBuilder_apply_extrinsic", call_data, backend, - runtime_code, overlayed_changes, ) } @@ -285,14 +347,12 @@ where pub(crate) fn finalize_block( &self, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>, ) -> Result { self.call_function( "BlockBuilder_finalize_block", vec![], backend, - runtime_code, overlayed_changes, ) } @@ -301,7 +361,6 @@ where &self, inherent: InherentData, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>, ) -> Result::Extrinsic>, sp_blockchain::Error> { let call_data = inherent.encode(); @@ -309,7 +368,6 @@ where "BlockBuilder_inherent_extrinsics", call_data, backend, - runtime_code, overlayed_changes, ) } @@ -343,16 +401,11 @@ where F: FnOnce( &Self, &TrieDeltaBackendFor, - RuntimeCode, &mut OverlayedChanges>, ) -> TransactionOutcome>, R: Decode, { let trie_backend = self.state.as_trie_backend(); - let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(trie_backend); - let runtime_code = state_runtime_code - .runtime_code() - .map_err(sp_blockchain::Error::RuntimeCode)?; let mut overlayed_changes = OverlayedChanges::default(); let (state_root, delta) = if let Some(changes) = &self.maybe_storage_changes { (changes.transaction_storage_root, Some(&changes.transaction)) @@ -362,13 +415,7 @@ where let trie_delta_backend = create_delta_backend_with_maybe_delta(trie_backend, delta, state_root); - let outcome = call( - self, - &trie_delta_backend, - runtime_code, - &mut overlayed_changes, - ); - + let outcome = call(self, &trie_delta_backend, &mut overlayed_changes); match outcome { TransactionOutcome::Commit(result) => { let state_version = sp_core::storage::StateVersion::V1; diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index dd79bba4f8..0d98890cf9 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -36,12 +36,13 @@ use sc_client_api::{backend, ExecutorProvider}; use sp_api::{ProvideRuntimeApi, TransactionOutcome}; pub use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ApplyExtrinsicFailed, Error}; -use sp_core::traits::{CodeExecutor, RuntimeCode}; +use sp_core::traits::CodeExecutor; use sp_runtime::traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}; use sp_runtime::Digest; use sp_state_machine::OverlayedChanges; use std::collections::VecDeque; use std::sync::Arc; +use std::time::Instant; /// A block that was build by [`BlockBuilder`] plus some additional data. /// @@ -103,9 +104,8 @@ where api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>| { - match api.initialize_block(header, backend, runtime_code, overlayed_changes) { + match api.initialize_block(header, backend, overlayed_changes) { Ok(_) => TransactionOutcome::Commit(Ok(())), Err(e) => TransactionOutcome::Rollback(Err(e)), } @@ -127,13 +127,12 @@ where /// Execute the block's list of extrinsics. fn execute_extrinsics(&mut self) -> Result<(), Error> { for (index, xt) in self.extrinsics.iter().enumerate() { + let start = Instant::now(); let res = self.api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>| { - match api.apply_extrinsic(xt.clone(), backend, runtime_code, overlayed_changes) - { + match api.apply_extrinsic(xt.clone(), backend, overlayed_changes) { Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( ApplyExtrinsicFailed::Validity(tx_validity).into(), @@ -146,6 +145,11 @@ where if let Err(e) = res { tracing::debug!("Apply extrinsic at index {index} failed: {e}"); } + tracing::warn!( + "Extrinsic {:?} execution took: {:?} nanos", + index, + start.elapsed().as_nanos() + ); } Ok(()) @@ -171,10 +175,8 @@ where self.api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>| { - match api.apply_extrinsic(xt.clone(), backend, runtime_code, overlayed_changes) - { + match api.apply_extrinsic(xt.clone(), backend, overlayed_changes) { Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( ApplyExtrinsicFailed::Validity(tx_validity).into(), @@ -207,14 +209,19 @@ where /// supplied by `self.api`, combined as [`BuiltBlock`]. /// The storage proof will be `Some(_)` when proof recording was enabled. pub fn build(mut self) -> Result, Error> { + let start = Instant::now(); self.execute_extrinsics()?; + tracing::warn!( + "Extrinsics execution took: {:?} ms", + start.elapsed().as_millis() + ); + let start = Instant::now(); let header = self.api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>| { - match api.finalize_block(backend, runtime_code, overlayed_changes) { + match api.finalize_block(backend, overlayed_changes) { Ok(header) => TransactionOutcome::Commit(Ok(header)), Err(e) => TransactionOutcome::Rollback(Err(e)), } @@ -233,6 +240,10 @@ where .collect_storage_changes() .expect("must always have the storage changes due to execution above"); + tracing::warn!( + "Winding up storage changes took: {:?} ms", + start.elapsed().as_millis() + ); Ok(BuiltBlock { block: Block::new(header, self.extrinsics.into()), storage_changes, @@ -249,14 +260,12 @@ where let exts = api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, - runtime_code: RuntimeCode, overlayed_changes: &mut OverlayedChanges>| { // `create_inherents` should not change any state, to ensure this we always rollback // the transaction. TransactionOutcome::Rollback(api.inherent_extrinsics( inherent_data, backend, - runtime_code, overlayed_changes, )) }, From ae83448a2a9ba631c98b938f8c0f939340c33bc5 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Tue, 14 Jan 2025 09:45:02 +0530 Subject: [PATCH 11/14] update struct comment and remove debug points --- domains/client/block-builder/src/custom_api.rs | 2 +- domains/client/block-builder/src/lib.rs | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index a6cbc1db2b..5e755bc5fe 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -37,7 +37,7 @@ struct MappedStorageChanges { pub offchain_storage_changes: HashMap<(Vec, Vec), OffchainOverlayedChange>, /// A transaction for the backend that contains all changes from /// [`main_storage_changes`](StorageChanges::main_storage_changes) and from - /// [`child_storage_changes`](StorageChanges::child_storage_changes). + /// [`child_storage_changes`](StorageChanges::child_storage_changes) but excluding /// [`offchain_storage_changes`](StorageChanges::offchain_storage_changes). pub transaction: BackendTransaction, /// The storage root after applying the transaction. diff --git a/domains/client/block-builder/src/lib.rs b/domains/client/block-builder/src/lib.rs index 0d98890cf9..7040c7910e 100644 --- a/domains/client/block-builder/src/lib.rs +++ b/domains/client/block-builder/src/lib.rs @@ -42,7 +42,6 @@ use sp_runtime::Digest; use sp_state_machine::OverlayedChanges; use std::collections::VecDeque; use std::sync::Arc; -use std::time::Instant; /// A block that was build by [`BlockBuilder`] plus some additional data. /// @@ -127,7 +126,6 @@ where /// Execute the block's list of extrinsics. fn execute_extrinsics(&mut self) -> Result<(), Error> { for (index, xt) in self.extrinsics.iter().enumerate() { - let start = Instant::now(); let res = self.api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, @@ -145,11 +143,6 @@ where if let Err(e) = res { tracing::debug!("Apply extrinsic at index {index} failed: {e}"); } - tracing::warn!( - "Extrinsic {:?} execution took: {:?} nanos", - index, - start.elapsed().as_nanos() - ); } Ok(()) @@ -209,14 +202,7 @@ where /// supplied by `self.api`, combined as [`BuiltBlock`]. /// The storage proof will be `Some(_)` when proof recording was enabled. pub fn build(mut self) -> Result, Error> { - let start = Instant::now(); self.execute_extrinsics()?; - tracing::warn!( - "Extrinsics execution took: {:?} ms", - start.elapsed().as_millis() - ); - let start = Instant::now(); - let header = self.api.execute_in_transaction( |api: &TrieBackendApi, backend: &TrieDeltaBackendFor, @@ -240,10 +226,6 @@ where .collect_storage_changes() .expect("must always have the storage changes due to execution above"); - tracing::warn!( - "Winding up storage changes took: {:?} ms", - start.elapsed().as_millis() - ); Ok(BuiltBlock { block: Block::new(header, self.extrinsics.into()), storage_changes, From c3ee9a33ee784a1d838da583f824ccdf7c2d229b Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 17 Jan 2025 13:13:06 +0530 Subject: [PATCH 12/14] add storage overlay checks pallet --- Cargo.lock | 15 ++ Cargo.toml | 1 + .../pallets/storage_overlay_checks/Cargo.toml | 35 +++ .../pallets/storage_overlay_checks/src/lib.rs | 202 ++++++++++++++++++ domains/test/runtime/auto-id/Cargo.toml | 2 + domains/test/runtime/auto-id/src/lib.rs | 5 + domains/test/runtime/evm/Cargo.toml | 2 + domains/test/runtime/evm/src/lib.rs | 5 + 8 files changed, 267 insertions(+) create mode 100644 domains/test/pallets/storage_overlay_checks/Cargo.toml create mode 100644 domains/test/pallets/storage_overlay_checks/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7a395a12e2..cb7079c34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1037,6 +1037,7 @@ dependencies = [ "pallet-domain-id", "pallet-domain-sudo", "pallet-messenger", + "pallet-storage-overlay-checks", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", @@ -3319,6 +3320,7 @@ dependencies = [ "pallet-evm-precompile-sha3fips", "pallet-evm-precompile-simple", "pallet-messenger", + "pallet-storage-overlay-checks", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", @@ -7916,6 +7918,19 @@ dependencies = [ "sp-weights", ] +[[package]] +name = "pallet-storage-overlay-checks" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + [[package]] name = "pallet-subspace" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0ddc3ee148..11fc2511dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "domains/runtime/*", "domains/service", "domains/test/runtime/*", + "domains/test/pallets/*", "domains/test/service", "shared/*", "test/subspace-test-client", diff --git a/domains/test/pallets/storage_overlay_checks/Cargo.toml b/domains/test/pallets/storage_overlay_checks/Cargo.toml new file mode 100644 index 0000000000..f8deba67c4 --- /dev/null +++ b/domains/test/pallets/storage_overlay_checks/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "pallet-storage-overlay-checks" +version = "0.1.0" +authors = ["Subspace Labs "] +edition = "2021" +license = "0BSD" +homepage = "https://subspace.network" +repository = "https://github.com/autonomys/subspace" +description = "Test pallet to check the storage overlay changes" +include = [ + "/src", + "/Cargo.toml", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } +frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +frame-system = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +log = { version = "0.4.22", default-features = false } +scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } +sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } + + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", +] diff --git a/domains/test/pallets/storage_overlay_checks/src/lib.rs b/domains/test/pallets/storage_overlay_checks/src/lib.rs new file mode 100644 index 0000000000..b55a8e1d29 --- /dev/null +++ b/domains/test/pallets/storage_overlay_checks/src/lib.rs @@ -0,0 +1,202 @@ +//! Test pallet to check the overlay changes during the block execution and post block execution. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +use frame_system::pallet_prelude::BlockNumberFor; +pub use pallet::*; +use sp_core::H256; + +const LOG_TARGET: &str = "runtime::storage_overlay_checks"; + +#[frame_support::pallet] +mod pallet { + use crate::StorageParams; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::BlockNumberFor; + use sp_core::H256; + use sp_runtime::traits::Zero; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::storage] + pub(super) type Svvq = StorageValue<_, H256, ValueQuery>; + + #[pallet::storage] + pub(super) type Svoq = StorageValue<_, H256, OptionQuery>; + + #[pallet::storage] + pub(super) type Smvq = StorageMap<_, Identity, H256, H256, ValueQuery>; + + #[pallet::storage] + pub(super) type Smoq = StorageMap<_, Identity, H256, H256, OptionQuery>; + + #[pallet::storage] + pub(super) type Sdmvq = + StorageDoubleMap<_, Identity, H256, Identity, H256, H256, ValueQuery>; + + #[pallet::storage] + pub(super) type Sdmoq = + StorageDoubleMap<_, Identity, H256, Identity, H256, H256, OptionQuery>; + + #[pallet::storage] + pub(super) type Snmvq = StorageNMap< + _, + ( + NMapKey, + NMapKey, + NMapKey, + ), + H256, + ValueQuery, + >; + + #[pallet::storage] + pub(super) type Snmoq = StorageNMap< + _, + ( + NMapKey, + NMapKey, + NMapKey, + ), + H256, + OptionQuery, + >; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(n: BlockNumberFor) -> Weight { + // even blocks, storages must be set already and are emptied + // odd block, storages must be empty and are set + let params = StorageParams::default(); + if n % BlockNumberFor::::from(2u32) == Zero::zero() { + Pallet::::check_storage_exists(n, params); + Pallet::::clear_storage(n); + } else { + Pallet::::check_storage_empty(n, params); + Pallet::::set_storage(n, params); + } + + Weight::zero() + } + + fn on_finalize(n: BlockNumberFor) { + // even blocks, storages must be emptied during initialize + // odd block, storages must be set during initialize + let params = StorageParams::default(); + if n % BlockNumberFor::::from(2u32) == Zero::zero() { + Pallet::::check_storage_empty(n, params); + } else { + Pallet::::check_storage_exists(n, params); + } + } + } +} + +#[derive(Clone, Copy)] +struct StorageParams { + key1: H256, + key2: H256, + key3: H256, + value: H256, +} + +impl Default for StorageParams { + fn default() -> Self { + Self { + key1: H256::repeat_byte(1), + key2: H256::repeat_byte(2), + key3: H256::repeat_byte(3), + value: H256::repeat_byte(4), + } + } +} + +impl Pallet { + fn set_storage(n: BlockNumberFor, params: StorageParams) { + log::debug!(target: LOG_TARGET, "Setting storages at: {:?}", n); + let StorageParams { + key1, + key2, + key3, + value, + } = params; + + Svvq::::set(value); + Svoq::::set(Some(value)); + Smvq::::set(key1, value); + Smoq::::set(key1, Some(value)); + Sdmvq::::set(key1, key2, value); + Sdmoq::::set(key1, key2, Some(value)); + Snmvq::::set((key1, key2, key3), value); + Snmoq::::set((key1, key2, key3), Some(value)); + } + + fn clear_storage(n: BlockNumberFor) { + log::debug!(target: LOG_TARGET, "Clearing storages at: {:?}", n); + Svvq::::kill(); + Svoq::::kill(); + let _ = Smvq::::clear(u32::MAX, None); + let _ = Smoq::::clear(u32::MAX, None); + let _ = Sdmvq::::clear(u32::MAX, None); + let _ = Sdmoq::::clear(u32::MAX, None); + let _ = Snmvq::::clear(u32::MAX, None); + let _ = Snmoq::::clear(u32::MAX, None); + } + + fn check_storage_exists(n: BlockNumberFor, params: StorageParams) { + log::debug!(target: LOG_TARGET, "Checking storages exists at: {:?}", n); + let StorageParams { + key1, + key2, + key3, + value, + } = params; + + assert_eq!(Svvq::::get(), value); + assert_eq!(Svoq::::get(), Some(value)); + assert_eq!(Smvq::::get(key1), value); + assert_eq!(Smoq::::get(key1), Some(value)); + assert_eq!(Sdmvq::::get(key1, key2), value); + assert_eq!(Sdmoq::::get(key1, key2), Some(value)); + assert_eq!(Snmvq::::get((key1, key2, key3)), value); + assert_eq!(Snmoq::::get((key1, key2, key3)), Some(value)); + } + + fn check_storage_empty(n: BlockNumberFor, params: StorageParams) { + log::debug!(target: LOG_TARGET, "Checking storages empty at: {:?}", n); + let StorageParams { + key1, + key2, + key3, + value: _, + } = params; + + assert!(!Svvq::::exists()); + + assert!(!Svoq::::exists()); + assert_eq!(Svoq::::get(), None); + + assert!(!Smvq::::contains_key(key1)); + + assert!(!Smoq::::contains_key(key1)); + assert_eq!(Smoq::::get(key1), None); + + assert!(!Sdmvq::::contains_key(key1, key2)); + + assert!(!Sdmoq::::contains_key(key1, key2)); + assert_eq!(Sdmoq::::get(key1, key2), None); + + assert!(!Snmvq::::contains_key((key1, key2, key3))); + + assert!(!Snmoq::::contains_key((key1, key2, key3))); + assert_eq!(Snmoq::::get((key1, key2, key3)), None); + } +} diff --git a/domains/test/runtime/auto-id/Cargo.toml b/domains/test/runtime/auto-id/Cargo.toml index a69eed2e75..b2e7282260 100644 --- a/domains/test/runtime/auto-id/Cargo.toml +++ b/domains/test/runtime/auto-id/Cargo.toml @@ -33,6 +33,7 @@ pallet-block-fees = { version = "0.1.0", path = "../../../pallets/block-fees", d pallet-domain-id = { version = "0.1.0", path = "../../../pallets/domain-id", default-features = false } pallet-domain-sudo = { version = "0.1.0", path = "../../../pallets/domain-sudo", default-features = false } pallet-messenger = { version = "0.1.0", path = "../../../pallets/messenger", default-features = false } +pallet-storage-overlay-checks = { version = "0.1.0", path = "../../pallets/storage_overlay_checks", default-features = false } pallet-timestamp = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } pallet-transaction-payment = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } pallet-transaction-payment-rpc-runtime-api = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } @@ -85,6 +86,7 @@ std = [ "pallet-domain-id/std", "pallet-domain-sudo/std", "pallet-messenger/std", + "pallet-storage-overlay-checks/std", "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", diff --git a/domains/test/runtime/auto-id/src/lib.rs b/domains/test/runtime/auto-id/src/lib.rs index 018f347322..25e151aa2e 100644 --- a/domains/test/runtime/auto-id/src/lib.rs +++ b/domains/test/runtime/auto-id/src/lib.rs @@ -457,6 +457,8 @@ impl pallet_domain_sudo::Config for Runtime { type IntoRuntimeCall = IntoRuntimeCall; } +impl pallet_storage_overlay_checks::Config for Runtime {} + // Create the runtime by composing the FRAME pallets that were previously configured. // // NOTE: Currently domain runtime does not naturally support the pallets with inherent extrinsics. @@ -488,6 +490,9 @@ construct_runtime!( // Sudo account Sudo: pallet_domain_sudo = 100, + + // checks + StorageOverlayChecks: pallet_storage_overlay_checks = 200, } ); diff --git a/domains/test/runtime/evm/Cargo.toml b/domains/test/runtime/evm/Cargo.toml index 2f26410460..f5977e6995 100644 --- a/domains/test/runtime/evm/Cargo.toml +++ b/domains/test/runtime/evm/Cargo.toml @@ -41,6 +41,7 @@ pallet-evm-precompile-modexp = { version = "2.0.0-dev", default-features = false pallet-evm-precompile-sha3fips = { version = "2.0.0-dev", default-features = false, git = "https://github.com/autonomys/frontier", rev = "f80f9e2bad338f3bf3854b256b3c4edea23e5968" } pallet-evm-precompile-simple = { version = "2.0.0-dev", default-features = false, git = "https://github.com/autonomys/frontier", rev = "f80f9e2bad338f3bf3854b256b3c4edea23e5968" } pallet-messenger = { version = "0.1.0", path = "../../../pallets/messenger", default-features = false } +pallet-storage-overlay-checks = { version = "0.1.0", path = "../../pallets/storage_overlay_checks", default-features = false } pallet-timestamp = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } pallet-transaction-payment = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } pallet-transaction-payment-rpc-runtime-api = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } @@ -98,6 +99,7 @@ std = [ "pallet-evm-precompile-sha3fips/std", "pallet-evm-precompile-simple/std", "pallet-messenger/std", + "pallet-storage-overlay-checks/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 1d50a4a141..00dee075e4 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -732,6 +732,8 @@ impl pallet_domain_sudo::Config for Runtime { type IntoRuntimeCall = IntoRuntimeCall; } +impl pallet_storage_overlay_checks::Config for Runtime {} + // Create the runtime by composing the FRAME pallets that were previously configured. // // NOTE: Currently domain runtime does not naturally support the pallets with inherent extrinsics. @@ -764,6 +766,9 @@ construct_runtime!( // Sudo account Sudo: pallet_domain_sudo = 100, + + // checks + StorageOverlayChecks: pallet_storage_overlay_checks = 200, } ); From 3019471c5875813379d91d0d273ef09fc1bd8ddf Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Sat, 18 Jan 2025 01:13:44 +0530 Subject: [PATCH 13/14] purge all the zero referenced nodes post consolidation. Thanks for the solution @NingLin-P --- domains/client/block-builder/src/custom_api.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/domains/client/block-builder/src/custom_api.rs b/domains/client/block-builder/src/custom_api.rs index 5e755bc5fe..bcfffef38a 100644 --- a/domains/client/block-builder/src/custom_api.rs +++ b/domains/client/block-builder/src/custom_api.rs @@ -118,6 +118,7 @@ impl MappedStorageChanges { self.offchain_storage_changes .extend(offchain_storage_changes); self.transaction.consolidate(transaction); + self.transaction.purge(); self.transaction_index_changes .append(&mut transaction_index_changes); let previous_storage_root = self.transaction_storage_root; From 35aacb530791de32fa408647ddf7e0c1c7e3397e Mon Sep 17 00:00:00 2001 From: linning Date: Fri, 24 Jan 2025 05:18:46 +0800 Subject: [PATCH 14/14] Add test test_custom_api_storage_root_match_upstream_root Signed-off-by: linning --- Cargo.lock | 2 + domains/client/domain-operator/src/tests.rs | 144 +++++++++++++++++++- domains/test/primitives/src/lib.rs | 3 + domains/test/runtime/auto-id/Cargo.toml | 1 + domains/test/runtime/auto-id/src/lib.rs | 7 + domains/test/runtime/evm/Cargo.toml | 1 + domains/test/runtime/evm/src/lib.rs | 7 + 7 files changed, 162 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb7079c34f..b990f2343c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,7 @@ dependencies = [ "sp-domains", "sp-genesis-builder", "sp-inherents", + "sp-io", "sp-messenger", "sp-messenger-host-functions", "sp-mmr-primitives", @@ -3334,6 +3335,7 @@ dependencies = [ "sp-domains", "sp-genesis-builder", "sp-inherents", + "sp-io", "sp-messenger", "sp-messenger-host-functions", "sp-mmr-primitives", diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 6bc062ff65..4e1473aca4 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -6,10 +6,12 @@ use crate::tests::TxPoolError::InvalidTransaction as TxPoolInvalidTransaction; use crate::OperatorSlotInfo; use codec::{Decode, Encode}; use cross_domain_message_gossip::get_channel_state; +use domain_block_builder::BlockBuilderApi; +use domain_runtime_primitives::opaque::Block as DomainBlock; use domain_runtime_primitives::{AccountId20Converter, AccountIdConverter, Hash}; use domain_test_primitives::{OnchainStateApi, TimestampApi}; use domain_test_service::evm_domain_test_runtime::{Header, UncheckedExtrinsic}; -use domain_test_service::EcdsaKeyring::{Alice, Bob, Charlie, Eve}; +use domain_test_service::EcdsaKeyring::{Alice, Bob, Charlie, Dave, Eve}; use domain_test_service::Sr25519Keyring::{self, Alice as Sr25519Alice, Ferdie}; use domain_test_service::{construct_extrinsic_generic, AUTO_ID_DOMAIN_ID, EVM_DOMAIN_ID}; use futures::StreamExt; @@ -22,7 +24,8 @@ use sc_transaction_pool::error::Error as PoolError; use sc_transaction_pool_api::error::Error as TxPoolError; use sc_transaction_pool_api::TransactionPool; use sc_utils::mpsc::tracing_unbounded; -use sp_api::{ProvideRuntimeApi, StorageProof}; +use sp_api::{ApiExt, Core, ProvideRuntimeApi, StorageProof}; +use sp_blockchain::ApplyExtrinsicFailed; use sp_consensus::SyncOracle; use sp_core::storage::StateVersion; use sp_core::traits::{FetchRuntimeCode, SpawnEssentialNamed}; @@ -50,7 +53,7 @@ use sp_runtime::traits::{ use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidityError, }; -use sp_runtime::OpaqueExtrinsic; +use sp_runtime::{Digest, OpaqueExtrinsic, TransactionOutcome}; use sp_state_machine::backend::AsTrieBackend; use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; @@ -5805,3 +5808,138 @@ async fn test_stale_fork_xdm_true_invalid_fraud_proof() { // We check for timeouts last, because they are the least useful test failure message. assert!(!timed_out); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_custom_api_storage_root_match_upstream_root() { + let directory = TempDir::new().expect("Must be able to create temporary directory"); + + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + let tokio_handle = tokio::runtime::Handle::current(); + + // Start Ferdie + let mut ferdie = MockConsensusNode::run( + tokio_handle.clone(), + Ferdie, + BasePath::new(directory.path().join("ferdie")), + ); + + // Run Alice (a evm domain authority node) + let mut alice = domain_test_service::DomainNodeBuilder::new( + tokio_handle.clone(), + BasePath::new(directory.path().join("alice")), + ) + .build_evm_node(Role::Authority, Alice, &mut ferdie) + .await; + + produce_blocks!(ferdie, alice, 3).await.unwrap(); + + let domain_parent_number = alice.client.info().best_number; + let domain_parent_hash = alice.client.info().best_hash; + let alice_account_nonce = alice.account_nonce(); + let alice_total_balance = alice.free_balance(Alice.to_account_id()); + // Success tx + let tx1 = alice.construct_extrinsic( + alice_account_nonce, + pallet_balances::Call::transfer_allow_death { + dest: Bob.to_account_id(), + value: 1234567890987654321, + }, + ); + // Tx fail during execution due to insufficient fund + let tx2 = alice.construct_extrinsic( + alice_account_nonce + 1, + pallet_balances::Call::transfer_allow_death { + dest: Charlie.to_account_id(), + value: alice_total_balance + 1, + }, + ); + // Tx transfer all of Alice's fund + let tx3 = alice.construct_extrinsic( + alice_account_nonce + 2, + pallet_balances::Call::transfer_all { + dest: Dave.to_account_id(), + keep_alive: false, + }, + ); + // Tx fail during pre-dispatch due to unable to pay tx fee + let tx4 = alice.construct_extrinsic( + alice_account_nonce + 3, + pallet_balances::Call::transfer_allow_death { + dest: Charlie.to_account_id(), + value: 1, + }, + ); + for tx in [tx1, tx2, tx3, tx4] { + alice + .send_extrinsic(tx) + .await + .expect("Failed to send extrinsic"); + } + + // Produce a domain block that contains the previously sent extrinsic + produce_blocks!(ferdie, alice, 1).await.unwrap(); + + // Get the receipt of that block, its execution trace is generated by the custom API instance + let (_, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await; + let receipt = bundle.into_receipt(); + assert_eq!(receipt.execution_trace.len(), 8); + + let mut roots = vec![]; + let runtime_api_instance = alice.client.runtime_api(); + + let init_header = <::Header as HeaderT>::new( + domain_parent_number + 1u32, + Default::default(), + Default::default(), + domain_parent_hash, + Digest { + logs: vec![DigestItem::consensus_block_info( + receipt.consensus_block_hash, + )], + }, + ); + runtime_api_instance + .initialize_block(domain_parent_hash, &init_header) + .unwrap(); + roots.push( + runtime_api_instance + .storage_root(domain_parent_hash) + .unwrap(), + ); + + let domain_block_body = { + let best_hash = alice.client.info().best_hash; + alice.client.block_body(best_hash).unwrap().unwrap() + }; + for xt in domain_block_body { + let _ = runtime_api_instance.execute_in_transaction(|api| { + match api.apply_extrinsic(domain_parent_hash, xt) { + Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), + Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( + ApplyExtrinsicFailed::Validity(tx_validity).into(), + )), + Err(e) => TransactionOutcome::Rollback(Err(sp_blockchain::Error::from(e))), + } + }); + roots.push( + runtime_api_instance + .storage_root(domain_parent_hash) + .unwrap(), + ); + } + + let finalized_header = runtime_api_instance + .finalize_block(domain_parent_hash) + .unwrap(); + roots.push((*finalized_header.state_root()).into()); + + let roots: Vec<::Hash> = roots + .into_iter() + .map(|r| ::Hash::decode(&mut r.as_slice()).unwrap()) + .collect(); + + assert_eq!(receipt.execution_trace, roots); +} diff --git a/domains/test/primitives/src/lib.rs b/domains/test/primitives/src/lib.rs index b0cfeeafde..763eb5b3dc 100644 --- a/domains/test/primitives/src/lib.rs +++ b/domains/test/primitives/src/lib.rs @@ -28,5 +28,8 @@ sp_api::decl_runtime_apis! { /// Api to get the current domain transaction byte fee fn consensus_chain_byte_fee() -> Balance; + + /// Get the storage root + fn storage_root() -> [u8; 32]; } } diff --git a/domains/test/runtime/auto-id/Cargo.toml b/domains/test/runtime/auto-id/Cargo.toml index b2e7282260..6eb70abfdf 100644 --- a/domains/test/runtime/auto-id/Cargo.toml +++ b/domains/test/runtime/auto-id/Cargo.toml @@ -50,6 +50,7 @@ sp-messenger = { version = "0.1.0", default-features = false, path = "../../../p sp-messenger-host-functions = { version = "0.1.0", default-features = false, path = "../../../primitives/messenger-host-functions" } sp-mmr-primitives = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-offchain = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-session = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-std = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } diff --git a/domains/test/runtime/auto-id/src/lib.rs b/domains/test/runtime/auto-id/src/lib.rs index 25e151aa2e..e90b68463f 100644 --- a/domains/test/runtime/auto-id/src/lib.rs +++ b/domains/test/runtime/auto-id/src/lib.rs @@ -986,6 +986,13 @@ impl_runtime_apis! { fn consensus_chain_byte_fee() -> Balance { BlockFees::consensus_chain_byte_fee() } + + fn storage_root() -> [u8; 32] { + let version = ::Version::get().state_version(); + let root = sp_io::storage::root(version); + TryInto::<[u8; 32]>::try_into(root) + .expect("root is a SCALE encoded hash which uses H256; qed") + } } #[cfg(feature = "runtime-benchmarks")] diff --git a/domains/test/runtime/evm/Cargo.toml b/domains/test/runtime/evm/Cargo.toml index f5977e6995..acfd551a98 100644 --- a/domains/test/runtime/evm/Cargo.toml +++ b/domains/test/runtime/evm/Cargo.toml @@ -58,6 +58,7 @@ sp-messenger = { version = "0.1.0", default-features = false, path = "../../../p sp-messenger-host-functions = { version = "0.1.0", default-features = false, path = "../../../primitives/messenger-host-functions" } sp-mmr-primitives = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-offchain = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } +sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-session = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } sp-std = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "94a1a8143a89bbe9f938c1939ff68abc1519a305" } diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 00dee075e4..27e898ae59 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -1524,6 +1524,13 @@ impl_runtime_apis! { fn consensus_chain_byte_fee() -> Balance { BlockFees::consensus_chain_byte_fee() } + + fn storage_root() -> [u8; 32] { + let version = ::Version::get().state_version(); + let root = sp_io::storage::root(version); + TryInto::<[u8; 32]>::try_into(root) + .expect("root is a SCALE encoded hash which uses H256; qed") + } } impl sp_domain_sudo::DomainSudoApi for Runtime {