Skip to content

Commit

Permalink
add trie backend api
Browse files Browse the repository at this point in the history
  • Loading branch information
vedhavyas committed Jan 7, 2025
1 parent 9c454dc commit d31e08d
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 19 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions crates/sp-domains-fraud-proof/src/execution_prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<DB: HashDB<HashingFor<Block>, 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<HashingFor<Block>>, Block::Hash)>,
) -> sp_blockchain::Result<StorageProof> {
let state = self.backend.state_at(at)?;

Expand Down
1 change: 1 addition & 0 deletions domains/client/block-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
271 changes: 256 additions & 15 deletions domains/client/block-builder/src/custom_api.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,298 @@
//! 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<State, Block> =
<State as StateBackend<HashingFor<Block>>>::TrieBackendStorage;

type TrieDeltaBackendFor<'a, State, Block> = TrieBackend<
DeltaBackend<'a, TrieBackendStorageFor<State, Block>, HashingFor<Block>>,
HashingFor<Block>,
>;

type TrieBackendFor<State, Block> =
TrieBackend<TrieBackendStorageFor<State, Block>, HashingFor<Block>>;

/// 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<S, H>,
delta: BackendTransaction<H>,
post_delta_root: H::Out,
) -> TrieBackend<DeltaBackend<'a, S, H>, H>
where
S: 'a + TrieBackendStorage<H>,
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<S, H>,
delta: DB,
maybe_delta: Option<BackendTransaction<H>>,
post_delta_root: H::Out,
) -> TrieBackend<DeltaBackend<'a, S, H, DB>, H>
) -> TrieBackend<DeltaBackend<'a, S, H>, H>
where
S: 'a + TrieBackendStorage<H>,
H: 'a + Hasher,
H::Out: Codec,
DB: HashDB<H, DBValue>,
{
let essence = backend.essence();
let delta_backend = DeltaBackend {
backend: essence.backend_storage(),
delta,
delta: maybe_delta,
_phantom: PhantomData::<H>,
};
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>
pub struct DeltaBackend<'a, S, H>
where
S: 'a + TrieBackendStorage<H>,
H: 'a + Hasher,
DB: HashDB<H, DBValue>,
{
backend: &'a S,
delta: DB,
delta: Option<BackendTransaction<H>>,
_phantom: PhantomData<H>,
}

impl<'a, S, H, DB> TrieBackendStorage<H> for DeltaBackend<'a, S, H, DB>
impl<'a, S, H> TrieBackendStorage<H> for DeltaBackend<'a, S, H>
where
S: 'a + TrieBackendStorage<H>,
H: 'a + Hasher,
DB: HashDB<H, DBValue>,
{
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, 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>,
H: 'a + Hasher,
{
fn consolidate_delta(&mut self, db: BackendTransaction<H>) {
let delta = if let Some(mut delta) = self.delta.take() {
delta.consolidate(db);
delta
} else {
db
};
self.delta = Some(delta);
}
}

pub(crate) struct TrieBackendApi<Client, Block: BlockT, Backend: backend::Backend<Block>, Exec> {
parent_hash: Block::Hash,
parent_number: NumberFor<Block>,
client: Arc<Client>,
state: Backend::State,
executor: Exec,
maybe_storage_changes: Option<StorageChanges<HashingFor<Block>>>,
}

impl<Client, Block, Backend, Exec> TrieBackendApi<Client, Block, Backend, Exec>
where
Block: BlockT,
Backend: backend::Backend<Block>,
Client: ExecutorProvider<Block>,
Exec: CodeExecutor,
{
pub(crate) fn new(
parent_hash: Block::Hash,
parent_number: NumberFor<Block>,
client: Arc<Client>,
backend: Arc<Backend>,
executor: Exec,
) -> Result<Self, sp_blockchain::Error> {
let state = backend.state_at(parent_hash)?;
Ok(Self {
parent_hash,
parent_number,
client,
state,
executor,
maybe_storage_changes: None,
})
}

fn call_function<R: Decode>(
&self,
method: &str,
call_data: Vec<u8>,
trie_backend: &TrieDeltaBackendFor<Backend::State, Block>,
runtime_code: RuntimeCode,
overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
) -> Result<R, sp_blockchain::Error> {
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<HashingFor<Block>>) {
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<Backend::State, Block>,
runtime_code: RuntimeCode,
overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
) -> Result<ExtrinsicInclusionMode, sp_blockchain::Error> {
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: <Block as BlockT>::Extrinsic,
backend: &TrieDeltaBackendFor<Backend::State, Block>,
runtime_code: RuntimeCode,
overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
) -> Result<ApplyExtrinsicResult, sp_blockchain::Error> {
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<Backend::State, Block>,
runtime_code: RuntimeCode,
overlayed_changes: &mut OverlayedChanges<HashingFor<Block>>,
) -> Result<Block::Header, sp_blockchain::Error> {
self.call_function(
"BlockBuilder_finalize_block",
vec![],
backend,
runtime_code,
overlayed_changes,
)
}

pub(crate) fn execute_in_transaction<F, R>(
&mut self,
call: F,
) -> Result<R, sp_blockchain::Error>
where
F: FnOnce(
&Self,
&TrieDeltaBackendFor<Backend::State, Block>,
RuntimeCode,
&mut OverlayedChanges<HashingFor<Block>>,
) -> TransactionOutcome<R>,
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(&self.state, state_version)
.map_err(sp_blockchain::Error::StorageChanges)?;
self.consolidate_storage_changes(storage_changes);
result
}
TransactionOutcome::Rollback(result) => result,
};

Ok(result)
}
}
1 change: 1 addition & 0 deletions domains/client/block-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
//! initialize a block, to push extrinsics and to finalize a block.
#![warn(missing_docs)]
#![feature(let_chains)]

mod custom_api;

Expand Down

0 comments on commit d31e08d

Please sign in to comment.