mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 19:41:05 +00:00
Finalized should be before best (#14308)
* Finalized block should not be after best block * Remove unwrap * Apply code review suggestion Co-authored-by: Koute <koute@users.noreply.github.com> * Add test --------- Co-authored-by: Koute <koute@users.noreply.github.com>
This commit is contained in:
@@ -52,7 +52,7 @@ use sp_api::{
|
|||||||
};
|
};
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
self as blockchain, Backend as ChainBackend, CachedHeaderMetadata, Error,
|
self as blockchain, Backend as ChainBackend, CachedHeaderMetadata, Error,
|
||||||
HeaderBackend as ChainHeaderBackend, HeaderMetadata,
|
HeaderBackend as ChainHeaderBackend, HeaderMetadata, Info as BlockchainInfo,
|
||||||
};
|
};
|
||||||
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
|
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
|
||||||
|
|
||||||
@@ -717,7 +717,7 @@ where
|
|||||||
operation,
|
operation,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
None,
|
None,
|
||||||
info.best_hash,
|
&info,
|
||||||
make_notifications,
|
make_notifications,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -907,52 +907,56 @@ where
|
|||||||
fn apply_finality_with_block_hash(
|
fn apply_finality_with_block_hash(
|
||||||
&self,
|
&self,
|
||||||
operation: &mut ClientImportOperation<Block, B>,
|
operation: &mut ClientImportOperation<Block, B>,
|
||||||
block: Block::Hash,
|
hash: Block::Hash,
|
||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
best_block: Block::Hash,
|
info: &BlockchainInfo<Block>,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
) -> sp_blockchain::Result<()> {
|
) -> sp_blockchain::Result<()> {
|
||||||
// find tree route from last finalized to given block.
|
if hash == info.finalized_hash {
|
||||||
let last_finalized = self.backend.blockchain().last_finalized()?;
|
|
||||||
|
|
||||||
if block == last_finalized {
|
|
||||||
warn!(
|
warn!(
|
||||||
"Possible safety violation: attempted to re-finalize last finalized block {:?} ",
|
"Possible safety violation: attempted to re-finalize last finalized block {:?} ",
|
||||||
last_finalized
|
hash,
|
||||||
);
|
);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find tree route from last finalized to given block.
|
||||||
let route_from_finalized =
|
let route_from_finalized =
|
||||||
sp_blockchain::tree_route(self.backend.blockchain(), last_finalized, block)?;
|
sp_blockchain::tree_route(self.backend.blockchain(), info.finalized_hash, hash)?;
|
||||||
|
|
||||||
if let Some(retracted) = route_from_finalized.retracted().get(0) {
|
if let Some(retracted) = route_from_finalized.retracted().get(0) {
|
||||||
warn!(
|
warn!(
|
||||||
"Safety violation: attempted to revert finalized block {:?} which is not in the \
|
"Safety violation: attempted to revert finalized block {:?} which is not in the \
|
||||||
same chain as last finalized {:?}",
|
same chain as last finalized {:?}",
|
||||||
retracted, last_finalized
|
retracted, info.finalized_hash
|
||||||
);
|
);
|
||||||
|
|
||||||
return Err(sp_blockchain::Error::NotInFinalizedChain)
|
return Err(sp_blockchain::Error::NotInFinalizedChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is only one leaf, best block is guaranteed to be
|
// We may need to coercively update the best block if there is more than one
|
||||||
// a descendant of the new finalized block. If not,
|
// leaf or if the finalized block number is greater than last best number recorded
|
||||||
// we need to check.
|
// by the backend. This last condition may apply in case of consensus implementations
|
||||||
if self.backend.blockchain().leaves()?.len() > 1 {
|
// not always checking this condition.
|
||||||
|
let block_number = self
|
||||||
|
.backend
|
||||||
|
.blockchain()
|
||||||
|
.number(hash)?
|
||||||
|
.ok_or(Error::MissingHeader(format!("{hash:?}")))?;
|
||||||
|
if self.backend.blockchain().leaves()?.len() > 1 || info.best_number < block_number {
|
||||||
let route_from_best =
|
let route_from_best =
|
||||||
sp_blockchain::tree_route(self.backend.blockchain(), best_block, block)?;
|
sp_blockchain::tree_route(self.backend.blockchain(), info.best_hash, hash)?;
|
||||||
|
|
||||||
// if the block is not a direct ancestor of the current best chain,
|
// If the block is not a direct ancestor of the current best chain,
|
||||||
// then some other block is the common ancestor.
|
// then some other block is the common ancestor.
|
||||||
if route_from_best.common_block().hash != block {
|
if route_from_best.common_block().hash != hash {
|
||||||
// NOTE: we're setting the finalized block as best block, this might
|
// NOTE: we're setting the finalized block as best block, this might
|
||||||
// be slightly inaccurate since we might have a "better" block
|
// be slightly inaccurate since we might have a "better" block
|
||||||
// further along this chain, but since best chain selection logic is
|
// further along this chain, but since best chain selection logic is
|
||||||
// plugable we cannot make a better choice here. usages that need
|
// plugable we cannot make a better choice here. usages that need
|
||||||
// an accurate "best" block need to go through `SelectChain`
|
// an accurate "best" block need to go through `SelectChain`
|
||||||
// instead.
|
// instead.
|
||||||
operation.op.mark_head(block)?;
|
operation.op.mark_head(hash)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,8 +966,8 @@ where
|
|||||||
operation.op.mark_finalized(finalize_new.hash, None)?;
|
operation.op.mark_finalized(finalize_new.hash, None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(enacted.last().map(|e| e.hash), Some(block));
|
assert_eq!(enacted.last().map(|e| e.hash), Some(hash));
|
||||||
operation.op.mark_finalized(block, justification)?;
|
operation.op.mark_finalized(hash, justification)?;
|
||||||
|
|
||||||
if notify {
|
if notify {
|
||||||
let finalized =
|
let finalized =
|
||||||
@@ -985,7 +989,7 @@ where
|
|||||||
let header = self
|
let header = self
|
||||||
.backend
|
.backend
|
||||||
.blockchain()
|
.blockchain()
|
||||||
.header(block)?
|
.header(hash)?
|
||||||
.expect("Block to finalize expected to be onchain; qed");
|
.expect("Block to finalize expected to be onchain; qed");
|
||||||
|
|
||||||
operation.notify_finalized = Some(FinalizeSummary { header, finalized, stale_heads });
|
operation.notify_finalized = Some(FinalizeSummary { header, finalized, stale_heads });
|
||||||
@@ -1129,7 +1133,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get blockchain info.
|
/// Get blockchain info.
|
||||||
pub fn chain_info(&self) -> blockchain::Info<Block> {
|
pub fn chain_info(&self) -> BlockchainInfo<Block> {
|
||||||
self.backend.blockchain().info()
|
self.backend.blockchain().info()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1896,8 +1900,8 @@ where
|
|||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
) -> sp_blockchain::Result<()> {
|
) -> sp_blockchain::Result<()> {
|
||||||
let last_best = self.backend.blockchain().info().best_hash;
|
let info = self.backend.blockchain().info();
|
||||||
self.apply_finality_with_block_hash(operation, hash, justification, last_best, notify)
|
self.apply_finality_with_block_hash(operation, hash, justification, &info, notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_block(
|
fn finalize_block(
|
||||||
|
|||||||
@@ -2001,3 +2001,41 @@ fn use_dalek_ext_works() {
|
|||||||
.verify_ed25519(a1.hash(), zero_ed_sig(), zero_ed_pub(), vec![])
|
.verify_ed25519(a1.hash(), zero_ed_sig(), zero_ed_pub(), vec![])
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn finalize_after_best_block_updates_best() {
|
||||||
|
let mut client = substrate_test_runtime_client::new();
|
||||||
|
|
||||||
|
// G -> A1
|
||||||
|
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||||
|
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
|
||||||
|
|
||||||
|
// A1 -> A2
|
||||||
|
let a2 = client
|
||||||
|
.new_block_at(a1.hash(), Default::default(), false)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block;
|
||||||
|
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
|
||||||
|
|
||||||
|
// A2 -> A3
|
||||||
|
let a3 = client
|
||||||
|
.new_block_at(a2.hash(), Default::default(), false)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block;
|
||||||
|
let (header, extrinsics) = a3.clone().deconstruct();
|
||||||
|
let mut import_params = BlockImportParams::new(BlockOrigin::Own, header);
|
||||||
|
import_params.body = Some(extrinsics);
|
||||||
|
import_params.fork_choice = Some(ForkChoiceStrategy::Custom(false));
|
||||||
|
block_on(client.import_block(import_params)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(client.chain_info().best_hash, a2.hash());
|
||||||
|
|
||||||
|
client.finalize_block(a3.hash(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(client.chain_info().finalized_hash, a3.hash());
|
||||||
|
assert_eq!(client.chain_info().best_hash, a3.hash());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user