Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Use changes tries in query_storage RPC #1082

Merged
merged 4 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions core/client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,20 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
}
}

impl<Block: BlockT> client::backend::PrunableStateChangesTrieStorage<Blake2Hasher> for DbChangesTrieStorage<Block> {
fn oldest_changes_trie_block(
&self,
config: &ChangesTrieConfiguration,
best_finalized_block: u64
) -> u64 {
let min_blocks_to_keep = match self.min_blocks_to_keep {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being late to the party, this can just be replaced by self.min_blocks_to_keep.unwrap_or(1).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome! :) No, it can't, because None branch has return in it. I've changed code a bit so that now there's no explicit return + temp var.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh okay. ty :)

Some(min_blocks_to_keep) => min_blocks_to_keep,
None => return 1,
};
state_machine::oldest_non_pruned_changes_trie(config, min_blocks_to_keep, best_finalized_block)
}
}

impl<Block: BlockT> state_machine::ChangesTrieRootsStorage<Blake2Hasher> for DbChangesTrieStorage<Block> {
fn root(&self, anchor: &state_machine::ChangesTrieAnchorBlockId<H256>, block: u64) -> Result<Option<H256>, String> {
// check API requirement
Expand Down
9 changes: 8 additions & 1 deletion core/client/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! Substrate Client data backend

use error;
use primitives::ChangesTrieConfiguration;
use runtime_primitives::{generic::BlockId, Justification, StorageMap, ChildrenStorageMap};
use runtime_primitives::traits::{AuthorityIdFor, Block as BlockT, NumberFor};
use state_machine::backend::Backend as StateBackend;
Expand Down Expand Up @@ -113,7 +114,7 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
/// Associated state backend type.
type State: StateBackend<H>;
/// Changes trie storage.
type ChangesTrieStorage: StateChangesTrieStorage<H>;
type ChangesTrieStorage: PrunableStateChangesTrieStorage<H>;

/// Begin a new block insertion transaction with given parent block id.
/// When constructing the genesis, this is called with all-zero hash.
Expand Down Expand Up @@ -154,6 +155,12 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
}
}

/// Changes trie storage that supports pruning.
pub trait PrunableStateChangesTrieStorage<H: Hasher>: StateChangesTrieStorage<H> {
/// Get number block of oldest, non-pruned changes trie.
fn oldest_changes_trie_block(&self, config: &ChangesTrieConfiguration, best_finalized: u64) -> u64;
}

/// Mark for all Backend implementations, that are making use of state data, stored locally.
pub trait LocalBackend<Block, H>: Backend<Block, H>
where
Expand Down
81 changes: 52 additions & 29 deletions core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use state_machine::{
key_changes, key_changes_proof, OverlayedChanges
};

use backend::{self, BlockImportOperation};
use backend::{self, BlockImportOperation, PrunableStateChangesTrieStorage};
use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend};
use call_executor::{CallExecutor, LocalCallExecutor};
use executor::{RuntimeVersion, RuntimeInfo};
Expand Down Expand Up @@ -339,35 +339,54 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
Ok((header, proof))
}

/// Get longest range within [first; last] that is possible to use in `key_changes`
/// and `key_changes_proof` calls.
/// Range could be shortened from the beginning if some changes tries have been pruned.
/// Returns Ok(None) if changes trues are not supported.
pub fn max_key_changes_range(
&self,
first: NumberFor<Block>,
last: BlockId<Block>,
) -> error::Result<Option<(NumberFor<Block>, BlockId<Block>)>> {
let (config, storage) = match self.require_changes_trie().ok() {
Some((config, storage)) => (config, storage),
None => return Ok(None),
};
let first = first.as_();
let last_num = self.backend.blockchain().expect_block_number_from_id(&last)?.as_();
if first > last_num {
return Err(error::ErrorKind::ChangesTrieAccessFailed("Invalid changes trie range".into()).into());
}
let finalized_number = self.backend.blockchain().info()?.finalized_number;
let oldest = storage.oldest_changes_trie_block(&config, finalized_number.as_());
let first = As::sa(::std::cmp::max(first, oldest));
Ok(Some((first, last)))
}

/// Get pairs of (block, extrinsic) where key has been changed at given blocks range.
/// Works only for runtimes that are supporting changes tries.
pub fn key_changes(
&self,
first: Block::Hash,
last: Block::Hash,
key: &[u8]
first: NumberFor<Block>,
last: BlockId<Block>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to switch API from hash to number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you identify blocks range by two hashes, there's additional ambiguity - blocks could be from different forks. When you identify range by first_number..last_hash, the only possible issue is when number(last) < first. That's the only reason, iirc.

key: &StorageKey
) -> error::Result<Vec<(NumberFor<Block>, u32)>> {
let config = self.changes_trie_config()?;
let storage = self.backend.changes_trie_storage();
let (config, storage) = match (config, storage) {
(Some(config), Some(storage)) => (config, storage),
_ => return Err(error::ErrorKind::ChangesTriesNotSupported.into()),
};
let (config, storage) = self.require_changes_trie()?;
let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?.as_();
let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?;

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,
first_number,
&*storage,
first.as_(),
&ChangesTrieAnchorBlockId {
hash: convert_hash(&last),
hash: convert_hash(&last_hash),
number: last_number,
},
self.backend.blockchain().info()?.best_number.as_(),
key)
&key.0)
.and_then(|r| r.map(|r| r.map(|(block, tx)| (As::sa(block), tx))).collect::<Result<_, _>>())
.map_err(|err| error::ErrorKind::ChangesTrieAccessFailed(err).into())
.map(|r| r.into_iter().map(|(b, e)| (As::sa(b), e)).collect())
}

/// Get proof for computation of (block, extrinsic) pairs where key has been changed at given blocks range.
Expand All @@ -382,7 +401,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
key: &[u8]
key: &StorageKey
) -> error::Result<ChangesProof<Block::Header>> {
self.key_changes_proof_with_cht_size(
first,
Expand All @@ -401,7 +420,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
key: &[u8],
key: &StorageKey,
cht_size: u64,
) -> error::Result<ChangesProof<Block::Header>> {
struct AccessedRootsRecorder<'a, Block: BlockT> {
Expand Down Expand Up @@ -431,14 +450,9 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
}
}

let config = self.changes_trie_config()?;
let storage = self.backend.changes_trie_storage();
let (config, storage) = match (config, storage) {
(Some(config), Some(storage)) => (config, storage),
_ => return Err(error::ErrorKind::ChangesTriesNotSupported.into()),
};

let (config, storage) = self.require_changes_trie()?;
let min_number = self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?;

let recording_storage = AccessedRootsRecorder::<Block> {
storage,
min: min_number.as_(),
Expand All @@ -462,7 +476,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
number: last_number,
},
max_number.as_(),
key
&key.0
)
.map_err(|err| error::Error::from(error::ErrorKind::ChangesTrieAccessFailed(err)))?;

Expand Down Expand Up @@ -512,6 +526,16 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
Ok(proof)
}

/// Returns changes trie configuration and storage or an error if it is not supported.
fn require_changes_trie(&self) -> error::Result<(ChangesTrieConfiguration, &B::ChangesTrieStorage)> {
let config = self.changes_trie_config()?;
let storage = self.backend.changes_trie_storage();
match (config, storage) {
(Some(config), Some(storage)) => Ok((config, storage)),
_ => Err(error::ErrorKind::ChangesTriesNotSupported.into()),
}
}

/// Create a new block, built on the head of the chain.
pub fn new_block<InherentData>(
&self
Expand Down Expand Up @@ -1697,9 +1721,8 @@ pub(crate) mod tests {
let (client, _, test_cases) = prepare_client_with_key_changes();

for (index, (begin, end, key, expected_result)) in test_cases.into_iter().enumerate() {
let begin = client.block_hash(begin).unwrap().unwrap();
let end = client.block_hash(end).unwrap().unwrap();
let actual_result = client.key_changes(begin, end, &key).unwrap();
let actual_result = client.key_changes(begin, BlockId::Hash(end), &StorageKey(key)).unwrap();
match actual_result == expected_result {
true => (),
false => panic!(format!("Failed test {}: actual = {:?}, expected = {:?}",
Expand Down
32 changes: 26 additions & 6 deletions core/client/src/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ use parking_lot::RwLock;
use error;
use backend::{self, NewBlockState};
use light;
use primitives::storage::well_known_keys;
use primitives::{ChangesTrieConfiguration, storage::well_known_keys};
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero,
NumberFor, As, Digest, DigestItem, AuthorityIdFor};
use runtime_primitives::{Justification, StorageMap, ChildrenStorageMap};
use blockchain::{self, BlockStatus, HeaderBackend};
use state_machine::backend::{Backend as StateBackend, InMemory, Consolidate};
use state_machine::InMemoryChangesTrieStorage;
use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId};
use hash_db::Hasher;
use heapsize::HeapSizeOf;
use leaves::LeafSet;
Expand Down Expand Up @@ -505,7 +505,7 @@ where
H::Out: HeapSizeOf + Ord,
{
states: RwLock<HashMap<Block::Hash, InMemory<H>>>,
changes_trie_storage: InMemoryChangesTrieStorage<H>,
changes_trie_storage: ChangesTrieStorage<H>,
blockchain: Blockchain<Block>,
}

Expand All @@ -519,7 +519,7 @@ where
pub fn new() -> Backend<Block, H> {
Backend {
states: RwLock::new(HashMap::new()),
changes_trie_storage: InMemoryChangesTrieStorage::new(),
changes_trie_storage: ChangesTrieStorage(InMemoryChangesTrieStorage::new()),
blockchain: Blockchain::new(),
}
}
Expand Down Expand Up @@ -555,7 +555,7 @@ where
type BlockImportOperation = BlockImportOperation<Block, H>;
type Blockchain = Blockchain<Block>;
type State = InMemory<H>;
type ChangesTrieStorage = InMemoryChangesTrieStorage<H>;
type ChangesTrieStorage = ChangesTrieStorage<H>;

fn begin_operation(&self, block: BlockId<Block>) -> error::Result<Self::BlockImportOperation> {
let state = match block {
Expand Down Expand Up @@ -587,7 +587,7 @@ where
if let Some(changes_trie_root) = changes_trie_root {
if let Some(changes_trie_update) = operation.changes_trie_update {
let changes_trie_root: H::Out = changes_trie_root.into();
self.changes_trie_storage.insert(header.number().as_(), changes_trie_root, changes_trie_update);
self.changes_trie_storage.0.insert(header.number().as_(), changes_trie_root, changes_trie_update);
}
}

Expand Down Expand Up @@ -652,6 +652,26 @@ impl<Block: BlockT> blockchain::Cache<Block> for Cache<Block> {
}
}

/// Prunable in-memory changes trie storage.
pub struct ChangesTrieStorage<H: Hasher>(InMemoryChangesTrieStorage<H>) where H::Out: HeapSizeOf;
impl<H: Hasher> backend::PrunableStateChangesTrieStorage<H> for ChangesTrieStorage<H> where H::Out: HeapSizeOf {
fn oldest_changes_trie_block(&self, _config: &ChangesTrieConfiguration, _best_finalized: u64) -> u64 {
0
}
}

impl<H: Hasher> state_machine::ChangesTrieRootsStorage<H> for ChangesTrieStorage<H> where H::Out: HeapSizeOf {
fn root(&self, anchor: &ChangesTrieAnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
self.0.root(anchor, block)
}
}

impl<H: Hasher> state_machine::ChangesTrieStorage<H> for ChangesTrieStorage<H> where H::Out: HeapSizeOf {
fn get(&self, key: &H::Out) -> Result<Option<state_machine::DBValue>, String> {
self.0.get(key)
}
}

/// Insert authorities entry into in-memory blockchain cache. Extracted as a separate function to use it in tests.
pub fn cache_authorities_at<Block: BlockT>(
blockchain: &Blockchain<Block>,
Expand Down
5 changes: 2 additions & 3 deletions core/client/src/light/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ use futures::{Future, IntoFuture};
use parking_lot::RwLock;

use runtime_primitives::{generic::BlockId, Justification, StorageMap, ChildrenStorageMap};
use state_machine::{Backend as StateBackend, InMemoryChangesTrieStorage, TrieBackend};
use state_machine::{Backend as StateBackend, TrieBackend};
use runtime_primitives::traits::{Block as BlockT, NumberFor, AuthorityIdFor};

use in_mem;
use backend::{AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState};
use blockchain::HeaderBackend as BlockchainHeaderBackend;
Expand Down Expand Up @@ -95,7 +94,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
type BlockImportOperation = ImportOperation<Block, S, F>;
type Blockchain = Blockchain<S, F>;
type State = OnDemandState<Block, S, F>;
type ChangesTrieStorage = InMemoryChangesTrieStorage<H>;
type ChangesTrieStorage = in_mem::ChangesTrieStorage<H>;

fn begin_operation(&self, _block: BlockId<Block>) -> ClientResult<Self::BlockImportOperation> {
Ok(ImportOperation {
Expand Down
12 changes: 8 additions & 4 deletions core/client/src/light/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ pub mod tests {
RemoteCallRequest, RemoteHeaderRequest};
use light::blockchain::tests::{DummyStorage, DummyBlockchain};
use primitives::{twox_128, Blake2Hasher};
use primitives::storage::well_known_keys;
use primitives::storage::{StorageKey, well_known_keys};
use runtime_primitives::generic::BlockId;
use state_machine::Backend;
use super::*;
Expand Down Expand Up @@ -546,6 +546,7 @@ pub mod tests {
let end_hash = remote_client.block_hash(end).unwrap().unwrap();

// 'fetch' changes proof from remote node
let key = StorageKey(key);
let remote_proof = remote_client.key_changes_proof(
begin_hash, end_hash, begin_hash, max_hash, &key
).unwrap();
Expand All @@ -558,7 +559,7 @@ pub mod tests {
last_block: (end, end_hash),
max_block: (max, max_hash),
tries_roots: (begin, begin_hash, local_roots_range),
key: key,
key: key.0,
retry_count: None,
};
let local_result = local_checker.check_changes_proof(&request, ChangesProof {
Expand All @@ -583,6 +584,7 @@ pub mod tests {
// (1, 4, dave.clone(), vec![(4, 0), (1, 1), (1, 0)]),
let (remote_client, remote_roots, _) = prepare_client_with_key_changes();
let dave = twox_128(&runtime::system::balance_of_key(Keyring::Dave.to_raw_public().into())).to_vec();
let dave = StorageKey(dave);

// 'fetch' changes proof from remote node:
// we're fetching changes for range b1..b4
Expand Down Expand Up @@ -611,7 +613,7 @@ pub mod tests {
last_block: (4, b4),
max_block: (4, b4),
tries_roots: (3, b3, vec![remote_roots[2].clone(), remote_roots[3].clone()]),
key: dave,
key: dave.0,
retry_count: None,
};
let local_result = local_checker.check_changes_proof_with_cht_size(&request, ChangesProof {
Expand Down Expand Up @@ -640,6 +642,7 @@ pub mod tests {
let end_hash = remote_client.block_hash(end).unwrap().unwrap();

// 'fetch' changes proof from remote node
let key = StorageKey(key);
let remote_proof = remote_client.key_changes_proof(
begin_hash, end_hash, begin_hash, max_hash, &key).unwrap();

Expand All @@ -650,7 +653,7 @@ pub mod tests {
last_block: (end, end_hash),
max_block: (max, max_hash),
tries_roots: (begin, begin_hash, local_roots_range.clone()),
key: key,
key: key.0,
retry_count: None,
};

Expand Down Expand Up @@ -693,6 +696,7 @@ pub mod tests {
let local_cht_root = cht::compute_root::<Header, Blake2Hasher, _>(
4, 0, remote_roots.iter().cloned().map(|ct| Ok(Some(ct)))).unwrap();
let dave = twox_128(&runtime::system::balance_of_key(Keyring::Dave.to_raw_public().into())).to_vec();
let dave = StorageKey(dave);

// 'fetch' changes proof from remote node:
// we're fetching changes for range b1..b4
Expand Down
6 changes: 3 additions & 3 deletions core/network/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, AuthorityId
use runtime_primitives::generic::{BlockId};
use consensus::{ImportBlock, ImportResult};
use runtime_primitives::Justification;
use primitives::{H256, Blake2Hasher};
use primitives::{H256, Blake2Hasher, storage::StorageKey};

/// Local client abstraction for the network.
pub trait Client<Block: BlockT>: Send + Sync {
Expand Down Expand Up @@ -66,7 +66,7 @@ pub trait Client<Block: BlockT>: Send + Sync {
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
key: &[u8]
key: &StorageKey
) -> Result<ChangesProof<Block::Header>, Error>;
}

Expand Down Expand Up @@ -125,7 +125,7 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
key: &[u8]
key: &StorageKey
) -> Result<ChangesProof<Block::Header>, Error> {
(self as &SubstrateClient<B, E, Block, RA>).key_changes_proof(first, last, min, max, key)
}
Expand Down
Loading