Implement change trie for child trie. (#3122)

* Initial implementation, some redundancy is awkward and there is
some useless computation (but there is a pending pr for that).
Next are tests.

* Minimal tests and fix extend child.

* implement iterator for change child trie.

* prune child trie.

* Fix pruning test.

* bump spec version.

* Avoid empty child trie (could also be checked before)

* tabs.

* Fix child digest overriding each others.

* Restore doc deleted on merge.

* Check correct child value on extrinsics build.

* Revert runtime version update.
This commit is contained in:
cheme
2019-09-02 11:10:42 +02:00
committed by Svyatoslav Nikolsky
parent 06658e0c3c
commit e3d0c60a31
23 changed files with 670 additions and 138 deletions
+14 -3
View File
@@ -535,6 +535,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
&self,
first: NumberFor<Block>,
last: BlockId<Block>,
storage_key: Option<&StorageKey>,
key: &StorageKey
) -> error::Result<Vec<(NumberFor<Block>, u32)>> {
let (config, storage) = self.require_changes_trie()?;
@@ -557,6 +558,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
number: last_number,
},
self.backend.blockchain().info().best_number,
storage_key.as_ref().map(|sk| sk.0.as_slice()),
&key.0)
.and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::<Result<_, _>>())
.map_err(|err| error::Error::ChangesTrieAccessFailed(err))
@@ -574,13 +576,15 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
key: &StorageKey
storage_key: Option<&StorageKey>,
key: &StorageKey,
) -> error::Result<ChangesProof<Block::Header>> {
self.key_changes_proof_with_cht_size(
first,
last,
min,
max,
storage_key,
key,
cht::size(),
)
@@ -593,6 +597,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
last: Block::Hash,
min: Block::Hash,
max: Block::Hash,
storage_key: Option<&StorageKey>,
key: &StorageKey,
cht_size: NumberFor<Block>,
) -> error::Result<ChangesProof<Block::Header>> {
@@ -670,7 +675,8 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
number: last_number,
},
max_number,
&key.0
storage_key.as_ref().map(|sk| sk.0.as_slice()),
&key.0,
)
.map_err(|err| error::Error::from(error::Error::ChangesTrieAccessFailed(err)))?;
@@ -2580,7 +2586,12 @@ pub(crate) mod tests {
for (index, (begin, end, key, expected_result)) in test_cases.into_iter().enumerate() {
let end = client.block_hash(end).unwrap().unwrap();
let actual_result = client.key_changes(begin, BlockId::Hash(end), &StorageKey(key)).unwrap();
let actual_result = client.key_changes(
begin,
BlockId::Hash(end),
None,
&StorageKey(key),
).unwrap();
match actual_result == expected_result {
true => (),
false => panic!(format!("Failed test {}: actual = {:?}, expected = {:?}",
@@ -381,6 +381,10 @@ where
// whole state is not available on light node
}
fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, _prefix: &[u8], _action: A) {
// whole state is not available on light node
}
fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, _storage_key: &[u8], _action: A) {
// whole state is not available on light node
}
@@ -461,6 +465,15 @@ where
}
}
fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], action: A) {
match *self {
OnDemandOrGenesisState::OnDemand(ref state) =>
StateBackend::<H>::for_key_values_with_prefix(state, prefix, action),
OnDemandOrGenesisState::Genesis(ref state) => state.for_key_values_with_prefix(prefix, action),
}
}
fn for_keys_in_child_storage<A: FnMut(&[u8])>(&self, storage_key: &[u8], action: A) {
match *self {
OnDemandOrGenesisState::OnDemand(ref state) =>
+10 -4
View File
@@ -110,6 +110,8 @@ pub struct RemoteChangesRequest<Header: HeaderT> {
/// Known changes trie roots for the range of blocks [tries_roots.0..max_block].
/// Proofs for roots of ascendants of tries_roots.0 are provided by the remote node.
pub tries_roots: (Header::Number, Header::Hash, Vec<Header::Hash>),
/// Optional Child Storage key to read.
pub storage_key: Option<Vec<u8>>,
/// Storage key to read.
pub key: Vec<u8>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
@@ -307,6 +309,7 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>, F> LightDataChecker<E, H, B, S, F
number: request.last_block.0,
},
remote_max_block,
request.storage_key.as_ref().map(Vec::as_slice),
&request.key)
.map_err(|err| ClientError::ChangesTrieAccessFailed(err))
}
@@ -749,7 +752,7 @@ pub mod tests {
// '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
begin_hash, end_hash, begin_hash, max_hash, None, &key
).unwrap();
// check proof on local client
@@ -761,6 +764,7 @@ pub mod tests {
max_block: (max, max_hash),
tries_roots: (begin, begin_hash, local_roots_range),
key: key.0,
storage_key: None,
retry_count: None,
};
let local_result = local_checker.check_changes_proof(&request, ChangesProof {
@@ -795,7 +799,7 @@ pub mod tests {
let b3 = remote_client.block_hash_from_id(&BlockId::Number(3)).unwrap().unwrap();
let b4 = remote_client.block_hash_from_id(&BlockId::Number(4)).unwrap().unwrap();
let remote_proof = remote_client.key_changes_proof_with_cht_size(
b1, b4, b3, b4, &dave, 4
b1, b4, b3, b4, None, &dave, 4
).unwrap();
// prepare local checker, having a root of changes trie CHT#0
@@ -814,6 +818,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()]),
storage_key: None,
key: dave.0,
retry_count: None,
};
@@ -845,7 +850,7 @@ pub mod tests {
// '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();
begin_hash, end_hash, begin_hash, max_hash, None, &key).unwrap();
let local_roots_range = local_roots.clone()[(begin - 1) as usize..].to_vec();
let request = RemoteChangesRequest::<Header> {
@@ -854,6 +859,7 @@ pub mod tests {
last_block: (end, end_hash),
max_block: (max, max_hash),
tries_roots: (begin, begin_hash, local_roots_range.clone()),
storage_key: None,
key: key.0,
retry_count: None,
};
@@ -907,7 +913,7 @@ pub mod tests {
let b3 = remote_client.block_hash_from_id(&BlockId::Number(3)).unwrap().unwrap();
let b4 = remote_client.block_hash_from_id(&BlockId::Number(4)).unwrap().unwrap();
let remote_proof = remote_client.key_changes_proof_with_cht_size(
b1, b4, b3, b4, &dave, 4
b1, b4, b3, b4, None, &dave, 4
).unwrap();
// fails when changes trie CHT is missing from the local db