mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 19:21:13 +00:00
Changes tries: support forks (#950)
* forks support in changes trie storage * moved convert_hash to primitives * optimize ChangesTrieRootsStorage::root when anchor is canonicalized
This commit is contained in:
committed by
Gav Wood
parent
037f9dde10
commit
6e3c56c135
@@ -64,7 +64,7 @@ use hash_db::Hasher;
|
||||
use kvdb::{KeyValueDB, DBTransaction};
|
||||
use trie::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use primitives::{H256, AuthorityId, Blake2Hasher, ChangesTrieConfiguration};
|
||||
use primitives::{H256, AuthorityId, Blake2Hasher, ChangesTrieConfiguration, convert_hash};
|
||||
use primitives::storage::well_known_keys;
|
||||
use runtime_primitives::{generic::BlockId, Justification, StorageMap, ChildrenStorageMap};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor, Zero, Digest, DigestItem};
|
||||
@@ -145,7 +145,7 @@ impl<'a> state_db::MetaDb for StateMetaDb<'a> {
|
||||
/// Block database
|
||||
pub struct BlockchainDb<Block: BlockT> {
|
||||
db: Arc<KeyValueDB>,
|
||||
meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
|
||||
meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
|
||||
leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
|
||||
Ok(BlockchainDb {
|
||||
db,
|
||||
leaves: RwLock::new(leaves),
|
||||
meta: RwLock::new(meta),
|
||||
meta: Arc::new(RwLock::new(meta)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -387,6 +387,7 @@ impl state_machine::Storage<Blake2Hasher> for DbGenesisStorage {
|
||||
|
||||
pub struct DbChangesTrieStorage<Block: BlockT> {
|
||||
db: Arc<KeyValueDB>,
|
||||
meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
|
||||
min_blocks_to_keep: Option<u64>,
|
||||
_phantom: ::std::marker::PhantomData<Block>,
|
||||
}
|
||||
@@ -400,7 +401,7 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
}
|
||||
|
||||
/// Prune obsolete changes tries.
|
||||
pub fn prune(&self, config: Option<ChangesTrieConfiguration>, tx: &mut DBTransaction, block: NumberFor<Block>) {
|
||||
pub fn prune(&self, config: Option<ChangesTrieConfiguration>, tx: &mut DBTransaction, block_hash: Block::Hash, block_num: NumberFor<Block>) {
|
||||
// never prune on archive nodes
|
||||
let min_blocks_to_keep = match self.min_blocks_to_keep {
|
||||
Some(min_blocks_to_keep) => min_blocks_to_keep,
|
||||
@@ -418,23 +419,54 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
&config,
|
||||
&*self,
|
||||
min_blocks_to_keep,
|
||||
block.as_(),
|
||||
&state_machine::ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&block_hash),
|
||||
number: block_num.as_(),
|
||||
},
|
||||
|node| tx.delete(columns::CHANGES_TRIE, node.as_ref()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> state_machine::ChangesTrieRootsStorage<Blake2Hasher> for DbChangesTrieStorage<Block> {
|
||||
fn root(&self, block: u64) -> Result<Option<H256>, String> {
|
||||
Ok(read_db::<Block>(&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Number(As::sa(block)))
|
||||
.map_err(|err| format!("{}", err))
|
||||
.and_then(|header| match header {
|
||||
Some(header) => Block::Header::decode(&mut &header[..])
|
||||
.ok_or_else(|| format!("Failed to parse header of block {}", block))
|
||||
.map(Some),
|
||||
None => Ok(None)
|
||||
})?
|
||||
.and_then(|header| header.digest().log(DigestItem::as_changes_trie_root)
|
||||
.map(|root| H256::from_slice(root.as_ref()))))
|
||||
fn root(&self, anchor: &state_machine::ChangesTrieAnchorBlockId<H256>, block: u64) -> Result<Option<H256>, String> {
|
||||
// check API requirement
|
||||
assert!(block <= anchor.number, "API requirement");
|
||||
|
||||
// we need to get hash of the block to resolve changes trie root
|
||||
let block_id = if block <= self.meta.read().finalized_number.as_() {
|
||||
// if block is finalized, we could just read canonical hash
|
||||
BlockId::Number(As::sa(block))
|
||||
} else {
|
||||
// the block is not finalized
|
||||
let mut current_num = anchor.number;
|
||||
let mut current_hash: Block::Hash = convert_hash(&anchor.hash);
|
||||
let maybe_anchor_header: Block::Header = ::utils::require_header::<Block>(
|
||||
&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Number(As::sa(current_num))
|
||||
).map_err(|e| e.to_string())?;
|
||||
if maybe_anchor_header.hash() == current_hash {
|
||||
// if anchor is canonicalized, then the block is also canonicalized
|
||||
BlockId::Number(As::sa(block))
|
||||
} else {
|
||||
// else (block is not finalized + anchor is not canonicalized):
|
||||
// => we should find the required block hash by traversing
|
||||
// back from the anchor to the block with given number
|
||||
while current_num != block {
|
||||
let current_header: Block::Header = ::utils::require_header::<Block>(
|
||||
&*self.db, columns::HASH_LOOKUP, columns::HEADER, BlockId::Hash(current_hash)
|
||||
).map_err(|e| e.to_string())?;
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
current_num = current_num - 1;
|
||||
}
|
||||
|
||||
BlockId::Hash(current_hash)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(::utils::require_header::<Block>(&*self.db, columns::HASH_LOOKUP, columns::HEADER, block_id)
|
||||
.map_err(|e| e.to_string())?
|
||||
.digest().log(DigestItem::as_changes_trie_root)
|
||||
.map(|root| H256::from_slice(root.as_ref())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,6 +512,7 @@ impl<Block: BlockT> Backend<Block> {
|
||||
fn from_kvdb(db: Arc<KeyValueDB>, pruning: PruningMode, canonicalization_delay: u64) -> Result<Self, client::error::Error> {
|
||||
let is_archive_pruning = pruning.is_archive();
|
||||
let blockchain = BlockchainDb::new(db.clone())?;
|
||||
let meta = blockchain.meta.clone();
|
||||
let map_e = |e: state_db::Error<io::Error>| ::client::error::Error::from(format!("State database error: {:?}", e));
|
||||
let state_db: StateDb<Block::Hash, H256> = StateDb::new(pruning, &StateMetaDb(&*db)).map_err(map_e)?;
|
||||
let storage_db = StorageDb {
|
||||
@@ -488,6 +521,7 @@ impl<Block: BlockT> Backend<Block> {
|
||||
};
|
||||
let changes_tries_storage = DbChangesTrieStorage {
|
||||
db,
|
||||
meta,
|
||||
min_blocks_to_keep: if is_archive_pruning { None } else { Some(MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR) },
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
@@ -567,7 +601,7 @@ impl<Block: BlockT> Backend<Block> {
|
||||
let changes_trie_config: Option<ChangesTrieConfiguration> = self.state_at(BlockId::Hash(parent_hash))?
|
||||
.storage(well_known_keys::CHANGES_TRIE_CONFIG)?
|
||||
.and_then(|v| Decode::decode(&mut &*v));
|
||||
self.changes_tries_storage.prune(changes_trie_config, transaction, f_num);
|
||||
self.changes_tries_storage.prune(changes_trie_config, transaction, f_hash, f_num);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1191,10 +1225,13 @@ mod tests {
|
||||
#[test]
|
||||
fn changes_trie_storage_works() {
|
||||
let backend = Backend::<Block>::new_test(1000, 100);
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 1000;
|
||||
|
||||
|
||||
let check_changes = |backend: &Backend<Block>, block: u64, changes: Vec<(Vec<u8>, Vec<u8>)>| {
|
||||
let (changes_root, mut changes_trie_update) = prepare_changes(changes);
|
||||
assert_eq!(backend.changes_tries_storage.root(block), Ok(Some(changes_root)));
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: Default::default(), number: block };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, block), Ok(Some(changes_root)));
|
||||
|
||||
for (key, (val, _)) in changes_trie_update.drain() {
|
||||
assert_eq!(backend.changes_trie_storage().unwrap().get(&key), Ok(Some(val)));
|
||||
@@ -1218,9 +1255,66 @@ mod tests {
|
||||
check_changes(&backend, 2, changes2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changes_trie_storage_works_with_forks() {
|
||||
let backend = Backend::<Block>::new_test(1000, 100);
|
||||
|
||||
let changes0 = vec![(b"k0".to_vec(), b"v0".to_vec())];
|
||||
let changes1 = vec![(b"k1".to_vec(), b"v1".to_vec())];
|
||||
let changes2 = vec![(b"k2".to_vec(), b"v2".to_vec())];
|
||||
let block0 = insert_header(&backend, 0, Default::default(), changes0.clone(), Default::default());
|
||||
let block1 = insert_header(&backend, 1, block0, changes1.clone(), Default::default());
|
||||
let block2 = insert_header(&backend, 2, block1, changes2.clone(), Default::default());
|
||||
|
||||
let changes2_1_0 = vec![(b"k3".to_vec(), b"v3".to_vec())];
|
||||
let changes2_1_1 = vec![(b"k4".to_vec(), b"v4".to_vec())];
|
||||
let block2_1_0 = insert_header(&backend, 3, block2, changes2_1_0.clone(), Default::default());
|
||||
let block2_1_1 = insert_header(&backend, 4, block2_1_0, changes2_1_1.clone(), Default::default());
|
||||
|
||||
let changes2_2_0 = vec![(b"k5".to_vec(), b"v5".to_vec())];
|
||||
let changes2_2_1 = vec![(b"k6".to_vec(), b"v6".to_vec())];
|
||||
let block2_2_0 = insert_header(&backend, 3, block2, changes2_2_0.clone(), Default::default());
|
||||
let block2_2_1 = insert_header(&backend, 4, block2_2_0, changes2_2_1.clone(), Default::default());
|
||||
|
||||
// finalize block1
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 1;
|
||||
|
||||
// branch1: when asking for finalized block hash
|
||||
let (changes1_root, _) = prepare_changes(changes1);
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 1), Ok(Some(changes1_root)));
|
||||
|
||||
// branch2: when asking for finalized block hash
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_2_1, number: 4 };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 1), Ok(Some(changes1_root)));
|
||||
|
||||
// branch1: when asking for non-finalized block hash (search by traversal)
|
||||
let (changes2_1_0_root, _) = prepare_changes(changes2_1_0);
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_1_0_root)));
|
||||
|
||||
// branch2: when asking for non-finalized block hash (search using canonicalized hint)
|
||||
let (changes2_2_0_root, _) = prepare_changes(changes2_2_0);
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_2_1, number: 4 };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
|
||||
|
||||
// finalize first block of branch2 (block2_2_0)
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 3;
|
||||
|
||||
// branch2: when asking for finalized block of this branch
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
|
||||
|
||||
// branch1: when asking for finalized block of other branch
|
||||
// => result is incorrect (returned for the block of branch1), but this is expected,
|
||||
// because the other fork is abandoned (forked before finalized header)
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: block2_1_1, number: 4 };
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, 3), Ok(Some(changes2_2_0_root)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changes_tries_are_pruned_on_finalization() {
|
||||
let mut backend = Backend::<Block>::new_test(1000, 100);
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 1000;
|
||||
backend.changes_tries_storage.min_blocks_to_keep = Some(8);
|
||||
let config = ChangesTrieConfiguration {
|
||||
digest_interval: 2,
|
||||
@@ -1243,26 +1337,27 @@ mod tests {
|
||||
let _ = insert_header(&backend, 12, block11, vec![(b"key_at_12".to_vec(), b"val_at_12".to_vec())], Default::default());
|
||||
|
||||
// check that roots of all tries are in the columns::CHANGES_TRIE
|
||||
let anchor = state_machine::ChangesTrieAnchorBlockId { hash: Default::default(), number: 100 };
|
||||
fn read_changes_trie_root(backend: &Backend<Block>, num: u64) -> H256 {
|
||||
backend.blockchain().header(BlockId::Number(num)).unwrap().unwrap().digest().logs().iter()
|
||||
.find(|i| i.as_changes_trie_root().is_some()).unwrap().as_changes_trie_root().unwrap().clone()
|
||||
}
|
||||
let root1 = read_changes_trie_root(&backend, 1); assert_eq!(backend.changes_tries_storage.root(1).unwrap(), Some(root1));
|
||||
let root2 = read_changes_trie_root(&backend, 2); assert_eq!(backend.changes_tries_storage.root(2).unwrap(), Some(root2));
|
||||
let root3 = read_changes_trie_root(&backend, 3); assert_eq!(backend.changes_tries_storage.root(3).unwrap(), Some(root3));
|
||||
let root4 = read_changes_trie_root(&backend, 4); assert_eq!(backend.changes_tries_storage.root(4).unwrap(), Some(root4));
|
||||
let root5 = read_changes_trie_root(&backend, 5); assert_eq!(backend.changes_tries_storage.root(5).unwrap(), Some(root5));
|
||||
let root6 = read_changes_trie_root(&backend, 6); assert_eq!(backend.changes_tries_storage.root(6).unwrap(), Some(root6));
|
||||
let root7 = read_changes_trie_root(&backend, 7); assert_eq!(backend.changes_tries_storage.root(7).unwrap(), Some(root7));
|
||||
let root8 = read_changes_trie_root(&backend, 8); assert_eq!(backend.changes_tries_storage.root(8).unwrap(), Some(root8));
|
||||
let root9 = read_changes_trie_root(&backend, 9); assert_eq!(backend.changes_tries_storage.root(9).unwrap(), Some(root9));
|
||||
let root10 = read_changes_trie_root(&backend, 10); assert_eq!(backend.changes_tries_storage.root(10).unwrap(), Some(root10));
|
||||
let root11 = read_changes_trie_root(&backend, 11); assert_eq!(backend.changes_tries_storage.root(11).unwrap(), Some(root11));
|
||||
let root12 = read_changes_trie_root(&backend, 12); assert_eq!(backend.changes_tries_storage.root(12).unwrap(), Some(root12));
|
||||
let root1 = read_changes_trie_root(&backend, 1); assert_eq!(backend.changes_tries_storage.root(&anchor, 1).unwrap(), Some(root1));
|
||||
let root2 = read_changes_trie_root(&backend, 2); assert_eq!(backend.changes_tries_storage.root(&anchor, 2).unwrap(), Some(root2));
|
||||
let root3 = read_changes_trie_root(&backend, 3); assert_eq!(backend.changes_tries_storage.root(&anchor, 3).unwrap(), Some(root3));
|
||||
let root4 = read_changes_trie_root(&backend, 4); assert_eq!(backend.changes_tries_storage.root(&anchor, 4).unwrap(), Some(root4));
|
||||
let root5 = read_changes_trie_root(&backend, 5); assert_eq!(backend.changes_tries_storage.root(&anchor, 5).unwrap(), Some(root5));
|
||||
let root6 = read_changes_trie_root(&backend, 6); assert_eq!(backend.changes_tries_storage.root(&anchor, 6).unwrap(), Some(root6));
|
||||
let root7 = read_changes_trie_root(&backend, 7); assert_eq!(backend.changes_tries_storage.root(&anchor, 7).unwrap(), Some(root7));
|
||||
let root8 = read_changes_trie_root(&backend, 8); assert_eq!(backend.changes_tries_storage.root(&anchor, 8).unwrap(), Some(root8));
|
||||
let root9 = read_changes_trie_root(&backend, 9); assert_eq!(backend.changes_tries_storage.root(&anchor, 9).unwrap(), Some(root9));
|
||||
let root10 = read_changes_trie_root(&backend, 10); assert_eq!(backend.changes_tries_storage.root(&anchor, 10).unwrap(), Some(root10));
|
||||
let root11 = read_changes_trie_root(&backend, 11); assert_eq!(backend.changes_tries_storage.root(&anchor, 11).unwrap(), Some(root11));
|
||||
let root12 = read_changes_trie_root(&backend, 12); assert_eq!(backend.changes_tries_storage.root(&anchor, 12).unwrap(), Some(root12));
|
||||
|
||||
// now simulate finalization of block#12, causing prune of tries at #1..#4
|
||||
let mut tx = DBTransaction::new();
|
||||
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, 12);
|
||||
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, Default::default(), 12);
|
||||
backend.storage.db.write(tx).unwrap();
|
||||
assert!(backend.changes_tries_storage.get(&root1).unwrap().is_none());
|
||||
assert!(backend.changes_tries_storage.get(&root2).unwrap().is_none());
|
||||
@@ -1275,7 +1370,7 @@ mod tests {
|
||||
|
||||
// now simulate finalization of block#16, causing prune of tries at #5..#8
|
||||
let mut tx = DBTransaction::new();
|
||||
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, 16);
|
||||
backend.changes_tries_storage.prune(Some(config.clone()), &mut tx, Default::default(), 16);
|
||||
backend.storage.db.write(tx).unwrap();
|
||||
assert!(backend.changes_tries_storage.get(&root5).unwrap().is_none());
|
||||
assert!(backend.changes_tries_storage.get(&root6).unwrap().is_none());
|
||||
@@ -1286,7 +1381,7 @@ mod tests {
|
||||
// => no changes tries are pruned, because we never prune in archive mode
|
||||
backend.changes_tries_storage.min_blocks_to_keep = None;
|
||||
let mut tx = DBTransaction::new();
|
||||
backend.changes_tries_storage.prune(Some(config), &mut tx, 20);
|
||||
backend.changes_tries_storage.prune(Some(config), &mut tx, Default::default(), 20);
|
||||
backend.storage.db.write(tx).unwrap();
|
||||
assert!(backend.changes_tries_storage.get(&root9).unwrap().is_some());
|
||||
assert!(backend.changes_tries_storage.get(&root10).unwrap().is_some());
|
||||
|
||||
@@ -187,6 +187,17 @@ pub fn read_header<Block: BlockT>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Required header from the database.
|
||||
pub fn require_header<Block: BlockT>(
|
||||
db: &KeyValueDB,
|
||||
col_index: Option<u32>,
|
||||
col: Option<u32>,
|
||||
id: BlockId<Block>,
|
||||
) -> client::error::Result<Block::Header> {
|
||||
read_header(db, col_index, col, id)
|
||||
.and_then(|header| header.ok_or_else(|| client::error::ErrorKind::UnknownBlock(format!("{}", id)).into()))
|
||||
}
|
||||
|
||||
/// Read meta from the database.
|
||||
pub fn read_meta<Block>(db: &KeyValueDB, col_meta: Option<u32>, col_header: Option<u32>) -> Result<
|
||||
Meta<<<Block as BlockT>::Header as HeaderT>::Number, Block::Hash>,
|
||||
|
||||
@@ -27,7 +27,7 @@ use hash_db;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie;
|
||||
|
||||
use primitives::H256;
|
||||
use primitives::{H256, convert_hash};
|
||||
use runtime_primitives::traits::{As, Header as HeaderT, SimpleArithmetic, One};
|
||||
use state_machine::backend::InMemory as InMemoryState;
|
||||
use state_machine::{prove_read, read_proof_check};
|
||||
@@ -113,8 +113,7 @@ pub fn check_proof<Header, Hasher>(
|
||||
Hasher: hash_db::Hasher,
|
||||
Hasher::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
let mut root: Hasher::Out = Default::default();
|
||||
root.as_mut().copy_from_slice(local_root.as_ref());
|
||||
let root: Hasher::Out = convert_hash(&local_root);
|
||||
let local_cht_key = encode_cht_key(local_number);
|
||||
let local_cht_value = read_proof_check::<Hasher>(root, remote_proof,
|
||||
&local_cht_key).map_err(|e| ClientError::from(e))?;
|
||||
|
||||
@@ -30,14 +30,14 @@ use consensus::{ImportBlock, ImportResult, BlockOrigin};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash};
|
||||
use runtime_primitives::{ApplyResult, BuildStorage};
|
||||
use runtime_api as api;
|
||||
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration};
|
||||
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash};
|
||||
use primitives::storage::{StorageKey, StorageData};
|
||||
use primitives::storage::well_known_keys;
|
||||
use codec::{Encode, Decode};
|
||||
use state_machine::{
|
||||
Backend as StateBackend, CodeExecutor,
|
||||
ExecutionStrategy, ExecutionManager, prove_read,
|
||||
key_changes, key_changes_proof, OverlayedChanges
|
||||
ExecutionStrategy, ExecutionManager, ChangesTrieAnchorBlockId,
|
||||
prove_read, key_changes, key_changes_proof, OverlayedChanges
|
||||
};
|
||||
|
||||
use backend::{self, BlockImportOperation};
|
||||
@@ -355,7 +355,10 @@ impl<B, E, Block> Client<B, E, Block> where
|
||||
config,
|
||||
storage,
|
||||
self.require_block_number_from_id(&BlockId::Hash(first))?.as_(),
|
||||
self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
|
||||
&ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&last),
|
||||
number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
|
||||
},
|
||||
self.backend.blockchain().info()?.best_number.as_(),
|
||||
key)
|
||||
.map_err(|err| error::ErrorKind::ChangesTrieAccessFailed(err).into())
|
||||
@@ -388,7 +391,10 @@ impl<B, E, Block> Client<B, E, Block> where
|
||||
config,
|
||||
storage,
|
||||
self.require_block_number_from_id(&BlockId::Hash(first))?.as_(),
|
||||
self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
|
||||
&ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&last),
|
||||
number: self.require_block_number_from_id(&BlockId::Hash(last))?.as_(),
|
||||
},
|
||||
max_number.as_(),
|
||||
key)
|
||||
.map_err(|err| error::ErrorKind::ChangesTrieAccessFailed(err).into())
|
||||
|
||||
@@ -21,6 +21,7 @@ use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use futures::{IntoFuture, Future};
|
||||
|
||||
use primitives::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,
|
||||
@@ -136,8 +137,7 @@ pub fn check_execution_proof<Header, E, H>(
|
||||
|
||||
{
|
||||
let local_state_root = request.header.state_root();
|
||||
let mut root: H::Out = Default::default();
|
||||
root.as_mut().copy_from_slice(local_state_root.as_ref());
|
||||
let root: H::Out = convert_hash(&local_state_root);
|
||||
|
||||
let mut changes = OverlayedChanges::default();
|
||||
let local_result = execution_proof_check::<H, _>(
|
||||
|
||||
@@ -21,10 +21,10 @@ use futures::IntoFuture;
|
||||
|
||||
use hash_db::Hasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use primitives::ChangesTrieConfiguration;
|
||||
use primitives::{ChangesTrieConfiguration, convert_hash};
|
||||
use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, NumberFor};
|
||||
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, read_proof_check,
|
||||
key_changes_proof_check};
|
||||
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId,
|
||||
read_proof_check, key_changes_proof_check};
|
||||
|
||||
use call_executor::CallResult;
|
||||
use cht;
|
||||
@@ -192,9 +192,8 @@ impl<E, Block, H> FetchChecker<Block> for LightDataChecker<E, H>
|
||||
request: &RemoteReadRequest<Block::Header>,
|
||||
remote_proof: Vec<Vec<u8>>
|
||||
) -> ClientResult<Option<Vec<u8>>> {
|
||||
let mut root: H::Out = Default::default();
|
||||
root.as_mut().copy_from_slice(request.header.state_root().as_ref());
|
||||
read_proof_check::<H>(root, remote_proof, &request.key).map_err(Into::into)
|
||||
read_proof_check::<H>(convert_hash(request.header.state_root()), remote_proof, &request.key)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn check_execution_proof(
|
||||
@@ -229,7 +228,10 @@ impl<E, Block, H> FetchChecker<Block> for LightDataChecker<E, H>
|
||||
},
|
||||
remote_proof,
|
||||
first_number,
|
||||
request.last_block.0.as_(),
|
||||
&ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&request.last_block.1),
|
||||
number: request.last_block.0.as_(),
|
||||
},
|
||||
remote_max.as_(),
|
||||
&request.key)
|
||||
.map(|pairs| pairs.into_iter().map(|(b, x)| (As::sa(b), x)).collect())
|
||||
@@ -248,15 +250,12 @@ impl<'a, H, Hash> ChangesTrieRootsStorage<H> for RootsStorage<'a, Hash>
|
||||
H: Hasher,
|
||||
Hash: 'a + Send + Sync + Clone + AsRef<[u8]>,
|
||||
{
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String> {
|
||||
fn root(&self, _anchor: &ChangesTrieAnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
|
||||
// we can't ask for roots from parallel forks here => ignore anchor
|
||||
Ok(block.checked_sub(self.first)
|
||||
.and_then(|index| self.roots.get(index as usize))
|
||||
.cloned()
|
||||
.map(|root| {
|
||||
let mut hasher_root: H::Out = Default::default();
|
||||
hasher_root.as_mut().copy_from_slice(root.as_ref());
|
||||
hasher_root
|
||||
}))
|
||||
.map(|root| convert_hash(&root)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ use wasm_utils::UserError;
|
||||
use primitives::{blake2_256, twox_128, twox_256, ed25519};
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use primitives::sandbox as sandbox_primitives;
|
||||
use primitives::Blake2Hasher;
|
||||
use primitives::{H256, Blake2Hasher};
|
||||
use trie::ordered_trie_root;
|
||||
use sandbox;
|
||||
|
||||
@@ -412,8 +412,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
ext_storage_changes_root(block: u64, result: *mut u8) -> u32 => {
|
||||
let r = this.ext.storage_changes_root(block);
|
||||
ext_storage_changes_root(parent_hash_data: *const u8, parent_hash_len: u32, parent_number: u64, result: *mut u8) -> u32 => {
|
||||
let mut parent_hash = H256::default();
|
||||
if parent_hash_len != parent_hash.as_ref().len() as u32 {
|
||||
return Err(UserError("Invalid parent_hash_len in ext_storage_changes_root").into());
|
||||
}
|
||||
let raw_parent_hash = this.memory.get(parent_hash_data, parent_hash_len as usize)
|
||||
.map_err(|_| UserError("Invalid attempt to get parent_hash in ext_storage_changes_root"))?;
|
||||
parent_hash.as_mut().copy_from_slice(&raw_parent_hash[..]);
|
||||
let r = this.ext.storage_changes_root(parent_hash, parent_number);
|
||||
if let Some(ref r) = r {
|
||||
this.memory.set(result, &r[..]).map_err(|_| UserError("Invalid attempt to set memory in ext_storage_changes_root"))?;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,16 @@ impl_rest!(H160, 20);
|
||||
impl_rest!(H256, 32);
|
||||
impl_rest!(H512, 64);
|
||||
|
||||
/// Hash conversion. Used to convert between unbound associated hash types in traits,
|
||||
/// implemented by the same hash type.
|
||||
/// Panics if used to convert between different hash types.
|
||||
pub fn convert_hash<H1: Default + AsMut<[u8]>, H2: AsRef<[u8]>>(src: &H2) -> H1 {
|
||||
let mut dest = H1::default();
|
||||
assert_eq!(dest.as_mut().len(), src.as_ref().len());
|
||||
dest.as_mut().copy_from_slice(src.as_ref());
|
||||
dest
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -107,7 +107,7 @@ mod changes_trie;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::hash::{H160, H256, H512};
|
||||
pub use self::hash::{H160, H256, H512, convert_hash};
|
||||
pub use self::uint::U256;
|
||||
pub use authority_id::AuthorityId;
|
||||
pub use changes_trie::ChangesTrieConfiguration;
|
||||
|
||||
@@ -157,9 +157,9 @@ pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
}
|
||||
|
||||
/// "Commit" all existing operations and get the resultant storage change root.
|
||||
pub fn storage_changes_root(block: u64) -> Option<H256> {
|
||||
pub fn storage_changes_root(parent_hash: [u8; 32], parent_num: u64) -> Option<H256> {
|
||||
ext::with(|ext|
|
||||
ext.storage_changes_root(block)
|
||||
ext.storage_changes_root(parent_hash.into(), parent_num)
|
||||
).unwrap_or(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ extern "C" {
|
||||
fn ext_get_child_storage_into(storage_key_data: *const u8, storage_key_len: u32, key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32;
|
||||
fn ext_storage_root(result: *mut u8);
|
||||
fn ext_child_storage_root(storage_key_data: *const u8, storage_key_len: u32, written_out: *mut u32) -> *mut u8;
|
||||
fn ext_storage_changes_root(block: u64, result: *mut u8) -> u32;
|
||||
fn ext_storage_changes_root(parent_hash_data: *const u8, parent_hash_len: u32, parent_num: u64, result: *mut u8) -> u32;
|
||||
fn ext_blake2_256_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8);
|
||||
fn ext_chain_id() -> u64;
|
||||
fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8);
|
||||
@@ -269,10 +269,10 @@ pub fn child_storage_root(storage_key: &[u8]) -> Option<Vec<u8>> {
|
||||
}
|
||||
|
||||
/// The current storage' changes root.
|
||||
pub fn storage_changes_root(block: u64) -> Option<[u8; 32]> {
|
||||
pub fn storage_changes_root(parent_hash: [u8; 32], parent_num: u64) -> Option<[u8; 32]> {
|
||||
let mut result: [u8; 32] = Default::default();
|
||||
let is_set = unsafe {
|
||||
ext_storage_changes_root(block, result.as_mut_ptr())
|
||||
ext_storage_changes_root(parent_hash.as_ptr(), parent_hash.len() as u32, parent_num, result.as_mut_ptr())
|
||||
};
|
||||
|
||||
if is_set != 0 {
|
||||
|
||||
@@ -276,7 +276,7 @@ pub trait Hash: 'static + MaybeSerializeDebug + Clone + Eq + PartialEq { // Stup
|
||||
fn storage_root() -> Self::Output;
|
||||
|
||||
/// Acquire the global storage changes root.
|
||||
fn storage_changes_root(block: u64) -> Option<Self::Output>;
|
||||
fn storage_changes_root(parent_hash: Self::Output, parent_number: u64) -> Option<Self::Output>;
|
||||
}
|
||||
|
||||
/// Blake2-256 Hash implementation.
|
||||
@@ -308,8 +308,8 @@ impl Hash for BlakeTwo256 {
|
||||
fn storage_root() -> Self::Output {
|
||||
runtime_io::storage_root().into()
|
||||
}
|
||||
fn storage_changes_root(block: u64) -> Option<Self::Output> {
|
||||
runtime_io::storage_changes_root(block).map(Into::into)
|
||||
fn storage_changes_root(parent_hash: Self::Output, parent_number: u64) -> Option<Self::Output> {
|
||||
runtime_io::storage_changes_root(parent_hash.into(), parent_number).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use overlayed_changes::OverlayedChanges;
|
||||
use trie_backend_essence::{TrieBackendStorage, TrieBackendEssence};
|
||||
use changes_trie::build_iterator::digest_build_iterator;
|
||||
use changes_trie::input::{InputKey, InputPair, DigestIndex, ExtrinsicIndex};
|
||||
use changes_trie::{Configuration, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, Storage};
|
||||
|
||||
/// Prepare input pairs for building a changes trie of given block.
|
||||
///
|
||||
@@ -37,7 +37,7 @@ pub fn prepare_input<'a, B, S, H>(
|
||||
backend: &B,
|
||||
storage: Option<&'a S>,
|
||||
changes: &OverlayedChanges,
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
) -> Result<Option<Vec<InputPair>>, String>
|
||||
where
|
||||
B: Backend<H>,
|
||||
@@ -54,10 +54,10 @@ pub fn prepare_input<'a, B, S, H>(
|
||||
let mut input = Vec::new();
|
||||
input.extend(prepare_extrinsics_input(
|
||||
backend,
|
||||
block,
|
||||
parent.number + 1,
|
||||
changes)?);
|
||||
input.extend(prepare_digest_input::<_, H>(
|
||||
block,
|
||||
parent,
|
||||
config,
|
||||
storage)?);
|
||||
|
||||
@@ -73,7 +73,6 @@ fn prepare_extrinsics_input<B, H>(
|
||||
where
|
||||
B: Backend<H>,
|
||||
H: Hasher,
|
||||
|
||||
{
|
||||
let mut extrinsic_map = BTreeMap::<Vec<u8>, BTreeSet<u32>>::new();
|
||||
for (key, val) in changes.prospective.top.iter().chain(changes.committed.top.iter()) {
|
||||
@@ -103,19 +102,19 @@ fn prepare_extrinsics_input<B, H>(
|
||||
|
||||
/// Prepare DigestIndex input pairs.
|
||||
fn prepare_digest_input<'a, S, H>(
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
config: &Configuration,
|
||||
storage: &'a S
|
||||
) -> Result<impl Iterator<Item=InputPair>, String>
|
||||
) -> Result<impl Iterator<Item=InputPair> + 'a, String>
|
||||
where
|
||||
S: Storage<H>,
|
||||
&'a S: TrieBackendStorage<H>,
|
||||
H: Hasher,
|
||||
H::Out: HeapSizeOf,
|
||||
H::Out: 'a + HeapSizeOf,
|
||||
{
|
||||
let mut digest_map = BTreeMap::<Vec<u8>, BTreeSet<u64>>::new();
|
||||
for digest_build_block in digest_build_iterator(config, block) {
|
||||
let trie_root = storage.root(digest_build_block)?;
|
||||
for digest_build_block in digest_build_iterator(config, parent.number + 1) {
|
||||
let trie_root = storage.root(parent, digest_build_block)?;
|
||||
let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block))?;
|
||||
let trie_storage = TrieBackendEssence::<_, H>::new(storage, trie_root);
|
||||
|
||||
@@ -136,7 +135,7 @@ fn prepare_digest_input<'a, S, H>(
|
||||
|
||||
Ok(digest_map.into_iter()
|
||||
.map(move |(key, set)| InputPair::DigestIndex(DigestIndex {
|
||||
block,
|
||||
block: parent.number + 1,
|
||||
key
|
||||
}, set.into_iter().collect())))
|
||||
}
|
||||
@@ -228,7 +227,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_non_digest_block() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 5).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 4 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![101] }, vec![1]),
|
||||
@@ -239,7 +238,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_digest_block_l1() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 3 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
|
||||
@@ -255,7 +254,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_digest_block_l2() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 16).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 15 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![101] }, vec![1]),
|
||||
@@ -279,7 +278,7 @@ mod test {
|
||||
extrinsics: Some(vec![1].into_iter().collect())
|
||||
});
|
||||
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 3 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
|
||||
|
||||
@@ -23,7 +23,7 @@ use codec::{Decode, Encode};
|
||||
use hash_db::{HashDB, Hasher};
|
||||
use heapsize::HeapSizeOf;
|
||||
use substrate_trie::{Recorder, MemoryDB};
|
||||
use changes_trie::{Configuration, RootsStorage, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, RootsStorage, Storage};
|
||||
use changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue};
|
||||
use changes_trie::storage::{TrieBackendAdapter, InMemoryStorage};
|
||||
use proving_backend::ProvingBackendEssence;
|
||||
@@ -35,7 +35,7 @@ pub fn key_changes<S: Storage<H>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<(u64, u32)>, String> where H::Out: HeapSizeOf {
|
||||
@@ -46,7 +46,7 @@ pub fn key_changes<S: Storage<H>, H: Hasher>(
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -62,7 +62,7 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<Vec<u8>>, String> where H::Out: HeapSizeOf {
|
||||
@@ -73,7 +73,7 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -98,7 +98,7 @@ pub fn key_changes_proof_check<S: RootsStorage<H>, H: Hasher>(
|
||||
roots_storage: &S,
|
||||
proof: Vec<Vec<u8>>,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8]
|
||||
) -> Result<Vec<(u64, u32)>, String> where H::Out: HeapSizeOf {
|
||||
@@ -115,7 +115,7 @@ pub fn key_changes_proof_check<S: RootsStorage<H>, H: Hasher>(
|
||||
storage: &proof_db,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -174,12 +174,12 @@ impl<'a> Iterator for SurfaceIterator<'a> {
|
||||
|
||||
/// Drilldown iterator - receives 'digest points' from surface iterator and explores
|
||||
/// every point until extrinsic is found.
|
||||
pub struct DrilldownIteratorEssence<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
pub struct DrilldownIteratorEssence<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
key: &'a [u8],
|
||||
roots_storage: &'a RS,
|
||||
storage: &'a S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &'a AnchorBlockId<H::Out>,
|
||||
surface: SurfaceIterator<'a>,
|
||||
|
||||
extrinsics: VecDeque<(u64, u32)>,
|
||||
@@ -213,14 +213,14 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
// not having a changes trie root is an error because:
|
||||
// we never query roots for future blocks
|
||||
// AND trie roots for old blocks are known (both on full + light node)
|
||||
let trie_root = self.roots_storage.root(block)?
|
||||
let trie_root = self.roots_storage.root(&self.end, block)?
|
||||
.ok_or_else(|| format!("Changes trie root for block {} is not found", block))?;
|
||||
|
||||
// only return extrinsics for blocks before self.max
|
||||
// most of blocks will be filtered out beore pushing to `self.blocks`
|
||||
// here we just throwing away changes at digest blocks we're processing
|
||||
debug_assert!(block >= self.begin, "We shall not touch digests earlier than a range' begin");
|
||||
if block <= self.end {
|
||||
if block <= self.end.number {
|
||||
let extrinsics_key = ExtrinsicIndex { block, key: self.key.to_vec() }.encode();
|
||||
let extrinsics = trie_reader(&self.storage, trie_root, &extrinsics_key);
|
||||
if let Some(extrinsics) = extrinsics? {
|
||||
@@ -239,7 +239,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
// filter level0 blocks here because we tend to use digest blocks,
|
||||
// AND digest block changes could also include changes for out-of-range blocks
|
||||
let begin = self.begin;
|
||||
let end = self.end;
|
||||
let end = self.end.number;
|
||||
self.blocks.extend(blocks.into_iter()
|
||||
.rev()
|
||||
.filter(|b| level > 1 || (*b >= begin && *b <= end))
|
||||
@@ -261,7 +261,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
}
|
||||
|
||||
/// Exploring drilldown operator.
|
||||
struct DrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct DrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> Iterator
|
||||
}
|
||||
|
||||
/// Proving drilldown iterator.
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
proof_recorder: RefCell<Recorder<H::Out>>,
|
||||
}
|
||||
@@ -427,23 +427,23 @@ mod tests {
|
||||
fn drilldown_iterator_works() {
|
||||
let (config, storage) = prepare_for_drilldown();
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 16, 16, &[42]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 2, 4, &[42]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 2 }, 4, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 3, 4, &[42]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 3 }, 4, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 7, 8, 8, &[42]);
|
||||
&config, &storage, 7, &AnchorBlockId { hash: Default::default(), number: 8 }, 8, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 5, 7, 8, &[42]);
|
||||
&config, &storage, 5, &AnchorBlockId { hash: Default::default(), number: 7 }, 8, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(6, 3)]));
|
||||
}
|
||||
|
||||
@@ -453,16 +453,16 @@ mod tests {
|
||||
storage.clear_storage();
|
||||
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 100, 1000, &[42]).is_err());
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 1000, &[42]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drilldown_iterator_fails_when_range_is_invalid() {
|
||||
let (config, storage) = prepare_for_drilldown();
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 100, 50, &[42]).is_err());
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 50, &[42]).is_err());
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 20, 10, 100, &[42]).is_err());
|
||||
&config, &storage, 20, &AnchorBlockId { hash: Default::default(), number: 10 }, 100, &[42]).is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -474,7 +474,7 @@ mod tests {
|
||||
let (remote_config, remote_storage) = prepare_for_drilldown();
|
||||
let remote_proof = key_changes_proof::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&remote_config, &remote_storage,
|
||||
0, 16, 16, &[42]).unwrap();
|
||||
0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]).unwrap();
|
||||
|
||||
// happens on local light node:
|
||||
|
||||
@@ -483,7 +483,7 @@ mod tests {
|
||||
local_storage.clear_storage();
|
||||
let local_result = key_changes_proof_check::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&local_config, &local_storage, remote_proof,
|
||||
0, 16, 16, &[42]);
|
||||
0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]);
|
||||
|
||||
// check that drilldown result is the same as if it was happening at the full node
|
||||
assert_eq!(local_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
|
||||
@@ -58,10 +58,20 @@ use trie::{DBValue, trie_root};
|
||||
/// Changes that are made outside of extrinsics are marked with this index;
|
||||
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
|
||||
|
||||
/// Block identifier that could be used to determine fork of this block.
|
||||
#[derive(Debug)]
|
||||
pub struct AnchorBlockId<Hash: ::std::fmt::Debug> {
|
||||
/// Hash of this block.
|
||||
pub hash: Hash,
|
||||
/// Number of this block.
|
||||
pub number: u64,
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
pub trait RootsStorage<H: Hasher>: Send + Sync {
|
||||
/// Get changes trie root for given block.
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String>;
|
||||
/// Get changes trie root for the block with given number which is an ancestor (or the block
|
||||
/// itself) of the anchor_block (i.e. anchor_block.number >= block).
|
||||
fn root(&self, anchor: &AnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String>;
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
@@ -79,13 +89,13 @@ pub fn compute_changes_trie_root<'a, B: Backend<H>, S: Storage<H>, H: Hasher>(
|
||||
backend: &B,
|
||||
storage: Option<&'a S>,
|
||||
changes: &OverlayedChanges,
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
) -> Option<(H::Out, Vec<(Vec<u8>, Vec<u8>)>)>
|
||||
where
|
||||
&'a S: TrieBackendStorage<H>,
|
||||
H::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
let input_pairs = prepare_input::<B, S, H>(backend, storage, changes, block)
|
||||
let input_pairs = prepare_input::<B, S, H>(backend, storage, changes, parent)
|
||||
.expect("storage is not allowed to fail within runtime")?;
|
||||
let transaction = input_pairs.into_iter()
|
||||
.map(Into::into)
|
||||
|
||||
@@ -21,7 +21,7 @@ use heapsize::HeapSizeOf;
|
||||
use substrate_trie::Recorder;
|
||||
use proving_backend::ProvingBackendEssence;
|
||||
use trie_backend_essence::TrieBackendEssence;
|
||||
use changes_trie::{Configuration, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, Storage};
|
||||
use changes_trie::storage::TrieBackendAdapter;
|
||||
|
||||
/// Prune obslete changes tries. Puning happens at the same block, where highest
|
||||
@@ -33,21 +33,21 @@ pub fn prune<S: Storage<H>, H: Hasher, F: FnMut(H::Out)>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
min_blocks_to_keep: u64,
|
||||
current_block: u64,
|
||||
current_block: &AnchorBlockId<H::Out>,
|
||||
mut remove_trie_node: F,
|
||||
)
|
||||
where
|
||||
H::Out: HeapSizeOf,
|
||||
{
|
||||
// we only CAN prune at block where max-level-digest is created
|
||||
let digest_interval = match config.digest_level_at_block(current_block) {
|
||||
let digest_interval = match config.digest_level_at_block(current_block.number) {
|
||||
Some((digest_level, digest_interval, _)) if digest_level == config.digest_levels =>
|
||||
digest_interval,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// select range for pruning
|
||||
let (first, last) = match pruning_range(min_blocks_to_keep, current_block, digest_interval) {
|
||||
let (first, last) = match pruning_range(min_blocks_to_keep, current_block.number, digest_interval) {
|
||||
Some((first, last)) => (first, last),
|
||||
None => return,
|
||||
};
|
||||
@@ -55,7 +55,7 @@ pub fn prune<S: Storage<H>, H: Hasher, F: FnMut(H::Out)>(
|
||||
// delete changes trie for every block in range
|
||||
// TODO: limit `max_digest_interval` so that this cycle won't involve huge ranges
|
||||
for block in first..last+1 {
|
||||
let root = match storage.root(block) {
|
||||
let root = match storage.root(current_block, block) {
|
||||
Ok(Some(root)) => root,
|
||||
Ok(None) => continue,
|
||||
Err(error) => {
|
||||
@@ -139,7 +139,7 @@ mod tests {
|
||||
H::Out: HeapSizeOf,
|
||||
{
|
||||
let mut pruned_trie_nodes = HashSet::new();
|
||||
prune(config, storage, min_blocks_to_keep, current_block,
|
||||
prune(config, storage, min_blocks_to_keep, &AnchorBlockId { hash: Default::default(), number: current_block },
|
||||
|node| { pruned_trie_nodes.insert(node); });
|
||||
pruned_trie_nodes
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use trie::DBValue;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use changes_trie::{RootsStorage, Storage};
|
||||
use changes_trie::{AnchorBlockId, RootsStorage, Storage};
|
||||
use trie_backend_essence::TrieBackendStorage;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -110,7 +110,7 @@ impl<H: Hasher> InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
}
|
||||
|
||||
impl<H: Hasher> RootsStorage<H> for InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String> {
|
||||
fn root(&self, _anchor_block: &AnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
|
||||
Ok(self.data.read().roots.get(&block).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use std::{error, fmt, cmp::Ord};
|
||||
use backend::{Backend, Consolidate};
|
||||
use changes_trie::{Storage as ChangesTrieStorage, compute_changes_trie_root};
|
||||
use changes_trie::{AnchorBlockId, Storage as ChangesTrieStorage, compute_changes_trie_root};
|
||||
use {Externalities, OverlayedChanges};
|
||||
use hash_db::Hasher;
|
||||
use primitives::storage::well_known_keys::is_child_storage_key;
|
||||
@@ -288,12 +288,12 @@ where
|
||||
Some(self.child_storage_root_transaction(storage_key).0)
|
||||
}
|
||||
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
|
||||
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> {
|
||||
let root_and_tx = compute_changes_trie_root::<_, T, H>(
|
||||
self.backend,
|
||||
self.changes_trie_storage.clone(),
|
||||
self.overlay,
|
||||
block,
|
||||
&AnchorBlockId { hash: parent, number: parent_num },
|
||||
);
|
||||
let root_and_tx = root_and_tx.map(|(root, changes)| {
|
||||
let mut calculated_root = Default::default();
|
||||
@@ -305,7 +305,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
(block, mdb, root)
|
||||
(parent_num + 1, mdb, root)
|
||||
});
|
||||
let root = root_and_tx.as_ref().map(|(_, _, root)| root.clone());
|
||||
self.changes_trie_transaction = root_and_tx;
|
||||
@@ -353,7 +353,7 @@ mod tests {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, None);
|
||||
assert_eq!(ext.storage_changes_root(100), None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -363,7 +363,7 @@ mod tests {
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
assert_eq!(ext.storage_changes_root(100), None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -372,7 +372,7 @@ mod tests {
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
assert_eq!(ext.storage_changes_root(100),
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 99),
|
||||
Some(hex!("5b829920b9c8d554a19ee2a1ba593c4f2ee6fc32822d083e04236d693e8358d5").into()));
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ mod tests {
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
assert_eq!(ext.storage_changes_root(100),
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 99),
|
||||
Some(hex!("bcf494e41e29a15c9ae5caa053fe3cb8b446ee3e02a254efbdec7a19235b76e4").into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ pub use trie::{TrieMut, TrieDBMut, DBValue, MemoryDB};
|
||||
pub use testing::TestExternalities;
|
||||
pub use ext::Ext;
|
||||
pub use backend::Backend;
|
||||
pub use changes_trie::{Storage as ChangesTrieStorage,
|
||||
pub use changes_trie::{
|
||||
AnchorBlockId as ChangesTrieAnchorBlockId,
|
||||
Storage as ChangesTrieStorage,
|
||||
RootsStorage as ChangesTrieRootsStorage,
|
||||
InMemoryStorage as InMemoryChangesTrieStorage,
|
||||
key_changes, key_changes_proof, key_changes_proof_check,
|
||||
@@ -154,8 +156,8 @@ pub trait Externalities<H: Hasher> {
|
||||
/// Returns None if key provided is not a storage key. This can due to not being started with CHILD_STORAGE_KEY_PREFIX, or the trie implementation regards the key as invalid.
|
||||
fn child_storage_root(&mut self, storage_key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Get the change trie root of the current storage overlay at given block.
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> where H::Out: Ord;
|
||||
/// Get the change trie root of the current storage overlay at a block wth given parent.
|
||||
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> where H::Out: Ord;
|
||||
}
|
||||
|
||||
/// Code execution engine.
|
||||
|
||||
@@ -22,7 +22,7 @@ use hash_db::Hasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::trie_root;
|
||||
use backend::InMemory;
|
||||
use changes_trie::{compute_changes_trie_root, InMemoryStorage as ChangesTrieInMemoryStorage};
|
||||
use changes_trie::{compute_changes_trie_root, InMemoryStorage as ChangesTrieInMemoryStorage, AnchorBlockId};
|
||||
use primitives::storage::well_known_keys::CHANGES_TRIE_CONFIG;
|
||||
use super::{Externalities, OverlayedChanges};
|
||||
|
||||
@@ -136,12 +136,12 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
|
||||
None
|
||||
}
|
||||
|
||||
fn storage_changes_root(&mut self, block: u64) -> Option<H::Out> {
|
||||
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> {
|
||||
compute_changes_trie_root::<_, _, H>(
|
||||
&InMemory::default(),
|
||||
Some(&self.changes_trie_storage),
|
||||
&self.changes,
|
||||
block,
|
||||
&AnchorBlockId { hash: parent, number: parent_num },
|
||||
).map(|(root, _)| root.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn execute_block(block: Block) {
|
||||
|
||||
// check digest
|
||||
let mut digest = Digest::default();
|
||||
if let Some(storage_changes_root) = storage_changes_root(header.number) {
|
||||
if let Some(storage_changes_root) = storage_changes_root(header.parent_hash.into(), header.number - 1) {
|
||||
digest.push(generic::DigestItem::ChangesTrieRoot::<Hash, u64>(storage_changes_root.into()));
|
||||
}
|
||||
assert!(digest == header.digest, "Header digest items must match that calculated.");
|
||||
@@ -160,7 +160,7 @@ pub fn finalise_block() -> Header {
|
||||
let number = <Number>::take();
|
||||
let parent_hash = <ParentHash>::take();
|
||||
let storage_root = BlakeTwo256::storage_root();
|
||||
let storage_changes_root = BlakeTwo256::storage_changes_root(number);
|
||||
let storage_changes_root = BlakeTwo256::storage_changes_root(parent_hash, number - 1);
|
||||
|
||||
let mut digest = Digest::default();
|
||||
if let Some(storage_changes_root) = storage_changes_root {
|
||||
|
||||
BIN
Binary file not shown.
@@ -734,7 +734,7 @@ mod tests {
|
||||
let mut t = new_test_ext(true);
|
||||
Executor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1(true).0, true).0.unwrap();
|
||||
|
||||
assert!(t.storage_changes_root(1).is_some());
|
||||
assert!(t.storage_changes_root(Default::default(), 0).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -742,6 +742,6 @@ mod tests {
|
||||
let mut t = new_test_ext(true);
|
||||
WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "execute_block", &block1(true).0).unwrap();
|
||||
|
||||
assert!(t.storage_changes_root(1).is_some());
|
||||
assert!(t.storage_changes_root(Default::default(), 0).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
@@ -284,7 +284,7 @@ impl<T: Trait> Module<T> {
|
||||
let mut digest = <Digest<T>>::take();
|
||||
let extrinsics_root = <ExtrinsicsRoot<T>>::take();
|
||||
let storage_root = T::Hashing::storage_root();
|
||||
let storage_changes_root = T::Hashing::storage_changes_root(number.as_());
|
||||
let storage_changes_root = T::Hashing::storage_changes_root(parent_hash, number.as_() - 1);
|
||||
|
||||
// we can't compute changes trie root earlier && put it to the Digest
|
||||
// because it will include all currently existing temporaries
|
||||
|
||||
Reference in New Issue
Block a user