mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 06:57:58 +00:00
Prune leaves on finality (#1871)
* support multiple leaves at same height in leaves set * prune leaves on finalization * test leaves behavior in client-db * fix no-std compilation
This commit is contained in:
committed by
GitHub
parent
72a8927ea3
commit
64685c0536
@@ -51,7 +51,7 @@ use state_machine::backend::Backend as StateBackend;
|
||||
use executor::RuntimeInfo;
|
||||
use state_machine::{CodeExecutor, DBValue};
|
||||
use crate::utils::{Meta, db_err, meta_keys, open_database, read_db, block_id_to_lookup_key, read_meta};
|
||||
use client::LeafSet;
|
||||
use client::leaves::{LeafSet, FinalizationDisplaced};
|
||||
use client::children;
|
||||
use state_db::StateDb;
|
||||
use crate::storage_cache::{CachingState, SharedCache, new_shared_cache};
|
||||
@@ -714,11 +714,18 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
header: &Block::Header,
|
||||
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> {
|
||||
// TODO: ensure best chain contains this block.
|
||||
let number = *header.number();
|
||||
self.ensure_sequential_finalization(header, last_finalized)?;
|
||||
self.note_finalized(transaction, header, *hash)?;
|
||||
self.note_finalized(
|
||||
transaction,
|
||||
header,
|
||||
*hash,
|
||||
finalization_displaced,
|
||||
)?;
|
||||
|
||||
if let Some(justification) = justification {
|
||||
transaction.put(
|
||||
columns::JUSTIFICATION,
|
||||
@@ -767,10 +774,13 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
-> Result<(), client::error::Error>
|
||||
{
|
||||
let mut transaction = DBTransaction::new();
|
||||
let mut finalization_displaced_leaves = None;
|
||||
|
||||
operation.apply_aux(&mut transaction);
|
||||
|
||||
let mut meta_updates = Vec::new();
|
||||
let mut last_finalized_hash = self.blockchain.meta.read().finalized_hash;
|
||||
|
||||
if !operation.finalized_blocks.is_empty() {
|
||||
for (block, justification) in operation.finalized_blocks {
|
||||
let block_hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
||||
@@ -782,6 +792,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
&block_header,
|
||||
Some(last_finalized_hash),
|
||||
justification,
|
||||
&mut finalization_displaced_leaves,
|
||||
)?);
|
||||
last_finalized_hash = block_hash;
|
||||
}
|
||||
@@ -846,7 +857,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
if finalized {
|
||||
// TODO: ensure best chain contains this block.
|
||||
self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
|
||||
self.note_finalized(&mut transaction, header, hash)?;
|
||||
self.note_finalized(
|
||||
&mut transaction,
|
||||
header,
|
||||
hash,
|
||||
&mut finalization_displaced_leaves,
|
||||
)?;
|
||||
} else {
|
||||
// canonicalize blocks which are old enough, regardless of finality.
|
||||
self.force_delayed_canonicalize(&mut transaction, hash, *header.number())?
|
||||
@@ -892,9 +908,16 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
|
||||
if let Some((number, hash, enacted, retracted, displaced_leaf, is_best)) = imported {
|
||||
if let Err(e) = write_result {
|
||||
let mut leaves = self.blockchain.leaves.write();
|
||||
let mut undo = leaves.undo();
|
||||
if let Some(displaced_leaf) = displaced_leaf {
|
||||
self.blockchain.leaves.write().undo(displaced_leaf);
|
||||
undo.undo_import(displaced_leaf);
|
||||
}
|
||||
|
||||
if let Some(finalization_displaced) = finalization_displaced_leaves {
|
||||
undo.undo_finalization(finalization_displaced);
|
||||
}
|
||||
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
@@ -924,6 +947,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
transaction: &mut DBTransaction,
|
||||
f_header: &Block::Header,
|
||||
f_hash: Block::Hash,
|
||||
displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>
|
||||
) -> Result<(), client::error::Error> where
|
||||
Block: BlockT<Hash=H256>,
|
||||
{
|
||||
@@ -947,6 +971,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
|
||||
self.changes_tries_storage.prune(changes_trie_config, transaction, f_hash, f_num);
|
||||
}
|
||||
|
||||
let new_displaced = self.blockchain.leaves.write().finalize_height(f_num);
|
||||
match displaced {
|
||||
x @ &mut None => *x = Some(new_displaced),
|
||||
&mut Some(ref mut displaced) => displaced.merge(new_displaced),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1036,22 +1066,27 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
|
||||
let mut transaction = DBTransaction::new();
|
||||
let hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
||||
let header = self.blockchain.expect_header(block)?;
|
||||
let commit = || {
|
||||
let mut displaced = None;
|
||||
let commit = |displaced| {
|
||||
let (hash, number, is_best, is_finalized) = self.finalize_block_with_transaction(
|
||||
&mut transaction,
|
||||
&hash,
|
||||
&header,
|
||||
None,
|
||||
justification,
|
||||
displaced,
|
||||
)?;
|
||||
self.storage.db.write(transaction).map_err(db_err)?;
|
||||
self.blockchain.update_meta(hash, number, is_best, is_finalized);
|
||||
Ok(())
|
||||
};
|
||||
match commit() {
|
||||
match commit(&mut displaced) {
|
||||
Ok(()) => self.storage.state_db.apply_pending(),
|
||||
e @ Err(_) => {
|
||||
self.storage.state_db.revert_pending();
|
||||
if let Some(displaced) = displaced {
|
||||
self.blockchain.leaves.write().undo().undo_finalization(displaced);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@@ -1152,6 +1187,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::columns;
|
||||
use client::backend::Backend as BTrait;
|
||||
use client::blockchain::Backend as BLBTrait;
|
||||
use client::backend::BlockImportOperation as Op;
|
||||
use runtime_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
|
||||
use runtime_primitives::traits::{Hash, BlakeTwo256};
|
||||
@@ -1789,8 +1825,6 @@ mod tests {
|
||||
BlockId::Hash(block1),
|
||||
).unwrap();
|
||||
|
||||
println!("{:?}", tree_route);
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![block1]);
|
||||
@@ -1815,6 +1849,30 @@ mod tests {
|
||||
test_client::trait_tests::test_blockchain_query_by_number_gets_canonical(backend);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leaves_pruned_on_finality() {
|
||||
let backend: Backend<Block> = Backend::new_test(10, 10);
|
||||
let block0 = insert_header(&backend, 0, Default::default(), Default::default(), Default::default());
|
||||
|
||||
let block1_a = insert_header(&backend, 1, block0, Default::default(), Default::default());
|
||||
let block1_b = insert_header(&backend, 1, block0, Default::default(), [1; 32].into());
|
||||
let block1_c = insert_header(&backend, 1, block0, Default::default(), [2; 32].into());
|
||||
|
||||
assert_eq!(backend.blockchain().leaves().unwrap(), vec![block1_a, block1_b, block1_c]);
|
||||
|
||||
let block2_a = insert_header(&backend, 2, block1_a, Default::default(), Default::default());
|
||||
let block2_b = insert_header(&backend, 2, block1_b, Default::default(), Default::default());
|
||||
let block2_c = insert_header(&backend, 2, block1_b, Default::default(), [1; 32].into());
|
||||
|
||||
assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a, block2_b, block2_c, block1_c]);
|
||||
|
||||
backend.finalize_block(BlockId::hash(block1_a), None).unwrap();
|
||||
backend.finalize_block(BlockId::hash(block2_a), None).unwrap();
|
||||
|
||||
// leaves at same height stay. Leaves at lower heights pruned.
|
||||
assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a, block2_b, block2_c]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aux() {
|
||||
let backend: Backend<test_client::runtime::Block> = Backend::new_test(0, 0);
|
||||
|
||||
@@ -24,7 +24,8 @@ use kvdb::{KeyValueDB, DBTransaction};
|
||||
use client::backend::{AuxStore, NewBlockState};
|
||||
use client::blockchain::{BlockStatus, Cache as BlockchainCache,
|
||||
HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo};
|
||||
use client::{cht, LeafSet};
|
||||
use client::cht;
|
||||
use client::leaves::{LeafSet, FinalizationDisplaced};
|
||||
use client::error::{ErrorKind as ClientErrorKind, Result as ClientResult};
|
||||
use client::light::blockchain::Storage as LightBlockchainStorage;
|
||||
use parity_codec::{Decode, Encode};
|
||||
@@ -250,6 +251,7 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
transaction: &mut DBTransaction,
|
||||
header: &Block::Header,
|
||||
hash: Block::Hash,
|
||||
displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
||||
) -> ClientResult<()> {
|
||||
let meta = self.meta.read();
|
||||
if &meta.finalized_hash != header.parent_hash() {
|
||||
@@ -311,6 +313,12 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
let new_displaced = self.leaves.write().finalize_height(header.number().clone());
|
||||
match displaced {
|
||||
x @ &mut None => *x = Some(new_displaced),
|
||||
&mut Some(ref mut displaced) => displaced.merge(new_displaced),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -366,6 +374,7 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
leaf_state: NewBlockState,
|
||||
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
||||
) -> ClientResult<()> {
|
||||
let mut finalization_displaced_leaves = None;
|
||||
let mut transaction = DBTransaction::new();
|
||||
|
||||
let hash = header.hash();
|
||||
@@ -405,7 +414,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
};
|
||||
|
||||
if finalized {
|
||||
self.note_finalized(&mut transaction, &header, hash)?;
|
||||
self.note_finalized(
|
||||
&mut transaction,
|
||||
&header,
|
||||
hash,
|
||||
&mut finalization_displaced_leaves,
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -425,10 +439,18 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
debug!("Light DB Commit {:?} ({})", hash, number);
|
||||
let write_result = self.db.write(transaction).map_err(db_err);
|
||||
if let Err(e) = write_result {
|
||||
let mut leaves = self.leaves.write();
|
||||
let mut undo = leaves.undo();
|
||||
|
||||
// revert leaves set update if there was one.
|
||||
if let Some(displaced_leaf) = displaced_leaf {
|
||||
leaves.undo(displaced_leaf);
|
||||
undo.undo_import(displaced_leaf);
|
||||
}
|
||||
|
||||
if let Some(finalization_displaced) = finalization_displaced_leaves {
|
||||
undo.undo_finalization(finalization_displaced);
|
||||
}
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@@ -464,10 +486,11 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
|
||||
fn finalize_header(&self, id: BlockId<Block>) -> ClientResult<()> {
|
||||
if let Some(header) = self.header(id)? {
|
||||
let mut displaced = None;
|
||||
let mut transaction = DBTransaction::new();
|
||||
let hash = header.hash();
|
||||
let number = *header.number();
|
||||
self.note_finalized(&mut transaction, &header, hash.clone())?;
|
||||
self.note_finalized(&mut transaction, &header, hash.clone(), &mut displaced)?;
|
||||
{
|
||||
let mut cache = self.cache.0.write();
|
||||
let cache_ops = cache.transaction(&mut transaction)
|
||||
@@ -477,7 +500,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
|
||||
)?
|
||||
.into_ops();
|
||||
|
||||
self.db.write(transaction).map_err(db_err)?;
|
||||
if let Err(e) = self.db.write(transaction).map_err(db_err) {
|
||||
if let Some(displaced) = displaced {
|
||||
self.leaves.write().undo().undo_finalization(displaced);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
cache.commit(cache_ops);
|
||||
}
|
||||
self.update_meta(hash, header.number().clone(), false, true);
|
||||
@@ -940,4 +968,28 @@ pub(crate) mod tests {
|
||||
assert_eq!(db.get_aux(&[2]).unwrap(), Some(vec![102]));
|
||||
assert_eq!(db.get_aux(&[3]).unwrap(), Some(vec![103]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_leaves_pruned_on_finality() {
|
||||
let db = LightStorage::<Block>::new_test();
|
||||
let block0 = insert_final_block(&db, None, || default_header(&Default::default(), 0));
|
||||
|
||||
let block1_a = insert_block(&db, None, || default_header(&block0, 1));
|
||||
let block1_b = insert_block(&db, None, || header_with_extrinsics_root(&block0, 1, [1; 32].into()));
|
||||
let block1_c = insert_block(&db, None, || header_with_extrinsics_root(&block0, 1, [2; 32].into()));
|
||||
|
||||
assert_eq!(db.leaves.read().hashes(), vec![block1_a, block1_b, block1_c]);
|
||||
|
||||
let block2_a = insert_block(&db, None, || default_header(&block1_a, 2));
|
||||
let block2_b = insert_block(&db, None, || header_with_extrinsics_root(&block1_b, 2, [1; 32].into()));
|
||||
let block2_c = insert_block(&db, None, || header_with_extrinsics_root(&block1_b, 2, [2; 32].into()));
|
||||
|
||||
assert_eq!(db.leaves.read().hashes(), vec![block2_a, block2_b, block2_c, block1_c]);
|
||||
|
||||
db.finalize_header(BlockId::hash(block1_a)).unwrap();
|
||||
db.finalize_header(BlockId::hash(block2_a)).unwrap();
|
||||
|
||||
// leaves at same height stay. Leaves at lower heights pruned.
|
||||
assert_eq!(db.leaves.read().hashes(), vec![block2_a, block2_b, block2_c]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user