Reject blocks without justification which don't have the best number (#105)

* Initial commit

Forked at: 6c74beab7b
Parent branch: origin/master

* Reject blocks without justification

* Revert "Reject blocks without justification"

This reverts commit ee60e12097939d4ccfe987a71db9a876319ae5ba.

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* CLEANUP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* CLEANUP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* WIP

Forked at: 6c74beab7b
Parent branch: origin/master

* Move HeadData to primitives

* Update network/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update network/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* CLEANUP

Forked at: 6c74beab7b
Parent branch: origin/master

* fix

* CLEANUP

Forked at: 6c74beab7b
Parent branch: origin/master

* messages

* for the greater good

* Update primitives/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update network/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update network/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update network/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Cecile Tonglet
2020-06-03 14:15:52 +02:00
committed by GitHub
parent 6c74beab7b
commit ccf05e5022
6 changed files with 89 additions and 24 deletions
+1
View File
@@ -930,6 +930,7 @@ dependencies = [
name = "cumulus-network" name = "cumulus-network"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cumulus-primitives",
"cumulus-test-runtime", "cumulus-test-runtime",
"futures 0.3.4", "futures 0.3.4",
"log 0.4.8", "log 0.4.8",
+6 -9
View File
@@ -20,7 +20,7 @@ use cumulus_network::{
DelayedBlockAnnounceValidator, JustifiedBlockAnnounceValidator, WaitToAnnounce, DelayedBlockAnnounceValidator, JustifiedBlockAnnounceValidator, WaitToAnnounce,
}; };
use cumulus_primitives::{ use cumulus_primitives::{
inherents::VALIDATION_FUNCTION_PARAMS_IDENTIFIER as VFP_IDENT, HeadData, inherents::VALIDATION_FUNCTION_PARAMS_IDENTIFIER as VFP_IDENT,
validation_function_params::ValidationFunctionParams, validation_function_params::ValidationFunctionParams,
}; };
use cumulus_runtime::ParachainBlockData; use cumulus_runtime::ParachainBlockData;
@@ -56,12 +56,6 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc, time::Duration, pin::Pin};
use parking_lot::Mutex; use parking_lot::Mutex;
/// The head data of the parachain, stored in the relay chain.
#[derive(Decode, Encode, Debug)]
struct HeadData<Block: BlockT> {
header: Block::Header,
}
/// The implementation of the Cumulus `Collator`. /// The implementation of the Cumulus `Collator`.
pub struct Collator<Block: BlockT, PF, BI> { pub struct Collator<Block: BlockT, PF, BI> {
proposer_factory: Arc<Mutex<PF>>, proposer_factory: Arc<Mutex<PF>>,
@@ -340,7 +334,10 @@ where
+ Sync + Sync
+ 'static, + 'static,
Backend: sc_client_api::Backend<Block> + 'static, Backend: sc_client_api::Backend<Block> + 'static,
Client: Finalizer<Block, Backend> + UsageProvider<Block> + Send + Sync + 'static, Client: Finalizer<Block, Backend> + UsageProvider<Block> + HeaderBackend<Block>
+ Send
+ Sync
+ 'static,
{ {
type ParachainContext = Collator<Block, PF, BI>; type ParachainContext = Collator<Block, PF, BI>;
@@ -359,7 +356,7 @@ where
Extrinsic: codec::Codec + Send + Sync + 'static, Extrinsic: codec::Codec + Send + Sync + 'static,
{ {
self.delayed_block_announce_validator.set( self.delayed_block_announce_validator.set(
Box::new(JustifiedBlockAnnounceValidator::new(polkadot_client.clone())), Box::new(JustifiedBlockAnnounceValidator::new(polkadot_client.clone(), self.para_id)),
); );
let follow = let follow =
+3
View File
@@ -19,6 +19,9 @@ polkadot-statement-table = { git = "https://github.com/paritytech/polkadot", bra
polkadot-validation = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" } polkadot-validation = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" }
polkadot-network = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" } polkadot-network = { git = "https://github.com/paritytech/polkadot", branch = "cumulus-branch" }
# cumulus deps
cumulus-primitives = { path = "../primitives" }
# other deps # other deps
codec = { package = "parity-scale-codec", version = "1.3.0", features = [ "derive" ] } codec = { package = "parity-scale-codec", version = "1.3.0", features = [ "derive" ] }
futures = { version = "0.3.1", features = ["compat"] } futures = { version = "0.3.1", features = ["compat"] }
+35 -7
View File
@@ -24,17 +24,19 @@ mod tests;
use sp_api::ProvideRuntimeApi; use sp_api::ProvideRuntimeApi;
use sp_blockchain::{Error as ClientError, HeaderBackend}; use sp_blockchain::{Error as ClientError, HeaderBackend};
use sp_consensus::block_validation::{BlockAnnounceValidator, Validation}; use sp_consensus::block_validation::{BlockAnnounceValidator, Validation};
use sp_runtime::{generic::BlockId, traits::Block as BlockT}; use sp_runtime::{generic::BlockId, traits::{Block as BlockT, Header as HeaderT}};
use polkadot_collator::Network as CollatorNetwork; use polkadot_collator::Network as CollatorNetwork;
use polkadot_network::legacy::gossip::{GossipMessage, GossipStatement}; use polkadot_network::legacy::gossip::{GossipMessage, GossipStatement};
use polkadot_primitives::{ use polkadot_primitives::{
parachain::ParachainHost, parachain::{ParachainHost, Id as ParaId},
Block as PBlock, Hash as PHash, Block as PBlock, Hash as PHash,
}; };
use polkadot_statement_table::{SignedStatement, Statement}; use polkadot_statement_table::{SignedStatement, Statement};
use polkadot_validation::check_statement; use polkadot_validation::check_statement;
use cumulus_primitives::HeadData;
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use futures::{pin_mut, select, StreamExt}; use futures::{pin_mut, select, StreamExt};
use futures::channel::oneshot; use futures::channel::oneshot;
@@ -54,13 +56,15 @@ use parking_lot::Mutex;
pub struct JustifiedBlockAnnounceValidator<B, P> { pub struct JustifiedBlockAnnounceValidator<B, P> {
phantom: PhantomData<B>, phantom: PhantomData<B>,
polkadot_client: Arc<P>, polkadot_client: Arc<P>,
para_id: ParaId,
} }
impl<B, P> JustifiedBlockAnnounceValidator<B, P> { impl<B, P> JustifiedBlockAnnounceValidator<B, P> {
pub fn new(polkadot_client: Arc<P>) -> Self { pub fn new(polkadot_client: Arc<P>, para_id: ParaId) -> Self {
Self { Self {
phantom: Default::default(), phantom: Default::default(),
polkadot_client, polkadot_client,
para_id,
} }
} }
} }
@@ -75,9 +79,34 @@ where
header: &B::Header, header: &B::Header,
mut data: &[u8], mut data: &[u8],
) -> Result<Validation, Box<dyn std::error::Error + Send>> { ) -> Result<Validation, Box<dyn std::error::Error + Send>> {
// If no data is provided the announce is valid. let runtime_api = self.polkadot_client.runtime_api();
let polkadot_info = self.polkadot_client.info();
if data.is_empty() { if data.is_empty() {
return Ok(Validation::Success); // Check if block is equal or higher than best (this requires a justification)
let runtime_api_block_id = BlockId::Hash(polkadot_info.best_hash);
let block_number = header.number();
let local_validation_data = runtime_api
.local_validation_data(&runtime_api_block_id, self.para_id)
.map_err(|e| Box::new(ClientError::Msg(format!("{:?}", e))) as Box<_>)?
.ok_or_else(|| {
Box::new(ClientError::Msg("Could not find parachain head in relay chain".into())) as Box<_>
})?;
let parent_head = HeadData::<B>::decode(&mut &local_validation_data.parent_head.0[..])
.map_err(|e| Box::new(ClientError::Msg(format!("Failed to decode parachain head: {:?}", e))) as Box<_>)?;
let known_best_number = parent_head.header.number();
return Ok(if block_number >= known_best_number {
trace!(
target: "cumulus-network",
"validation failed because a justification is needed if the block at the top of the chain."
);
Validation::Failure
} else {
Validation::Success
});
} }
// Check data is a gossip message. // Check data is a gossip message.
@@ -108,7 +137,7 @@ where
} = gossip_statement; } = gossip_statement;
// Check that the relay chain parent of the block is the relay chain head // Check that the relay chain parent of the block is the relay chain head
let best_number = self.polkadot_client.info().best_number; let best_number = polkadot_info.best_number;
match self.polkadot_client.number(relay_chain_leaf) { match self.polkadot_client.number(relay_chain_leaf) {
Err(err) => { Err(err) => {
@@ -133,7 +162,6 @@ where
}, },
} }
let runtime_api = self.polkadot_client.runtime_api();
let runtime_api_block_id = BlockId::Hash(relay_chain_leaf); let runtime_api_block_id = BlockId::Hash(relay_chain_leaf);
let signing_context = runtime_api let signing_context = runtime_api
.signing_context(&runtime_api_block_id) .signing_context(&runtime_api_block_id)
+35 -8
View File
@@ -48,12 +48,15 @@ fn make_validator_and_client() -> (
let builder = TestClientBuilder::new(); let builder = TestClientBuilder::new();
let client = Arc::new(TestApi::new(Arc::new(builder.build()))); let client = Arc::new(TestApi::new(Arc::new(builder.build())));
(JustifiedBlockAnnounceValidator::new(client.clone()), client) (
JustifiedBlockAnnounceValidator::new(client.clone(), ParaId::from(56)),
client,
)
} }
fn default_header() -> Header { fn default_header() -> Header {
Header { Header {
number: Default::default(), number: 1,
digest: Default::default(), digest: Default::default(),
extrinsics_root: Default::default(), extrinsics_root: Default::default(),
parent_hash: Default::default(), parent_hash: Default::default(),
@@ -91,13 +94,34 @@ fn make_gossip_message_and_header(
} }
#[test] #[test]
fn valid_if_no_data() { fn valid_if_no_data_and_less_than_best_known_number() {
let mut validator = make_validator(); let mut validator = make_validator();
let header = default_header(); let header = Header {
number: 0,
..default_header()
};
let res = validator.validate(&header, &[]);
assert!( assert_eq!(
matches!(validator.validate(&header, &[]), Ok(Validation::Success)), res.unwrap(),
"validating without data is always a success", Validation::Success,
"validating without data with block number < best known number is always a success",
);
}
#[test]
fn invalid_if_no_data_exceeds_best_known_number() {
let mut validator = make_validator();
let header = Header {
number: 1,
..default_header()
};
let res = validator.validate(&header, &[]);
assert_eq!(
res.unwrap(),
Validation::Failure,
"validation fails if no justification and block number >= best known number",
); );
} }
@@ -399,7 +423,10 @@ sp_api::mock_impl_runtime_apis! {
} }
fn local_validation_data(_: ParaId) -> Option<LocalValidationData> { fn local_validation_data(_: ParaId) -> Option<LocalValidationData> {
Some(Default::default()) Some(LocalValidationData {
parent_head: HeadData::<Block> { header: default_header() }.encode().into(),
..Default::default()
})
} }
fn get_heads(_: Vec<<PBlock as BlockT>::Extrinsic>) -> Option<Vec<AbridgedCandidateReceipt>> { fn get_heads(_: Vec<<PBlock as BlockT>::Extrinsic>) -> Option<Vec<AbridgedCandidateReceipt>> {
+9
View File
@@ -20,6 +20,9 @@
pub mod validation_function_params; pub mod validation_function_params;
use codec::{Decode, Encode};
use sp_runtime::traits::Block as BlockT;
/// Identifiers and types related to Cumulus Inherents /// Identifiers and types related to Cumulus Inherents
pub mod inherents { pub mod inherents {
use sp_inherents::InherentIdentifier; use sp_inherents::InherentIdentifier;
@@ -64,3 +67,9 @@ pub trait UpwardMessageSender {
/// Returns an error if sending failed. /// Returns an error if sending failed.
fn send_upward_message(msg: &()) -> Result<(), ()>; fn send_upward_message(msg: &()) -> Result<(), ()>;
} }
/// The head data of the parachain, stored in the relay chain.
#[derive(Decode, Encode, Debug)]
pub struct HeadData<Block: BlockT> {
pub header: Block::Header,
}