mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 07:01:05 +00:00
State cache and other performance optimizations (#1345)
* State caching * Better code caching * Execution optimizaton * More optimizations * Updated wasmi * Caching test * Style * Style * Reverted some minor changes * Style and typos * Style and typos * Removed panics on missing memory
This commit is contained in:
committed by
Gav Wood
parent
e0639c435b
commit
b104c02eb6
@@ -68,9 +68,11 @@ pub trait BlockImportOperation<Block, H> where
|
||||
/// has been used to check justification of this block).
|
||||
fn update_authorities(&mut self, authorities: Vec<AuthorityIdFor<Block>>);
|
||||
/// Inject storage data into the database.
|
||||
fn update_storage(&mut self, update: <Self::State as StateBackend<H>>::Transaction) -> error::Result<()>;
|
||||
fn update_db_storage(&mut self, update: <Self::State as StateBackend<H>>::Transaction) -> error::Result<()>;
|
||||
/// Inject storage data into the database replacing any existing data.
|
||||
fn reset_storage(&mut self, top: StorageMap, children: ChildrenStorageMap) -> error::Result<H::Out>;
|
||||
/// Set top level storage changes.
|
||||
fn update_storage(&mut self, update: Vec<(Vec<u8>, Option<Vec<u8>>)>) -> error::Result<()>;
|
||||
/// Inject changes trie data into the database.
|
||||
fn update_changes_trie(&mut self, update: MemoryDB<H>) -> error::Result<()>;
|
||||
/// Update auxiliary keys. Values are `None` if should be deleted.
|
||||
@@ -127,6 +129,10 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
|
||||
fn changes_trie_storage(&self) -> Option<&Self::ChangesTrieStorage>;
|
||||
/// Returns state backend with post-state of given block.
|
||||
fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State>;
|
||||
/// Destroy state and save any useful data, such as cache.
|
||||
fn destroy_state(&self, _state: Self::State) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were
|
||||
/// successfully reverted.
|
||||
fn revert(&self, n: NumberFor<Block>) -> error::Result<NumberFor<Block>>;
|
||||
|
||||
@@ -24,22 +24,11 @@ use state_machine::{self, OverlayedChanges, Ext,
|
||||
use executor::{RuntimeVersion, RuntimeInfo, NativeVersion};
|
||||
use hash_db::Hasher;
|
||||
use trie::MemoryDB;
|
||||
use codec::Decode;
|
||||
use primitives::{H256, Blake2Hasher};
|
||||
use primitives::storage::well_known_keys;
|
||||
|
||||
use backend;
|
||||
use error;
|
||||
|
||||
/// Information regarding the result of a call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallResult {
|
||||
/// The data that was returned from the call.
|
||||
pub return_data: Vec<u8>,
|
||||
/// The changes made to the state by the call.
|
||||
pub changes: OverlayedChanges,
|
||||
}
|
||||
|
||||
/// Method call executor.
|
||||
pub trait CallExecutor<B, H>
|
||||
where
|
||||
@@ -58,7 +47,7 @@ where
|
||||
id: &BlockId<B>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
) -> Result<CallResult, error::Error>;
|
||||
) -> Result<Vec<u8>, error::Error>;
|
||||
|
||||
/// Execute a contextual call on top of state in a block of a given hash.
|
||||
///
|
||||
@@ -163,16 +152,22 @@ where
|
||||
id: &BlockId<Block>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
) -> error::Result<CallResult> {
|
||||
) -> error::Result<Vec<u8>> {
|
||||
let mut changes = OverlayedChanges::default();
|
||||
let (return_data, _, _) = self.call_at_state(
|
||||
&self.backend.state_at(*id)?,
|
||||
let state = self.backend.state_at(*id)?;
|
||||
let return_data = state_machine::execute_using_consensus_failure_handler(
|
||||
&state,
|
||||
self.backend.changes_trie_storage(),
|
||||
&mut changes,
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
native_when_possible(),
|
||||
)?;
|
||||
Ok(CallResult { return_data, changes })
|
||||
false,
|
||||
)
|
||||
.map(|(result, _, _)| result)?;
|
||||
self.backend.destroy_state(state)?;
|
||||
Ok(return_data)
|
||||
}
|
||||
|
||||
fn contextual_call<
|
||||
@@ -192,28 +187,40 @@ where
|
||||
//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())?;
|
||||
state_machine::execute_using_consensus_failure_handler(
|
||||
&state,
|
||||
self.backend.changes_trie_storage(),
|
||||
changes,
|
||||
&self.executor,
|
||||
"Core_initialise_block",
|
||||
&header.encode(),
|
||||
manager.clone(),
|
||||
false,
|
||||
)?;
|
||||
*initialised_block = Some(*at);
|
||||
}
|
||||
|
||||
self.call_at_state(&state, changes, method, call_data, manager).map(|cr| cr.0)
|
||||
let result = state_machine::execute_using_consensus_failure_handler(
|
||||
&state,
|
||||
self.backend.changes_trie_storage(),
|
||||
changes,
|
||||
&self.executor,
|
||||
method,
|
||||
call_data,
|
||||
manager,
|
||||
false,
|
||||
)
|
||||
.map(|(result, _, _)| result)?;
|
||||
|
||||
self.backend.destroy_state(state)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn runtime_version(&self, id: &BlockId<Block>) -> error::Result<RuntimeVersion> {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let state = self.backend.state_at(*id)?;
|
||||
use state_machine::Backend;
|
||||
let code = state.storage(well_known_keys::CODE)
|
||||
.map_err(|e| error::ErrorKind::Execution(Box::new(e)))?
|
||||
.ok_or(error::ErrorKind::VersionInvalid)?
|
||||
.to_vec();
|
||||
let heap_pages = state.storage(well_known_keys::HEAP_PAGES)
|
||||
.map_err(|e| error::ErrorKind::Execution(Box::new(e)))?
|
||||
.and_then(|v| u64::decode(&mut &v[..]))
|
||||
.unwrap_or(1024) as usize;
|
||||
|
||||
let mut ext = Ext::new(&mut overlay, &state, self.backend.changes_trie_storage());
|
||||
self.executor.runtime_version(&mut ext, heap_pages, &code)
|
||||
self.executor.runtime_version(&mut ext)
|
||||
.ok_or(error::ErrorKind::VersionInvalid.into())
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,6 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
let (genesis_storage, children_genesis_storage) = build_genesis_storage.build_storage()?;
|
||||
let mut op = backend.begin_operation(BlockId::Hash(Default::default()))?;
|
||||
let state_root = op.reset_storage(genesis_storage, children_genesis_storage)?;
|
||||
|
||||
let genesis_block = genesis::construct_genesis_block::<Block>(state_root.into());
|
||||
info!("Initialising Genesis block/state (state: {}, header-hash: {})", genesis_block.header().state_root(), genesis_block.header().hash());
|
||||
op.set_block_data(
|
||||
@@ -284,8 +283,8 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) {
|
||||
Some(cached_value) => Ok(cached_value),
|
||||
None => self.executor.call(id, "Core_authorities", &[])
|
||||
.and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r.return_data[..])
|
||||
.ok_or(error::ErrorKind::InvalidAuthoritiesSet.into()))
|
||||
.and_then(|r| Vec::<AuthorityIdFor<Block>>::decode(&mut &r[..])
|
||||
.ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +601,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
);
|
||||
let (_, storage_update, changes_update) = r?;
|
||||
overlay.commit_prospective();
|
||||
(Some(storage_update), Some(changes_update), Some(overlay.into_committed()))
|
||||
(Some(storage_update), Some(changes_update), Some(overlay.into_committed().collect()))
|
||||
},
|
||||
None => (None, None, None)
|
||||
};
|
||||
@@ -633,7 +632,10 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
transaction.update_authorities(authorities);
|
||||
}
|
||||
if let Some(storage_update) = storage_update {
|
||||
transaction.update_storage(storage_update)?;
|
||||
transaction.update_db_storage(storage_update)?;
|
||||
}
|
||||
if let Some(storage_changes) = storage_changes.clone() {
|
||||
transaction.update_storage(storage_changes)?;
|
||||
}
|
||||
if let Some(Some(changes_update)) = changes_update {
|
||||
transaction.update_changes_trie(changes_update)?;
|
||||
@@ -646,7 +648,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
if let Some(storage_changes) = storage_changes {
|
||||
// TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes?
|
||||
self.storage_notifications.lock()
|
||||
.trigger(&hash, storage_changes);
|
||||
.trigger(&hash, storage_changes.into_iter());
|
||||
}
|
||||
|
||||
if finalized {
|
||||
|
||||
@@ -448,7 +448,7 @@ where
|
||||
self.pending_authorities = Some(authorities);
|
||||
}
|
||||
|
||||
fn update_storage(&mut self, update: <InMemory<H> as StateBackend<H>>::Transaction) -> error::Result<()> {
|
||||
fn update_db_storage(&mut self, update: <InMemory<H> as StateBackend<H>>::Transaction) -> error::Result<()> {
|
||||
self.new_state = Some(self.old_state.update(update));
|
||||
Ok(())
|
||||
}
|
||||
@@ -491,6 +491,10 @@ where
|
||||
self.aux = Some(ops.into_iter().collect());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_storage(&mut self, _update: Vec<(Vec<u8>, Option<Vec<u8>>)>) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// In-memory backend. Keeps all states and blocks in memory. Useful for testing.
|
||||
|
||||
@@ -102,7 +102,7 @@ mod notifications;
|
||||
#[cfg(feature = "std")]
|
||||
pub use blockchain::Info as ChainInfo;
|
||||
#[cfg(feature = "std")]
|
||||
pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor};
|
||||
pub use call_executor::{CallExecutor, LocalCallExecutor};
|
||||
#[cfg(feature = "std")]
|
||||
pub use client::{
|
||||
new_with_backend,
|
||||
|
||||
@@ -188,7 +188,7 @@ where
|
||||
self.authorities = Some(authorities);
|
||||
}
|
||||
|
||||
fn update_storage(&mut self, _update: <Self::State as StateBackend<H>>::Transaction) -> ClientResult<()> {
|
||||
fn update_db_storage(&mut self, _update: <Self::State as StateBackend<H>>::Transaction) -> ClientResult<()> {
|
||||
// we're not storing anything locally => ignore changes
|
||||
Ok(())
|
||||
}
|
||||
@@ -210,6 +210,11 @@ where
|
||||
self.aux_ops = ops.into_iter().collect();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_storage(&mut self, _update: Vec<(Vec<u8>, Option<Vec<u8>>)>) -> ClientResult<()> {
|
||||
// we're not storing anything locally => ignore changes
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
|
||||
|
||||
@@ -31,7 +31,7 @@ use state_machine::{self, Backend as StateBackend, CodeExecutor, OverlayedChange
|
||||
use hash_db::Hasher;
|
||||
|
||||
use blockchain::Backend as ChainBackend;
|
||||
use call_executor::{CallExecutor, CallResult};
|
||||
use call_executor::CallExecutor;
|
||||
use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
|
||||
use light::fetcher::{Fetcher, RemoteCallRequest};
|
||||
use executor::{RuntimeVersion, NativeVersion};
|
||||
@@ -74,7 +74,7 @@ where
|
||||
{
|
||||
type Error = ClientError;
|
||||
|
||||
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<CallResult> {
|
||||
fn call(&self, id: &BlockId<Block>, method: &str, call_data: &[u8]) -> ClientResult<Vec<u8>> {
|
||||
let block_hash = self.blockchain.expect_block_hash_from_id(id)?;
|
||||
let block_header = self.blockchain.expect_header(id.clone())?;
|
||||
|
||||
@@ -105,12 +105,12 @@ where
|
||||
return Err(ClientErrorKind::NotAvailableOnLightClient.into());
|
||||
}
|
||||
|
||||
self.call(at, method, call_data).map(|cr| cr.return_data)
|
||||
self.call(at, method, call_data)
|
||||
}
|
||||
|
||||
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
|
||||
let call_result = self.call(id, "version", &[])?;
|
||||
RuntimeVersion::decode(&mut call_result.return_data.as_slice())
|
||||
RuntimeVersion::decode(&mut call_result.as_slice())
|
||||
.ok_or_else(|| ClientErrorKind::VersionInvalid.into())
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ pub fn check_execution_proof<Header, E, H>(
|
||||
executor: &E,
|
||||
request: &RemoteCallRequest<Header>,
|
||||
remote_proof: Vec<Vec<u8>>
|
||||
) -> ClientResult<CallResult>
|
||||
) -> ClientResult<Vec<u8>>
|
||||
where
|
||||
Header: HeaderT,
|
||||
E: CodeExecutor<H>,
|
||||
@@ -226,7 +226,7 @@ pub fn check_execution_proof<Header, E, H>(
|
||||
&request.call_data,
|
||||
)?;
|
||||
|
||||
Ok(CallResult { return_data: local_result, changes })
|
||||
Ok(local_result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -273,7 +273,7 @@ mod tests {
|
||||
retry_count: None,
|
||||
}, remote_execution_proof).unwrap();
|
||||
|
||||
(remote_result, local_result.return_data)
|
||||
(remote_result, local_result)
|
||||
}
|
||||
|
||||
// prepare remote client
|
||||
|
||||
@@ -28,7 +28,6 @@ use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, NumberF
|
||||
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId,
|
||||
TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage};
|
||||
|
||||
use call_executor::CallResult;
|
||||
use cht;
|
||||
use error::{Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult};
|
||||
use light::blockchain::{Blockchain, Storage as BlockchainStorage};
|
||||
@@ -118,7 +117,7 @@ pub trait Fetcher<Block: BlockT>: Send + Sync {
|
||||
/// Remote storage read future.
|
||||
type RemoteReadResult: IntoFuture<Item=Option<Vec<u8>>, Error=ClientError>;
|
||||
/// Remote call result future.
|
||||
type RemoteCallResult: IntoFuture<Item=CallResult, Error=ClientError>;
|
||||
type RemoteCallResult: IntoFuture<Item=Vec<u8>, Error=ClientError>;
|
||||
/// Remote changes result future.
|
||||
type RemoteChangesResult: IntoFuture<Item=Vec<(NumberFor<Block>, u32)>, Error=ClientError>;
|
||||
|
||||
@@ -156,7 +155,7 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {
|
||||
&self,
|
||||
request: &RemoteCallRequest<Block::Header>,
|
||||
remote_proof: Vec<Vec<u8>>
|
||||
) -> ClientResult<CallResult>;
|
||||
) -> ClientResult<Vec<u8>>;
|
||||
/// Check remote changes query proof.
|
||||
fn check_changes_proof(
|
||||
&self,
|
||||
@@ -344,7 +343,7 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
|
||||
&self,
|
||||
request: &RemoteCallRequest<Block::Header>,
|
||||
remote_proof: Vec<Vec<u8>>
|
||||
) -> ClientResult<CallResult> {
|
||||
) -> ClientResult<Vec<u8>> {
|
||||
check_execution_proof::<_, _, H>(&self.executor, request, remote_proof)
|
||||
}
|
||||
|
||||
@@ -392,7 +391,6 @@ pub mod tests {
|
||||
use futures::future::{ok, err, FutureResult};
|
||||
use parking_lot::Mutex;
|
||||
use keyring::Keyring;
|
||||
use call_executor::CallResult;
|
||||
use client::tests::prepare_client_with_key_changes;
|
||||
use executor::{self, NativeExecutionDispatch};
|
||||
use error::Error as ClientError;
|
||||
@@ -410,12 +408,12 @@ pub mod tests {
|
||||
use state_machine::Backend;
|
||||
use super::*;
|
||||
|
||||
pub type OkCallFetcher = Mutex<CallResult>;
|
||||
pub type OkCallFetcher = Mutex<Vec<u8>>;
|
||||
|
||||
impl Fetcher<Block> for OkCallFetcher {
|
||||
type RemoteHeaderResult = FutureResult<Header, ClientError>;
|
||||
type RemoteReadResult = FutureResult<Option<Vec<u8>>, ClientError>;
|
||||
type RemoteCallResult = FutureResult<CallResult, ClientError>;
|
||||
type RemoteCallResult = FutureResult<Vec<u8>, ClientError>;
|
||||
type RemoteChangesResult = FutureResult<Vec<(NumberFor<Block>, u32)>, ClientError>;
|
||||
|
||||
fn remote_header(&self, _request: RemoteHeaderRequest<Header>) -> Self::RemoteHeaderResult {
|
||||
|
||||
Reference in New Issue
Block a user