mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 23:21:06 +00:00
Optimize tree route to sync faster (#3665)
* Introduce HeaderMetadata and use it for tree_route. Add lowest_common_ancestor. * Add tests.
This commit is contained in:
@@ -42,6 +42,7 @@ use client::backend::NewBlockState;
|
||||
use client::blockchain::{well_known_cache_keys, HeaderBackend};
|
||||
use client::{ForkBlocks, ExecutionStrategies};
|
||||
use client::backend::{StorageCollection, ChildStorageCollection};
|
||||
use client::error::Result as ClientResult;
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::{Hasher, Prefix};
|
||||
use kvdb::{KeyValueDB, DBTransaction};
|
||||
@@ -61,10 +62,11 @@ use state_machine::{
|
||||
DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, ChangesTrieBuildCache,
|
||||
backend::Backend as StateBackend,
|
||||
};
|
||||
use crate::utils::{Meta, db_err, meta_keys, read_db, block_id_to_lookup_key, read_meta};
|
||||
use crate::utils::{Meta, db_err, meta_keys, read_db, read_meta};
|
||||
use client::leaves::{LeafSet, FinalizationDisplaced};
|
||||
use client::children;
|
||||
use state_db::StateDb;
|
||||
use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
|
||||
use crate::storage_cache::{CachingState, SharedCache, new_shared_cache};
|
||||
use log::{trace, debug, warn};
|
||||
pub use state_db::PruningMode;
|
||||
@@ -271,16 +273,18 @@ pub struct BlockchainDb<Block: BlockT> {
|
||||
db: Arc<dyn KeyValueDB>,
|
||||
meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
|
||||
leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
|
||||
header_metadata_cache: HeaderMetadataCache<Block>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockchainDb<Block> {
|
||||
fn new(db: Arc<dyn KeyValueDB>) -> Result<Self, client::error::Error> {
|
||||
fn new(db: Arc<dyn KeyValueDB>) -> ClientResult<Self> {
|
||||
let meta = read_meta::<Block>(&*db, columns::META, columns::HEADER)?;
|
||||
let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?;
|
||||
Ok(BlockchainDb {
|
||||
db,
|
||||
leaves: RwLock::new(leaves),
|
||||
meta: Arc::new(RwLock::new(meta)),
|
||||
header_metadata_cache: HeaderMetadataCache::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -310,7 +314,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
|
||||
}
|
||||
|
||||
impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
|
||||
fn header(&self, id: BlockId<Block>) -> Result<Option<Block::Header>, client::error::Error> {
|
||||
fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
|
||||
utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)
|
||||
}
|
||||
|
||||
@@ -325,7 +329,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self, id: BlockId<Block>) -> Result<client::blockchain::BlockStatus, client::error::Error> {
|
||||
fn status(&self, id: BlockId<Block>) -> ClientResult<client::blockchain::BlockStatus> {
|
||||
let exists = match id {
|
||||
BlockId::Hash(_) => read_db(
|
||||
&*self.db,
|
||||
@@ -341,16 +345,11 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
|
||||
}
|
||||
}
|
||||
|
||||
fn number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, client::error::Error> {
|
||||
if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::KEY_LOOKUP, BlockId::Hash(hash))? {
|
||||
let number = utils::lookup_key_to_number(&lookup_key)?;
|
||||
Ok(Some(number))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
|
||||
Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number))
|
||||
}
|
||||
|
||||
fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>, client::error::Error> {
|
||||
fn hash(&self, number: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
|
||||
self.header(BlockId::Number(number)).and_then(|maybe_header| match maybe_header {
|
||||
Some(header) => Ok(Some(header.hash().clone())),
|
||||
None => Ok(None),
|
||||
@@ -359,7 +358,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl
|
||||
}
|
||||
|
||||
impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
|
||||
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, client::error::Error> {
|
||||
fn body(&self, id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
|
||||
match read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, id)? {
|
||||
Some(body) => match Decode::decode(&mut &body[..]) {
|
||||
Ok(body) => Ok(Some(body)),
|
||||
@@ -371,7 +370,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>, client::error::Error> {
|
||||
fn justification(&self, id: BlockId<Block>) -> ClientResult<Option<Justification>> {
|
||||
match read_db(&*self.db, columns::KEY_LOOKUP, columns::JUSTIFICATION, id)? {
|
||||
Some(justification) => match Decode::decode(&mut &justification[..]) {
|
||||
Ok(justification) => Ok(Some(justification)),
|
||||
@@ -383,7 +382,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
fn last_finalized(&self) -> Result<Block::Hash, client::error::Error> {
|
||||
fn last_finalized(&self) -> ClientResult<Block::Hash> {
|
||||
Ok(self.meta.read().finalized_hash.clone())
|
||||
}
|
||||
|
||||
@@ -391,11 +390,11 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
|
||||
None
|
||||
}
|
||||
|
||||
fn leaves(&self) -> Result<Vec<Block::Hash>, client::error::Error> {
|
||||
fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
|
||||
Ok(self.leaves.read().hashes())
|
||||
}
|
||||
|
||||
fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>, client::error::Error> {
|
||||
fn children(&self, parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
|
||||
children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)
|
||||
}
|
||||
}
|
||||
@@ -406,6 +405,31 @@ impl<Block: BlockT> client::blockchain::ProvideCache<Block> for BlockchainDb<Blo
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
|
||||
type Error = client::error::Error;
|
||||
|
||||
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.header_metadata_cache.header_metadata(hash).or_else(|_| {
|
||||
self.header(BlockId::hash(hash))?.map(|header| {
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header_metadata.hash,
|
||||
header_metadata.clone(),
|
||||
);
|
||||
header_metadata
|
||||
}).ok_or(client::error::Error::UnknownBlock("header not found in db".to_owned()))
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
self.header_metadata_cache.insert_header_metadata(hash, metadata)
|
||||
}
|
||||
|
||||
fn remove_header_metadata(&self, hash: Block::Hash) {
|
||||
self.header_metadata_cache.remove_header_metadata(hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// Database transaction
|
||||
pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
|
||||
old_state: CachingState<Blake2Hasher, RefTrackingState<Block>, Block>,
|
||||
@@ -437,7 +461,7 @@ where Block: BlockT<Hash=H256>,
|
||||
{
|
||||
type State = CachingState<Blake2Hasher, RefTrackingState<Block>, Block>;
|
||||
|
||||
fn state(&self) -> Result<Option<&Self::State>, client::error::Error> {
|
||||
fn state(&self) -> ClientResult<Option<&Self::State>> {
|
||||
Ok(Some(&self.old_state))
|
||||
}
|
||||
|
||||
@@ -447,7 +471,7 @@ where Block: BlockT<Hash=H256>,
|
||||
body: Option<Vec<Block::Extrinsic>>,
|
||||
justification: Option<Justification>,
|
||||
leaf_state: NewBlockState,
|
||||
) -> Result<(), client::error::Error> {
|
||||
) -> ClientResult<()> {
|
||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||
self.pending_block = Some(PendingBlock {
|
||||
header,
|
||||
@@ -462,7 +486,7 @@ where Block: BlockT<Hash=H256>,
|
||||
// Currently cache isn't implemented on full nodes.
|
||||
}
|
||||
|
||||
fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> Result<(), client::error::Error> {
|
||||
fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> ClientResult<()> {
|
||||
self.db_updates = update;
|
||||
Ok(())
|
||||
}
|
||||
@@ -471,7 +495,7 @@ where Block: BlockT<Hash=H256>,
|
||||
&mut self,
|
||||
top: StorageOverlay,
|
||||
children: ChildrenStorageOverlay
|
||||
) -> Result<H256, client::error::Error> {
|
||||
) -> ClientResult<H256> {
|
||||
|
||||
if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||
return Err(client::error::Error::GenesisInvalid.into());
|
||||
@@ -499,13 +523,13 @@ where Block: BlockT<Hash=H256>,
|
||||
fn update_changes_trie(
|
||||
&mut self,
|
||||
update: ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>,
|
||||
) -> Result<(), client::error::Error> {
|
||||
) -> ClientResult<()> {
|
||||
self.changes_trie_updates = update.0;
|
||||
self.changes_trie_cache_update = Some(update.1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_aux<I>(&mut self, ops: I) -> Result<(), client::error::Error>
|
||||
fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
|
||||
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
|
||||
{
|
||||
self.aux_ops.append(&mut ops.into_iter().collect());
|
||||
@@ -516,18 +540,18 @@ where Block: BlockT<Hash=H256>,
|
||||
&mut self,
|
||||
update: StorageCollection,
|
||||
child_update: ChildStorageCollection,
|
||||
) -> Result<(), client::error::Error> {
|
||||
) -> ClientResult<()> {
|
||||
self.storage_updates = update;
|
||||
self.child_storage_updates = child_update;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> Result<(), client::error::Error> {
|
||||
fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> ClientResult<()> {
|
||||
self.finalized_blocks.push((block, justification));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mark_head(&mut self, block: BlockId<Block>) -> Result<(), client::error::Error> {
|
||||
fn mark_head(&mut self, block: BlockId<Block>) -> ClientResult<()> {
|
||||
assert!(self.set_head.is_none(), "Only one set head per operation is allowed");
|
||||
self.set_head = Some(block);
|
||||
Ok(())
|
||||
@@ -751,11 +775,11 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
/// Create a new instance of database backend.
|
||||
///
|
||||
/// The pruning window is how old a block must be before the state is pruned.
|
||||
pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> client::error::Result<Self> {
|
||||
pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> {
|
||||
Self::new_inner(config, canonicalization_delay)
|
||||
}
|
||||
|
||||
fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> Result<Self, client::error::Error> {
|
||||
fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> {
|
||||
#[cfg(feature = "kvdb-rocksdb")]
|
||||
let db = crate::utils::open_database(&config, columns::META, "full")?;
|
||||
#[cfg(not(feature = "kvdb-rocksdb"))]
|
||||
@@ -795,7 +819,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
db: Arc<dyn KeyValueDB>,
|
||||
canonicalization_delay: u64,
|
||||
config: &DatabaseSettings
|
||||
) -> Result<Self, client::error::Error> {
|
||||
) -> ClientResult<Self> {
|
||||
let is_archive_pruning = config.pruning.is_archive();
|
||||
let blockchain = BlockchainDb::new(db.clone())?;
|
||||
let meta = blockchain.meta.clone();
|
||||
@@ -881,7 +905,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
///
|
||||
/// Currently changes tries configuration is set up once (at genesis) and could not
|
||||
/// be changed. Thus, we'll actually read value once and then just use cached value.
|
||||
fn changes_trie_config(&self, block: Block::Hash) -> Result<Option<ChangesTrieConfiguration>, client::error::Error> {
|
||||
fn changes_trie_config(&self, block: Block::Hash) -> ClientResult<Option<ChangesTrieConfiguration>> {
|
||||
let mut cached_changes_trie_config = self.changes_trie_config.lock();
|
||||
match cached_changes_trie_config.clone() {
|
||||
Some(cached_changes_trie_config) => Ok(cached_changes_trie_config),
|
||||
@@ -904,7 +928,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
/// In the case where the new best block is a block to be imported, `route_to`
|
||||
/// should be the parent of `best_to`. In the case where we set an existing block
|
||||
/// to be best, `route_to` should equal to `best_to`.
|
||||
fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(Vec<Block::Hash>, Vec<Block::Hash>), client::error::Error> {
|
||||
fn set_head_with_transaction(
|
||||
&self,
|
||||
transaction: &mut DBTransaction,
|
||||
route_to: Block::Hash,
|
||||
best_to: (NumberFor<Block>, Block::Hash),
|
||||
) -> ClientResult<(Vec<Block::Hash>, Vec<Block::Hash>)> {
|
||||
let mut enacted = Vec::default();
|
||||
let mut retracted = Vec::default();
|
||||
|
||||
@@ -912,12 +941,10 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
|
||||
// cannot find tree route with empty DB.
|
||||
if meta.best_hash != Default::default() {
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| self.blockchain.header(id)?.ok_or_else(
|
||||
|| client::error::Error::UnknownBlock(format!("{:?}", id))
|
||||
),
|
||||
BlockId::Hash(meta.best_hash),
|
||||
BlockId::Hash(route_to),
|
||||
let tree_route = header_metadata::tree_route(
|
||||
&self.blockchain,
|
||||
meta.best_hash,
|
||||
route_to,
|
||||
)?;
|
||||
|
||||
// uncanonicalize: check safety violations and ensure the numbers no longer
|
||||
@@ -968,7 +995,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
&self,
|
||||
header: &Block::Header,
|
||||
last_finalized: Option<Block::Hash>,
|
||||
) -> Result<(), client::error::Error> {
|
||||
) -> ClientResult<()> {
|
||||
let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash);
|
||||
if *header.parent_hash() != last_finalized {
|
||||
return Err(::client::error::Error::NonSequentialFinalization(
|
||||
@@ -986,7 +1013,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
last_finalized: Option<Block::Hash>,
|
||||
justification: Option<Justification>,
|
||||
finalization_displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
||||
) -> Result<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool), client::error::Error> {
|
||||
) -> ClientResult<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool)> {
|
||||
// TODO: ensure best chain contains this block.
|
||||
let number = *header.number();
|
||||
self.ensure_sequential_finalization(header, last_finalized)?;
|
||||
@@ -1014,7 +1041,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
)
|
||||
-> Result<(), client::error::Error>
|
||||
-> ClientResult<()>
|
||||
{
|
||||
let number_u64 = number.saturated_into::<u64>();
|
||||
if number_u64 > self.canonicalization_delay {
|
||||
@@ -1042,7 +1069,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
}
|
||||
|
||||
fn try_commit_operation(&self, mut operation: BlockImportOperation<Block, Blake2Hasher>)
|
||||
-> Result<(), client::error::Error>
|
||||
-> ClientResult<()>
|
||||
{
|
||||
let mut transaction = DBTransaction::new();
|
||||
let mut finalization_displaced_leaves = None;
|
||||
@@ -1088,6 +1115,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
hash,
|
||||
)?;
|
||||
|
||||
let header_metadata = CachedHeaderMetadata::from(&pending_block.header);
|
||||
self.blockchain.insert_header_metadata(
|
||||
header_metadata.hash,
|
||||
header_metadata,
|
||||
);
|
||||
|
||||
transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode());
|
||||
if let Some(body) = pending_block.body {
|
||||
transaction.put(columns::BODY, &lookup_key, &body.encode());
|
||||
@@ -1231,7 +1264,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
f_header: &Block::Header,
|
||||
f_hash: Block::Hash,
|
||||
displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>
|
||||
) -> Result<(), client::error::Error> where
|
||||
) -> ClientResult<()> where
|
||||
Block: BlockT<Hash=H256>,
|
||||
{
|
||||
let f_num = f_header.number().clone();
|
||||
@@ -1284,7 +1317,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has
|
||||
'c: 'a,
|
||||
I: IntoIterator<Item=&'a(&'c [u8], &'c [u8])>,
|
||||
D: IntoIterator<Item=&'a &'b [u8]>,
|
||||
>(&self, insert: I, delete: D) -> client::error::Result<()> {
|
||||
>(&self, insert: I, delete: D) -> ClientResult<()> {
|
||||
let mut transaction = DBTransaction::new();
|
||||
for (k, v) in insert {
|
||||
transaction.put(columns::AUX, k, v);
|
||||
@@ -1296,7 +1329,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_aux(&self, key: &[u8]) -> Result<Option<Vec<u8>>, client::error::Error> {
|
||||
fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
||||
Ok(self.storage.db.get(columns::AUX, key).map(|r| r.map(|v| v.to_vec())).map_err(db_err)?)
|
||||
}
|
||||
}
|
||||
@@ -1308,7 +1341,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
type ChangesTrieStorage = DbChangesTrieStorage<Block>;
|
||||
type OffchainStorage = offchain::LocalStorage;
|
||||
|
||||
fn begin_operation(&self) -> Result<Self::BlockImportOperation, client::error::Error> {
|
||||
fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
|
||||
let old_state = self.state_at(BlockId::Hash(Default::default()))?;
|
||||
Ok(BlockImportOperation {
|
||||
pending_block: None,
|
||||
@@ -1324,13 +1357,17 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
})
|
||||
}
|
||||
|
||||
fn begin_state_operation(&self, operation: &mut Self::BlockImportOperation, block: BlockId<Block>) -> Result<(), client::error::Error> {
|
||||
fn begin_state_operation(
|
||||
&self,
|
||||
operation: &mut Self::BlockImportOperation,
|
||||
block: BlockId<Block>,
|
||||
) -> ClientResult<()> {
|
||||
operation.old_state = self.state_at(block)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit_operation(&self, operation: Self::BlockImportOperation)
|
||||
-> Result<(), client::error::Error>
|
||||
-> ClientResult<()>
|
||||
{
|
||||
match self.try_commit_operation(operation) {
|
||||
Ok(_) => {
|
||||
@@ -1345,7 +1382,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
}
|
||||
|
||||
fn finalize_block(&self, block: BlockId<Block>, justification: Option<Justification>)
|
||||
-> Result<(), client::error::Error>
|
||||
-> ClientResult<()>
|
||||
{
|
||||
let mut transaction = DBTransaction::new();
|
||||
let hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
||||
@@ -1385,7 +1422,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
Some(self.offchain_storage.clone())
|
||||
}
|
||||
|
||||
fn revert(&self, n: NumberFor<Block>) -> Result<NumberFor<Block>, client::error::Error> {
|
||||
fn revert(&self, n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
|
||||
let mut best = self.blockchain.info().best_number;
|
||||
let finalized = self.blockchain.info().finalized_number;
|
||||
let revertible = best - finalized;
|
||||
@@ -1430,7 +1467,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
Some(used)
|
||||
}
|
||||
|
||||
fn state_at(&self, block: BlockId<Block>) -> Result<Self::State, client::error::Error> {
|
||||
fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
|
||||
use client::blockchain::HeaderBackend as BcHeaderBackend;
|
||||
|
||||
// special case for genesis initialization
|
||||
@@ -1466,7 +1503,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
!self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>())
|
||||
}
|
||||
|
||||
fn destroy_state(&self, state: Self::State) -> Result<(), client::error::Error> {
|
||||
fn destroy_state(&self, state: Self::State) -> ClientResult<()> {
|
||||
if let Some(hash) = state.cache.parent_hash.clone() {
|
||||
let is_best = || self.blockchain.meta.read().best_hash == hash;
|
||||
state.release().sync_cache(&[], &[], vec![], vec![], None, None, is_best);
|
||||
@@ -1493,6 +1530,8 @@ mod tests {
|
||||
use sr_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
|
||||
use sr_primitives::traits::{Hash, BlakeTwo256};
|
||||
use state_machine::{TrieMut, TrieDBMut, ChangesTrieRootsStorage, ChangesTrieStorage};
|
||||
use header_metadata::{lowest_common_ancestor, tree_route};
|
||||
|
||||
use test_client;
|
||||
|
||||
type Block = RawBlock<ExtrinsicWrapper<u64>>;
|
||||
@@ -1521,7 +1560,6 @@ mod tests {
|
||||
extrinsics_root: H256,
|
||||
) -> H256 {
|
||||
use sr_primitives::testing::Digest;
|
||||
|
||||
let (changes_root, changes_trie_update) = prepare_changes(changes);
|
||||
let digest = Digest {
|
||||
logs: vec![
|
||||
@@ -2077,11 +2115,7 @@ mod tests {
|
||||
let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default());
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(b2)
|
||||
).unwrap();
|
||||
let tree_route = tree_route(blockchain, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
|
||||
@@ -2089,11 +2123,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a1),
|
||||
BlockId::Hash(a3),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(blockchain, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -2101,11 +2131,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(a1),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(blockchain, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
|
||||
@@ -2113,11 +2139,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a2),
|
||||
BlockId::Hash(a2),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(blockchain, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a2);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -2134,11 +2156,7 @@ mod tests {
|
||||
let block1 = insert_header(&backend, 1, block0, Vec::new(), Default::default());
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(block0),
|
||||
BlockId::Hash(block1),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(blockchain, block0, block1).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -2146,6 +2164,64 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lowest_common_ancestor_works() {
|
||||
let backend = Backend::<Block>::new_test(1000, 100);
|
||||
let blockchain = backend.blockchain();
|
||||
let block0 = insert_header(&backend, 0, Default::default(), Vec::new(), Default::default());
|
||||
|
||||
// fork from genesis: 3 prong.
|
||||
let a1 = insert_header(&backend, 1, block0, Vec::new(), Default::default());
|
||||
let a2 = insert_header(&backend, 2, a1, Vec::new(), Default::default());
|
||||
let a3 = insert_header(&backend, 3, a2, Vec::new(), Default::default());
|
||||
|
||||
// fork from genesis: 2 prong.
|
||||
let b1 = insert_header(&backend, 1, block0, Vec::new(), H256::from([1; 32]));
|
||||
let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default());
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, block0);
|
||||
assert_eq!(lca.number, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a2, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a2, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(blockchain, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leaves_with_complex_block_tree() {
|
||||
let backend: Arc<Backend<test_client::runtime::Block>> = Arc::new(Backend::new_test(20, 20));
|
||||
|
||||
@@ -35,6 +35,7 @@ use codec::{Decode, Encode};
|
||||
use primitives::Blake2Hasher;
|
||||
use sr_primitives::generic::{DigestItem, BlockId};
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, NumberFor};
|
||||
use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache};
|
||||
use crate::cache::{DbCacheSync, DbCache, ComplexBlockId, EntryType as CacheEntryType};
|
||||
use crate::utils::{self, meta_keys, Meta, db_err, read_db, block_id_to_lookup_key, read_meta};
|
||||
use crate::DatabaseSettings;
|
||||
@@ -60,6 +61,7 @@ pub struct LightStorage<Block: BlockT> {
|
||||
db: Arc<dyn KeyValueDB>,
|
||||
meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
|
||||
cache: Arc<DbCacheSync<Block>>,
|
||||
header_metadata_cache: HeaderMetadataCache<Block>,
|
||||
}
|
||||
|
||||
impl<Block> LightStorage<Block>
|
||||
@@ -109,6 +111,7 @@ impl<Block> LightStorage<Block>
|
||||
db,
|
||||
meta: RwLock::new(meta),
|
||||
cache: Arc::new(DbCacheSync(RwLock::new(cache))),
|
||||
header_metadata_cache: HeaderMetadataCache::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,6 +195,31 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> {
|
||||
type Error = ClientError;
|
||||
|
||||
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.header_metadata_cache.header_metadata(hash).or_else(|_| {
|
||||
self.header(BlockId::hash(hash))?.map(|header| {
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header_metadata.hash,
|
||||
header_metadata.clone(),
|
||||
);
|
||||
header_metadata
|
||||
}).ok_or(ClientError::UnknownBlock("header not found in db".to_owned()))
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
self.header_metadata_cache.insert_header_metadata(hash, metadata)
|
||||
}
|
||||
|
||||
fn remove_header_metadata(&self, hash: Block::Hash) {
|
||||
self.header_metadata_cache.remove_header_metadata(hash);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> LightStorage<Block> {
|
||||
// Get block changes trie root, if available.
|
||||
fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
|
||||
@@ -208,17 +236,18 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
/// In the case where the new best block is a block to be imported, `route_to`
|
||||
/// should be the parent of `best_to`. In the case where we set an existing block
|
||||
/// to be best, `route_to` should equal to `best_to`.
|
||||
fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(), client::error::Error> {
|
||||
fn set_head_with_transaction(
|
||||
&self,
|
||||
transaction: &mut DBTransaction,
|
||||
route_to: Block::Hash,
|
||||
best_to: (NumberFor<Block>, Block::Hash),
|
||||
) -> ClientResult<()> {
|
||||
let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?;
|
||||
|
||||
// handle reorg.
|
||||
let meta = self.meta.read();
|
||||
if meta.best_hash != Default::default() {
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| self.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(meta.best_hash),
|
||||
BlockId::Hash(route_to),
|
||||
)?;
|
||||
let tree_route = header_metadata::tree_route(self, meta.best_hash, route_to)?;
|
||||
|
||||
// update block number to hash lookup entries.
|
||||
for retracted in tree_route.retracted() {
|
||||
@@ -418,6 +447,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
)?;
|
||||
transaction.put(columns::HEADER, &lookup_key, &header.encode());
|
||||
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header.hash().clone(),
|
||||
header_metadata,
|
||||
);
|
||||
|
||||
let is_genesis = number.is_zero();
|
||||
if is_genesis {
|
||||
self.cache.0.write().set_genesis_hash(hash);
|
||||
@@ -537,6 +572,7 @@ pub(crate) mod tests {
|
||||
use client::cht;
|
||||
use sr_primitives::generic::DigestItem;
|
||||
use sr_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper};
|
||||
use header_metadata::{lowest_common_ancestor, tree_route};
|
||||
use super::*;
|
||||
|
||||
type Block = RawBlock<ExtrinsicWrapper<u32>>;
|
||||
@@ -781,11 +817,7 @@ pub(crate) mod tests {
|
||||
let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(b2)
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
|
||||
@@ -793,11 +825,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a1),
|
||||
BlockId::Hash(a3),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -805,11 +833,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a3),
|
||||
BlockId::Hash(a1),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
|
||||
@@ -817,11 +841,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = ::client::blockchain::tree_route(
|
||||
|id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))),
|
||||
BlockId::Hash(a2),
|
||||
BlockId::Hash(a2),
|
||||
).unwrap();
|
||||
let tree_route = tree_route(&db, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a2);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
@@ -829,6 +849,63 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lowest_common_ancestor_works() {
|
||||
let db = LightStorage::new_test();
|
||||
let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
|
||||
// fork from genesis: 3 prong.
|
||||
let a1 = insert_block(&db, HashMap::new(), || default_header(&block0, 1));
|
||||
let a2 = insert_block(&db, HashMap::new(), || default_header(&a1, 2));
|
||||
let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3));
|
||||
|
||||
// fork from genesis: 2 prong.
|
||||
let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32])));
|
||||
let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2));
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, block0);
|
||||
assert_eq!(lca.number, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a1, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a3).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a1).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a1);
|
||||
assert_eq!(lca.number, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let lca = lowest_common_ancestor(&db, a2, a2).unwrap();
|
||||
|
||||
assert_eq!(lca.hash, a2);
|
||||
assert_eq!(lca.number, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorities_are_cached() {
|
||||
let db = LightStorage::new_test();
|
||||
|
||||
Reference in New Issue
Block a user