Allow blacklisting blocks from being finalized again after block revert (#6301)

* Allow blacklisting blocks from being finalized again after block revert

* Use BlockRules for storing unfinalized and add have_state_at in revert

* Move finalization_check in finalize_block upward

* Directly mark finalization blacklist as badblocks

* Remove obselete comment
This commit is contained in:
Wei Tang
2020-07-31 14:32:13 +02:00
committed by GitHub
parent 7db19db948
commit 3c5cbb00aa
9 changed files with 105 additions and 50 deletions
+48 -21
View File
@@ -50,8 +50,7 @@ mod subdb;
use std::sync::Arc;
use std::path::{Path, PathBuf};
use std::io;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use sc_client_api::{
UsageInfo, MemoryInfo, IoInfo, MemorySize,
@@ -70,6 +69,7 @@ use parking_lot::RwLock;
use sp_core::ChangesTrieConfiguration;
use sp_core::offchain::storage::{OffchainOverlayedChange, OffchainOverlayedChanges};
use sp_core::storage::{well_known_keys, ChildInfo};
use sp_arithmetic::traits::Saturating;
use sp_runtime::{generic::BlockId, Justification, Storage};
use sp_runtime::traits::{
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor,
@@ -962,6 +962,7 @@ impl<Block: BlockT> Backend<Block> {
// TODO: ensure best chain contains this block.
let number = *header.number();
self.ensure_sequential_finalization(header, last_finalized)?;
self.note_finalized(
transaction,
false,
@@ -1015,9 +1016,10 @@ impl<Block: BlockT> Backend<Block> {
Ok(())
}
fn try_commit_operation(&self, mut operation: BlockImportOperation<Block>)
-> ClientResult<()>
{
fn try_commit_operation(
&self,
mut operation: BlockImportOperation<Block>,
) -> ClientResult<()> {
let mut transaction = Transaction::new();
let mut finalization_displaced_leaves = None;
@@ -1404,7 +1406,10 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
Ok(())
}
fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> {
fn commit_operation(
&self,
operation: Self::BlockImportOperation,
) -> ClientResult<()> {
let usage = operation.old_state.usage_info();
self.state_usage.merge_sm(usage);
@@ -1420,9 +1425,11 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
}
}
fn finalize_block(&self, block: BlockId<Block>, justification: Option<Justification>)
-> ClientResult<()>
{
fn finalize_block(
&self,
block: BlockId<Block>,
justification: Option<Justification>,
) -> ClientResult<()> {
let mut transaction = Transaction::new();
let hash = self.blockchain.expect_block_hash_from_id(&block)?;
let header = self.blockchain.expect_header(block)?;
@@ -1488,7 +1495,13 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
})
}
fn revert(&self, n: NumberFor<Block>, revert_finalized: bool) -> ClientResult<NumberFor<Block>> {
fn revert(
&self,
n: NumberFor<Block>,
revert_finalized: bool,
) -> ClientResult<(NumberFor<Block>, HashSet<Block::Hash>)> {
let mut reverted_finalized = HashSet::new();
let mut best_number = self.blockchain.info().best_number;
let mut best_hash = self.blockchain.info().best_hash;
@@ -1507,18 +1520,28 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
return Ok(c.saturated_into::<NumberFor<Block>>())
}
let mut transaction = Transaction::new();
let removed_number = best_number;
let removed = self.blockchain.header(BlockId::Number(best_number))?.ok_or_else(
|| sp_blockchain::Error::UnknownBlock(
format!("Error reverting to {}. Block hash not found.", best_number)))?;
let removed_hash = removed.hash();
let prev_number = best_number.saturating_sub(One::one());
let prev_hash = self.blockchain.hash(prev_number)?.ok_or_else(
|| sp_blockchain::Error::UnknownBlock(
format!("Error reverting to {}. Block hash not found.", best_number))
)?;
if !self.have_state_at(&prev_hash, prev_number) {
return Ok(c.saturated_into::<NumberFor<Block>>())
}
match self.storage.state_db.revert_one() {
Some(commit) => {
apply_state_commit(&mut transaction, commit);
let removed_number = best_number;
let removed = self.blockchain.header(BlockId::Number(best_number))?.ok_or_else(
|| sp_blockchain::Error::UnknownBlock(
format!("Error reverting to {}. Block hash not found.", best_number)))?;
best_number -= One::one(); // prev block
best_hash = self.blockchain.hash(best_number)?.ok_or_else(
|| sp_blockchain::Error::UnknownBlock(
format!("Error reverting to {}. Block hash not found.", best_number)))?;
best_number = prev_number;
best_hash = prev_hash;
let update_finalized = best_number < finalized;
@@ -1531,7 +1554,12 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
),
)?;
if update_finalized {
transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, key.clone());
transaction.set_from_vec(
columns::META,
meta_keys::FINALIZED_BLOCK,
key.clone()
);
reverted_finalized.insert(removed_hash);
}
transaction.set_from_vec(columns::META, meta_keys::BEST_BLOCK, key);
transaction.remove(columns::KEY_LOOKUP, removed.hash().as_ref());
@@ -1562,7 +1590,7 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
revert_leaves()?;
Ok(reverted)
Ok((reverted, reverted_finalized))
}
fn blockchain(&self) -> &BlockchainDb<Block> {
@@ -1986,7 +2014,6 @@ pub(crate) mod tests {
backend.commit_operation(op).unwrap();
assert!(backend.storage.db.get(
columns::STATE,
&sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX)