mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-01 01:01:01 +00:00
Storing multiple Justifications per block (#7640)
* primitives/runtime: initial changes on supporting multiple Justifications * primitives/runtime: make Justifications strongly typed * Encode/decode Justifications * primitives/runtime: add Justification type * backend: apply_finality and finalize_block takes a single Justification * manual-seal: create engine id and let rpc take encoded justification * backend: skeleton functions for appending justifications * backend: initial implementation append_justification Initial implementation of append_justification on the Backend trait, and also remove unused skeleton functions for append_justificaton on Finaziler trait. k * backend: guard against duplicate consensus engine id * client/db: add check for block finality * client/api: add append_justification to in_mem db * client/light: add no-op append_justification * network: fix decode call for Justification * network: only send a single Justification in BlockData * network: minor comment update * protocol: update field names to distinguish single justification * client: further field renames to plural * client: update function names to plural justifications * client/db: upgrade existing database for new format * network: remove dependency on grandpa crate * db: fix check for finalized block * grandpa: check for multiple grandpa justifications hwne importing * backend: update Finalizer trait to take multiple Justifications * db: remove debugging statements in migration code * manual-seal: update note about engine id * db: fix check for finalized block * client: update variable name to reflect it is now plural * grandpa: fix incorrect empty Justications in test * primitives: make Justifications opaque to avoid being empty * network: fix detecting empty Justification * runtime: doc strings for Justifications functions * runtime: add into_justifications * primitives: check for duplicates in when adding to Justifications * network/test: use real grandpa engine id in test * client: fix reviewer comments * primitives: rename Justifications::push to append * backend: revert changes to Finalizer trait * backend: revert mark_finalized * backend: revert changes to finalize_block * backend: revert finalized_blocks * db: add a quick early return for performance * client: minor reviewer comments * service/test: use local ConsensusEngineId * network: add link to issue for sending multiple Justifications * Apply suggestions from code review Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> * Apply suggestions from code review Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> * network: tweaks to review suggestions * network: revert change to BlockData for backwards compatibility * Apply suggestion from code review Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * primitives: update doc comment for Justifications * client/db/upgrade: avoid grandpa crate dependency * consensus: revert to single Justification for import_justification * primitives: improve justifications docs * style cleanups * use and_then * client: rename JUSTIFICATIONS db column * network: revert to using FRNK in network-test Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -55,7 +55,7 @@ use crate::justification::GrandpaJustification;
|
||||
use crate::until_imported::UntilVoteTargetImported;
|
||||
use crate::voting_rule::VotingRule;
|
||||
use sp_finality_grandpa::{
|
||||
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof,
|
||||
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GRANDPA_ENGINE_ID,
|
||||
GrandpaApi, RoundNumber, SetId,
|
||||
};
|
||||
use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64};
|
||||
@@ -1326,10 +1326,13 @@ where
|
||||
|
||||
// ideally some handle to a synchronization oracle would be used
|
||||
// to avoid unconditionally notifying.
|
||||
client.apply_finality(import_op, BlockId::Hash(hash), justification, true).map_err(|e| {
|
||||
warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e);
|
||||
e
|
||||
})?;
|
||||
let justification = justification.map(|j| (GRANDPA_ENGINE_ID, j.clone()));
|
||||
client
|
||||
.apply_finality(import_op, BlockId::Hash(hash), justification, true)
|
||||
.map_err(|e| {
|
||||
warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e);
|
||||
e
|
||||
})?;
|
||||
telemetry!(
|
||||
telemetry;
|
||||
CONSENSUS_INFO;
|
||||
|
||||
@@ -43,11 +43,11 @@ use finality_grandpa::BlockNumberOps;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_blockchain::{Backend as BlockchainBackend, Error as ClientError, Result as ClientResult};
|
||||
use sp_runtime::{
|
||||
Justification, generic::BlockId,
|
||||
EncodedJustification, generic::BlockId,
|
||||
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
|
||||
};
|
||||
use sc_client_api::backend::Backend;
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use sp_finality_grandpa::{AuthorityId, GRANDPA_ENGINE_ID};
|
||||
|
||||
use crate::authorities::AuthoritySetChanges;
|
||||
use crate::justification::GrandpaJustification;
|
||||
@@ -190,8 +190,10 @@ where
|
||||
// Get the Justification stored at the last block of the set
|
||||
let last_block_for_set_id = BlockId::Number(last_block_for_set);
|
||||
let justification =
|
||||
if let Some(justification) = blockchain.justification(last_block_for_set_id)? {
|
||||
justification
|
||||
if let Some(grandpa_justification) = blockchain.justifications(last_block_for_set_id)?
|
||||
.and_then(|justifications| justifications.into_justification(GRANDPA_ENGINE_ID))
|
||||
{
|
||||
grandpa_justification
|
||||
} else {
|
||||
trace!(
|
||||
target: "afg",
|
||||
@@ -257,7 +259,7 @@ pub trait ProvableJustification<Header: HeaderT>: Encode + Decode {
|
||||
|
||||
/// Decode and verify justification.
|
||||
fn decode_and_verify(
|
||||
justification: &Justification,
|
||||
justification: &EncodedJustification,
|
||||
set_id: u64,
|
||||
authorities: &[(AuthorityId, u64)],
|
||||
) -> ClientResult<Self> {
|
||||
@@ -286,6 +288,7 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::authorities::AuthoritySetChanges;
|
||||
use sp_core::crypto::Public;
|
||||
use sp_runtime::Justifications;
|
||||
use sp_finality_grandpa::AuthorityList;
|
||||
use sc_client_api::NewBlockState;
|
||||
use sc_client_api::in_mem::Blockchain as InMemoryBlockchain;
|
||||
@@ -330,31 +333,27 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
fn test_blockchain() -> InMemoryBlockchain<Block> {
|
||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||
let blockchain = InMemoryBlockchain::<Block>::new();
|
||||
blockchain
|
||||
.insert(header(0).hash(), header(0), Some(vec![0]), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(1).hash(), header(1), Some(vec![1]), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(2).hash(), header(2), None, None, NewBlockState::Best)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(3).hash(), header(3), Some(vec![3]), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
let just0 = Some(Justifications::from((ID, vec![0])));
|
||||
let just1 = Some(Justifications::from((ID, vec![1])));
|
||||
let just2 = None;
|
||||
let just3 = Some(Justifications::from((ID, vec![3])));
|
||||
blockchain.insert(header(0).hash(), header(0), just0, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(1).hash(), header(1), just1, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(2).hash(), header(2), just2, None, NewBlockState::Best).unwrap();
|
||||
blockchain.insert(header(3).hash(), header(3), just3, None, NewBlockState::Final).unwrap();
|
||||
blockchain
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finality_proof_fails_if_no_more_last_finalized_blocks() {
|
||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||
let blockchain = test_blockchain();
|
||||
blockchain
|
||||
.insert(header(4).hash(), header(4), Some(vec![1]), None, NewBlockState::Best)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(5).hash(), header(5), Some(vec![2]), None, NewBlockState::Best)
|
||||
.unwrap();
|
||||
let just1 = Some(Justifications::from((ID, vec![1])));
|
||||
let just2 = Some(Justifications::from((ID, vec![2])));
|
||||
blockchain.insert(header(4).hash(), header(4), just1, None, NewBlockState::Best).unwrap();
|
||||
blockchain.insert(header(5).hash(), header(5), just2, None, NewBlockState::Best).unwrap();
|
||||
|
||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||
authority_set_changes.append(0, 5);
|
||||
@@ -430,22 +429,17 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn finality_proof_using_authority_set_changes_fails_with_undefined_start() {
|
||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||
let blockchain = test_blockchain();
|
||||
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
||||
let just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||
let just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||
blockchain
|
||||
.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(5).hash(), header(5), None, None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(6).hash(), header(6), None, None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(7).hash(), header(7), Some(just7.clone()), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
let grandpa_just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||
let grandpa_just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||
let just4 = Some(Justifications::from((ID, grandpa_just4)));
|
||||
let just7 = Some(Justifications::from((ID, grandpa_just7)));
|
||||
blockchain.insert(header(4).hash(), header(4), just4, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(5).hash(), header(5), None, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(6).hash(), header(6), None, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(7).hash(), header(7), just7, None, NewBlockState::Final).unwrap();
|
||||
|
||||
// We have stored the correct block number for the relevant set, but as we are missing the
|
||||
// block for the preceding set the start is not well-defined.
|
||||
@@ -462,22 +456,17 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn finality_proof_using_authority_set_changes_works() {
|
||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||
let blockchain = test_blockchain();
|
||||
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
||||
let just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||
let just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||
blockchain
|
||||
.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(5).hash(), header(5), None, None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(6).hash(), header(6), None, None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
blockchain
|
||||
.insert(header(7).hash(), header(7), Some(just7.clone()), None, NewBlockState::Final)
|
||||
.unwrap();
|
||||
let grandpa_just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||
let grandpa_just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||
let just4 = Some(Justifications::from((ID, grandpa_just4)));
|
||||
let just7 = Some(Justifications::from((ID, grandpa_just7.clone())));
|
||||
blockchain.insert(header(4).hash(), header(4), just4, None, NewBlockState::Final) .unwrap();
|
||||
blockchain.insert(header(5).hash(), header(5), None, None, NewBlockState::Final) .unwrap();
|
||||
blockchain.insert(header(6).hash(), header(6), None, None, NewBlockState::Final).unwrap();
|
||||
blockchain.insert(header(7).hash(), header(7), just7, None, NewBlockState::Final).unwrap();
|
||||
|
||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||
authority_set_changes.append(0, 4);
|
||||
@@ -497,7 +486,7 @@ pub(crate) mod tests {
|
||||
proof_of_5,
|
||||
FinalityProof {
|
||||
block: header(7).hash(),
|
||||
justification: just7,
|
||||
justification: grandpa_just7,
|
||||
unknown_headers: vec![header(6)],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -451,7 +451,7 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
|
||||
let pending_changes = self.make_authorities_changes(&mut block, hash, initial_sync)?;
|
||||
|
||||
// we don't want to finalize on `inner.import_block`
|
||||
let mut justification = block.justification.take();
|
||||
let mut justifications = block.justifications.take();
|
||||
let import_result = (&*self.inner).import_block(block, new_cache);
|
||||
|
||||
let mut imported_aux = {
|
||||
@@ -513,17 +513,20 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
|
||||
// need to apply first, drop any justification that might have been provided with
|
||||
// the block to make sure we request them from `sync` which will ensure they'll be
|
||||
// applied in-order.
|
||||
justification.take();
|
||||
justifications.take();
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
match justification {
|
||||
let grandpa_justification = justifications
|
||||
.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID));
|
||||
|
||||
match grandpa_justification {
|
||||
Some(justification) => {
|
||||
let import_res = self.import_justification(
|
||||
hash,
|
||||
number,
|
||||
justification,
|
||||
(GRANDPA_ENGINE_ID, justification),
|
||||
needs_justification,
|
||||
initial_sync,
|
||||
);
|
||||
@@ -637,8 +640,14 @@ where
|
||||
enacts_change: bool,
|
||||
initial_sync: bool,
|
||||
) -> Result<(), ConsensusError> {
|
||||
if justification.0 != GRANDPA_ENGINE_ID {
|
||||
return Err(ConsensusError::ClientImport(
|
||||
"GRANDPA can only import GRANDPA Justifications.".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let justification = GrandpaJustification::decode_and_verify_finalizes(
|
||||
&justification,
|
||||
&justification.1,
|
||||
(hash, number),
|
||||
self.authority_set.set_id(),
|
||||
&self.authority_set.current_authorities(),
|
||||
|
||||
@@ -39,7 +39,7 @@ use sp_consensus::{
|
||||
import_queue::BoxJustificationImport,
|
||||
};
|
||||
use std::{collections::{HashMap, HashSet}, pin::Pin};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
use sp_runtime::{Justifications, traits::{Block as BlockT, Header as HeaderT}};
|
||||
use sp_runtime::generic::{BlockId, DigestItem};
|
||||
use sp_core::H256;
|
||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||
@@ -369,7 +369,7 @@ fn finalize_3_voters_no_observers() {
|
||||
|
||||
// normally there's no justification for finalized blocks
|
||||
assert!(
|
||||
net.lock().peer(0).client().justification(&BlockId::Number(20)).unwrap().is_none(),
|
||||
net.lock().peer(0).client().justifications(&BlockId::Number(20)).unwrap().is_none(),
|
||||
"Extra justification for block#1",
|
||||
);
|
||||
}
|
||||
@@ -613,7 +613,7 @@ fn justification_is_generated_periodically() {
|
||||
// when block#32 (justification_period) is finalized, justification
|
||||
// is required => generated
|
||||
for i in 0..3 {
|
||||
assert!(net.lock().peer(i).client().justification(&BlockId::Number(32)).unwrap().is_some());
|
||||
assert!(net.lock().peer(i).client().justifications(&BlockId::Number(32)).unwrap().is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,12 +658,12 @@ fn sync_justifications_on_change_blocks() {
|
||||
// the first 3 peers are grandpa voters and therefore have already finalized
|
||||
// block 21 and stored a justification
|
||||
for i in 0..3 {
|
||||
assert!(net.lock().peer(i).client().justification(&BlockId::Number(21)).unwrap().is_some());
|
||||
assert!(net.lock().peer(i).client().justifications(&BlockId::Number(21)).unwrap().is_some());
|
||||
}
|
||||
|
||||
// the last peer should get the justification by syncing from other peers
|
||||
futures::executor::block_on(futures::future::poll_fn(move |cx| {
|
||||
if net.lock().peer(3).client().justification(&BlockId::Number(21)).unwrap().is_none() {
|
||||
if net.lock().peer(3).client().justifications(&BlockId::Number(21)).unwrap().is_none() {
|
||||
net.lock().poll(cx);
|
||||
Poll::Pending
|
||||
} else {
|
||||
@@ -868,7 +868,7 @@ fn test_bad_justification() {
|
||||
let block = || {
|
||||
let block = block.clone();
|
||||
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
|
||||
import.justification = Some(Vec::new());
|
||||
import.justifications = Some(Justifications::from((GRANDPA_ENGINE_ID, Vec::new())));
|
||||
import.body = Some(block.extrinsics);
|
||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||
|
||||
@@ -1583,7 +1583,7 @@ fn imports_justification_for_regular_blocks_on_import() {
|
||||
|
||||
// we import the block with justification attached
|
||||
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
|
||||
import.justification = Some(justification.encode());
|
||||
import.justifications = Some((GRANDPA_ENGINE_ID, justification.encode()).into());
|
||||
import.body = Some(block.extrinsics);
|
||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||
|
||||
@@ -1600,7 +1600,7 @@ fn imports_justification_for_regular_blocks_on_import() {
|
||||
|
||||
// the justification should be imported and available from the client
|
||||
assert!(
|
||||
client.justification(&BlockId::Hash(block_hash)).unwrap().is_some(),
|
||||
client.justifications(&BlockId::Hash(block_hash)).unwrap().is_some(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user