mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-25 04:45:42 +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:
@@ -22,7 +22,7 @@ use std::sync::Arc;
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use sp_core::ChangesTrieConfigurationRange;
|
use sp_core::ChangesTrieConfigurationRange;
|
||||||
use sp_core::offchain::OffchainStorage;
|
use sp_core::offchain::OffchainStorage;
|
||||||
use sp_runtime::{generic::BlockId, Justification, Storage};
|
use sp_runtime::{generic::BlockId, Justification, Justifications, Storage};
|
||||||
use sp_runtime::traits::{Block as BlockT, NumberFor, HashFor};
|
use sp_runtime::traits::{Block as BlockT, NumberFor, HashFor};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
ChangesTrieState, ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction,
|
ChangesTrieState, ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction,
|
||||||
@@ -148,7 +148,7 @@ pub trait BlockImportOperation<Block: BlockT> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
header: Block::Header,
|
header: Block::Header,
|
||||||
body: Option<Vec<Block::Extrinsic>>,
|
body: Option<Vec<Block::Extrinsic>>,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
state: NewBlockState,
|
state: NewBlockState,
|
||||||
) -> sp_blockchain::Result<()>;
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
@@ -197,6 +197,7 @@ pub trait BlockImportOperation<Block: BlockT> {
|
|||||||
id: BlockId<Block>,
|
id: BlockId<Block>,
|
||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
) -> sp_blockchain::Result<()>;
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
/// Mark a block as new head. If both block import and set head are specified, set head
|
/// Mark a block as new head. If both block import and set head are specified, set head
|
||||||
/// overrides block import's best block rule.
|
/// overrides block import's best block rule.
|
||||||
fn mark_head(&mut self, id: BlockId<Block>) -> sp_blockchain::Result<()>;
|
fn mark_head(&mut self, id: BlockId<Block>) -> sp_blockchain::Result<()>;
|
||||||
@@ -230,7 +231,6 @@ pub trait Finalizer<Block: BlockT, B: Backend<Block>> {
|
|||||||
notify: bool,
|
notify: bool,
|
||||||
) -> sp_blockchain::Result<()>;
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
|
|
||||||
/// Finalize a block.
|
/// Finalize a block.
|
||||||
///
|
///
|
||||||
/// This will implicitly finalize all blocks up to it and
|
/// This will implicitly finalize all blocks up to it and
|
||||||
@@ -250,7 +250,6 @@ pub trait Finalizer<Block: BlockT, B: Backend<Block>> {
|
|||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
) -> sp_blockchain::Result<()>;
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides access to an auxiliary database.
|
/// Provides access to an auxiliary database.
|
||||||
@@ -432,6 +431,15 @@ pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
|
|||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
) -> sp_blockchain::Result<()>;
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
|
/// Append justification to the block with the given Id.
|
||||||
|
///
|
||||||
|
/// This should only be called for blocks that are already finalized.
|
||||||
|
fn append_justification(
|
||||||
|
&self,
|
||||||
|
block: BlockId<Block>,
|
||||||
|
justification: Justification,
|
||||||
|
) -> sp_blockchain::Result<()>;
|
||||||
|
|
||||||
/// Returns reference to blockchain backend.
|
/// Returns reference to blockchain backend.
|
||||||
fn blockchain(&self) -> &Self::Blockchain;
|
fn blockchain(&self) -> &Self::Blockchain;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use sp_core::storage::StorageKey;
|
|||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{Block as BlockT, NumberFor},
|
traits::{Block as BlockT, NumberFor},
|
||||||
generic::{BlockId, SignedBlock},
|
generic::{BlockId, SignedBlock},
|
||||||
Justification,
|
Justifications,
|
||||||
};
|
};
|
||||||
use sp_consensus::BlockOrigin;
|
use sp_consensus::BlockOrigin;
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ pub trait BlockBackend<Block: BlockT> {
|
|||||||
/// Get block status.
|
/// Get block status.
|
||||||
fn block_status(&self, id: &BlockId<Block>) -> sp_blockchain::Result<sp_consensus::BlockStatus>;
|
fn block_status(&self, id: &BlockId<Block>) -> sp_blockchain::Result<sp_consensus::BlockStatus>;
|
||||||
|
|
||||||
/// Get block justification set by id.
|
/// Get block justifications for the block with the given id.
|
||||||
fn justification(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<Justification>>;
|
fn justifications(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<Justifications>>;
|
||||||
|
|
||||||
/// Get block hash by number.
|
/// Get block hash by number.
|
||||||
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
|
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>>;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use sp_core::{
|
|||||||
};
|
};
|
||||||
use sp_runtime::generic::BlockId;
|
use sp_runtime::generic::BlockId;
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, HashFor};
|
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, HashFor};
|
||||||
use sp_runtime::{Justification, Storage};
|
use sp_runtime::{Justification, Justifications, Storage};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
ChangesTrieTransaction, InMemoryBackend, Backend as StateBackend, StorageCollection,
|
ChangesTrieTransaction, InMemoryBackend, Backend as StateBackend, StorageCollection,
|
||||||
ChildStorageCollection,
|
ChildStorageCollection,
|
||||||
@@ -51,12 +51,12 @@ struct PendingBlock<B: BlockT> {
|
|||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone)]
|
||||||
enum StoredBlock<B: BlockT> {
|
enum StoredBlock<B: BlockT> {
|
||||||
Header(B::Header, Option<Justification>),
|
Header(B::Header, Option<Justifications>),
|
||||||
Full(B, Option<Justification>),
|
Full(B, Option<Justifications>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: BlockT> StoredBlock<B> {
|
impl<B: BlockT> StoredBlock<B> {
|
||||||
fn new(header: B::Header, body: Option<Vec<B::Extrinsic>>, just: Option<Justification>) -> Self {
|
fn new(header: B::Header, body: Option<Vec<B::Extrinsic>>, just: Option<Justifications>) -> Self {
|
||||||
match body {
|
match body {
|
||||||
Some(body) => StoredBlock::Full(B::new(header, body), just),
|
Some(body) => StoredBlock::Full(B::new(header, body), just),
|
||||||
None => StoredBlock::Header(header, just),
|
None => StoredBlock::Header(header, just),
|
||||||
@@ -70,7 +70,7 @@ impl<B: BlockT> StoredBlock<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justification(&self) -> Option<&Justification> {
|
fn justifications(&self) -> Option<&Justifications> {
|
||||||
match *self {
|
match *self {
|
||||||
StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref()
|
StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref()
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ impl<B: BlockT> StoredBlock<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justification>) {
|
fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justifications>) {
|
||||||
match self {
|
match self {
|
||||||
StoredBlock::Header(header, just) => (header, None, just),
|
StoredBlock::Header(header, just) => (header, None, just),
|
||||||
StoredBlock::Full(block, just) => {
|
StoredBlock::Full(block, just) => {
|
||||||
@@ -164,7 +164,7 @@ impl<Block: BlockT> Blockchain<Block> {
|
|||||||
&self,
|
&self,
|
||||||
hash: Block::Hash,
|
hash: Block::Hash,
|
||||||
header: <Block as BlockT>::Header,
|
header: <Block as BlockT>::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||||
new_state: NewBlockState,
|
new_state: NewBlockState,
|
||||||
) -> sp_blockchain::Result<()> {
|
) -> sp_blockchain::Result<()> {
|
||||||
@@ -176,7 +176,7 @@ impl<Block: BlockT> Blockchain<Block> {
|
|||||||
{
|
{
|
||||||
let mut storage = self.storage.write();
|
let mut storage = self.storage.write();
|
||||||
storage.leaves.import(hash.clone(), number.clone(), header.parent_hash().clone());
|
storage.leaves.import(hash.clone(), number.clone(), header.parent_hash().clone());
|
||||||
storage.blocks.insert(hash.clone(), StoredBlock::new(header, body, justification));
|
storage.blocks.insert(hash.clone(), StoredBlock::new(header, body, justifications));
|
||||||
|
|
||||||
if let NewBlockState::Final = new_state {
|
if let NewBlockState::Final = new_state {
|
||||||
storage.finalized_hash = hash;
|
storage.finalized_hash = hash;
|
||||||
@@ -285,16 +285,44 @@ impl<Block: BlockT> Blockchain<Block> {
|
|||||||
let block = storage.blocks.get_mut(&hash)
|
let block = storage.blocks.get_mut(&hash)
|
||||||
.expect("hash was fetched from a block in the db; qed");
|
.expect("hash was fetched from a block in the db; qed");
|
||||||
|
|
||||||
let block_justification = match block {
|
let block_justifications = match block {
|
||||||
StoredBlock::Header(_, ref mut j) | StoredBlock::Full(_, ref mut j) => j
|
StoredBlock::Header(_, ref mut j) | StoredBlock::Full(_, ref mut j) => j
|
||||||
};
|
};
|
||||||
|
|
||||||
*block_justification = justification;
|
*block_justifications = justification.map(Justifications::from);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_justification(&self, id: BlockId<Block>, justification: Justification)
|
||||||
|
-> sp_blockchain::Result<()>
|
||||||
|
{
|
||||||
|
let hash = self.expect_block_hash_from_id(&id)?;
|
||||||
|
let mut storage = self.storage.write();
|
||||||
|
|
||||||
|
let block = storage
|
||||||
|
.blocks
|
||||||
|
.get_mut(&hash)
|
||||||
|
.expect("hash was fetched from a block in the db; qed");
|
||||||
|
|
||||||
|
let block_justifications = match block {
|
||||||
|
StoredBlock::Header(_, ref mut j) | StoredBlock::Full(_, ref mut j) => j
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(stored_justifications) = block_justifications {
|
||||||
|
if !stored_justifications.append(justification) {
|
||||||
|
return Err(sp_blockchain::Error::BadJustification(
|
||||||
|
"Duplicate consensus engine ID".into()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*block_justifications = Some(Justifications::from(justification));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn write_aux(&self, ops: Vec<(Vec<u8>, Option<Vec<u8>>)>) {
|
fn write_aux(&self, ops: Vec<(Vec<u8>, Option<Vec<u8>>)>) {
|
||||||
let mut storage = self.storage.write();
|
let mut storage = self.storage.write();
|
||||||
for (k, v) in ops {
|
for (k, v) in ops {
|
||||||
@@ -365,9 +393,9 @@ impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justification(&self, id: BlockId<Block>) -> sp_blockchain::Result<Option<Justification>> {
|
fn justifications(&self, id: BlockId<Block>) -> sp_blockchain::Result<Option<Justifications>> {
|
||||||
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b|
|
Ok(self.id(id).and_then(|hash| self.storage.read().blocks.get(&hash).and_then(|b|
|
||||||
b.justification().map(|x| x.clone()))
|
b.justifications().map(|x| x.clone()))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,12 +536,12 @@ impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperatio
|
|||||||
&mut self,
|
&mut self,
|
||||||
header: <Block as BlockT>::Header,
|
header: <Block as BlockT>::Header,
|
||||||
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
state: NewBlockState,
|
state: NewBlockState,
|
||||||
) -> sp_blockchain::Result<()> {
|
) -> sp_blockchain::Result<()> {
|
||||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||||
self.pending_block = Some(PendingBlock {
|
self.pending_block = Some(PendingBlock {
|
||||||
block: StoredBlock::new(header, body, justification),
|
block: StoredBlock::new(header, body, justifications),
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -696,6 +724,14 @@ impl<Block: BlockT> backend::Backend<Block> for Backend<Block> where Block::Hash
|
|||||||
self.blockchain.finalize_header(block, justification)
|
self.blockchain.finalize_header(block, justification)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_justification(
|
||||||
|
&self,
|
||||||
|
block: BlockId<Block>,
|
||||||
|
justification: Justification,
|
||||||
|
) -> sp_blockchain::Result<()> {
|
||||||
|
self.blockchain.append_justification(block, justification)
|
||||||
|
}
|
||||||
|
|
||||||
fn blockchain(&self) -> &Self::Blockchain {
|
fn blockchain(&self) -> &Self::Blockchain {
|
||||||
&self.blockchain
|
&self.blockchain
|
||||||
}
|
}
|
||||||
@@ -766,3 +802,64 @@ pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{NewBlockState, in_mem::Blockchain};
|
||||||
|
use sp_api::{BlockId, HeaderT};
|
||||||
|
use sp_runtime::{ConsensusEngineId, Justifications};
|
||||||
|
use sp_blockchain::Backend;
|
||||||
|
use substrate_test_runtime::{Block, Header, H256};
|
||||||
|
|
||||||
|
pub const ID1: ConsensusEngineId = *b"TST1";
|
||||||
|
pub const ID2: ConsensusEngineId = *b"TST2";
|
||||||
|
|
||||||
|
fn header(number: u64) -> Header {
|
||||||
|
let parent_hash = match number {
|
||||||
|
0 => Default::default(),
|
||||||
|
_ => header(number - 1).hash(),
|
||||||
|
};
|
||||||
|
Header::new(number, H256::from_low_u64_be(0), H256::from_low_u64_be(0), parent_hash, Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_blockchain() -> Blockchain<Block> {
|
||||||
|
let blockchain = Blockchain::<Block>::new();
|
||||||
|
let just0 = Some(Justifications::from((ID1, vec![0])));
|
||||||
|
let just1 = Some(Justifications::from((ID1, vec![1])));
|
||||||
|
let just2 = None;
|
||||||
|
let just3 = Some(Justifications::from((ID1, 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 append_and_retrieve_justifications() {
|
||||||
|
let blockchain = test_blockchain();
|
||||||
|
let last_finalized = blockchain.last_finalized().unwrap();
|
||||||
|
let block = BlockId::Hash(last_finalized);
|
||||||
|
|
||||||
|
blockchain.append_justification(block, (ID2, vec![4])).unwrap();
|
||||||
|
let justifications = {
|
||||||
|
let mut just = Justifications::from((ID1, vec![3]));
|
||||||
|
just.append((ID2, vec![4]));
|
||||||
|
just
|
||||||
|
};
|
||||||
|
assert_eq!(blockchain.justifications(block).unwrap(), Some(justifications));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_duplicate_justifications_is_forbidden() {
|
||||||
|
let blockchain = test_blockchain();
|
||||||
|
let last_finalized = blockchain.last_finalized().unwrap();
|
||||||
|
let block = BlockId::Hash(last_finalized);
|
||||||
|
|
||||||
|
blockchain.append_justification(block, (ID2, vec![0])).unwrap();
|
||||||
|
assert!(matches!(
|
||||||
|
blockchain.append_justification(block, (ID2, vec![1])),
|
||||||
|
Err(sp_blockchain::Error::BadJustification(_)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ use sp_consensus::{
|
|||||||
use sc_client_api::{backend::AuxStore, BlockOf};
|
use sc_client_api::{backend::AuxStore, BlockOf};
|
||||||
use sp_blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, ProvideCache, HeaderBackend};
|
use sp_blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, ProvideCache, HeaderBackend};
|
||||||
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
use sp_runtime::{generic::{BlockId, OpaqueDigestItemId}, Justification};
|
use sp_runtime::{generic::{BlockId, OpaqueDigestItemId}, Justifications};
|
||||||
use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor, Zero};
|
use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor, Zero};
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
use sp_core::crypto::Pair;
|
use sp_core::crypto::Pair;
|
||||||
@@ -238,7 +238,7 @@ impl<B: BlockT, C, P, CAW> Verifier<B> for AuraVerifier<C, P, CAW> where
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
mut body: Option<Vec<B::Extrinsic>>,
|
mut body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let mut inherent_data = self.inherent_data_providers
|
let mut inherent_data = self.inherent_data_providers
|
||||||
@@ -317,7 +317,7 @@ impl<B: BlockT, C, P, CAW> Verifier<B> for AuraVerifier<C, P, CAW> where
|
|||||||
let mut import_block = BlockImportParams::new(origin, pre_header);
|
let mut import_block = BlockImportParams::new(origin, pre_header);
|
||||||
import_block.post_digests.push(seal);
|
import_block.post_digests.push(seal);
|
||||||
import_block.body = body;
|
import_block.body = body;
|
||||||
import_block.justification = justification;
|
import_block.justifications = justifications;
|
||||||
import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
import_block.post_hash = Some(hash);
|
import_block.post_hash = Some(hash);
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ use sp_core::crypto::Public;
|
|||||||
use sp_application_crypto::AppKey;
|
use sp_application_crypto::AppKey;
|
||||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, OpaqueDigestItemId}, Justification,
|
generic::{BlockId, OpaqueDigestItemId}, Justifications,
|
||||||
traits::{Block as BlockT, Header, DigestItemFor, Zero},
|
traits::{Block as BlockT, Header, DigestItemFor, Zero},
|
||||||
};
|
};
|
||||||
use sp_api::{ProvideRuntimeApi, NumberFor};
|
use sp_api::{ProvideRuntimeApi, NumberFor};
|
||||||
@@ -1097,15 +1097,15 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: Block::Header,
|
header: Block::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
mut body: Option<Vec<Block::Extrinsic>>,
|
mut body: Option<Vec<Block::Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
trace!(
|
trace!(
|
||||||
target: "babe",
|
target: "babe",
|
||||||
"Verifying origin: {:?} header: {:?} justification: {:?} body: {:?}",
|
"Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}",
|
||||||
origin,
|
origin,
|
||||||
header,
|
header,
|
||||||
justification,
|
justifications,
|
||||||
body,
|
body,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1194,7 +1194,7 @@ where
|
|||||||
let mut import_block = BlockImportParams::new(origin, pre_header);
|
let mut import_block = BlockImportParams::new(origin, pre_header);
|
||||||
import_block.post_digests.push(verified_info.seal);
|
import_block.post_digests.push(verified_info.seal);
|
||||||
import_block.body = body;
|
import_block.body = body;
|
||||||
import_block.justification = justification;
|
import_block.justifications = justifications;
|
||||||
import_block.intermediates.insert(
|
import_block.intermediates.insert(
|
||||||
Cow::from(INTERMEDIATE_KEY),
|
Cow::from(INTERMEDIATE_KEY),
|
||||||
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<dyn Any>,
|
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<dyn Any>,
|
||||||
|
|||||||
@@ -235,12 +235,12 @@ impl Verifier<TestBlock> for TestVerifier {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
mut header: TestHeader,
|
mut header: TestHeader,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<TestExtrinsic>>,
|
body: Option<Vec<TestExtrinsic>>,
|
||||||
) -> Result<(BlockImportParams<TestBlock, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<TestBlock, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
// apply post-sealing mutations (i.e. stripping seal, if desired).
|
// apply post-sealing mutations (i.e. stripping seal, if desired).
|
||||||
(self.mutator)(&mut header, Stage::PostSeal);
|
(self.mutator)(&mut header, Stage::PostSeal);
|
||||||
self.inner.verify(origin, header, justification, body)
|
self.inner.verify(origin, header, justifications, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use sp_consensus::{
|
|||||||
};
|
};
|
||||||
use sp_blockchain::HeaderBackend;
|
use sp_blockchain::HeaderBackend;
|
||||||
use sp_inherents::InherentDataProviders;
|
use sp_inherents::InherentDataProviders;
|
||||||
use sp_runtime::{traits::Block as BlockT, Justification};
|
use sp_runtime::{traits::Block as BlockT, Justifications, ConsensusEngineId};
|
||||||
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
|
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
|
||||||
use sc_transaction_pool::txpool;
|
use sc_transaction_pool::txpool;
|
||||||
use std::{sync::Arc, marker::PhantomData};
|
use std::{sync::Arc, marker::PhantomData};
|
||||||
@@ -49,6 +49,9 @@ pub use self::{
|
|||||||
};
|
};
|
||||||
use sp_api::{ProvideRuntimeApi, TransactionFor};
|
use sp_api::{ProvideRuntimeApi, TransactionFor};
|
||||||
|
|
||||||
|
/// The `ConsensusEngineId` of Manual Seal.
|
||||||
|
pub const MANUAL_SEAL_ENGINE_ID: ConsensusEngineId = [b'm', b'a', b'n', b'l'];
|
||||||
|
|
||||||
/// The verifier for the manual seal engine; instantly finalizes.
|
/// The verifier for the manual seal engine; instantly finalizes.
|
||||||
struct ManualSealVerifier;
|
struct ManualSealVerifier;
|
||||||
|
|
||||||
@@ -57,11 +60,11 @@ impl<B: BlockT> Verifier<B> for ManualSealVerifier {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let mut import_params = BlockImportParams::new(origin, header);
|
let mut import_params = BlockImportParams::new(origin, header);
|
||||||
import_params.justification = justification;
|
import_params.justifications = justifications;
|
||||||
import_params.body = body;
|
import_params.body = body;
|
||||||
import_params.finalized = false;
|
import_params.finalized = false;
|
||||||
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
@@ -193,6 +196,7 @@ pub async fn run_manual_seal<B, BI, CB, E, C, A, SC, CS>(
|
|||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
EngineCommand::FinalizeBlock { hash, sender, justification } => {
|
EngineCommand::FinalizeBlock { hash, sender, justification } => {
|
||||||
|
let justification = justification.map(|j| (MANUAL_SEAL_ENGINE_ID, j));
|
||||||
finalize_block(
|
finalize_block(
|
||||||
FinalizeBlockParams {
|
FinalizeBlockParams {
|
||||||
hash,
|
hash,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use futures::{
|
|||||||
SinkExt
|
SinkExt
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::EncodedJustification;
|
||||||
pub use self::gen_client::Client as ManualSealClient;
|
pub use self::gen_client::Client as ManualSealClient;
|
||||||
|
|
||||||
/// Future's type for jsonrpc
|
/// Future's type for jsonrpc
|
||||||
@@ -62,7 +62,7 @@ pub enum EngineCommand<Hash> {
|
|||||||
/// sender to report errors/success to the rpc.
|
/// sender to report errors/success to the rpc.
|
||||||
sender: Sender<()>,
|
sender: Sender<()>,
|
||||||
/// finalization justification
|
/// finalization justification
|
||||||
justification: Option<Justification>,
|
justification: Option<EncodedJustification>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ pub trait ManualSealApi<Hash> {
|
|||||||
fn finalize_block(
|
fn finalize_block(
|
||||||
&self,
|
&self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
justification: Option<Justification>
|
justification: Option<EncodedJustification>
|
||||||
) -> FutureResult<bool>;
|
) -> FutureResult<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ impl<Hash: Send + 'static> ManualSealApi<Hash> for ManualSeal<Hash> {
|
|||||||
Box::new(future.map_err(Error::from).compat())
|
Box::new(future.map_err(Error::from).compat())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_block(&self, hash: Hash, justification: Option<Justification>) -> FutureResult<bool> {
|
fn finalize_block(&self, hash: Hash, justification: Option<EncodedJustification>) -> FutureResult<bool> {
|
||||||
let mut sink = self.import_block_channel.clone();
|
let mut sink = self.import_block_channel.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
let (sender, receiver) = oneshot::channel();
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ use parking_lot::Mutex;
|
|||||||
use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents};
|
use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents};
|
||||||
use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId};
|
use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId};
|
||||||
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
use sp_runtime::{Justification, RuntimeString};
|
use sp_runtime::{Justifications, RuntimeString};
|
||||||
use sp_runtime::generic::{BlockId, Digest, DigestItem};
|
use sp_runtime::generic::{BlockId, Digest, DigestItem};
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
@@ -457,7 +457,7 @@ impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let hash = header.hash();
|
let hash = header.hash();
|
||||||
@@ -470,7 +470,7 @@ impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
|
|||||||
let mut import_block = BlockImportParams::new(origin, checked_header);
|
let mut import_block = BlockImportParams::new(origin, checked_header);
|
||||||
import_block.post_digests.push(seal);
|
import_block.post_digests.push(seal);
|
||||||
import_block.body = body;
|
import_block.body = body;
|
||||||
import_block.justification = justification;
|
import_block.justifications = justifications;
|
||||||
import_block.intermediates.insert(
|
import_block.intermediates.insert(
|
||||||
Cow::from(INTERMEDIATE_KEY),
|
Cow::from(INTERMEDIATE_KEY),
|
||||||
Box::new(intermediate) as Box<dyn Any>
|
Box::new(intermediate) as Box<dyn Any>
|
||||||
|
|||||||
@@ -955,7 +955,8 @@ mod tests {
|
|||||||
let block0 = insert_header_with_configuration_change(&backend, 0, Default::default(), None, config0);
|
let block0 = insert_header_with_configuration_change(&backend, 0, Default::default(), None, config0);
|
||||||
let config1 = Some(ChangesTrieConfiguration::new(2, 6));
|
let config1 = Some(ChangesTrieConfiguration::new(2, 6));
|
||||||
let block1 = insert_header_with_configuration_change(&backend, 1, block0, changes(0), config1);
|
let block1 = insert_header_with_configuration_change(&backend, 1, block0, changes(0), config1);
|
||||||
backend.finalize_block(BlockId::Number(1), Some(vec![42])).unwrap();
|
let just1 = Some((*b"TEST", vec![42]));
|
||||||
|
backend.finalize_block(BlockId::Number(1), just1).unwrap();
|
||||||
let config2 = Some(ChangesTrieConfiguration::new(2, 7));
|
let config2 = Some(ChangesTrieConfiguration::new(2, 7));
|
||||||
let block2 = insert_header_with_configuration_change(&backend, 2, block1, changes(1), config2);
|
let block2 = insert_header_with_configuration_change(&backend, 2, block1, changes(1), config2);
|
||||||
let config2_1 = Some(ChangesTrieConfiguration::new(2, 8));
|
let config2_1 = Some(ChangesTrieConfiguration::new(2, 8));
|
||||||
|
|||||||
+100
-16
@@ -57,10 +57,11 @@ use sc_client_api::{
|
|||||||
UsageInfo, MemoryInfo, IoInfo, MemorySize,
|
UsageInfo, MemoryInfo, IoInfo, MemorySize,
|
||||||
backend::{NewBlockState, PrunableStateChangesTrieStorage, ProvideChtRoots},
|
backend::{NewBlockState, PrunableStateChangesTrieStorage, ProvideChtRoots},
|
||||||
leaves::{LeafSet, FinalizationDisplaced}, cht,
|
leaves::{LeafSet, FinalizationDisplaced}, cht,
|
||||||
|
utils::is_descendent_of,
|
||||||
};
|
};
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
Result as ClientResult, Error as ClientError,
|
Result as ClientResult, Error as ClientError,
|
||||||
well_known_cache_keys, HeaderBackend,
|
well_known_cache_keys, Backend as _, HeaderBackend,
|
||||||
};
|
};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use hash_db::Prefix;
|
use hash_db::Prefix;
|
||||||
@@ -70,7 +71,7 @@ use sp_core::{Hasher, ChangesTrieConfiguration};
|
|||||||
use sp_core::offchain::OffchainOverlayedChange;
|
use sp_core::offchain::OffchainOverlayedChange;
|
||||||
use sp_core::storage::{well_known_keys, ChildInfo};
|
use sp_core::storage::{well_known_keys, ChildInfo};
|
||||||
use sp_arithmetic::traits::Saturating;
|
use sp_arithmetic::traits::Saturating;
|
||||||
use sp_runtime::{generic::{DigestItem, BlockId}, Justification, Storage};
|
use sp_runtime::{generic::{DigestItem, BlockId}, Justification, Justifications, Storage};
|
||||||
use sp_runtime::traits::{
|
use sp_runtime::traits::{
|
||||||
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor,
|
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, SaturatedConversion, HashFor,
|
||||||
};
|
};
|
||||||
@@ -351,7 +352,7 @@ pub(crate) mod columns {
|
|||||||
pub const KEY_LOOKUP: u32 = 3;
|
pub const KEY_LOOKUP: u32 = 3;
|
||||||
pub const HEADER: u32 = 4;
|
pub const HEADER: u32 = 4;
|
||||||
pub const BODY: u32 = 5;
|
pub const BODY: u32 = 5;
|
||||||
pub const JUSTIFICATION: u32 = 6;
|
pub const JUSTIFICATIONS: u32 = 6;
|
||||||
pub const CHANGES_TRIE: u32 = 7;
|
pub const CHANGES_TRIE: u32 = 7;
|
||||||
pub const AUX: u32 = 8;
|
pub const AUX: u32 = 8;
|
||||||
/// Offchain workers local storage
|
/// Offchain workers local storage
|
||||||
@@ -363,7 +364,7 @@ pub(crate) mod columns {
|
|||||||
|
|
||||||
struct PendingBlock<Block: BlockT> {
|
struct PendingBlock<Block: BlockT> {
|
||||||
header: Block::Header,
|
header: Block::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<Block::Extrinsic>>,
|
body: Option<Vec<Block::Extrinsic>>,
|
||||||
leaf_state: NewBlockState,
|
leaf_state: NewBlockState,
|
||||||
}
|
}
|
||||||
@@ -535,8 +536,8 @@ impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justification(&self, id: BlockId<Block>) -> ClientResult<Option<Justification>> {
|
fn justifications(&self, id: BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
||||||
match read_db(&*self.db, columns::KEY_LOOKUP, columns::JUSTIFICATION, id)? {
|
match read_db(&*self.db, columns::KEY_LOOKUP, columns::JUSTIFICATIONS, id)? {
|
||||||
Some(justification) => match Decode::decode(&mut &justification[..]) {
|
Some(justification) => match Decode::decode(&mut &justification[..]) {
|
||||||
Ok(justification) => Ok(Some(justification)),
|
Ok(justification) => Ok(Some(justification)),
|
||||||
Err(err) => return Err(sp_blockchain::Error::Backend(
|
Err(err) => return Err(sp_blockchain::Error::Backend(
|
||||||
@@ -716,7 +717,7 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block> for Bloc
|
|||||||
&mut self,
|
&mut self,
|
||||||
header: Block::Header,
|
header: Block::Header,
|
||||||
body: Option<Vec<Block::Extrinsic>>,
|
body: Option<Vec<Block::Extrinsic>>,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
leaf_state: NewBlockState,
|
leaf_state: NewBlockState,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||||
@@ -726,7 +727,7 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block> for Bloc
|
|||||||
self.pending_block = Some(PendingBlock {
|
self.pending_block = Some(PendingBlock {
|
||||||
header,
|
header,
|
||||||
body,
|
body,
|
||||||
justification,
|
justifications,
|
||||||
leaf_state,
|
leaf_state,
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1130,9 +1131,9 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
|
|
||||||
if let Some(justification) = justification {
|
if let Some(justification) = justification {
|
||||||
transaction.set_from_vec(
|
transaction.set_from_vec(
|
||||||
columns::JUSTIFICATION,
|
columns::JUSTIFICATIONS,
|
||||||
&utils::number_and_hash_to_lookup_key(number, hash)?,
|
&utils::number_and_hash_to_lookup_key(number, hash)?,
|
||||||
justification.encode(),
|
Justifications::from(justification).encode(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok((*hash, number, false, true))
|
Ok((*hash, number, false, true))
|
||||||
@@ -1241,8 +1242,8 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(justification) = pending_block.justification {
|
if let Some(justifications) = pending_block.justifications {
|
||||||
transaction.set_from_vec(columns::JUSTIFICATION, &lookup_key, justification.encode());
|
transaction.set_from_vec(columns::JUSTIFICATIONS, &lookup_key, justifications.encode());
|
||||||
}
|
}
|
||||||
|
|
||||||
if number.is_zero() {
|
if number.is_zero() {
|
||||||
@@ -1409,7 +1410,7 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
|
|
||||||
self.storage.db.commit(transaction)?;
|
self.storage.db.commit(transaction)?;
|
||||||
|
|
||||||
// Apply all in-memory state shanges.
|
// Apply all in-memory state changes.
|
||||||
// Code beyond this point can't fail.
|
// Code beyond this point can't fail.
|
||||||
|
|
||||||
if let Some((
|
if let Some((
|
||||||
@@ -1668,6 +1669,50 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_justification(
|
||||||
|
&self,
|
||||||
|
block: BlockId<Block>,
|
||||||
|
justification: Justification,
|
||||||
|
) -> ClientResult<()> {
|
||||||
|
let mut transaction: Transaction<DbHash> = Transaction::new();
|
||||||
|
let hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
||||||
|
let header = self.blockchain.expect_header(block)?;
|
||||||
|
let number = *header.number();
|
||||||
|
|
||||||
|
// Check if the block is finalized first.
|
||||||
|
let is_descendent_of = is_descendent_of(&self.blockchain, None);
|
||||||
|
let last_finalized = self.blockchain.last_finalized()?;
|
||||||
|
|
||||||
|
// We can do a quick check first, before doing a proper but more expensive check
|
||||||
|
if number > self.blockchain.info().finalized_number
|
||||||
|
|| (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?)
|
||||||
|
{
|
||||||
|
return Err(ClientError::NotInFinalizedChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
let justifications =
|
||||||
|
if let Some(mut stored_justifications) = self.blockchain.justifications(block)? {
|
||||||
|
if !stored_justifications.append(justification) {
|
||||||
|
return Err(ClientError::BadJustification(
|
||||||
|
"Duplicate consensus engine ID".into()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
stored_justifications
|
||||||
|
} else {
|
||||||
|
Justifications::from(justification)
|
||||||
|
};
|
||||||
|
|
||||||
|
transaction.set_from_vec(
|
||||||
|
columns::JUSTIFICATIONS,
|
||||||
|
&utils::number_and_hash_to_lookup_key(number, hash)?,
|
||||||
|
justifications.encode(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.storage.db.commit(transaction)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>> {
|
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>> {
|
||||||
Some(&self.changes_tries_storage)
|
Some(&self.changes_tries_storage)
|
||||||
}
|
}
|
||||||
@@ -1918,12 +1963,16 @@ pub(crate) mod tests {
|
|||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sc_client_api::backend::{Backend as BTrait, BlockImportOperation as Op};
|
use sc_client_api::backend::{Backend as BTrait, BlockImportOperation as Op};
|
||||||
use sc_client_api::blockchain::Backend as BLBTrait;
|
use sc_client_api::blockchain::Backend as BLBTrait;
|
||||||
|
use sp_runtime::ConsensusEngineId;
|
||||||
use sp_runtime::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
|
use sp_runtime::testing::{Header, Block as RawBlock, ExtrinsicWrapper};
|
||||||
use sp_runtime::traits::{Hash, BlakeTwo256};
|
use sp_runtime::traits::{Hash, BlakeTwo256};
|
||||||
use sp_runtime::generic::DigestItem;
|
use sp_runtime::generic::DigestItem;
|
||||||
use sp_state_machine::{TrieMut, TrieDBMut};
|
use sp_state_machine::{TrieMut, TrieDBMut};
|
||||||
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
||||||
|
|
||||||
|
const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0";
|
||||||
|
const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1";
|
||||||
|
|
||||||
pub(crate) type Block = RawBlock<ExtrinsicWrapper<u64>>;
|
pub(crate) type Block = RawBlock<ExtrinsicWrapper<u64>>;
|
||||||
|
|
||||||
pub fn prepare_changes(changes: Vec<(Vec<u8>, Vec<u8>)>) -> (H256, MemoryDB<BlakeTwo256>) {
|
pub fn prepare_changes(changes: Vec<(Vec<u8>, Vec<u8>)>) -> (H256, MemoryDB<BlakeTwo256>) {
|
||||||
@@ -2511,12 +2560,47 @@ pub(crate) mod tests {
|
|||||||
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
|
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
|
||||||
let _ = insert_header(&backend, 1, block0, None, Default::default());
|
let _ = insert_header(&backend, 1, block0, None, Default::default());
|
||||||
|
|
||||||
let justification = Some(vec![1, 2, 3]);
|
let justification = Some((CONS0_ENGINE_ID, vec![1, 2, 3]));
|
||||||
backend.finalize_block(BlockId::Number(1), justification.clone()).unwrap();
|
backend.finalize_block(BlockId::Number(1), justification.clone()).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
backend.blockchain().justification(BlockId::Number(1)).unwrap(),
|
backend.blockchain().justifications(BlockId::Number(1)).unwrap(),
|
||||||
justification,
|
justification.map(Justifications::from),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append_justification_to_finalized_block() {
|
||||||
|
use sc_client_api::blockchain::{Backend as BlockChainBackend};
|
||||||
|
|
||||||
|
let backend = Backend::<Block>::new_test(10, 10);
|
||||||
|
|
||||||
|
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
|
||||||
|
let _ = insert_header(&backend, 1, block0, None, Default::default());
|
||||||
|
|
||||||
|
let just0 = (CONS0_ENGINE_ID, vec![1, 2, 3]);
|
||||||
|
backend.finalize_block(
|
||||||
|
BlockId::Number(1),
|
||||||
|
Some(just0.clone().into()),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let just1 = (CONS1_ENGINE_ID, vec![4, 5]);
|
||||||
|
backend.append_justification(BlockId::Number(1), just1.clone()).unwrap();
|
||||||
|
|
||||||
|
let just2 = (CONS1_ENGINE_ID, vec![6, 7]);
|
||||||
|
assert!(matches!(
|
||||||
|
backend.append_justification(BlockId::Number(1), just2),
|
||||||
|
Err(ClientError::BadJustification(_))
|
||||||
|
));
|
||||||
|
|
||||||
|
let justifications = {
|
||||||
|
let mut just = Justifications::from(just0);
|
||||||
|
just.append(just1);
|
||||||
|
just
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
backend.blockchain().justifications(BlockId::Number(1)).unwrap(),
|
||||||
|
Some(justifications),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,17 +23,19 @@ use std::io::{Read, Write, ErrorKind};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use sp_runtime::traits::Block as BlockT;
|
use sp_runtime::traits::Block as BlockT;
|
||||||
use crate::utils::DatabaseType;
|
use crate::{columns, utils::DatabaseType};
|
||||||
use kvdb_rocksdb::{Database, DatabaseConfig};
|
use kvdb_rocksdb::{Database, DatabaseConfig};
|
||||||
|
use codec::Encode;
|
||||||
|
|
||||||
/// Version file name.
|
/// Version file name.
|
||||||
const VERSION_FILE_NAME: &'static str = "db_version";
|
const VERSION_FILE_NAME: &'static str = "db_version";
|
||||||
|
|
||||||
/// Current db version.
|
/// Current db version.
|
||||||
const CURRENT_VERSION: u32 = 2;
|
const CURRENT_VERSION: u32 = 3;
|
||||||
|
|
||||||
/// Number of columns in v1.
|
/// Number of columns in v1.
|
||||||
const V1_NUM_COLUMNS: u32 = 11;
|
const V1_NUM_COLUMNS: u32 = 11;
|
||||||
|
const V2_NUM_COLUMNS: u32 = 12;
|
||||||
|
|
||||||
/// Upgrade database to current version.
|
/// Upgrade database to current version.
|
||||||
pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||||
@@ -42,7 +44,11 @@ pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_bl
|
|||||||
let db_version = current_version(db_path)?;
|
let db_version = current_version(db_path)?;
|
||||||
match db_version {
|
match db_version {
|
||||||
0 => Err(sp_blockchain::Error::Backend(format!("Unsupported database version: {}", db_version)))?,
|
0 => Err(sp_blockchain::Error::Backend(format!("Unsupported database version: {}", db_version)))?,
|
||||||
1 => migrate_1_to_2::<Block>(db_path, db_type)?,
|
1 => {
|
||||||
|
migrate_1_to_2::<Block>(db_path, db_type)?;
|
||||||
|
migrate_2_to_3::<Block>(db_path, db_type)?
|
||||||
|
},
|
||||||
|
2 => migrate_2_to_3::<Block>(db_path, db_type)?,
|
||||||
CURRENT_VERSION => (),
|
CURRENT_VERSION => (),
|
||||||
_ => Err(sp_blockchain::Error::Backend(format!("Future database version: {}", db_version)))?,
|
_ => Err(sp_blockchain::Error::Backend(format!("Future database version: {}", db_version)))?,
|
||||||
}
|
}
|
||||||
@@ -62,6 +68,31 @@ fn migrate_1_to_2<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_b
|
|||||||
db.add_column().map_err(db_err)
|
db.add_column().map_err(db_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migration from version2 to version3:
|
||||||
|
/// - The format of the stored Justification changed to support multiple Justifications.
|
||||||
|
fn migrate_2_to_3<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||||
|
let db_path = db_path.to_str()
|
||||||
|
.ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?;
|
||||||
|
let db_cfg = DatabaseConfig::with_columns(V2_NUM_COLUMNS);
|
||||||
|
let db = Database::open(&db_cfg, db_path).map_err(db_err)?;
|
||||||
|
|
||||||
|
// Get all the keys we need to update
|
||||||
|
let keys: Vec<_> = db.iter(columns::JUSTIFICATIONS).map(|entry| entry.0).collect();
|
||||||
|
|
||||||
|
// Read and update each entry
|
||||||
|
let mut transaction = db.transaction();
|
||||||
|
for key in keys {
|
||||||
|
if let Some(justification) = db.get(columns::JUSTIFICATIONS, &key).map_err(db_err)? {
|
||||||
|
// Tag each Justification with the hardcoded ID for GRANDPA. Avoid the dependency on the GRANDPA crate
|
||||||
|
let justifications = sp_runtime::Justifications::from((*b"FRNK", justification));
|
||||||
|
transaction.put_vec(columns::JUSTIFICATIONS, &key, justifications.encode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.write(transaction).map_err(db_err)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads current database version from the file at given path.
|
/// Reads current database version from the file at given path.
|
||||||
/// If the file does not exist returns 0.
|
/// If the file does not exist returns 0.
|
||||||
fn current_version(path: &Path) -> sp_blockchain::Result<u32> {
|
fn current_version(path: &Path) -> sp_blockchain::Result<u32> {
|
||||||
@@ -141,8 +172,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn upgrade_from_1_to_2_works() {
|
fn upgrade_to_3_works() {
|
||||||
for version_from_file in &[None, Some(1)] {
|
for version_from_file in &[None, Some(1), Some(2)] {
|
||||||
let db_dir = tempfile::TempDir::new().unwrap();
|
let db_dir = tempfile::TempDir::new().unwrap();
|
||||||
let db_path = db_dir.path();
|
let db_path = db_dir.path();
|
||||||
create_db(db_path, *version_from_file);
|
create_db(db_path, *version_from_file);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use sc_finality_grandpa::{
|
|||||||
find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification,
|
find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification,
|
||||||
};
|
};
|
||||||
use sp_blockchain::Backend as BlockchainBackend;
|
use sp_blockchain::Backend as BlockchainBackend;
|
||||||
use sp_finality_grandpa::{AuthorityList, SetId};
|
use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, NumberFor},
|
traits::{Block as BlockT, NumberFor},
|
||||||
@@ -108,11 +108,14 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let justification = backend.justification(BlockId::Number(*last_block))?.expect(
|
let justification = backend
|
||||||
"header is last in set and contains standard change signal; \
|
.justifications(BlockId::Number(*last_block))?
|
||||||
must have justification; \
|
.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID))
|
||||||
qed.",
|
.expect(
|
||||||
);
|
"header is last in set and contains standard change signal; \
|
||||||
|
must have justification; \
|
||||||
|
qed.",
|
||||||
|
);
|
||||||
|
|
||||||
let justification = GrandpaJustification::<Block>::decode(&mut &justification[..])?;
|
let justification = GrandpaJustification::<Block>::decode(&mut &justification[..])?;
|
||||||
|
|
||||||
@@ -171,6 +174,7 @@ mod tests {
|
|||||||
use sc_finality_grandpa::{AuthoritySetChanges, GrandpaJustification};
|
use sc_finality_grandpa::{AuthoritySetChanges, GrandpaJustification};
|
||||||
use sp_blockchain::HeaderBackend;
|
use sp_blockchain::HeaderBackend;
|
||||||
use sp_consensus::BlockOrigin;
|
use sp_consensus::BlockOrigin;
|
||||||
|
use sp_finality_grandpa::GRANDPA_ENGINE_ID;
|
||||||
use sp_keyring::Ed25519Keyring;
|
use sp_keyring::Ed25519Keyring;
|
||||||
use sp_runtime::{generic::BlockId, traits::Header as _};
|
use sp_runtime::{generic::BlockId, traits::Header as _};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -272,7 +276,10 @@ mod tests {
|
|||||||
let justification = GrandpaJustification::from_commit(&client, 42, commit).unwrap();
|
let justification = GrandpaJustification::from_commit(&client, 42, commit).unwrap();
|
||||||
|
|
||||||
client
|
client
|
||||||
.finalize_block(BlockId::Hash(target_hash), Some(justification.encode()))
|
.finalize_block(
|
||||||
|
BlockId::Hash(target_hash),
|
||||||
|
Some((GRANDPA_ENGINE_ID, justification.encode()))
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
authority_set_changes.push((current_set_id, n));
|
authority_set_changes.push((current_set_id, n));
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ use crate::justification::GrandpaJustification;
|
|||||||
use crate::until_imported::UntilVoteTargetImported;
|
use crate::until_imported::UntilVoteTargetImported;
|
||||||
use crate::voting_rule::VotingRule;
|
use crate::voting_rule::VotingRule;
|
||||||
use sp_finality_grandpa::{
|
use sp_finality_grandpa::{
|
||||||
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof,
|
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GRANDPA_ENGINE_ID,
|
||||||
GrandpaApi, RoundNumber, SetId,
|
GrandpaApi, RoundNumber, SetId,
|
||||||
};
|
};
|
||||||
use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64};
|
use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64};
|
||||||
@@ -1326,10 +1326,13 @@ where
|
|||||||
|
|
||||||
// ideally some handle to a synchronization oracle would be used
|
// ideally some handle to a synchronization oracle would be used
|
||||||
// to avoid unconditionally notifying.
|
// to avoid unconditionally notifying.
|
||||||
client.apply_finality(import_op, BlockId::Hash(hash), justification, true).map_err(|e| {
|
let justification = justification.map(|j| (GRANDPA_ENGINE_ID, j.clone()));
|
||||||
warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e);
|
client
|
||||||
e
|
.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!(
|
||||||
telemetry;
|
telemetry;
|
||||||
CONSENSUS_INFO;
|
CONSENSUS_INFO;
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ use finality_grandpa::BlockNumberOps;
|
|||||||
use parity_scale_codec::{Encode, Decode};
|
use parity_scale_codec::{Encode, Decode};
|
||||||
use sp_blockchain::{Backend as BlockchainBackend, Error as ClientError, Result as ClientResult};
|
use sp_blockchain::{Backend as BlockchainBackend, Error as ClientError, Result as ClientResult};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
Justification, generic::BlockId,
|
EncodedJustification, generic::BlockId,
|
||||||
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
|
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
|
||||||
};
|
};
|
||||||
use sc_client_api::backend::Backend;
|
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::authorities::AuthoritySetChanges;
|
||||||
use crate::justification::GrandpaJustification;
|
use crate::justification::GrandpaJustification;
|
||||||
@@ -190,8 +190,10 @@ where
|
|||||||
// Get the Justification stored at the last block of the set
|
// Get the Justification stored at the last block of the set
|
||||||
let last_block_for_set_id = BlockId::Number(last_block_for_set);
|
let last_block_for_set_id = BlockId::Number(last_block_for_set);
|
||||||
let justification =
|
let justification =
|
||||||
if let Some(justification) = blockchain.justification(last_block_for_set_id)? {
|
if let Some(grandpa_justification) = blockchain.justifications(last_block_for_set_id)?
|
||||||
justification
|
.and_then(|justifications| justifications.into_justification(GRANDPA_ENGINE_ID))
|
||||||
|
{
|
||||||
|
grandpa_justification
|
||||||
} else {
|
} else {
|
||||||
trace!(
|
trace!(
|
||||||
target: "afg",
|
target: "afg",
|
||||||
@@ -257,7 +259,7 @@ pub trait ProvableJustification<Header: HeaderT>: Encode + Decode {
|
|||||||
|
|
||||||
/// Decode and verify justification.
|
/// Decode and verify justification.
|
||||||
fn decode_and_verify(
|
fn decode_and_verify(
|
||||||
justification: &Justification,
|
justification: &EncodedJustification,
|
||||||
set_id: u64,
|
set_id: u64,
|
||||||
authorities: &[(AuthorityId, u64)],
|
authorities: &[(AuthorityId, u64)],
|
||||||
) -> ClientResult<Self> {
|
) -> ClientResult<Self> {
|
||||||
@@ -286,6 +288,7 @@ pub(crate) mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::authorities::AuthoritySetChanges;
|
use crate::authorities::AuthoritySetChanges;
|
||||||
use sp_core::crypto::Public;
|
use sp_core::crypto::Public;
|
||||||
|
use sp_runtime::Justifications;
|
||||||
use sp_finality_grandpa::AuthorityList;
|
use sp_finality_grandpa::AuthorityList;
|
||||||
use sc_client_api::NewBlockState;
|
use sc_client_api::NewBlockState;
|
||||||
use sc_client_api::in_mem::Blockchain as InMemoryBlockchain;
|
use sc_client_api::in_mem::Blockchain as InMemoryBlockchain;
|
||||||
@@ -330,31 +333,27 @@ pub(crate) mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_blockchain() -> InMemoryBlockchain<Block> {
|
fn test_blockchain() -> InMemoryBlockchain<Block> {
|
||||||
|
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||||
let blockchain = InMemoryBlockchain::<Block>::new();
|
let blockchain = InMemoryBlockchain::<Block>::new();
|
||||||
blockchain
|
let just0 = Some(Justifications::from((ID, vec![0])));
|
||||||
.insert(header(0).hash(), header(0), Some(vec![0]), None, NewBlockState::Final)
|
let just1 = Some(Justifications::from((ID, vec![1])));
|
||||||
.unwrap();
|
let just2 = None;
|
||||||
blockchain
|
let just3 = Some(Justifications::from((ID, vec![3])));
|
||||||
.insert(header(1).hash(), header(1), Some(vec![1]), None, NewBlockState::Final)
|
blockchain.insert(header(0).hash(), header(0), just0, None, NewBlockState::Final).unwrap();
|
||||||
.unwrap();
|
blockchain.insert(header(1).hash(), header(1), just1, None, NewBlockState::Final).unwrap();
|
||||||
blockchain
|
blockchain.insert(header(2).hash(), header(2), just2, None, NewBlockState::Best).unwrap();
|
||||||
.insert(header(2).hash(), header(2), None, None, NewBlockState::Best)
|
blockchain.insert(header(3).hash(), header(3), just3, None, NewBlockState::Final).unwrap();
|
||||||
.unwrap();
|
|
||||||
blockchain
|
|
||||||
.insert(header(3).hash(), header(3), Some(vec![3]), None, NewBlockState::Final)
|
|
||||||
.unwrap();
|
|
||||||
blockchain
|
blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn finality_proof_fails_if_no_more_last_finalized_blocks() {
|
fn finality_proof_fails_if_no_more_last_finalized_blocks() {
|
||||||
|
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||||
let blockchain = test_blockchain();
|
let blockchain = test_blockchain();
|
||||||
blockchain
|
let just1 = Some(Justifications::from((ID, vec![1])));
|
||||||
.insert(header(4).hash(), header(4), Some(vec![1]), None, NewBlockState::Best)
|
let just2 = Some(Justifications::from((ID, vec![2])));
|
||||||
.unwrap();
|
blockchain.insert(header(4).hash(), header(4), just1, None, NewBlockState::Best).unwrap();
|
||||||
blockchain
|
blockchain.insert(header(5).hash(), header(5), just2, None, NewBlockState::Best).unwrap();
|
||||||
.insert(header(5).hash(), header(5), Some(vec![2]), None, NewBlockState::Best)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||||
authority_set_changes.append(0, 5);
|
authority_set_changes.append(0, 5);
|
||||||
@@ -430,22 +429,17 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn finality_proof_using_authority_set_changes_fails_with_undefined_start() {
|
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 blockchain = test_blockchain();
|
||||||
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
||||||
let just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
let grandpa_just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||||
let just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
let grandpa_just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||||
blockchain
|
let just4 = Some(Justifications::from((ID, grandpa_just4)));
|
||||||
.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final)
|
let just7 = Some(Justifications::from((ID, grandpa_just7)));
|
||||||
.unwrap();
|
blockchain.insert(header(4).hash(), header(4), just4, None, NewBlockState::Final).unwrap();
|
||||||
blockchain
|
blockchain.insert(header(5).hash(), header(5), None, None, NewBlockState::Final).unwrap();
|
||||||
.insert(header(5).hash(), header(5), None, None, NewBlockState::Final)
|
blockchain.insert(header(6).hash(), header(6), None, None, NewBlockState::Final).unwrap();
|
||||||
.unwrap();
|
blockchain.insert(header(7).hash(), header(7), just7, 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();
|
|
||||||
|
|
||||||
// We have stored the correct block number for the relevant set, but as we are missing the
|
// 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.
|
// block for the preceding set the start is not well-defined.
|
||||||
@@ -462,22 +456,17 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn finality_proof_using_authority_set_changes_works() {
|
fn finality_proof_using_authority_set_changes_works() {
|
||||||
|
use sp_finality_grandpa::GRANDPA_ENGINE_ID as ID;
|
||||||
let blockchain = test_blockchain();
|
let blockchain = test_blockchain();
|
||||||
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
let auth = vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)];
|
||||||
let just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
let grandpa_just4 = TestJustification((0, auth.clone()), vec![4]).encode();
|
||||||
let just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
let grandpa_just7 = TestJustification((1, auth.clone()), vec![7]).encode();
|
||||||
blockchain
|
let just4 = Some(Justifications::from((ID, grandpa_just4)));
|
||||||
.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final)
|
let just7 = Some(Justifications::from((ID, grandpa_just7.clone())));
|
||||||
.unwrap();
|
blockchain.insert(header(4).hash(), header(4), just4, None, NewBlockState::Final) .unwrap();
|
||||||
blockchain
|
blockchain.insert(header(5).hash(), header(5), None, None, NewBlockState::Final) .unwrap();
|
||||||
.insert(header(5).hash(), header(5), None, None, NewBlockState::Final)
|
blockchain.insert(header(6).hash(), header(6), None, None, NewBlockState::Final).unwrap();
|
||||||
.unwrap();
|
blockchain.insert(header(7).hash(), header(7), just7, 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 mut authority_set_changes = AuthoritySetChanges::empty();
|
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||||
authority_set_changes.append(0, 4);
|
authority_set_changes.append(0, 4);
|
||||||
@@ -497,7 +486,7 @@ pub(crate) mod tests {
|
|||||||
proof_of_5,
|
proof_of_5,
|
||||||
FinalityProof {
|
FinalityProof {
|
||||||
block: header(7).hash(),
|
block: header(7).hash(),
|
||||||
justification: just7,
|
justification: grandpa_just7,
|
||||||
unknown_headers: vec![header(6)],
|
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)?;
|
let pending_changes = self.make_authorities_changes(&mut block, hash, initial_sync)?;
|
||||||
|
|
||||||
// we don't want to finalize on `inner.import_block`
|
// 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 import_result = (&*self.inner).import_block(block, new_cache);
|
||||||
|
|
||||||
let mut imported_aux = {
|
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
|
// 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
|
// the block to make sure we request them from `sync` which will ensure they'll be
|
||||||
// applied in-order.
|
// 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) => {
|
Some(justification) => {
|
||||||
let import_res = self.import_justification(
|
let import_res = self.import_justification(
|
||||||
hash,
|
hash,
|
||||||
number,
|
number,
|
||||||
justification,
|
(GRANDPA_ENGINE_ID, justification),
|
||||||
needs_justification,
|
needs_justification,
|
||||||
initial_sync,
|
initial_sync,
|
||||||
);
|
);
|
||||||
@@ -637,8 +640,14 @@ where
|
|||||||
enacts_change: bool,
|
enacts_change: bool,
|
||||||
initial_sync: bool,
|
initial_sync: bool,
|
||||||
) -> Result<(), ConsensusError> {
|
) -> 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(
|
let justification = GrandpaJustification::decode_and_verify_finalizes(
|
||||||
&justification,
|
&justification.1,
|
||||||
(hash, number),
|
(hash, number),
|
||||||
self.authority_set.set_id(),
|
self.authority_set.set_id(),
|
||||||
&self.authority_set.current_authorities(),
|
&self.authority_set.current_authorities(),
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ use sp_consensus::{
|
|||||||
import_queue::BoxJustificationImport,
|
import_queue::BoxJustificationImport,
|
||||||
};
|
};
|
||||||
use std::{collections::{HashMap, HashSet}, pin::Pin};
|
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_runtime::generic::{BlockId, DigestItem};
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||||
@@ -369,7 +369,7 @@ fn finalize_3_voters_no_observers() {
|
|||||||
|
|
||||||
// normally there's no justification for finalized blocks
|
// normally there's no justification for finalized blocks
|
||||||
assert!(
|
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",
|
"Extra justification for block#1",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -613,7 +613,7 @@ fn justification_is_generated_periodically() {
|
|||||||
// when block#32 (justification_period) is finalized, justification
|
// when block#32 (justification_period) is finalized, justification
|
||||||
// is required => generated
|
// is required => generated
|
||||||
for i in 0..3 {
|
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
|
// the first 3 peers are grandpa voters and therefore have already finalized
|
||||||
// block 21 and stored a justification
|
// block 21 and stored a justification
|
||||||
for i in 0..3 {
|
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
|
// the last peer should get the justification by syncing from other peers
|
||||||
futures::executor::block_on(futures::future::poll_fn(move |cx| {
|
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);
|
net.lock().poll(cx);
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
@@ -868,7 +868,7 @@ fn test_bad_justification() {
|
|||||||
let block = || {
|
let block = || {
|
||||||
let block = block.clone();
|
let block = block.clone();
|
||||||
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
|
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.body = Some(block.extrinsics);
|
||||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
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
|
// we import the block with justification attached
|
||||||
let mut import = BlockImportParams::new(BlockOrigin::File, block.header);
|
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.body = Some(block.extrinsics);
|
||||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
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
|
// the justification should be imported and available from the client
|
||||||
assert!(
|
assert!(
|
||||||
client.justification(&BlockId::Hash(block_hash)).unwrap().is_some(),
|
client.justifications(&BlockId::Hash(block_hash)).unwrap().is_some(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use sp_state_machine::{
|
|||||||
Backend as StateBackend, TrieBackend, InMemoryBackend, ChangesTrieTransaction,
|
Backend as StateBackend, TrieBackend, InMemoryBackend, ChangesTrieTransaction,
|
||||||
StorageCollection, ChildStorageCollection,
|
StorageCollection, ChildStorageCollection,
|
||||||
};
|
};
|
||||||
use sp_runtime::{generic::BlockId, Justification, Storage};
|
use sp_runtime::{generic::BlockId, Justification, Justifications, Storage};
|
||||||
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header, HashFor};
|
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero, Header, HashFor};
|
||||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
@@ -199,6 +199,14 @@ impl<S, Block> ClientBackend<Block> for Backend<S, HashFor<Block>>
|
|||||||
self.blockchain.storage().finalize_header(block)
|
self.blockchain.storage().finalize_header(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_justification(
|
||||||
|
&self,
|
||||||
|
_block: BlockId<Block>,
|
||||||
|
_justification: Justification,
|
||||||
|
) -> ClientResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn blockchain(&self) -> &Blockchain<S> {
|
fn blockchain(&self) -> &Blockchain<S> {
|
||||||
&self.blockchain
|
&self.blockchain
|
||||||
}
|
}
|
||||||
@@ -278,7 +286,7 @@ impl<S, Block> BlockImportOperation<Block> for ImportOperation<Block, S>
|
|||||||
&mut self,
|
&mut self,
|
||||||
header: Block::Header,
|
header: Block::Header,
|
||||||
_body: Option<Vec<Block::Extrinsic>>,
|
_body: Option<Vec<Block::Extrinsic>>,
|
||||||
_justification: Option<Justification>,
|
_justifications: Option<Justifications>,
|
||||||
state: NewBlockState,
|
state: NewBlockState,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
self.leaf_state = state;
|
self.leaf_state = state;
|
||||||
@@ -356,7 +364,7 @@ impl<S, Block> BlockImportOperation<Block> for ImportOperation<Block, S>
|
|||||||
fn mark_finalized(
|
fn mark_finalized(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: BlockId<Block>,
|
block: BlockId<Block>,
|
||||||
_justification: Option<Justification>,
|
_justifications: Option<Justification>,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
self.finalized_blocks.push(block);
|
self.finalized_blocks.push(block);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use sp_runtime::{Justification, generic::BlockId};
|
use sp_runtime::{Justifications, generic::BlockId};
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
|
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
|
||||||
|
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
@@ -109,7 +109,7 @@ impl<S, Block> BlockchainBackend<Block> for Blockchain<S> where Block: BlockT, S
|
|||||||
Err(ClientError::NotAvailableOnLightClient)
|
Err(ClientError::NotAvailableOnLightClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justification(&self, _id: BlockId<Block>) -> ClientResult<Option<Justification>> {
|
fn justifications(&self, _id: BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
Err(ClientError::NotAvailableOnLightClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ use libp2p::swarm::{
|
|||||||
use log::debug;
|
use log::debug;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}};
|
use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}};
|
||||||
use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justification};
|
use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justifications};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::{HashSet, VecDeque},
|
collections::{HashSet, VecDeque},
|
||||||
@@ -84,7 +84,7 @@ pub struct Behaviour<B: BlockT> {
|
|||||||
/// Event generated by `Behaviour`.
|
/// Event generated by `Behaviour`.
|
||||||
pub enum BehaviourOut<B: BlockT> {
|
pub enum BehaviourOut<B: BlockT> {
|
||||||
BlockImport(BlockOrigin, Vec<IncomingBlock<B>>),
|
BlockImport(BlockOrigin, Vec<IncomingBlock<B>>),
|
||||||
JustificationImport(Origin, B::Hash, NumberFor<B>, Justification),
|
JustificationImport(Origin, B::Hash, NumberFor<B>, Justifications),
|
||||||
|
|
||||||
/// Started a random iterative Kademlia discovery query.
|
/// Started a random iterative Kademlia discovery query.
|
||||||
RandomKademliaStarted(ProtocolId),
|
RandomKademliaStarted(ProtocolId),
|
||||||
|
|||||||
@@ -275,12 +275,28 @@ impl<B: BlockT> BlockRequestHandler<B> {
|
|||||||
let number = *header.number();
|
let number = *header.number();
|
||||||
let hash = header.hash();
|
let hash = header.hash();
|
||||||
let parent_hash = *header.parent_hash();
|
let parent_hash = *header.parent_hash();
|
||||||
let justification = if get_justification {
|
let justifications = if get_justification {
|
||||||
self.client.justification(&BlockId::Hash(hash))?
|
self.client.justifications(&BlockId::Hash(hash))?
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let is_empty_justification = justification.as_ref().map(|j| j.is_empty()).unwrap_or(false);
|
|
||||||
|
// TODO: In a follow up PR tracked by https://github.com/paritytech/substrate/issues/8172
|
||||||
|
// we want to send/receive all justifications.
|
||||||
|
// For now we keep compatibility by selecting precisely the GRANDPA one, and not just
|
||||||
|
// the first one. When sending we could have just taken the first one, since we don't
|
||||||
|
// expect there to be any other kind currently, but when receiving we need to add the
|
||||||
|
// engine ID tag.
|
||||||
|
// The ID tag is hardcoded here to avoid depending on the GRANDPA crate, and will be
|
||||||
|
// removed when resolving the above issue.
|
||||||
|
let justification = justifications.and_then(|just| just.into_justification(*b"FRNK"));
|
||||||
|
|
||||||
|
let is_empty_justification = justification
|
||||||
|
.as_ref()
|
||||||
|
.map(|j| j.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let justification = justification.unwrap_or_default();
|
||||||
|
|
||||||
let body = if get_body {
|
let body = if get_body {
|
||||||
match self.client.block_body(&BlockId::Hash(hash))? {
|
match self.client.block_body(&BlockId::Hash(hash))? {
|
||||||
@@ -306,7 +322,7 @@ impl<B: BlockT> BlockRequestHandler<B> {
|
|||||||
body,
|
body,
|
||||||
receipt: Vec::new(),
|
receipt: Vec::new(),
|
||||||
message_queue: Vec::new(),
|
message_queue: Vec::new(),
|
||||||
justification: justification.unwrap_or_default(),
|
justification,
|
||||||
is_empty_justification,
|
is_empty_justification,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: sp_consensus::BlockOrigin,
|
origin: sp_consensus::BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<sp_runtime::Justification>,
|
justifications: Option<sp_runtime::Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
@@ -79,7 +79,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
let mut import = sp_consensus::BlockImportParams::new(origin, header);
|
let mut import = sp_consensus::BlockImportParams::new(origin, header);
|
||||||
import.body = body;
|
import.body = body;
|
||||||
import.finalized = self.0;
|
import.finalized = self.0;
|
||||||
import.justification = justification;
|
import.justifications = justifications;
|
||||||
import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain);
|
import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain);
|
||||||
Ok((import, maybe_keys))
|
Ok((import, maybe_keys))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ use sp_consensus::{
|
|||||||
block_validation::BlockAnnounceValidator,
|
block_validation::BlockAnnounceValidator,
|
||||||
import_queue::{BlockImportResult, BlockImportError, IncomingBlock, Origin}
|
import_queue::{BlockImportResult, BlockImportError, IncomingBlock, Origin}
|
||||||
};
|
};
|
||||||
use sp_runtime::{generic::BlockId, Justification};
|
use sp_runtime::{
|
||||||
use sp_runtime::traits::{
|
Justifications,
|
||||||
Block as BlockT, Header as HeaderT, NumberFor, Zero, CheckedSub
|
generic::BlockId,
|
||||||
|
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero, CheckedSub},
|
||||||
};
|
};
|
||||||
use sp_arithmetic::traits::SaturatedConversion;
|
use sp_arithmetic::traits::SaturatedConversion;
|
||||||
use sync::{ChainSync, SyncState};
|
use sync::{ChainSync, SyncState};
|
||||||
@@ -612,8 +613,8 @@ impl<B: BlockT> Protocol<B> {
|
|||||||
if request.fields == message::BlockAttributes::JUSTIFICATION {
|
if request.fields == message::BlockAttributes::JUSTIFICATION {
|
||||||
match self.sync.on_block_justification(peer_id, block_response) {
|
match self.sync.on_block_justification(peer_id, block_response) {
|
||||||
Ok(sync::OnBlockJustification::Nothing) => CustomMessageOutcome::None,
|
Ok(sync::OnBlockJustification::Nothing) => CustomMessageOutcome::None,
|
||||||
Ok(sync::OnBlockJustification::Import { peer, hash, number, justification }) =>
|
Ok(sync::OnBlockJustification::Import { peer, hash, number, justifications }) =>
|
||||||
CustomMessageOutcome::JustificationImport(peer, hash, number, justification),
|
CustomMessageOutcome::JustificationImport(peer, hash, number, justifications),
|
||||||
Err(sync::BadPeer(id, repu)) => {
|
Err(sync::BadPeer(id, repu)) => {
|
||||||
self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC);
|
self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC);
|
||||||
self.peerset_handle.report_peer(id, repu);
|
self.peerset_handle.report_peer(id, repu);
|
||||||
@@ -1134,7 +1135,7 @@ fn prepare_block_request<B: BlockT>(
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub enum CustomMessageOutcome<B: BlockT> {
|
pub enum CustomMessageOutcome<B: BlockT> {
|
||||||
BlockImport(BlockOrigin, Vec<IncomingBlock<B>>),
|
BlockImport(BlockOrigin, Vec<IncomingBlock<B>>),
|
||||||
JustificationImport(Origin, B::Hash, NumberFor<B>, Justification),
|
JustificationImport(Origin, B::Hash, NumberFor<B>, Justifications),
|
||||||
/// Notification protocols have been opened with a remote.
|
/// Notification protocols have been opened with a remote.
|
||||||
NotificationStreamOpened {
|
NotificationStreamOpened {
|
||||||
remote: PeerId,
|
remote: PeerId,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ pub struct RemoteReadResponse {
|
|||||||
pub mod generic {
|
pub mod generic {
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use codec::{Encode, Decode, Input, Output};
|
use codec::{Encode, Decode, Input, Output};
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::EncodedJustification;
|
||||||
use super::{
|
use super::{
|
||||||
RemoteReadResponse, Transactions, Direction,
|
RemoteReadResponse, Transactions, Direction,
|
||||||
RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId,
|
RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId,
|
||||||
@@ -233,7 +233,7 @@ pub mod generic {
|
|||||||
/// Block message queue if requested.
|
/// Block message queue if requested.
|
||||||
pub message_queue: Option<Vec<u8>>,
|
pub message_queue: Option<Vec<u8>>,
|
||||||
/// Justification if requested.
|
/// Justification if requested.
|
||||||
pub justification: Option<Justification>,
|
pub justification: Option<EncodedJustification>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifies starting point of a block sequence.
|
/// Identifies starting point of a block sequence.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ use extra_requests::ExtraRequests;
|
|||||||
use libp2p::PeerId;
|
use libp2p::PeerId;
|
||||||
use log::{debug, trace, warn, info, error};
|
use log::{debug, trace, warn, info, error};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
Justification,
|
EncodedJustification, Justifications,
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{
|
traits::{
|
||||||
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, CheckedSub, SaturatedConversion,
|
Block as BlockT, Header as HeaderT, NumberFor, Zero, One, CheckedSub, SaturatedConversion,
|
||||||
@@ -425,7 +425,7 @@ pub enum OnBlockJustification<B: BlockT> {
|
|||||||
peer: PeerId,
|
peer: PeerId,
|
||||||
hash: B::Hash,
|
hash: B::Hash,
|
||||||
number: NumberFor<B>,
|
number: NumberFor<B>,
|
||||||
justification: Justification
|
justifications: Justifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,11 +823,13 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
.drain(self.best_queued_number + One::one())
|
.drain(self.best_queued_number + One::one())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|block_data| {
|
.map(|block_data| {
|
||||||
|
let justifications =
|
||||||
|
legacy_justification_mapping(block_data.block.justification);
|
||||||
IncomingBlock {
|
IncomingBlock {
|
||||||
hash: block_data.block.hash,
|
hash: block_data.block.hash,
|
||||||
header: block_data.block.header,
|
header: block_data.block.header,
|
||||||
body: block_data.block.body,
|
body: block_data.block.body,
|
||||||
justification: block_data.block.justification,
|
justifications,
|
||||||
origin: block_data.origin,
|
origin: block_data.origin,
|
||||||
allow_missing_state: true,
|
allow_missing_state: true,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
@@ -846,7 +848,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
hash: b.hash,
|
hash: b.hash,
|
||||||
header: b.header,
|
header: b.header,
|
||||||
body: b.body,
|
body: b.body,
|
||||||
justification: b.justification,
|
justifications: legacy_justification_mapping(b.justification),
|
||||||
origin: Some(who.clone()),
|
origin: Some(who.clone()),
|
||||||
allow_missing_state: true,
|
allow_missing_state: true,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
@@ -955,7 +957,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
hash: b.hash,
|
hash: b.hash,
|
||||||
header: b.header,
|
header: b.header,
|
||||||
body: b.body,
|
body: b.body,
|
||||||
justification: b.justification,
|
justifications: legacy_justification_mapping(b.justification),
|
||||||
origin: Some(who.clone()),
|
origin: Some(who.clone()),
|
||||||
allow_missing_state: true,
|
allow_missing_state: true,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
@@ -1039,8 +1041,11 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((peer, hash, number, j)) = self.extra_justifications.on_response(who, justification) {
|
if let Some((peer, hash, number, j)) = self
|
||||||
return Ok(OnBlockJustification::Import { peer, hash, number, justification: j })
|
.extra_justifications
|
||||||
|
.on_response(who, legacy_justification_mapping(justification))
|
||||||
|
{
|
||||||
|
return Ok(OnBlockJustification::Import { peer, hash, number, justifications: j })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1597,6 +1602,14 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is purely during a backwards compatible transitionary period and should be removed
|
||||||
|
// once we can assume all nodes can send and receive multiple Justifications
|
||||||
|
// The ID tag is hardcoded here to avoid depending on the GRANDPA crate.
|
||||||
|
// TODO: https://github.com/paritytech/substrate/issues/8172
|
||||||
|
fn legacy_justification_mapping(justification: Option<EncodedJustification>) -> Option<Justifications> {
|
||||||
|
justification.map(|just| (*b"FRNK", just).into())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Metrics {
|
pub(crate) struct Metrics {
|
||||||
pub(crate) queued_blocks: u32,
|
pub(crate) queued_blocks: u32,
|
||||||
@@ -2396,7 +2409,8 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone();
|
let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone();
|
||||||
client.finalize_block(BlockId::Hash(finalized_block.hash()), Some(Vec::new())).unwrap();
|
let just = (*b"TEST", Vec::new());
|
||||||
|
client.finalize_block(BlockId::Hash(finalized_block.hash()), Some(just)).unwrap();
|
||||||
sync.update_chain_info(&info.best_hash, info.best_number);
|
sync.update_chain_info(&info.best_hash, info.best_number);
|
||||||
|
|
||||||
let peer_id1 = PeerId::random();
|
let peer_id1 = PeerId::random();
|
||||||
|
|||||||
@@ -1452,11 +1452,11 @@ impl<B: BlockT + 'static, H: ExHashT> Future for NetworkWorker<B, H> {
|
|||||||
}
|
}
|
||||||
this.import_queue.import_blocks(origin, blocks);
|
this.import_queue.import_blocks(origin, blocks);
|
||||||
},
|
},
|
||||||
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::JustificationImport(origin, hash, nb, justification))) => {
|
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::JustificationImport(origin, hash, nb, justifications))) => {
|
||||||
if let Some(metrics) = this.metrics.as_ref() {
|
if let Some(metrics) = this.metrics.as_ref() {
|
||||||
metrics.import_queue_justifications_submitted.inc();
|
metrics.import_queue_justifications_submitted.inc();
|
||||||
}
|
}
|
||||||
this.import_queue.import_justification(origin, hash, nb, justification);
|
this.import_queue.import_justifications(origin, hash, nb, justifications);
|
||||||
},
|
},
|
||||||
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. })) => {
|
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. })) => {
|
||||||
if let Some(metrics) = this.metrics.as_ref() {
|
if let Some(metrics) = this.metrics.as_ref() {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ fn build_test_full_node(config: config::NetworkConfiguration)
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: sp_consensus::BlockOrigin,
|
origin: sp_consensus::BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<sp_runtime::Justification>,
|
justifications: Option<sp_runtime::Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
@@ -79,7 +79,7 @@ fn build_test_full_node(config: config::NetworkConfiguration)
|
|||||||
let mut import = sp_consensus::BlockImportParams::new(origin, header);
|
let mut import = sp_consensus::BlockImportParams::new(origin, header);
|
||||||
import.body = body;
|
import.body = body;
|
||||||
import.finalized = self.0;
|
import.finalized = self.0;
|
||||||
import.justification = justification;
|
import.justifications = justifications;
|
||||||
import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain);
|
import.fork_choice = Some(sp_consensus::ForkChoiceStrategy::LongestChain);
|
||||||
Ok((import, maybe_keys))
|
Ok((import, maybe_keys))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock<Block>)
|
|||||||
|
|
||||||
let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1);
|
let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1);
|
||||||
let header = client.header(&BlockId::Number(1)).unwrap();
|
let header = client.header(&BlockId::Number(1)).unwrap();
|
||||||
let justification = client.justification(&BlockId::Number(1)).unwrap();
|
let justifications = client.justifications(&BlockId::Number(1)).unwrap();
|
||||||
let peer_id = PeerId::random();
|
let peer_id = PeerId::random();
|
||||||
(client, hash, number, peer_id.clone(), IncomingBlock {
|
(client, hash, number, peer_id.clone(), IncomingBlock {
|
||||||
hash,
|
hash,
|
||||||
header,
|
header,
|
||||||
body: Some(Vec::new()),
|
body: Some(Vec::new()),
|
||||||
justification,
|
justifications,
|
||||||
origin: Some(peer_id.clone()),
|
origin: Some(peer_id.clone()),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ use sp_core::H256;
|
|||||||
use sc_network::config::ProtocolConfig;
|
use sc_network::config::ProtocolConfig;
|
||||||
use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
|
use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::{Justification, Justifications};
|
||||||
use substrate_test_runtime_client::{self, AccountKeyring};
|
use substrate_test_runtime_client::{self, AccountKeyring};
|
||||||
use sc_service::client::Client;
|
use sc_service::client::Client;
|
||||||
pub use sc_network::config::EmptyTransactionPool;
|
pub use sc_network::config::EmptyTransactionPool;
|
||||||
@@ -109,7 +109,7 @@ impl<B: BlockT> Verifier<B> for PassThroughVerifier {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>
|
body: Option<Vec<B::Extrinsic>>
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let maybe_keys = header.digest()
|
let maybe_keys = header.digest()
|
||||||
@@ -120,7 +120,7 @@ impl<B: BlockT> Verifier<B> for PassThroughVerifier {
|
|||||||
let mut import = BlockImportParams::new(origin, header);
|
let mut import = BlockImportParams::new(origin, header);
|
||||||
import.body = body;
|
import.body = body;
|
||||||
import.finalized = self.finalized;
|
import.finalized = self.finalized;
|
||||||
import.justification = justification;
|
import.justifications = justifications;
|
||||||
import.fork_choice = Some(self.fork_choice.clone());
|
import.fork_choice = Some(self.fork_choice.clone());
|
||||||
|
|
||||||
Ok((import, maybe_keys))
|
Ok((import, maybe_keys))
|
||||||
@@ -184,10 +184,10 @@ impl PeersClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn justification(&self, block: &BlockId<Block>) -> ClientResult<Option<Justification>> {
|
pub fn justifications(&self, block: &BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
||||||
match *self {
|
match *self {
|
||||||
PeersClient::Full(ref client, ref _backend) => client.justification(block),
|
PeersClient::Full(ref client, ref _backend) => client.justifications(block),
|
||||||
PeersClient::Light(ref client, ref _backend) => client.justification(block),
|
PeersClient::Light(ref client, ref _backend) => client.justifications(block),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,11 +577,11 @@ impl<B: BlockT> Verifier<B> for VerifierAdapter<B> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>
|
body: Option<Vec<B::Extrinsic>>
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let hash = header.hash();
|
let hash = header.hash();
|
||||||
self.verifier.lock().verify(origin, header, justification, body).map_err(|e| {
|
self.verifier.lock().verify(origin, header, justifications, body).map_err(|e| {
|
||||||
self.failed_verifications.lock().insert(hash, e.clone());
|
self.failed_verifications.lock().insert(hash, e.clone());
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use futures::{Future, executor::block_on};
|
|||||||
use super::*;
|
use super::*;
|
||||||
use sp_consensus::block_validation::Validation;
|
use sp_consensus::block_validation::Validation;
|
||||||
use substrate_test_runtime::Header;
|
use substrate_test_runtime::Header;
|
||||||
|
use sp_runtime::Justifications;
|
||||||
|
|
||||||
fn test_ancestor_search_when_common_is(n: usize) {
|
fn test_ancestor_search_when_common_is(n: usize) {
|
||||||
sp_tracing::try_init_simple();
|
sp_tracing::try_init_simple();
|
||||||
@@ -248,13 +249,14 @@ fn sync_justifications() {
|
|||||||
net.block_until_sync();
|
net.block_until_sync();
|
||||||
|
|
||||||
// there's currently no justification for block #10
|
// there's currently no justification for block #10
|
||||||
assert_eq!(net.peer(0).client().justification(&BlockId::Number(10)).unwrap(), None);
|
assert_eq!(net.peer(0).client().justifications(&BlockId::Number(10)).unwrap(), None);
|
||||||
assert_eq!(net.peer(1).client().justification(&BlockId::Number(10)).unwrap(), None);
|
assert_eq!(net.peer(1).client().justifications(&BlockId::Number(10)).unwrap(), None);
|
||||||
|
|
||||||
// we finalize block #10, #15 and #20 for peer 0 with a justification
|
// we finalize block #10, #15 and #20 for peer 0 with a justification
|
||||||
net.peer(0).client().finalize_block(BlockId::Number(10), Some(Vec::new()), true).unwrap();
|
let just = (*b"FRNK", Vec::new());
|
||||||
net.peer(0).client().finalize_block(BlockId::Number(15), Some(Vec::new()), true).unwrap();
|
net.peer(0).client().finalize_block(BlockId::Number(10), Some(just.clone()), true).unwrap();
|
||||||
net.peer(0).client().finalize_block(BlockId::Number(20), Some(Vec::new()), true).unwrap();
|
net.peer(0).client().finalize_block(BlockId::Number(15), Some(just.clone()), true).unwrap();
|
||||||
|
net.peer(0).client().finalize_block(BlockId::Number(20), Some(just.clone()), true).unwrap();
|
||||||
|
|
||||||
let h1 = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
|
let h1 = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
|
||||||
let h2 = net.peer(1).client().header(&BlockId::Number(15)).unwrap().unwrap();
|
let h2 = net.peer(1).client().header(&BlockId::Number(15)).unwrap().unwrap();
|
||||||
@@ -269,10 +271,20 @@ fn sync_justifications() {
|
|||||||
net.poll(cx);
|
net.poll(cx);
|
||||||
|
|
||||||
for height in (10..21).step_by(5) {
|
for height in (10..21).step_by(5) {
|
||||||
if net.peer(0).client().justification(&BlockId::Number(height)).unwrap() != Some(Vec::new()) {
|
if net
|
||||||
|
.peer(0)
|
||||||
|
.client()
|
||||||
|
.justifications(&BlockId::Number(height))
|
||||||
|
.unwrap() != Some(Justifications::from((*b"FRNK", Vec::new())))
|
||||||
|
{
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
if net.peer(1).client().justification(&BlockId::Number(height)).unwrap() != Some(Vec::new()) {
|
if net
|
||||||
|
.peer(1)
|
||||||
|
.client()
|
||||||
|
.justifications(&BlockId::Number(height))
|
||||||
|
.unwrap() != Some(Justifications::from((*b"FRNK", Vec::new())))
|
||||||
|
{
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,7 +307,8 @@ fn sync_justifications_across_forks() {
|
|||||||
// for both and finalize the small fork instead.
|
// for both and finalize the small fork instead.
|
||||||
net.block_until_sync();
|
net.block_until_sync();
|
||||||
|
|
||||||
net.peer(0).client().finalize_block(BlockId::Hash(f1_best), Some(Vec::new()), true).unwrap();
|
let just = (*b"FRNK", Vec::new());
|
||||||
|
net.peer(0).client().finalize_block(BlockId::Hash(f1_best), Some(just), true).unwrap();
|
||||||
|
|
||||||
net.peer(1).request_justification(&f1_best, 10);
|
net.peer(1).request_justification(&f1_best, 10);
|
||||||
net.peer(1).request_justification(&f2_best, 11);
|
net.peer(1).request_justification(&f2_best, 11);
|
||||||
@@ -303,8 +316,16 @@ fn sync_justifications_across_forks() {
|
|||||||
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
||||||
net.poll(cx);
|
net.poll(cx);
|
||||||
|
|
||||||
if net.peer(0).client().justification(&BlockId::Number(10)).unwrap() == Some(Vec::new()) &&
|
if net
|
||||||
net.peer(1).client().justification(&BlockId::Number(10)).unwrap() == Some(Vec::new())
|
.peer(0)
|
||||||
|
.client()
|
||||||
|
.justifications(&BlockId::Number(10))
|
||||||
|
.unwrap() == Some(Justifications::from((*b"FRNK", Vec::new())))
|
||||||
|
&& net
|
||||||
|
.peer(1)
|
||||||
|
.client()
|
||||||
|
.justifications(&BlockId::Number(10))
|
||||||
|
.unwrap() == Some(Justifications::from((*b"FRNK", Vec::new())))
|
||||||
{
|
{
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} else {
|
||||||
@@ -696,8 +717,9 @@ fn can_sync_to_peers_with_wrong_common_block() {
|
|||||||
net.block_until_connected();
|
net.block_until_connected();
|
||||||
|
|
||||||
// both peers re-org to the same fork without notifying each other
|
// both peers re-org to the same fork without notifying each other
|
||||||
net.peer(0).client().finalize_block(BlockId::Hash(fork_hash), Some(Vec::new()), true).unwrap();
|
let just = Some((*b"FRNK", Vec::new()));
|
||||||
net.peer(1).client().finalize_block(BlockId::Hash(fork_hash), Some(Vec::new()), true).unwrap();
|
net.peer(0).client().finalize_block(BlockId::Hash(fork_hash), just.clone(), true).unwrap();
|
||||||
|
net.peer(1).client().finalize_block(BlockId::Hash(fork_hash), just, true).unwrap();
|
||||||
let final_hash = net.peer(0).push_blocks(1, false);
|
let final_hash = net.peer(0).push_blocks(1, false);
|
||||||
|
|
||||||
net.block_until_sync();
|
net.block_until_sync();
|
||||||
@@ -948,8 +970,8 @@ fn multiple_requests_are_accepted_as_long_as_they_are_not_fulfilled() {
|
|||||||
net.block_until_sync();
|
net.block_until_sync();
|
||||||
|
|
||||||
// there's currently no justification for block #10
|
// there's currently no justification for block #10
|
||||||
assert_eq!(net.peer(0).client().justification(&BlockId::Number(10)).unwrap(), None);
|
assert_eq!(net.peer(0).client().justifications(&BlockId::Number(10)).unwrap(), None);
|
||||||
assert_eq!(net.peer(1).client().justification(&BlockId::Number(10)).unwrap(), None);
|
assert_eq!(net.peer(1).client().justifications(&BlockId::Number(10)).unwrap(), None);
|
||||||
|
|
||||||
let h1 = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
|
let h1 = net.peer(1).client().header(&BlockId::Number(10)).unwrap().unwrap();
|
||||||
|
|
||||||
@@ -967,12 +989,21 @@ fn multiple_requests_are_accepted_as_long_as_they_are_not_fulfilled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block and make the justification available.
|
// Finalize the block and make the justification available.
|
||||||
net.peer(0).client().finalize_block(BlockId::Number(10), Some(Vec::new()), true).unwrap();
|
net.peer(0).client().finalize_block(
|
||||||
|
BlockId::Number(10),
|
||||||
|
Some((*b"FRNK", Vec::new())),
|
||||||
|
true,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
||||||
net.poll(cx);
|
net.poll(cx);
|
||||||
|
|
||||||
if net.peer(1).client().justification(&BlockId::Number(10)).unwrap() != Some(Vec::new()) {
|
if net
|
||||||
|
.peer(1)
|
||||||
|
.client()
|
||||||
|
.justifications(&BlockId::Number(10))
|
||||||
|
.unwrap() != Some(Justifications::from((*b"FRNK", Vec::new())))
|
||||||
|
{
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ impl<Block, Client, F> ChainBackend<Client, Block> for LightChain<Block, Client,
|
|||||||
.compat()
|
.compat()
|
||||||
.map(move |body| Some(SignedBlock {
|
.map(move |body| Some(SignedBlock {
|
||||||
block: Block::new(header, body),
|
block: Block::new(header, body),
|
||||||
justification: None,
|
justifications: None,
|
||||||
}))
|
}))
|
||||||
.map_err(client_err)
|
.map_err(client_err)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ fn should_return_a_block() {
|
|||||||
// Genesis block is not justified
|
// Genesis block is not justified
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
api.block(Some(client.genesis_hash()).into()).wait(),
|
api.block(Some(client.genesis_hash()).into()).wait(),
|
||||||
Ok(Some(SignedBlock { justification: None, .. }))
|
Ok(Some(SignedBlock { justifications: None, .. }))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ fn import_block_to_queue<TBl, TImpQu>(
|
|||||||
hash,
|
hash,
|
||||||
header: Some(header),
|
header: Some(header),
|
||||||
body: Some(extrinsics),
|
body: Some(extrinsics),
|
||||||
justification: signed_block.justification,
|
justifications: signed_block.justifications,
|
||||||
origin: None,
|
origin: None,
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
import_existing: force,
|
import_existing: force,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ use sc_telemetry::{
|
|||||||
SUBSTRATE_INFO,
|
SUBSTRATE_INFO,
|
||||||
};
|
};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
Justification, BuildStorage,
|
Justification, Justifications, BuildStorage,
|
||||||
generic::{BlockId, SignedBlock, DigestItem},
|
generic::{BlockId, SignedBlock, DigestItem},
|
||||||
traits::{
|
traits::{
|
||||||
Block as BlockT, Header as HeaderT, Zero, NumberFor,
|
Block as BlockT, Header as HeaderT, Zero, NumberFor,
|
||||||
@@ -625,7 +625,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
|||||||
let BlockImportParams {
|
let BlockImportParams {
|
||||||
origin,
|
origin,
|
||||||
header,
|
header,
|
||||||
justification,
|
justifications,
|
||||||
post_digests,
|
post_digests,
|
||||||
body,
|
body,
|
||||||
storage_changes,
|
storage_changes,
|
||||||
@@ -637,7 +637,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
|||||||
..
|
..
|
||||||
} = import_block;
|
} = import_block;
|
||||||
|
|
||||||
assert!(justification.is_some() && finalized || justification.is_none());
|
assert!(justifications.is_some() && finalized || justifications.is_none());
|
||||||
|
|
||||||
if !intermediates.is_empty() {
|
if !intermediates.is_empty() {
|
||||||
return Err(Error::IncompletePipeline)
|
return Err(Error::IncompletePipeline)
|
||||||
@@ -665,7 +665,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
|||||||
origin,
|
origin,
|
||||||
hash,
|
hash,
|
||||||
import_headers,
|
import_headers,
|
||||||
justification,
|
justifications,
|
||||||
body,
|
body,
|
||||||
storage_changes,
|
storage_changes,
|
||||||
new_cache,
|
new_cache,
|
||||||
@@ -704,7 +704,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
|||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
hash: Block::Hash,
|
hash: Block::Hash,
|
||||||
import_headers: PrePostHeader<Block::Header>,
|
import_headers: PrePostHeader<Block::Header>,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<Block::Extrinsic>>,
|
body: Option<Vec<Block::Extrinsic>>,
|
||||||
storage_changes: Option<sp_api::StorageChanges<backend::StateBackendFor<B, Block>, Block>>,
|
storage_changes: Option<sp_api::StorageChanges<backend::StateBackendFor<B, Block>, Block>>,
|
||||||
new_cache: HashMap<CacheKeyId, Vec<u8>>,
|
new_cache: HashMap<CacheKeyId, Vec<u8>>,
|
||||||
@@ -820,7 +820,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
|||||||
operation.op.set_block_data(
|
operation.op.set_block_data(
|
||||||
import_headers.post().clone(),
|
import_headers.post().clone(),
|
||||||
body,
|
body,
|
||||||
justification,
|
justifications,
|
||||||
leaf_state,
|
leaf_state,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -1926,9 +1926,9 @@ impl<B, E, Block, RA> BlockBackend<Block> for Client<B, E, Block, RA>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<SignedBlock<Block>>> {
|
fn block(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<SignedBlock<Block>>> {
|
||||||
Ok(match (self.header(id)?, self.body(id)?, self.justification(id)?) {
|
Ok(match (self.header(id)?, self.body(id)?, self.justifications(id)?) {
|
||||||
(Some(header), Some(extrinsics), justification) =>
|
(Some(header), Some(extrinsics), justifications) =>
|
||||||
Some(SignedBlock { block: Block::new(header, extrinsics), justification }),
|
Some(SignedBlock { block: Block::new(header, extrinsics), justifications }),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1937,8 +1937,8 @@ impl<B, E, Block, RA> BlockBackend<Block> for Client<B, E, Block, RA>
|
|||||||
Client::block_status(self, id)
|
Client::block_status(self, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn justification(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<Justification>> {
|
fn justifications(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<Justifications>> {
|
||||||
self.backend.blockchain().justification(*id)
|
self.backend.blockchain().justifications(*id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>> {
|
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>> {
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ use sc_light::{
|
|||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{BlakeTwo256, HashFor, NumberFor},
|
generic::BlockId,
|
||||||
generic::BlockId, traits::{Block as _, Header as HeaderT}, Digest,
|
traits::{BlakeTwo256, Block as _, HashFor, Header as HeaderT, NumberFor},
|
||||||
|
Digest, Justifications,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -377,7 +378,7 @@ fn execution_proof_is_generated_and_checked() {
|
|||||||
remote_client.import_justified(
|
remote_client.import_justified(
|
||||||
BlockOrigin::Own,
|
BlockOrigin::Own,
|
||||||
remote_client.new_block(digest).unwrap().build().unwrap().block,
|
remote_client.new_block(digest).unwrap().build().unwrap().block,
|
||||||
Default::default(),
|
Justifications::from((*b"TEST", Default::default())),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,9 @@ use sc_client_db::{
|
|||||||
};
|
};
|
||||||
use sc_block_builder::BlockBuilderProvider;
|
use sc_block_builder::BlockBuilderProvider;
|
||||||
use sc_service::client::{self, Client, LocalCallExecutor, new_in_mem};
|
use sc_service::client::{self, Client, LocalCallExecutor, new_in_mem};
|
||||||
use sp_runtime::traits::{
|
use sp_runtime::{
|
||||||
BlakeTwo256, Block as BlockT, Header as HeaderT,
|
ConsensusEngineId,
|
||||||
|
traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
|
||||||
};
|
};
|
||||||
use substrate_test_runtime::TestAPI;
|
use substrate_test_runtime::TestAPI;
|
||||||
use sp_state_machine::backend::Backend as _;
|
use sp_state_machine::backend::Backend as _;
|
||||||
@@ -51,12 +52,14 @@ use sp_consensus::{
|
|||||||
};
|
};
|
||||||
use sp_storage::StorageKey;
|
use sp_storage::StorageKey;
|
||||||
use sp_trie::{TrieConfiguration, trie_types::Layout};
|
use sp_trie::{TrieConfiguration, trie_types::Layout};
|
||||||
use sp_runtime::{generic::BlockId, DigestItem};
|
use sp_runtime::{generic::BlockId, DigestItem, Justifications};
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
|
||||||
mod light;
|
mod light;
|
||||||
mod db;
|
mod db;
|
||||||
|
|
||||||
|
const TEST_ENGINE_ID: ConsensusEngineId = *b"TEST";
|
||||||
|
|
||||||
native_executor_instance!(
|
native_executor_instance!(
|
||||||
Executor,
|
Executor,
|
||||||
substrate_test_runtime_client::runtime::api::dispatch,
|
substrate_test_runtime_client::runtime::api::dispatch,
|
||||||
@@ -1016,7 +1019,7 @@ fn import_with_justification() {
|
|||||||
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
||||||
|
|
||||||
// A2 -> A3
|
// A2 -> A3
|
||||||
let justification = vec![1, 2, 3];
|
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
|
||||||
let a3 = client.new_block_at(
|
let a3 = client.new_block_at(
|
||||||
&BlockId::Hash(a2.hash()),
|
&BlockId::Hash(a2.hash()),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
@@ -1030,17 +1033,17 @@ fn import_with_justification() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.justification(&BlockId::Hash(a3.hash())).unwrap(),
|
client.justifications(&BlockId::Hash(a3.hash())).unwrap(),
|
||||||
Some(justification),
|
Some(justification),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.justification(&BlockId::Hash(a1.hash())).unwrap(),
|
client.justifications(&BlockId::Hash(a1.hash())).unwrap(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.justification(&BlockId::Hash(a2.hash())).unwrap(),
|
client.justifications(&BlockId::Hash(a2.hash())).unwrap(),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1088,7 +1091,7 @@ fn importing_diverged_finalized_block_should_trigger_reorg() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// importing B1 as finalized should trigger a re-org and set it as new best
|
// importing B1 as finalized should trigger a re-org and set it as new best
|
||||||
let justification = vec![1, 2, 3];
|
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
|
||||||
client.import_justified(BlockOrigin::Own, b1.clone(), justification).unwrap();
|
client.import_justified(BlockOrigin::Own, b1.clone(), justification).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
||||||
use sp_runtime::generic::BlockId;
|
use sp_runtime::generic::BlockId;
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::Justifications;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync {
|
|||||||
pub trait Backend<Block: BlockT>: HeaderBackend<Block> + HeaderMetadata<Block, Error=Error> {
|
pub trait Backend<Block: BlockT>: HeaderBackend<Block> + HeaderMetadata<Block, Error=Error> {
|
||||||
/// Get block body. Returns `None` if block is not found.
|
/// Get block body. Returns `None` if block is not found.
|
||||||
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
|
fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
|
||||||
/// Get block justification. Returns `None` if justification does not exist.
|
/// Get block justifications. Returns `None` if no justification exists.
|
||||||
fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>>;
|
fn justifications(&self, id: BlockId<Block>) -> Result<Option<Justifications>>;
|
||||||
/// Get last finalized block hash.
|
/// Get last finalized block hash.
|
||||||
fn last_finalized(&self) -> Result<Block::Hash>;
|
fn last_finalized(&self) -> Result<Block::Hash>;
|
||||||
/// Returns data cache reference, if it is enabled on this backend.
|
/// Returns data cache reference, if it is enabled on this backend.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
//! Block import helpers.
|
//! Block import helpers.
|
||||||
|
|
||||||
use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor, HashFor};
|
use sp_runtime::traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor, HashFor};
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::{Justification, Justifications};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -128,8 +128,8 @@ pub struct BlockImportParams<Block: BlockT, Transaction> {
|
|||||||
/// re-executed in a runtime that checks digest equivalence -- the
|
/// re-executed in a runtime that checks digest equivalence -- the
|
||||||
/// post-runtime digests are pushed back on after.
|
/// post-runtime digests are pushed back on after.
|
||||||
pub header: Block::Header,
|
pub header: Block::Header,
|
||||||
/// Justification provided for this block from the outside.
|
/// Justification(s) provided for this block from the outside.
|
||||||
pub justification: Option<Justification>,
|
pub justifications: Option<Justifications>,
|
||||||
/// Digest items that have been added after the runtime for external
|
/// Digest items that have been added after the runtime for external
|
||||||
/// work, like a consensus signature.
|
/// work, like a consensus signature.
|
||||||
pub post_digests: Vec<DigestItemFor<Block>>,
|
pub post_digests: Vec<DigestItemFor<Block>>,
|
||||||
@@ -174,7 +174,7 @@ impl<Block: BlockT, Transaction> BlockImportParams<Block, Transaction> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
origin, header,
|
origin, header,
|
||||||
justification: None,
|
justifications: None,
|
||||||
post_digests: Vec::new(),
|
post_digests: Vec::new(),
|
||||||
body: None,
|
body: None,
|
||||||
storage_changes: None,
|
storage_changes: None,
|
||||||
@@ -219,7 +219,7 @@ impl<Block: BlockT, Transaction> BlockImportParams<Block, Transaction> {
|
|||||||
BlockImportParams {
|
BlockImportParams {
|
||||||
origin: self.origin,
|
origin: self.origin,
|
||||||
header: self.header,
|
header: self.header,
|
||||||
justification: self.justification,
|
justifications: self.justifications,
|
||||||
post_digests: self.post_digests,
|
post_digests: self.post_digests,
|
||||||
body: self.body,
|
body: self.body,
|
||||||
storage_changes: None,
|
storage_changes: None,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use sp_runtime::{Justification, traits::{Block as BlockT, Header as _, NumberFor}};
|
use sp_runtime::{Justifications, traits::{Block as BlockT, Header as _, NumberFor}};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error as ConsensusError,
|
error::Error as ConsensusError,
|
||||||
@@ -68,8 +68,8 @@ pub struct IncomingBlock<B: BlockT> {
|
|||||||
pub header: Option<<B as BlockT>::Header>,
|
pub header: Option<<B as BlockT>::Header>,
|
||||||
/// Block body if requested.
|
/// Block body if requested.
|
||||||
pub body: Option<Vec<<B as BlockT>::Extrinsic>>,
|
pub body: Option<Vec<<B as BlockT>::Extrinsic>>,
|
||||||
/// Justification if requested.
|
/// Justification(s) if requested.
|
||||||
pub justification: Option<Justification>,
|
pub justifications: Option<Justifications>,
|
||||||
/// The peer, we received this from
|
/// The peer, we received this from
|
||||||
pub origin: Option<Origin>,
|
pub origin: Option<Origin>,
|
||||||
/// Allow importing the block skipping state verification if parent state is missing.
|
/// Allow importing the block skipping state verification if parent state is missing.
|
||||||
@@ -90,7 +90,7 @@ pub trait Verifier<B: BlockT>: Send + Sync {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: B::Header,
|
header: B::Header,
|
||||||
justification: Option<Justification>,
|
justifications: Option<Justifications>,
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
body: Option<Vec<B::Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String>;
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String>;
|
||||||
}
|
}
|
||||||
@@ -102,13 +102,13 @@ pub trait Verifier<B: BlockT>: Send + Sync {
|
|||||||
pub trait ImportQueue<B: BlockT>: Send {
|
pub trait ImportQueue<B: BlockT>: Send {
|
||||||
/// Import bunch of blocks.
|
/// Import bunch of blocks.
|
||||||
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec<IncomingBlock<B>>);
|
fn import_blocks(&mut self, origin: BlockOrigin, blocks: Vec<IncomingBlock<B>>);
|
||||||
/// Import a block justification.
|
/// Import block justifications.
|
||||||
fn import_justification(
|
fn import_justifications(
|
||||||
&mut self,
|
&mut self,
|
||||||
who: Origin,
|
who: Origin,
|
||||||
hash: B::Hash,
|
hash: B::Hash,
|
||||||
number: NumberFor<B>,
|
number: NumberFor<B>,
|
||||||
justification: Justification
|
justifications: Justifications
|
||||||
);
|
);
|
||||||
/// Polls for actions to perform on the network.
|
/// Polls for actions to perform on the network.
|
||||||
///
|
///
|
||||||
@@ -182,8 +182,8 @@ pub(crate) fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction
|
|||||||
) -> Result<BlockImportResult<NumberFor<B>>, BlockImportError> {
|
) -> Result<BlockImportResult<NumberFor<B>>, BlockImportError> {
|
||||||
let peer = block.origin;
|
let peer = block.origin;
|
||||||
|
|
||||||
let (header, justification) = match (block.header, block.justification) {
|
let (header, justifications) = match (block.header, block.justifications) {
|
||||||
(Some(header), justification) => (header, justification),
|
(Some(header), justifications) => (header, justifications),
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
if let Some(ref peer) = peer {
|
if let Some(ref peer) = peer {
|
||||||
debug!(target: "sync", "Header {} was not provided by {} ", block.hash, peer);
|
debug!(target: "sync", "Header {} was not provided by {} ", block.hash, peer);
|
||||||
@@ -238,7 +238,7 @@ pub(crate) fn import_single_block_metered<B: BlockT, V: Verifier<B>, Transaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
let started = wasm_timer::Instant::now();
|
let started = wasm_timer::Instant::now();
|
||||||
let (mut import_block, maybe_keys) = verifier.verify(block_origin, header, justification, block.body)
|
let (mut import_block, maybe_keys) = verifier.verify(block_origin, header, justifications, block.body)
|
||||||
.map_err(|msg| {
|
.map_err(|msg| {
|
||||||
if let Some(ref peer) = peer {
|
if let Some(ref peer) = peer {
|
||||||
trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg);
|
trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
use std::{pin::Pin, time::Duration, marker::PhantomData};
|
use std::{pin::Pin, time::Duration, marker::PhantomData};
|
||||||
use futures::{prelude::*, task::Context, task::Poll};
|
use futures::{prelude::*, task::Context, task::Poll};
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use sp_runtime::{Justification, traits::{Block as BlockT, Header as HeaderT, NumberFor}};
|
use sp_runtime::{Justification, Justifications, traits::{Block as BlockT, Header as HeaderT, NumberFor}};
|
||||||
use sp_utils::mpsc::{TracingUnboundedSender, tracing_unbounded, TracingUnboundedReceiver};
|
use sp_utils::mpsc::{TracingUnboundedSender, tracing_unbounded, TracingUnboundedReceiver};
|
||||||
use prometheus_endpoint::Registry;
|
use prometheus_endpoint::Registry;
|
||||||
|
|
||||||
@@ -112,22 +112,24 @@ impl<B: BlockT, Transaction: Send> ImportQueue<B> for BasicQueue<B, Transaction>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_justification(
|
fn import_justifications(
|
||||||
&mut self,
|
&mut self,
|
||||||
who: Origin,
|
who: Origin,
|
||||||
hash: B::Hash,
|
hash: B::Hash,
|
||||||
number: NumberFor<B>,
|
number: NumberFor<B>,
|
||||||
justification: Justification,
|
justifications: Justifications,
|
||||||
) {
|
) {
|
||||||
let res = self.justification_sender.unbounded_send(
|
for justification in justifications {
|
||||||
worker_messages::ImportJustification(who, hash, number, justification),
|
let res = self.justification_sender.unbounded_send(
|
||||||
);
|
worker_messages::ImportJustification(who, hash, number, justification),
|
||||||
|
|
||||||
if res.is_err() {
|
|
||||||
log::error!(
|
|
||||||
target: "sync",
|
|
||||||
"import_justification: Background import task is no longer alive"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
log::error!(
|
||||||
|
target: "sync",
|
||||||
|
"import_justification: Background import task is no longer alive"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +283,7 @@ impl<B: BlockT> BlockImportWorker<B> {
|
|||||||
who: Origin,
|
who: Origin,
|
||||||
hash: B::Hash,
|
hash: B::Hash,
|
||||||
number: NumberFor<B>,
|
number: NumberFor<B>,
|
||||||
justification: Justification
|
justification: Justification,
|
||||||
) {
|
) {
|
||||||
let started = wasm_timer::Instant::now();
|
let started = wasm_timer::Instant::now();
|
||||||
let success = self.justification_import.as_mut().map(|justification_import| {
|
let success = self.justification_import.as_mut().map(|justification_import| {
|
||||||
@@ -442,7 +444,7 @@ mod tests {
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
header: Header,
|
header: Header,
|
||||||
_justification: Option<Justification>,
|
_justifications: Option<Justifications>,
|
||||||
_body: Option<Vec<Extrinsic>>,
|
_body: Option<Vec<Extrinsic>>,
|
||||||
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
Ok((BlockImportParams::new(origin, header), None))
|
Ok((BlockImportParams::new(origin, header), None))
|
||||||
@@ -541,7 +543,7 @@ mod tests {
|
|||||||
hash,
|
hash,
|
||||||
header: Some(header),
|
header: Some(header),
|
||||||
body: None,
|
body: None,
|
||||||
justification: None,
|
justifications: None,
|
||||||
origin: None,
|
origin: None,
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
@@ -554,12 +556,11 @@ mod tests {
|
|||||||
|
|
||||||
let mut import_justification = || {
|
let mut import_justification = || {
|
||||||
let hash = Hash::random();
|
let hash = Hash::random();
|
||||||
|
|
||||||
block_on(finality_sender.send(worker_messages::ImportJustification(
|
block_on(finality_sender.send(worker_messages::ImportJustification(
|
||||||
libp2p::PeerId::random(),
|
libp2p::PeerId::random(),
|
||||||
hash,
|
hash,
|
||||||
1,
|
1,
|
||||||
Vec::new(),
|
(*b"TEST", Vec::new()),
|
||||||
)))
|
)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use crate::traits::{
|
|||||||
self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf,
|
self, Member, Block as BlockT, Header as HeaderT, MaybeSerialize, MaybeMallocSizeOf,
|
||||||
NumberFor,
|
NumberFor,
|
||||||
};
|
};
|
||||||
use crate::Justification;
|
use crate::Justifications;
|
||||||
|
|
||||||
/// Something to identify a block.
|
/// Something to identify a block.
|
||||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
||||||
@@ -112,5 +112,5 @@ pub struct SignedBlock<Block> {
|
|||||||
/// Full block.
|
/// Full block.
|
||||||
pub block: Block,
|
pub block: Block,
|
||||||
/// Block justification.
|
/// Block justification.
|
||||||
pub justification: Option<Justification>,
|
pub justifications: Option<Justifications>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,65 @@ pub use either::Either;
|
|||||||
/// the block itself would allow swapping justifications to change the block's hash
|
/// the block itself would allow swapping justifications to change the block's hash
|
||||||
/// (and thus fork the chain). Sending a `Justification` alongside a block instead
|
/// (and thus fork the chain). Sending a `Justification` alongside a block instead
|
||||||
/// bypasses this problem.
|
/// bypasses this problem.
|
||||||
pub type Justification = Vec<u8>;
|
///
|
||||||
|
/// Each justification is provided as an encoded blob, and is tagged with an ID
|
||||||
|
/// to identify the consensus engine that generated the proof (we might have
|
||||||
|
/// multiple justifications from different engines for the same block).
|
||||||
|
pub type Justification = (ConsensusEngineId, EncodedJustification);
|
||||||
|
|
||||||
|
/// The encoded justification specific to a consensus engine.
|
||||||
|
pub type EncodedJustification = Vec<u8>;
|
||||||
|
|
||||||
|
/// Collection of justifications for a given block, multiple justifications may
|
||||||
|
/// be provided by different consensus engines for the same block.
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
|
||||||
|
pub struct Justifications(Vec<Justification>);
|
||||||
|
|
||||||
|
impl Justifications {
|
||||||
|
/// Return an iterator over the justifications.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &Justification> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a justification. Returns false if a justification with the same
|
||||||
|
/// `ConsensusEngineId` already exists, in which case the justification is
|
||||||
|
/// not inserted.
|
||||||
|
pub fn append(&mut self, justification: Justification) -> bool {
|
||||||
|
if self.get(justification.0).is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.0.push(justification);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the encoded justification for the given consensus engine, if it
|
||||||
|
/// exists.
|
||||||
|
pub fn get(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification> {
|
||||||
|
self.iter().find(|j| j.0 == engine_id).map(|j| &j.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the encoded justification for the given consensus
|
||||||
|
/// engine, if it exists.
|
||||||
|
pub fn into_justification(self, engine_id: ConsensusEngineId) -> Option<EncodedJustification> {
|
||||||
|
self.into_iter().find(|j| j.0 == engine_id).map(|j| j.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Justifications {
|
||||||
|
type Item = Justification;
|
||||||
|
type IntoIter = sp_std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Justification> for Justifications {
|
||||||
|
fn from(justification: Justification) -> Self {
|
||||||
|
Self(vec![justification])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
use traits::{Verify, Lazy};
|
use traits::{Verify, Lazy};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use sp_consensus::{
|
|||||||
BlockImportParams, BlockImport, BlockOrigin, Error as ConsensusError,
|
BlockImportParams, BlockImport, BlockOrigin, Error as ConsensusError,
|
||||||
ForkChoiceStrategy,
|
ForkChoiceStrategy,
|
||||||
};
|
};
|
||||||
use sp_runtime::Justification;
|
use sp_runtime::{Justification, Justifications};
|
||||||
use sp_runtime::traits::{Block as BlockT};
|
use sp_runtime::traits::{Block as BlockT};
|
||||||
use sp_runtime::generic::BlockId;
|
use sp_runtime::generic::BlockId;
|
||||||
use codec::alloc::collections::hash_map::HashMap;
|
use codec::alloc::collections::hash_map::HashMap;
|
||||||
@@ -51,15 +51,14 @@ pub trait ClientBlockImportExt<Block: BlockT>: Sized {
|
|||||||
fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
|
fn import_as_best(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
|
||||||
|
|
||||||
/// Import a block and finalize it.
|
/// Import a block and finalize it.
|
||||||
fn import_as_final(&mut self, origin: BlockOrigin, block: Block)
|
fn import_as_final(&mut self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
|
||||||
-> Result<(), ConsensusError>;
|
|
||||||
|
|
||||||
/// Import block with justification, finalizes block.
|
/// Import block with justification(s), finalizes block.
|
||||||
fn import_justified(
|
fn import_justified(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
block: Block,
|
block: Block,
|
||||||
justification: Justification
|
justifications: Justifications,
|
||||||
) -> Result<(), ConsensusError>;
|
) -> Result<(), ConsensusError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,11 +118,11 @@ impl<Block: BlockT, T, Transaction> ClientBlockImportExt<Block> for std::sync::A
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
block: Block,
|
block: Block,
|
||||||
justification: Justification,
|
justifications: Justifications,
|
||||||
) -> Result<(), ConsensusError> {
|
) -> Result<(), ConsensusError> {
|
||||||
let (header, extrinsics) = block.deconstruct();
|
let (header, extrinsics) = block.deconstruct();
|
||||||
let mut import = BlockImportParams::new(origin, header);
|
let mut import = BlockImportParams::new(origin, header);
|
||||||
import.justification = Some(justification);
|
import.justifications = Some(justifications);
|
||||||
import.body = Some(extrinsics);
|
import.body = Some(extrinsics);
|
||||||
import.finalized = true;
|
import.finalized = true;
|
||||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
@@ -168,11 +167,11 @@ impl<B, E, RA, Block: BlockT> ClientBlockImportExt<Block> for Client<B, E, Block
|
|||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
origin: BlockOrigin,
|
||||||
block: Block,
|
block: Block,
|
||||||
justification: Justification,
|
justifications: Justifications,
|
||||||
) -> Result<(), ConsensusError> {
|
) -> Result<(), ConsensusError> {
|
||||||
let (header, extrinsics) = block.deconstruct();
|
let (header, extrinsics) = block.deconstruct();
|
||||||
let mut import = BlockImportParams::new(origin, header);
|
let mut import = BlockImportParams::new(origin, header);
|
||||||
import.justification = Some(justification);
|
import.justifications = Some(justifications);
|
||||||
import.body = Some(extrinsics);
|
import.body = Some(extrinsics);
|
||||||
import.finalized = true;
|
import.finalized = true;
|
||||||
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
|
|||||||
Reference in New Issue
Block a user