diff --git a/substrate/core/client/src/blockchain.rs b/substrate/core/client/src/blockchain.rs index 8c46e5ebe2..ddc8ba866a 100644 --- a/substrate/core/client/src/blockchain.rs +++ b/substrate/core/client/src/blockchain.rs @@ -36,11 +36,6 @@ pub trait HeaderBackend: Send + Sync { /// Get block hash by number. Returns `None` if the header is not in the chain. fn hash(&self, number: NumberFor) -> Result>; - /// Get block header. Returns `UnknownBlock` error if block is not found. - fn expect_header(&self, id: BlockId) -> Result { - self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into()) - } - /// Convert an arbitrary block ID into a block hash. fn block_hash_from_id(&self, id: &BlockId) -> Result> { match *id { @@ -56,6 +51,23 @@ pub trait HeaderBackend: Send + Sync { BlockId::Number(n) => Ok(Some(n)), } } + + /// Get block header. Returns `UnknownBlock` error if block is not found. + fn expect_header(&self, id: BlockId) -> Result { + self.header(id)?.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into()) + } + + /// Convert an arbitrary block ID into a block number. Returns `UnknownBlock` error if block is not found. + fn expect_block_number_from_id(&self, id: &BlockId) -> Result> { + self.block_number_from_id(id) + .and_then(|n| n.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into())) + } + + /// Convert an arbitrary block ID into a block hash. Returns `UnknownBlock` error if block is not found. + fn expect_block_hash_from_id(&self, id: &BlockId) -> Result { + self.block_hash_from_id(id) + .and_then(|n| n.ok_or_else(|| ErrorKind::UnknownBlock(format!("{}", id)).into())) + } } /// Blockchain database backend. Does not perform any validation. diff --git a/substrate/core/client/src/call_executor.rs b/substrate/core/client/src/call_executor.rs index 16580fe8d9..678ea97c78 100644 --- a/substrate/core/client/src/call_executor.rs +++ b/substrate/core/client/src/call_executor.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use std::cmp::Ord; +use codec::Encode; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::Block as BlockT; use state_machine::{self, OverlayedChanges, Ext, @@ -45,7 +46,6 @@ where B: BlockT, H: Hasher, H::Out: Ord, - { /// Externalities error type. type Error: state_machine::Error; @@ -53,12 +53,32 @@ where /// Execute a call to a contract on top of state in a block of given hash. /// /// No changes are made. - fn call(&self, + fn call( + &self, id: &BlockId, method: &str, call_data: &[u8], ) -> Result; + /// Execute a contextual call on top of state in a block of a given hash. + /// + /// No changes are made. + /// Before executing the method, passed header is installed as the current header + /// of the execution context. + fn contextual_call< + PB: Fn() -> error::Result, + EM: Fn(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, + >( + &self, + at: &BlockId, + method: &str, + call_data: &[u8], + changes: &mut OverlayedChanges, + initialised_block: &mut Option>, + prepare_environment_block: PB, + manager: ExecutionManager, + ) -> error::Result> where ExecutionManager: Clone; + /// Extract RuntimeVersion of given block /// /// No changes are made. @@ -81,11 +101,27 @@ where /// Execute a call to a contract on top of given state, gathering execution proof. /// /// No changes are made. - fn prove_at_state>(&self, + fn prove_at_state>( + &self, state: S, overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] + ) -> Result<(Vec, Vec>), error::Error> { + let trie_state = state.try_into_trie_backend() + .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box)?; + self.prove_at_trie_state(&trie_state, overlay, method, call_data) + } + + /// Execute a call to a contract on top of given trie state, gathering execution proof. + /// + /// No changes are made. + fn prove_at_trie_state>( + &self, + trie_state: &state_machine::TrieBackend, + overlay: &mut OverlayedChanges, + method: &str, + call_data: &[u8] ) -> Result<(Vec, Vec>), error::Error>; /// Get runtime version if supported. @@ -139,6 +175,30 @@ where Ok(CallResult { return_data, changes }) } + fn contextual_call< + PB: Fn() -> error::Result, + EM: Fn(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, + >( + &self, + at: &BlockId, + method: &str, + call_data: &[u8], + changes: &mut OverlayedChanges, + initialised_block: &mut Option>, + prepare_environment_block: PB, + manager: ExecutionManager, + ) -> Result, error::Error> where ExecutionManager: Clone { + let state = self.backend.state_at(*at)?; + //TODO: Find a better way to prevent double block initialization + if method != "Core_initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) { + let header = prepare_environment_block()?; + self.call_at_state(&state, changes, "Core_initialise_block", &header.encode(), manager.clone())?; + *initialised_block = Some(*at); + } + + self.call_at_state(&state, changes, method, call_data, manager).map(|cr| cr.0) + } + fn runtime_version(&self, id: &BlockId) -> error::Result { let mut overlay = OverlayedChanges::default(); let state = self.backend.state_at(*id)?; @@ -178,15 +238,16 @@ where ).map_err(Into::into) } - fn prove_at_state>(&self, - state: S, - changes: &mut OverlayedChanges, + fn prove_at_trie_state>( + &self, + trie_state: &state_machine::TrieBackend, + overlay: &mut OverlayedChanges, method: &str, call_data: &[u8] ) -> Result<(Vec, Vec>), error::Error> { - state_machine::prove_execution( - state, - changes, + state_machine::prove_execution_on_trie_backend( + trie_state, + overlay, &self.executor, method, call_data, diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index c555b95ad8..2c85bbd588 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -43,14 +43,13 @@ use state_machine::{ ChangesTrieRootsStorage, ChangesTrieStorage, key_changes, key_changes_proof, OverlayedChanges }; -use codec::Encode; use backend::{self, BlockImportOperation}; use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend}; use call_executor::{CallExecutor, LocalCallExecutor}; use executor::{RuntimeVersion, RuntimeInfo}; use notifications::{StorageNotifications, StorageEventStream}; -use light::fetcher::ChangesProof; +use light::{call_executor::prove_execution, fetcher::ChangesProof}; use {cht, error, in_mem, block_builder::{self, api::BlockBuilder as BlockBuilderAPI}, genesis, consensus}; /// Type that implements `futures::Stream` of block import events. @@ -318,7 +317,9 @@ impl Client where /// /// No changes are made. pub fn execution_proof(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<(Vec, Vec>)> { - self.state_at(id).and_then(|state| self.executor.prove_at_state(state, &mut Default::default(), method, call_data)) + let state = self.state_at(id)?; + let header = self.prepare_environment_block(id)?; + prove_execution(state, header, &self.executor, method, call_data) } /// Reads given header and generates CHT-based header proof. @@ -326,31 +327,6 @@ impl Client where self.header_proof_with_cht_size(id, cht::SIZE) } - pub(crate) fn call_at_state( - &self, - at: &BlockId, - function: &'static str, - args: Vec, - changes: &mut OverlayedChanges - ) -> error::Result> { - let state = self.state_at(at)?; - - let execution_manager = || match self.api_execution_strategy { - ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, - ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm, - ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { - warn!("Consensus error between wasm and native runtime execution at block {:?}", at); - warn!(" Function {:?}", function); - warn!(" Native result {:?}", native_result); - warn!(" Wasm result {:?}", wasm_result); - wasm_result - }), - }; - - self.executor.call_at_state(&state, changes, function, &args, execution_manager()) - .map(|res| res.0) - } - /// Get block hash by number. pub fn block_hash(&self, block_number: <::Header as HeaderT>::Number) -> error::Result> { self.backend.blockchain().hash(block_number) @@ -359,7 +335,7 @@ impl Client where /// Reads given header and generates CHT-based header proof for CHT of given size. pub fn header_proof_with_cht_size(&self, id: &BlockId, cht_size: u64) -> error::Result<(Block::Header, Vec>)> { let proof_error = || error::ErrorKind::Backend(format!("Failed to generate header proof for {:?}", id)); - let header = self.header(id)?.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", id)))?; + let header = self.backend.blockchain().expect_header(*id)?; let block_num = *header.number(); let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?; let cht_start = cht::start_number(cht_size, cht_num); @@ -383,13 +359,15 @@ impl Client where _ => return Err(error::ErrorKind::ChangesTriesNotSupported.into()), }; + let first_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(first))?.as_(); + let last_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(last))?.as_(); key_changes::<_, Blake2Hasher>( &config, storage, - self.require_block_number_from_id(&BlockId::Hash(first))?.as_(), + first_number, &ChangesTrieAnchorBlockId { hash: convert_hash(&last), - number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(), + number: last_number, }, self.backend.blockchain().info()?.best_number.as_(), key) @@ -465,7 +443,7 @@ impl Client where _ => return Err(error::ErrorKind::ChangesTriesNotSupported.into()), }; - let min_number = self.require_block_number_from_id(&BlockId::Hash(min))?; + let min_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?; let recording_storage = AccessedRootsRecorder:: { storage, min: min_number.as_(), @@ -474,17 +452,19 @@ impl Client where let max_number = ::std::cmp::min( self.backend.blockchain().info()?.best_number, - self.require_block_number_from_id(&BlockId::Hash(max))?, + self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(max))?, ); // fetch key changes proof + let first_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(first))?.as_(); + let last_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(last))?.as_(); let key_changes_proof = key_changes_proof::<_, Blake2Hasher>( &config, &recording_storage, - self.require_block_number_from_id(&BlockId::Hash(first))?.as_(), + first_number, &ChangesTrieAnchorBlockId { hash: convert_hash(&last), - number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(), + number: last_number, }, max_number.as_(), key @@ -806,12 +786,7 @@ impl Client where /// while performing major synchronization work. pub fn finalize_block(&self, id: BlockId, justification: Option, notify: bool) -> error::Result<()> { let last_best = self.backend.blockchain().info()?.best_hash; - let to_finalize_hash = match id { - BlockId::Hash(h) => h, - BlockId::Number(n) => self.backend.blockchain().hash(n)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("No block with number {:?}", n)))?, - }; - + let to_finalize_hash = self.backend.blockchain().expect_block_hash_from_id(&id)?; self.apply_finality(to_finalize_hash, justification, last_best, notify) } @@ -845,12 +820,6 @@ impl Client where } } - /// Convert an arbitrary block ID into a block hash, returning error if the block is unknown. - fn require_block_number_from_id(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().block_number_from_id(id) - .and_then(|n| n.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{}", id)).into())) - } - /// Get block header by id. pub fn header(&self, id: &BlockId) -> error::Result::Header>> { self.backend.blockchain().header(*id) @@ -992,6 +961,17 @@ impl Client where .map_err(|e| error::Error::from_state(Box::new(e)))? .and_then(|c| Decode::decode(&mut &*c))) } + + /// Prepare in-memory header that is used in execution environment. + fn prepare_environment_block(&self, parent: &BlockId) -> error::Result { + Ok(<::Header as HeaderT>::new( + self.backend.blockchain().expect_block_number_from_id(parent)? + As::sa(1), + Default::default(), + Default::default(), + self.backend.blockchain().expect_block_hash_from_id(&parent)?, + Default::default(), + )) + } } impl ChainHeaderBackend for Client where @@ -1049,25 +1029,20 @@ impl CallRuntimeAt for Client where changes: &mut OverlayedChanges, initialised_block: &mut Option>, ) -> error::Result> { - //TODO: Find a better way to prevent double block initialization - if function != "Core_initialise_block" - && initialised_block.map(|id| id != *at).unwrap_or(true) { - let parent = at; - let header = <::Header as HeaderT>::new( - self.block_number_from_id(parent)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))? - + As::sa(1), - Default::default(), - Default::default(), - self.block_hash_from_id(&parent)? - .ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?, - Default::default() - ); - self.call_at_state(at, "Core_initialise_block", header.encode(), changes)?; - *initialised_block = Some(*at); - } + let execution_manager = match self.api_execution_strategy { + ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, + ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm, + ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { + warn!("Consensus error between wasm and native runtime execution at block {:?}", at); + warn!(" Function {:?}", function); + warn!(" Native result {:?}", native_result); + warn!(" Wasm result {:?}", wasm_result); + wasm_result + }), + }; - self.call_at_state(at, function, args, changes) + self.executor.contextual_call(at, function, &args, changes, initialised_block, + || self.prepare_environment_block(at), execution_manager) } fn runtime_version_at(&self, at: &BlockId) -> error::Result { diff --git a/substrate/core/client/src/light/call_executor.rs b/substrate/core/client/src/light/call_executor.rs index e77bf3309b..d30e459215 100644 --- a/substrate/core/client/src/light/call_executor.rs +++ b/substrate/core/client/src/light/call_executor.rs @@ -17,15 +17,17 @@ //! Light client call exector. Executes methods on remote full nodes, fetching //! execution proof and checking it locally. +use std::collections::HashSet; use std::marker::PhantomData; use std::sync::Arc; use futures::{IntoFuture, Future}; -use primitives::convert_hash; +use codec::Encode; +use primitives::{H256, Blake2Hasher, convert_hash}; use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; -use state_machine::{Backend as StateBackend, CodeExecutor, OverlayedChanges, - execution_proof_check, ExecutionManager}; +use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT}; +use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChanges, + create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager}; use hash_db::Hasher; use blockchain::Backend as ChainBackend; @@ -73,11 +75,7 @@ where type Error = ClientError; fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> ClientResult { - let block_hash = match *id { - BlockId::Hash(hash) => hash, - BlockId::Number(number) => self.blockchain.hash(number)? - .ok_or_else(|| ClientErrorKind::UnknownBlock(format!("{}", number)))?, - }; + let block_hash = self.blockchain.expect_block_hash_from_id(id)?; let block_header = self.blockchain.expect_header(id.clone())?; self.fetcher.remote_call(RemoteCallRequest { @@ -89,6 +87,27 @@ where }).into_future().wait() } + fn contextual_call< + PB: Fn() -> ClientResult, + EM: Fn(Result, Self::Error>, Result, Self::Error>) -> Result, Self::Error>, + >( + &self, + at: &BlockId, + method: &str, + call_data: &[u8], + changes: &mut OverlayedChanges, + initialised_block: &mut Option>, + _prepare_environment_block: PB, + _manager: ExecutionManager, + ) -> ClientResult> where ExecutionManager: Clone { + // it is only possible to execute contextual call if changes are empty + if !changes.is_empty() || initialised_block.is_some() { + return Err(ClientErrorKind::NotAvailableOnLightClient.into()); + } + + self.call(at, method, call_data).map(|cr| cr.return_data) + } + fn runtime_version(&self, id: &BlockId) -> ClientResult { let call_result = self.call(id, "version", &[])?; RuntimeVersion::decode(&mut call_result.return_data.as_slice()) @@ -108,9 +127,9 @@ where Err(ClientErrorKind::NotAvailableOnLightClient.into()) } - fn prove_at_state>( + fn prove_at_trie_state>( &self, - _state: S, + _state: &state_machine::TrieBackend, _changes: &mut OverlayedChanges, _method: &str, _call_data: &[u8] @@ -123,7 +142,49 @@ where } } -/// Check remote execution proof using given backend. +/// Prove contextual execution using given block header in environment. +/// +/// Method is executed using passed header as environment' current block. +/// Proof includes both environment preparation proof and method execution proof. +pub fn prove_execution( + state: S, + header: Block::Header, + executor: &E, + method: &str, + call_data: &[u8], +) -> ClientResult<(Vec, Vec>)> + where + Block: BlockT, + S: StateBackend, + E: CallExecutor, +{ + let trie_state = state.try_into_trie_backend() + .ok_or_else(|| Box::new(state_machine::ExecutionError::UnableToGenerateProof) as Box)?; + + // prepare execution environment + record preparation proof + let mut changes = Default::default(); + let (_, init_proof) = executor.prove_at_trie_state( + &trie_state, + &mut changes, + "Core_initialise_block", + &header.encode(), + )?; + + // execute method + record execution proof + let (result, exec_proof) = executor.prove_at_trie_state(&trie_state, &mut changes, method, call_data)?; + let total_proof = init_proof.into_iter() + .chain(exec_proof.into_iter()) + .collect::>() + .into_iter() + .collect(); + + Ok((result, total_proof)) +} + +/// Check remote contextual execution proof using given backend. +/// +/// Method is executed using passed header as environment' current block. +/// Proof shoul include both environment preparation proof and method execution proof. pub fn check_execution_proof( executor: &E, request: &RemoteCallRequest
, @@ -134,54 +195,109 @@ pub fn check_execution_proof( E: CodeExecutor, H: Hasher, H::Out: Ord + HeapSizeOf, - { let local_state_root = request.header.state_root(); let root: H::Out = convert_hash(&local_state_root); + // prepare execution environment + check preparation proof let mut changes = OverlayedChanges::default(); - let local_result = execution_proof_check::( - root, - remote_proof, + let trie_backend = create_proof_check_backend(root, remote_proof)?; + let next_block =
::new( + *request.header.number() + As::sa(1), + Default::default(), + Default::default(), + request.header.hash(), + Default::default(), + ); + execution_proof_check_on_trie_backend::( + &trie_backend, + &mut changes, + executor, + "Core_initialise_block", + &next_block.encode(), + )?; + + // execute method + let local_result = execution_proof_check_on_trie_backend::( + &trie_backend, &mut changes, executor, &request.method, - &request.call_data)?; + &request.call_data, + )?; Ok(CallResult { return_data: local_result, changes }) } #[cfg(test)] mod tests { - use test_client; + use consensus::BlockOrigin; + use test_client::{self, runtime::{Block, Header}, runtime::RuntimeApi, TestClient}; use executor::NativeExecutionDispatch; use super::*; #[test] fn execution_proof_is_generated_and_checked() { + type TestClient = test_client::client::Client< + test_client::Backend, + test_client::Executor, + Block, + RuntimeApi + >; + + fn execute(remote_client: &TestClient, at: u64, method: &'static str) -> (Vec, Vec) { + let remote_block_id = BlockId::Number(at); + let remote_root = remote_client.state_at(&remote_block_id) + .unwrap().storage_root(::std::iter::empty()).0; + + // 'fetch' execution proof from remote node + let (remote_result, remote_execution_proof) = remote_client.execution_proof( + &remote_block_id, + method, + &[] + ).unwrap(); + + // check remote execution proof locally + let local_executor = test_client::LocalExecutor::new(); + let local_result = check_execution_proof(&local_executor, &RemoteCallRequest { + block: test_client::runtime::Hash::default(), + header: test_client::runtime::Header { + state_root: remote_root.into(), + parent_hash: Default::default(), + number: at, + extrinsics_root: Default::default(), + digest: Default::default(), + }, + method: method.into(), + call_data: vec![], + retry_count: None, + }, remote_execution_proof).unwrap(); + + (remote_result, local_result.return_data) + } + // prepare remote client let remote_client = test_client::new(); - let remote_block_id = BlockId::Number(0); - let remote_block_storage_root = remote_client.state_at(&remote_block_id) - .unwrap().storage_root(::std::iter::empty()).0; + for _ in 1..3 { + remote_client.import_justified( + BlockOrigin::Own, + remote_client.new_block().unwrap().bake().unwrap(), + Default::default(), + ).unwrap(); + } - // 'fetch' execution proof from remote node - let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "Core_authorities", &[]).unwrap().1; + // check method that doesn't requires environment + let (remote, local) = execute(&remote_client, 0, "Core_authorities"); + assert_eq!(remote, local); - // check remote execution proof locally - let local_executor = test_client::LocalExecutor::new(); - check_execution_proof(&local_executor, &RemoteCallRequest { - block: test_client::runtime::Hash::default(), - header: test_client::runtime::Header { - state_root: remote_block_storage_root.into(), - parent_hash: Default::default(), - number: 0, - extrinsics_root: Default::default(), - digest: Default::default(), - }, - method: "Core_authorities".into(), - call_data: vec![], - retry_count: None, - }, remote_execution_proof).unwrap(); + // check method that requires environment + let (_, block) = execute(&remote_client, 0, "BlockBuilder_finalise_block"); + let local_block: Header = Decode::decode(&mut &block[..]).unwrap(); + assert_eq!(local_block.number, 1); + + // check method that requires environment + let (_, block) = execute(&remote_client, 2, "BlockBuilder_finalise_block"); + let local_block: Header = Decode::decode(&mut &block[..]).unwrap(); + assert_eq!(local_block.number, 3); } } diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 0b7f1ccd6d..e09f1ba111 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -61,8 +61,8 @@ pub use changes_trie::{ key_changes, key_changes_proof, key_changes_proof_check, prune as prune_changes_tries}; pub use overlayed_changes::OverlayedChanges; -pub use proving_backend::create_proof_check_backend_storage; -pub use trie_backend_essence::Storage; +pub use proving_backend::{create_proof_check_backend, create_proof_check_backend_storage}; +pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend::TrieBackend; /// Default num of pages for the heap @@ -191,6 +191,7 @@ pub enum ExecutionStrategy { } /// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. +#[derive(Clone)] pub enum ExecutionManager { /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. NativeWhenPossible, @@ -381,14 +382,6 @@ where } /// Prove execution using the given state backend, overlayed changes, and call executor. -/// Produces a state-backend-specific "transaction" which can be used to apply the changes -/// to the backing store, such as the disk. -/// Execution proof is the set of all 'touched' storage DBValues from the backend. -/// -/// On an error, no prospective changes are written to the overlay. -/// -/// Note: changes to code will be in place if this call is made again. For running partial -/// blocks (e.g. a transaction at a time), ensure a different method is used. pub fn prove_execution( backend: B, overlay: &mut OverlayedChanges, @@ -404,7 +397,32 @@ where { let trie_backend = backend.try_into_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - let proving_backend = proving_backend::ProvingBackend::new(&trie_backend); + prove_execution_on_trie_backend(&trie_backend, overlay, exec, method, call_data) +} + +/// Prove execution using the given trie backend, overlayed changes, and call executor. +/// Produces a state-backend-specific "transaction" which can be used to apply the changes +/// to the backing store, such as the disk. +/// Execution proof is the set of all 'touched' storage DBValues from the backend. +/// +/// On an error, no prospective changes are written to the overlay. +/// +/// Note: changes to code will be in place if this call is made again. For running partial +/// blocks (e.g. a transaction at a time), ensure a different method is used. +pub fn prove_execution_on_trie_backend( + trie_backend: &TrieBackend, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], +) -> Result<(Vec, Vec>), Box> +where + S: trie_backend_essence::TrieBackendStorage, + H: Hasher, + Exec: CodeExecutor, + H::Out: Ord + HeapSizeOf, +{ + let proving_backend = proving_backend::ProvingBackend::new(trie_backend); let (result, _, _) = execute::, _>( &proving_backend, None, @@ -432,9 +450,31 @@ where Exec: CodeExecutor, H::Out: Ord + HeapSizeOf, { - let backend = proving_backend::create_proof_check_backend::(root.into(), proof)?; - execute::, _>(&backend, None, overlay, exec, method, call_data, ExecutionStrategy::NativeWhenPossible) - .map(|(result, _, _)| result) + let trie_backend = proving_backend::create_proof_check_backend::(root.into(), proof)?; + execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data) +} + +/// Check execution proof on proving backend, generated by `prove_execution` call. +pub fn execution_proof_check_on_trie_backend( + trie_backend: &TrieBackend, H>, + overlay: &mut OverlayedChanges, + exec: &Exec, + method: &str, + call_data: &[u8], +) -> Result, Box> +where + H: Hasher, + Exec: CodeExecutor, + H::Out: Ord + HeapSizeOf, +{ + execute::, _>( + trie_backend, + None, overlay, + exec, + method, + call_data, + ExecutionStrategy::NativeWhenPossible + ).map(|(result, _, _)| result) } /// Generate storage read proof. @@ -447,7 +487,6 @@ where H: Hasher, H::Out: Ord + HeapSizeOf { - let trie_backend = backend.try_into_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; prove_read_on_trie_backend(&trie_backend, key) diff --git a/substrate/core/state-machine/src/overlayed_changes.rs b/substrate/core/state-machine/src/overlayed_changes.rs index 239dc0b438..29ff7262da 100644 --- a/substrate/core/state-machine/src/overlayed_changes.rs +++ b/substrate/core/state-machine/src/overlayed_changes.rs @@ -82,6 +82,11 @@ impl OverlayedChangeSet { } impl OverlayedChanges { + /// Whether the overlayed changes are empty. + pub fn is_empty(&self) -> bool { + self.prospective.is_empty() && self.committed.is_empty() + } + /// Sets the changes trie configuration. /// /// Returns false if configuration has been set already and we now trying diff --git a/substrate/core/state-machine/src/trie_backend_essence.rs b/substrate/core/state-machine/src/trie_backend_essence.rs index 2c2281b730..cd5c046941 100644 --- a/substrate/core/state-machine/src/trie_backend_essence.rs +++ b/substrate/core/state-machine/src/trie_backend_essence.rs @@ -217,6 +217,7 @@ impl<'a, /// Key-value pairs storage that is used by trie backend essence. pub trait TrieBackendStorage: Send + Sync { + /// Get the value stored at key. fn get(&self, key: &H::Out) -> Result, String>; }