Strip out old XCMP primitives (#823)

* WIP

* WIp

* Mostly get tests to compile

* Fix adder collator

* Remove more stuff

* Revert some changes to av store

* Fix av store tests

* Nitpicks

* Restore some things

* Small changes

* Remvoe unused error variants
This commit is contained in:
Ashley
2020-02-13 17:12:05 +01:00
committed by GitHub
parent 5385b9af82
commit 5f9e602af7
30 changed files with 117 additions and 2105 deletions
+1 -11
View File
@@ -28,8 +28,7 @@ use keystore::KeyStorePtr;
use polkadot_primitives::{ use polkadot_primitives::{
Hash, Block, Hash, Block,
parachain::{ parachain::{
Id as ParaId, BlockData, CandidateReceipt, Message, AvailableMessages, ErasureChunk, Id as ParaId, BlockData, CandidateReceipt, ErasureChunk, ParachainHost
ParachainHost,
}, },
}; };
use sp_runtime::traits::{BlakeTwo256, Hash as HashT, HasherFor}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, HasherFor};
@@ -126,10 +125,6 @@ pub struct Data {
pub parachain_id: ParaId, pub parachain_id: ParaId,
/// Block data. /// Block data.
pub block_data: BlockData, pub block_data: BlockData,
/// Outgoing message queues from execution of the block, if any.
///
/// The tuple pairs the message queue root and the queue data.
pub outgoing_queues: Option<AvailableMessages>,
} }
/// Handle to the availability store. /// Handle to the availability store.
@@ -384,9 +379,4 @@ impl Store {
{ {
self.inner.block_data_by_candidate(relay_parent, candidate_hash) self.inner.block_data_by_candidate(relay_parent, candidate_hash)
} }
/// Query message queue data by message queue root hash.
pub fn queue_by_root(&self, queue_root: &Hash) -> Option<Vec<Message>> {
self.inner.queue_by_root(queue_root)
}
} }
+6 -64
View File
@@ -21,9 +21,7 @@ use codec::{Encode, Decode};
use polkadot_erasure_coding::{self as erasure}; use polkadot_erasure_coding::{self as erasure};
use polkadot_primitives::{ use polkadot_primitives::{
Hash, Hash,
parachain::{ parachain::{BlockData, CandidateReceipt, ErasureChunk},
BlockData, CandidateReceipt, Message, ErasureChunk
},
}; };
use log::{trace, warn}; use log::{trace, warn};
@@ -130,18 +128,6 @@ impl Store {
data.block_data.encode() data.block_data.encode()
); );
if let Some(outgoing_queues) = data.outgoing_queues {
// This is kept forever and not pruned.
for (root, messages) in outgoing_queues.0 {
tx.put_vec(
columns::DATA,
root.as_ref(),
messages.encode(),
);
}
}
self.inner.write(tx) self.inner.write(tx)
} }
@@ -287,14 +273,13 @@ impl Store {
columns::DATA, columns::DATA,
&block_data_key(&relay_parent, &receipt.block_data_hash) &block_data_key(&relay_parent, &receipt.block_data_hash)
) { ) {
if let Ok((block_data, outgoing_queues)) = erasure::reconstruct( if let Ok(block_data) = erasure::reconstruct(
n_validators as usize, n_validators as usize,
v.iter().map(|chunk| (chunk.chunk.as_ref(), chunk.index as usize))) { v.iter().map(|chunk| (chunk.chunk.as_ref(), chunk.index as usize))) {
self.make_available(Data { self.make_available(Data {
relay_parent: *relay_parent, relay_parent: *relay_parent,
parachain_id: receipt.parachain_index, parachain_id: receipt.parachain_index,
block_data, block_data,
outgoing_queues,
})?; })?;
} }
} }
@@ -387,11 +372,6 @@ impl Store {
}) })
} }
/// Query message queue data by message queue root hash.
pub fn queue_by_root(&self, queue_root: &Hash) -> Option<Vec<Message>> {
self.query_inner(columns::DATA, queue_root.as_ref())
}
fn block_hash_to_candidate_hash(&self, block_hash: Hash) -> Option<Hash> { fn block_hash_to_candidate_hash(&self, block_hash: Hash) -> Option<Hash> {
self.query_inner(columns::META, &block_to_candidate_key(&block_hash)) self.query_inner(columns::META, &block_to_candidate_key(&block_hash))
} }
@@ -414,8 +394,8 @@ impl Store {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use polkadot_erasure_coding::{self as erasure}; use polkadot_erasure_coding as erasure;
use polkadot_primitives::parachain::{Id as ParaId, AvailableMessages}; use polkadot_primitives::parachain::Id as ParaId;
#[test] #[test]
fn finalization_removes_unneeded() { fn finalization_removes_unneeded() {
@@ -444,14 +424,12 @@ mod tests {
relay_parent, relay_parent,
parachain_id: para_id_1, parachain_id: para_id_1,
block_data: block_data_1.clone(), block_data: block_data_1.clone(),
outgoing_queues: None,
}).unwrap(); }).unwrap();
store.make_available(Data { store.make_available(Data {
relay_parent, relay_parent,
parachain_id: para_id_2, parachain_id: para_id_2,
block_data: block_data_2.clone(), block_data: block_data_2.clone(),
outgoing_queues: None,
}).unwrap(); }).unwrap();
let candidate_1 = CandidateReceipt { let candidate_1 = CandidateReceipt {
@@ -460,7 +438,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: Default::default(), head_data: Default::default(),
parent_head: Default::default(), parent_head: Default::default(),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: block_data_1.hash(), block_data_hash: block_data_1.hash(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -473,7 +450,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: Default::default(), head_data: Default::default(),
parent_head: Default::default(), parent_head: Default::default(),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: block_data_2.hash(), block_data_hash: block_data_2.hash(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -516,34 +492,12 @@ mod tests {
let para_id = 5.into(); let para_id = 5.into();
let block_data = BlockData(vec![1, 2, 3]); let block_data = BlockData(vec![1, 2, 3]);
let message_queue_root_1 = [0x42; 32].into();
let message_queue_root_2 = [0x43; 32].into();
let message_a = Message(vec![1, 2, 3, 4]);
let message_b = Message(vec![4, 5, 6, 7]);
let outgoing_queues = AvailableMessages(vec![
(message_queue_root_1, vec![message_a.clone()]),
(message_queue_root_2, vec![message_b.clone()]),
]);
let store = Store::new_in_memory(); let store = Store::new_in_memory();
store.make_available(Data { store.make_available(Data {
relay_parent, relay_parent,
parachain_id: para_id, parachain_id: para_id,
block_data: block_data.clone(), block_data,
outgoing_queues: Some(outgoing_queues),
}).unwrap(); }).unwrap();
assert_eq!(
store.queue_by_root(&message_queue_root_1),
Some(vec![message_a]),
);
assert_eq!(
store.queue_by_root(&message_queue_root_2),
Some(vec![message_b]),
);
} }
#[test] #[test]
@@ -554,21 +508,10 @@ mod tests {
let block_data_hash = block_data.hash(); let block_data_hash = block_data.hash();
let n_validators = 5; let n_validators = 5;
let message_queue_root_1 = [0x42; 32].into();
let message_queue_root_2 = [0x43; 32].into();
let message_a = Message(vec![1, 2, 3, 4]);
let message_b = Message(vec![5, 6, 7, 8]);
let outgoing_queues = Some(AvailableMessages(vec![
(message_queue_root_1, vec![message_a.clone()]),
(message_queue_root_2, vec![message_b.clone()]),
]));
let erasure_chunks = erasure::obtain_chunks( let erasure_chunks = erasure::obtain_chunks(
n_validators, n_validators,
&block_data, &block_data,
outgoing_queues.as_ref()).unwrap(); ).unwrap();
let branches = erasure::branches(erasure_chunks.as_ref()); let branches = erasure::branches(erasure_chunks.as_ref());
@@ -578,7 +521,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: Default::default(), head_data: Default::default(),
parent_head: Default::default(), parent_head: Default::default(),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: block_data.hash(), block_data_hash: block_data.hash(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
+4 -4
View File
@@ -35,7 +35,7 @@ use consensus_common::{
use polkadot_primitives::{Block, BlockId, Hash}; use polkadot_primitives::{Block, BlockId, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
CandidateReceipt, ParachainHost, ValidatorId, CandidateReceipt, ParachainHost, ValidatorId,
ValidatorPair, AvailableMessages, BlockData, ErasureChunk, ValidatorPair, ErasureChunk, PoVBlock,
}; };
use futures::{prelude::*, future::select, channel::{mpsc, oneshot}, task::{Spawn, SpawnExt}}; use futures::{prelude::*, future::select, channel::{mpsc, oneshot}, task::{Spawn, SpawnExt}};
use keystore::KeyStorePtr; use keystore::KeyStorePtr;
@@ -90,7 +90,7 @@ pub(crate) struct ParachainBlocks {
/// The relay parent of the block these parachain blocks belong to. /// The relay parent of the block these parachain blocks belong to.
pub relay_parent: Hash, pub relay_parent: Hash,
/// The blocks themselves. /// The blocks themselves.
pub blocks: Vec<(CandidateReceipt, Option<(BlockData, AvailableMessages)>)>, pub blocks: Vec<(CandidateReceipt, Option<PoVBlock>)>,
/// A sender to signal the result asynchronously. /// A sender to signal the result asynchronously.
pub result: oneshot::Sender<Result<(), Error>>, pub result: oneshot::Sender<Result<(), Error>>,
} }
@@ -367,7 +367,7 @@ where
runtime_handle: &Handle, runtime_handle: &Handle,
sender: &mut mpsc::UnboundedSender<WorkerMsg>, sender: &mut mpsc::UnboundedSender<WorkerMsg>,
relay_parent: Hash, relay_parent: Hash,
blocks: Vec<(CandidateReceipt, Option<(BlockData, AvailableMessages)>)>, blocks: Vec<(CandidateReceipt, Option<PoVBlock>)>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let hashes: Vec<_> = blocks.iter().map(|(c, _)| c.hash()).collect(); let hashes: Vec<_> = blocks.iter().map(|(c, _)| c.hash()).collect();
@@ -375,7 +375,7 @@ where
for (candidate, block) in blocks.into_iter() { for (candidate, block) in blocks.into_iter() {
let _ = self.availability_store.add_candidate(&candidate); let _ = self.availability_store.add_candidate(&candidate);
if let Some((_block, _msgs)) = block { if let Some(_block) = block {
// Should we be breaking block into chunks here and gossiping it and so on? // Should we be breaking block into chunks here and gossiping it and so on?
} }
+16 -166
View File
@@ -48,7 +48,6 @@ use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::pin::Pin;
use futures::{future, Future, Stream, FutureExt, TryFutureExt, StreamExt, task::Spawn}; use futures::{future, Future, Stream, FutureExt, TryFutureExt, StreamExt, task::Spawn};
use log::warn; use log::warn;
@@ -57,18 +56,17 @@ use sp_core::{Pair, Blake2Hasher};
use polkadot_primitives::{ use polkadot_primitives::{
BlockId, Hash, Block, BlockId, Hash, Block,
parachain::{ parachain::{
self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId, self, BlockData, DutyRoster, HeadData, Id as ParaId,
OutgoingMessages, PoVBlock, Status as ParachainStatus, ValidatorId, CollatorPair, PoVBlock, Status as ParachainStatus, ValidatorId, CollatorPair,
} }
}; };
use polkadot_cli::{ use polkadot_cli::{
ProvideRuntimeApi, AbstractService, ParachainHost, IsKusama, ProvideRuntimeApi, AbstractService, ParachainHost, IsKusama,
service::{self, Roles, SelectChain} service::{self, Roles, SelectChain}
}; };
use polkadot_network::legacy::validation::{LeafWorkParams, ValidationNetwork}; use polkadot_network::legacy::validation::ValidationNetwork;
pub use polkadot_cli::{VersionInfo, load_spec, service::Configuration}; pub use polkadot_cli::{VersionInfo, load_spec, service::Configuration};
pub use polkadot_network::legacy::validation::Incoming;
pub use polkadot_validation::SignedStatement; pub use polkadot_validation::SignedStatement;
pub use polkadot_primitives::parachain::CollatorId; pub use polkadot_primitives::parachain::CollatorId;
pub use sc_network::PeerId; pub use sc_network::PeerId;
@@ -111,14 +109,14 @@ pub struct InvalidHead;
/// Collation errors. /// Collation errors.
#[derive(Debug)] #[derive(Debug)]
pub enum Error<R> { pub enum Error {
/// Error on the relay-chain side of things. /// Error on the relay-chain side of things.
Polkadot(R), Polkadot(String),
/// Error on the collator side of things. /// Error on the collator side of things.
Collator(InvalidHead), Collator(InvalidHead),
} }
impl<R: fmt::Display> fmt::Display for Error<R> { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::Polkadot(ref err) => write!(f, "Polkadot node error: {}", err), Error::Polkadot(ref err) => write!(f, "Polkadot node error: {}", err),
@@ -162,65 +160,41 @@ pub trait BuildParachainContext {
/// This can be implemented through an externally attached service or a stub. /// This can be implemented through an externally attached service or a stub.
/// This is expected to be a lightweight, shared type like an Arc. /// This is expected to be a lightweight, shared type like an Arc.
pub trait ParachainContext: Clone { pub trait ParachainContext: Clone {
type ProduceCandidate: Future<Output = Result<(BlockData, HeadData, OutgoingMessages), InvalidHead>>; type ProduceCandidate: Future<Output = Result<(BlockData, HeadData), InvalidHead>>;
/// Produce a candidate, given the relay parent hash, the latest ingress queue information /// Produce a candidate, given the relay parent hash, the latest ingress queue information
/// and the last parachain head. /// and the last parachain head.
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate(
&mut self, &mut self,
relay_parent: Hash, relay_parent: Hash,
status: ParachainStatus, status: ParachainStatus,
ingress: I,
) -> Self::ProduceCandidate; ) -> Self::ProduceCandidate;
} }
/// Relay chain context needed to collate.
/// This encapsulates a network and local database which may store
/// some of the input.
pub trait RelayChainContext {
type Error: std::fmt::Debug;
/// Future that resolves to the un-routed egress queues of a parachain.
/// The first item is the oldest.
type FutureEgress: Future<Output = Result<ConsolidatedIngress, Self::Error>>;
/// Get un-routed egress queues from a parachain to the local parachain.
fn unrouted_egress(&self, _id: ParaId) -> Self::FutureEgress;
}
/// Produce a candidate for the parachain, with given contexts, parent head, and signing key. /// Produce a candidate for the parachain, with given contexts, parent head, and signing key.
pub async fn collate<R, P>( pub async fn collate<P>(
relay_parent: Hash, relay_parent: Hash,
local_id: ParaId, local_id: ParaId,
parachain_status: ParachainStatus, parachain_status: ParachainStatus,
relay_context: R,
mut para_context: P, mut para_context: P,
key: Arc<CollatorPair>, key: Arc<CollatorPair>,
) )
-> Result<(parachain::Collation, OutgoingMessages), Error<R::Error>> -> Result<parachain::Collation, Error>
where where
R: RelayChainContext,
P: ParachainContext, P: ParachainContext,
P::ProduceCandidate: Send, P::ProduceCandidate: Send,
{ {
let ingress = relay_context.unrouted_egress(local_id).await.map_err(Error::Polkadot)?; let (block_data, head_data) = para_context.produce_candidate(
let (block_data, head_data, mut outgoing) = para_context.produce_candidate(
relay_parent, relay_parent,
parachain_status, parachain_status,
ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg)))
).map_err(Error::Collator).await?; ).map_err(Error::Collator).await?;
let block_data_hash = block_data.hash(); let block_data_hash = block_data.hash();
let signature = key.sign(block_data_hash.as_ref()); let signature = key.sign(block_data_hash.as_ref());
let egress_queue_roots =
polkadot_validation::egress_roots(&mut outgoing.outgoing_messages);
let info = parachain::CollationInfo { let info = parachain::CollationInfo {
parachain_index: local_id, parachain_index: local_id,
collator: key.public(), collator: key.public(),
signature, signature,
egress_queue_roots,
head_data, head_data,
block_data_hash, block_data_hash,
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -230,47 +204,10 @@ pub async fn collate<R, P>(
info, info,
pov: PoVBlock { pov: PoVBlock {
block_data, block_data,
ingress,
}, },
}; };
Ok((collation, outgoing)) Ok(collation)
}
/// Polkadot-api context.
struct ApiContext<P, SP> {
network: Arc<ValidationNetwork<P, SP>>,
parent_hash: Hash,
validators: Vec<ValidatorId>,
}
impl<P: 'static, SP: 'static> RelayChainContext for ApiContext<P, SP> where
P: ProvideRuntimeApi<Block> + Send + Sync,
P::Api: ParachainHost<Block>,
SP: Spawn + Clone + Send + Sync
{
type Error = String;
type FutureEgress = Pin<Box<dyn Future<Output=Result<ConsolidatedIngress, String>> + Send>>;
fn unrouted_egress(&self, _id: ParaId) -> Self::FutureEgress {
let network = self.network.clone();
let parent_hash = self.parent_hash;
let authorities = self.validators.clone();
async move {
// TODO: https://github.com/paritytech/polkadot/issues/253
//
// Fetch ingress and accumulate all unrounted egress
let _session = network.instantiate_leaf_work(LeafWorkParams {
local_session_key: None,
parent_hash,
authorities,
})
.map_err(|e| format!("unable to instantiate validation session: {:?}", e));
Ok(ConsolidatedIngress(Vec::new()))
}.boxed()
}
} }
/// Run the collator node using the given `service`. /// Run the collator node using the given `service`.
@@ -378,7 +315,6 @@ fn run_collator_node<S, P, Extrinsic>(
let client = client.clone(); let client = client.clone();
let key = key.clone(); let key = key.clone();
let parachain_context = parachain_context.clone(); let parachain_context = parachain_context.clone();
let validation_network = validation_network.clone();
let work = future::lazy(move |_| { let work = future::lazy(move |_| {
let api = client.runtime_api(); let api = client.runtime_api();
@@ -395,27 +331,19 @@ fn run_collator_node<S, P, Extrinsic>(
try_fr!(api.duty_roster(&id)), try_fr!(api.duty_roster(&id)),
); );
let context = ApiContext {
network: validation_network,
parent_hash: relay_parent,
validators,
};
let collation_work = collate( let collation_work = collate(
relay_parent, relay_parent,
para_id, para_id,
status, status,
context,
parachain_context, parachain_context,
key, key,
).map_ok(move |(collation, outgoing)| { ).map_ok(move |collation| {
network.with_spec(move |spec, ctx| { network.with_spec(move |spec, ctx| {
let res = spec.add_local_collation( let res = spec.add_local_collation(
ctx, ctx,
relay_parent, relay_parent,
targets, targets,
collation, collation,
outgoing,
); );
tokio::spawn(res.boxed()); tokio::spawn(res.boxed());
@@ -514,104 +442,26 @@ pub fn run_collator<P>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::HashMap; use polkadot_primitives::parachain::FeeSchedule;
use polkadot_primitives::parachain::{TargetedMessage, FeeSchedule};
use keyring::Sr25519Keyring; use keyring::Sr25519Keyring;
use super::*; use super::*;
#[derive(Default, Clone)]
struct DummyRelayChainContext {
ingress: HashMap<ParaId, ConsolidatedIngress>
}
impl RelayChainContext for DummyRelayChainContext {
type Error = ();
type FutureEgress = Box<dyn Future<Output=Result<ConsolidatedIngress,()>> + Unpin>;
fn unrouted_egress(&self, para_id: ParaId) -> Self::FutureEgress {
match self.ingress.get(&para_id) {
Some(ingress) => Box::new(future::ok(ingress.clone())),
None => Box::new(future::pending()),
}
}
}
#[derive(Clone)] #[derive(Clone)]
struct DummyParachainContext; struct DummyParachainContext;
impl ParachainContext for DummyParachainContext { impl ParachainContext for DummyParachainContext {
type ProduceCandidate = future::Ready<Result<(BlockData, HeadData, OutgoingMessages), InvalidHead>>; type ProduceCandidate = future::Ready<Result<(BlockData, HeadData), InvalidHead>>;
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate(
&mut self, &mut self,
_relay_parent: Hash, _relay_parent: Hash,
_status: ParachainStatus, _status: ParachainStatus,
ingress: I,
) -> Self::ProduceCandidate { ) -> Self::ProduceCandidate {
// send messages right back. // send messages right back.
future::ok(( future::ok((
BlockData(vec![1, 2, 3, 4, 5,]), BlockData(vec![1, 2, 3, 4, 5,]),
HeadData(vec![9, 9, 9]), HeadData(vec![9, 9, 9]),
OutgoingMessages {
outgoing_messages: ingress.into_iter().map(|(id, msg)| TargetedMessage {
target: id,
data: msg.0,
}).collect(),
}
)) ))
} }
} }
#[test]
fn collates_correct_queue_roots() {
let mut context = DummyRelayChainContext::default();
let id = ParaId::from(100);
let a = ParaId::from(123);
let b = ParaId::from(456);
let messages_from_a = vec![
Message(vec![1, 1, 1]),
Message(b"helloworld".to_vec()),
];
let messages_from_b = vec![
Message(b"dogglesworth".to_vec()),
Message(b"buy_1_chili_con_carne_here_is_my_cash".to_vec()),
];
let root_a = ::polkadot_validation::message_queue_root(
messages_from_a.iter().map(|msg| &msg.0)
);
let root_b = ::polkadot_validation::message_queue_root(
messages_from_b.iter().map(|msg| &msg.0)
);
context.ingress.insert(id, ConsolidatedIngress(vec![
(b, messages_from_b),
(a, messages_from_a),
]));
let future = collate(
Default::default(),
id,
ParachainStatus {
head_data: HeadData(vec![5]),
balance: 10,
fee_schedule: FeeSchedule {
base: 0,
per_byte: 1,
},
},
context.clone(),
DummyParachainContext,
Arc::new(Sr25519Keyring::Alice.pair().into()),
);
let collation = futures::executor::block_on(future).unwrap().0;
// ascending order by root.
assert_eq!(collation.info.egress_queue_roots, vec![(a, root_a), (b, root_b)]);
}
} }
+5 -9
View File
@@ -27,7 +27,7 @@
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use reed_solomon::galois_16::{self, ReedSolomon}; use reed_solomon::galois_16::{self, ReedSolomon};
use primitives::{Hash as H256, BlakeTwo256, HashT}; use primitives::{Hash as H256, BlakeTwo256, HashT};
use primitives::parachain::{BlockData, AvailableMessages}; use primitives::parachain::BlockData;
use sp_core::Blake2Hasher; use sp_core::Blake2Hasher;
use trie::{EMPTY_PREFIX, MemoryDB, Trie, TrieMut, trie_types::{TrieDBMut, TrieDB}}; use trie::{EMPTY_PREFIX, MemoryDB, Trie, TrieMut, trie_types::{TrieDBMut, TrieDB}};
@@ -125,11 +125,11 @@ fn code_params(n_validators: usize) -> Result<CodeParams, Error> {
/// Obtain erasure-coded chunks, one for each validator. /// Obtain erasure-coded chunks, one for each validator.
/// ///
/// Works only up to 65536 validators, and `n_validators` must be non-zero. /// Works only up to 65536 validators, and `n_validators` must be non-zero.
pub fn obtain_chunks(n_validators: usize, block_data: &BlockData, outgoing: Option<&AvailableMessages>) pub fn obtain_chunks(n_validators: usize, block_data: &BlockData)
-> Result<Vec<Vec<u8>>, Error> -> Result<Vec<Vec<u8>>, Error>
{ {
let params = code_params(n_validators)?; let params = code_params(n_validators)?;
let encoded = (block_data, outgoing).encode(); let encoded = block_data.encode();
if encoded.is_empty() { if encoded.is_empty() {
return Err(Error::BadPayload); return Err(Error::BadPayload);
@@ -151,7 +151,7 @@ pub fn obtain_chunks(n_validators: usize, block_data: &BlockData, outgoing: Opti
/// ///
/// Works only up to 65536 validators, and `n_validators` must be non-zero. /// Works only up to 65536 validators, and `n_validators` must be non-zero.
pub fn reconstruct<'a, I: 'a>(n_validators: usize, chunks: I) pub fn reconstruct<'a, I: 'a>(n_validators: usize, chunks: I)
-> Result<(BlockData, Option<AvailableMessages>), Error> -> Result<BlockData, Error>
where I: IntoIterator<Item=(&'a [u8], usize)> where I: IntoIterator<Item=(&'a [u8], usize)>
{ {
let params = code_params(n_validators)?; let params = code_params(n_validators)?;
@@ -402,11 +402,9 @@ mod tests {
#[test] #[test]
fn round_trip_block_data() { fn round_trip_block_data() {
let block_data = BlockData((0..255).collect()); let block_data = BlockData((0..255).collect());
let ex = Some(AvailableMessages(Vec::new()));
let chunks = obtain_chunks( let chunks = obtain_chunks(
10, 10,
&block_data, &block_data,
ex.as_ref(),
).unwrap(); ).unwrap();
assert_eq!(chunks.len(), 10); assert_eq!(chunks.len(), 10);
@@ -422,18 +420,16 @@ mod tests {
].iter().cloned(), ].iter().cloned(),
).unwrap(); ).unwrap();
assert_eq!(reconstructed, (block_data, ex)); assert_eq!(reconstructed, block_data);
} }
#[test] #[test]
fn construct_valid_branches() { fn construct_valid_branches() {
let block_data = BlockData(vec![2; 256]); let block_data = BlockData(vec![2; 256]);
let ex = Some(AvailableMessages(Vec::new()));
let chunks = obtain_chunks( let chunks = obtain_chunks(
10, 10,
&block_data, &block_data,
ex.as_ref(),
).unwrap(); ).unwrap();
assert_eq!(chunks.len(), 10); assert_eq!(chunks.len(), 10);
+1 -6
View File
@@ -236,15 +236,12 @@ impl CollatorPool {
mod tests { mod tests {
use super::*; use super::*;
use sp_core::crypto::UncheckedInto; use sp_core::crypto::UncheckedInto;
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{CandidateReceipt, BlockData, PoVBlock, HeadData};
CandidateReceipt, BlockData, PoVBlock, HeadData, ConsolidatedIngress,
};
use futures::executor::block_on; use futures::executor::block_on;
fn make_pov(block_data: Vec<u8>) -> PoVBlock { fn make_pov(block_data: Vec<u8>) -> PoVBlock {
PoVBlock { PoVBlock {
block_data: BlockData(block_data), block_data: BlockData(block_data),
ingress: ConsolidatedIngress(Vec::new()),
} }
} }
@@ -294,7 +291,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: [3; 32].into(), block_data_hash: [3; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -324,7 +320,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: [3; 32].into(), block_data_hash: [3; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -93,11 +93,6 @@ impl PeerData {
pub(super) fn knowledge_at_mut(&mut self, parent_hash: &Hash) -> Option<&mut Knowledge> { pub(super) fn knowledge_at_mut(&mut self, parent_hash: &Hash) -> Option<&mut Knowledge> {
self.live.get_mut(parent_hash) self.live.get_mut(parent_hash)
} }
/// Get an iterator over all live leaves of this peer.
pub(super) fn leaves(&self) -> impl Iterator<Item = &Hash> {
self.live.keys()
}
} }
/// An impartial view of what topics and data are valid based on attestation session data. /// An impartial view of what topics and data are valid based on attestation session data.
@@ -1,339 +0,0 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Data structures and synchronous logic for ICMP message gossip.
//!
//! The parent-module documentation describes some rationale of the general
//! gossip protocol design.
//!
//! The ICMP message-routing gossip works according to those rationale.
//!
//! In this protocol, we perform work under 4 conditions:
//! ### 1. Upon observation of a new leaf in the block-DAG.
//!
//! We first communicate the best leaves to our neighbors in the gossip graph
//! by the means of a neighbor packet. Then, we query to discover the trie roots
//! of all un-routed message queues from the perspective of each of those leaves.
//!
//! For any trie root in the unrouted set for the new leaf, if we have the corresponding
//! queue, we send it to any peers with the new leaf in their latest advertised set.
//!
//! Which parachain those messages go to and from is unimportant, because this is
//! an everybody-sees-everything style protocol. The only important property is "liveness":
//! that the queue root is un-routed at one of the leaves we perceive to be at the head
//! of the block-DAG.
//!
//! In Substrate gossip, every message is associated with a topic. Typically,
//! many messages are grouped under a single topic. In this gossip system, each queue
//! gets its own topic, which is based on the root hash of the queue. This is because
//! many different chain leaves may have the same queue as un-routed, so it's better than
//! attempting to group message packets by the leaf they appear unrouted at.
//!
//! ### 2. Upon a neighbor packet from a peer.
//!
//! The neighbor packet from a peer should contain perceived chain heads of that peer.
//! If there is any overlap between our perceived chain heads and theirs, we send
//! them any known, un-routed message queue from either set.
//!
//! ### 3. Upon receiving a message queue from a peer.
//!
//! If the message queue is in the un-routed set of one of the latest leaves we've updated to,
//! we accept it and relay to any peers who need that queue as well.
//!
//! If not, we report the peer to the peer-set manager for sending us bad data.
//!
//! ### 4. Periodic Pruning
//!
//! We prune messages that are not un-routed from the view of any leaf and cease
//! to attempt to send them to any peer.
use sp_runtime::traits::{BlakeTwo256, Hash as HashT};
use polkadot_primitives::Hash;
use std::collections::{HashMap, HashSet};
use sp_blockchain::Error as ClientError;
use super::{MAX_CHAIN_HEADS, GossipValidationResult, LeavesVec, ChainContext};
/// Construct a topic for a message queue root deterministically.
pub fn queue_topic(queue_root: Hash) -> Hash {
let mut v = queue_root.as_ref().to_vec();
v.extend(b"message_queue");
BlakeTwo256::hash(&v[..])
}
/// A view of which queue roots are current for a given set of leaves.
#[derive(Default)]
pub struct View {
leaves: LeavesVec,
leaf_topics: HashMap<Hash, HashSet<Hash>>, // leaf_hash -> { topics }
expected_queues: HashMap<Hash, (Hash, bool)>, // topic -> (queue-root, known)
}
impl View {
/// Update the set of current leaves. This is called when we perceive a new bset leaf-set.
pub fn update_leaves<T: ChainContext + ?Sized, I>(&mut self, context: &T, new_leaves: I)
-> Result<(), ClientError>
where I: Iterator<Item=Hash>
{
let new_leaves = new_leaves.take(MAX_CHAIN_HEADS);
let old_leaves = std::mem::replace(&mut self.leaves, new_leaves.collect());
let expected_queues = &mut self.expected_queues;
let leaves = &self.leaves;
self.leaf_topics.retain(|l, topics| {
if leaves.contains(l) { return true }
// prune out all data about old leaves we don't follow anymore.
for topic in topics.iter() {
expected_queues.remove(topic);
}
false
});
let mut res = Ok(());
// add in new data about fresh leaves.
for new_leaf in &self.leaves {
if old_leaves.contains(new_leaf) { continue }
let mut this_leaf_topics = HashSet::new();
let r = context.leaf_unrouted_roots(new_leaf, &mut |&queue_root| {
let topic = queue_topic(queue_root);
this_leaf_topics.insert(topic);
expected_queues.entry(topic).or_insert((queue_root, false));
});
if r.is_err() {
if let Err(e) = res {
log::debug!(target: "message_routing", "Ignored duplicate error {}", e)
};
res = r;
}
self.leaf_topics.insert(*new_leaf, this_leaf_topics);
}
res
}
/// Validate an incoming message queue against this view. If it is accepted
/// by our view of un-routed message queues, we will keep and re-propagate.
pub fn validate_queue_and_note_known(&mut self, messages: &super::GossipParachainMessages)
-> (GossipValidationResult<Hash>, sc_network::ReputationChange)
{
let ostensible_topic = queue_topic(messages.queue_root);
match self.expected_queues.get_mut(&ostensible_topic) {
None => (GossipValidationResult::Discard, super::cost::UNNEEDED_ICMP_MESSAGES),
Some(&mut (_, ref mut known)) => {
if !messages.queue_root_is_correct() {
(
GossipValidationResult::Discard,
super::cost::icmp_messages_root_mismatch(messages.messages.len()),
)
} else {
*known = true;
(
GossipValidationResult::ProcessAndKeep(ostensible_topic),
super::benefit::NEW_ICMP_MESSAGES,
)
}
}
}
}
/// Whether a message with given topic is live.
pub fn is_topic_live(&self, topic: &Hash) -> bool {
self.expected_queues.get(topic).is_some()
}
/// Whether a message is allowed under the intersection of the given leaf-set
/// and our own.
pub fn allowed_intersecting(&self, other_leaves: &LeavesVec, topic: &Hash) -> bool {
for i in other_leaves {
for j in &self.leaves {
if i == j {
let leaf_topics = self.leaf_topics.get(i)
.expect("leaf_topics are mutated only in update_leaves; \
we have an entry for each item in self.leaves; \
i is in self.leaves; qed");
if leaf_topics.contains(topic) {
return true;
}
}
}
}
false
}
/// Get topics of all message queues a peer is interested in - this is useful
/// when a peer has informed us of their new best leaves.
pub fn intersection_topics(&self, other_leaves: &LeavesVec) -> impl Iterator<Item=Hash> {
let deduplicated = other_leaves.iter()
.filter_map(|l| self.leaf_topics.get(l))
.flat_map(|topics| topics.iter().cloned())
.collect::<HashSet<_>>();
deduplicated.into_iter()
}
/// Iterate over all live message queues for which the data is marked as not locally known,
/// calling a closure with `(topic, root)`. The closure will return whether the queue data is
/// unknown.
///
/// This is called when we should send un-routed message queues that we are
/// newly aware of to peers - as in when we update our leaves.
pub fn sweep_unknown_queues(&mut self, mut check_known: impl FnMut(&Hash, &Hash) -> bool) {
for (topic, &mut (ref queue_root, ref mut known)) in self.expected_queues.iter_mut() {
if !*known {
*known = check_known(topic, queue_root)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::legacy::tests::TestChainContext;
use crate::legacy::gossip::{Known, GossipParachainMessages};
use polkadot_primitives::parachain::Message as ParachainMessage;
fn hash(x: u8) -> Hash {
[x; 32].into()
}
fn message_queue(from: u8, to: u8) -> Option<[[u8; 2]; 1]> {
if from == to {
None
} else {
Some([[from, to]])
}
}
fn message_queue_root(from: u8, to: u8) -> Option<Hash> {
message_queue(from, to).map(
|q| polkadot_validation::message_queue_root(q.iter())
)
}
// check that our view has all of the roots of the message queues
// emitted in the heads identified in `our_heads`, and none of the others.
fn check_roots(view: &mut View, our_heads: &[u8], n_heads: u8) -> bool {
for i in 0..n_heads {
for j in 0..n_heads {
if let Some(messages) = message_queue(i, j) {
let queue_root = message_queue_root(i, j).unwrap();
let messages = GossipParachainMessages {
queue_root,
messages: messages.iter().map(|m| ParachainMessage(m.to_vec())).collect(),
};
let had_queue = match view.validate_queue_and_note_known(&messages).0 {
GossipValidationResult::ProcessAndKeep(topic) => topic == queue_topic(queue_root),
_ => false,
};
if our_heads.contains(&i) != had_queue {
return false
}
}
}
}
true
}
#[test]
fn update_leaves_none_in_common() {
let mut ctx = TestChainContext::default();
let n_heads = 5;
for i in 0..n_heads {
ctx.known_map.insert(hash(i as u8), Known::Leaf);
let messages_out: Vec<_> = (0..n_heads).filter_map(|j| message_queue_root(i, j)).collect();
if !messages_out.is_empty() {
ctx.ingress_roots.insert(hash(i as u8), messages_out);
}
}
// initialize the view with 2 leaves.
let mut view = View::default();
view.update_leaves(
&ctx,
[hash(0), hash(1)].iter().cloned(),
).unwrap();
// we should have all queue roots that were
// un-routed from the perspective of those 2
// leaves and no others.
assert!(check_roots(&mut view, &[0, 1], n_heads));
// after updating to a disjoint set,
// the property that we are aware of all un-routed
// from the perspective of our known leaves should
// remain the same.
view.update_leaves(
&ctx,
[hash(2), hash(3), hash(4)].iter().cloned(),
).unwrap();
assert!(check_roots(&mut view, &[2, 3, 4], n_heads));
}
#[test]
fn update_leaves_overlapping() {
let mut ctx = TestChainContext::default();
let n_heads = 5;
for i in 0..n_heads {
ctx.known_map.insert(hash(i as u8), Known::Leaf);
let messages_out: Vec<_> = (0..n_heads).filter_map(|j| message_queue_root(i, j)).collect();
if !messages_out.is_empty() {
ctx.ingress_roots.insert(hash(i as u8), messages_out);
}
}
let mut view = View::default();
view.update_leaves(
&ctx,
[hash(0), hash(1), hash(2)].iter().cloned(),
).unwrap();
assert!(check_roots(&mut view, &[0, 1, 2], n_heads));
view.update_leaves(
&ctx,
[hash(2), hash(3), hash(4)].iter().cloned(),
).unwrap();
// after updating to a leaf-set overlapping with the prior,
// the property that we are aware of all un-routed
// from the perspective of our known leaves should
// remain the same.
assert!(check_roots(&mut view, &[2, 3, 4], n_heads));
}
}
+8 -414
View File
@@ -49,7 +49,7 @@
//! Peers who send information which was not allowed under a recent neighbor packet //! Peers who send information which was not allowed under a recent neighbor packet
//! will be noted as non-beneficial to Substrate's peer-set management utility. //! will be noted as non-beneficial to Substrate's peer-set management utility.
use sp_runtime::{generic::BlockId, traits::{BlakeTwo256, Hash as HashT}}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT};
use sp_blockchain::Error as ClientError; use sp_blockchain::Error as ClientError;
use sc_network::{config::Roles, Context, PeerId, ReputationChange}; use sc_network::{config::Roles, Context, PeerId, ReputationChange};
use sc_network::{NetworkService as SubstrateNetworkService, specialization::NetworkSpecialization}; use sc_network::{NetworkService as SubstrateNetworkService, specialization::NetworkSpecialization};
@@ -60,7 +60,7 @@ use sc_network_gossip::{
use polkadot_validation::{SignedStatement}; use polkadot_validation::{SignedStatement};
use polkadot_primitives::{Block, Hash}; use polkadot_primitives::{Block, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
ParachainHost, ValidatorId, Message as ParachainMessage, ErasureChunk as PrimitiveChunk ParachainHost, ValidatorId, ErasureChunk as PrimitiveChunk
}; };
use polkadot_erasure_coding::{self as erasure}; use polkadot_erasure_coding::{self as erasure};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
@@ -72,15 +72,12 @@ use std::sync::Arc;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use futures::prelude::*; use futures::prelude::*;
use parking_lot::RwLock; use parking_lot::RwLock;
use log::warn;
use crate::legacy::{GossipMessageStream, NetworkService, GossipService, PolkadotProtocol, router::attestation_topic}; use crate::legacy::{GossipMessageStream, NetworkService, GossipService, PolkadotProtocol, router::attestation_topic};
use attestation::{View as AttestationView, PeerData as AttestationPeerData}; use attestation::{View as AttestationView, PeerData as AttestationPeerData};
use message_routing::{View as MessageRoutingView};
mod attestation; mod attestation;
mod message_routing;
/// The engine ID of the polkadot attestation system. /// The engine ID of the polkadot attestation system.
pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"dot1"; pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"dot1";
@@ -99,8 +96,6 @@ mod benefit {
pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation"); pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation");
/// When a peer sends us a previously-unknown erasure chunk. /// When a peer sends us a previously-unknown erasure chunk.
pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk"); pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk");
/// When a peer sends us a previously-unknown message packet.
pub const NEW_ICMP_MESSAGES: Rep = Rep::new(50, "Polkadot: New ICMP messages");
} }
mod cost { mod cost {
@@ -119,19 +114,10 @@ mod cost {
pub const BAD_SIGNATURE: Rep = Rep::new(-500, "Polkadot: Bad signature"); pub const BAD_SIGNATURE: Rep = Rep::new(-500, "Polkadot: Bad signature");
/// A peer sent us a bad neighbor packet. /// A peer sent us a bad neighbor packet.
pub const BAD_NEIGHBOR_PACKET: Rep = Rep::new(-300, "Polkadot: Bad neighbor"); pub const BAD_NEIGHBOR_PACKET: Rep = Rep::new(-300, "Polkadot: Bad neighbor");
/// A peer sent us an ICMP queue we haven't advertised a need for.
pub const UNNEEDED_ICMP_MESSAGES: Rep = Rep::new(-100, "Polkadot: Unexpected ICMP message");
/// A peer sent us an erasure chunk referring to a candidate that we are not aware of. /// A peer sent us an erasure chunk referring to a candidate that we are not aware of.
pub const ORPHANED_ERASURE_CHUNK: Rep = Rep::new(-10, "An erasure chunk from unknown candidate"); pub const ORPHANED_ERASURE_CHUNK: Rep = Rep::new(-10, "An erasure chunk from unknown candidate");
/// A peer sent us an erasure chunk that does not match candidate's erasure root. /// A peer sent us an erasure chunk that does not match candidate's erasure root.
pub const ERASURE_CHUNK_WRONG_ROOT: Rep = Rep::new(-100, "Chunk doesn't match encoding root"); pub const ERASURE_CHUNK_WRONG_ROOT: Rep = Rep::new(-100, "Chunk doesn't match encoding root");
/// A peer sent us an ICMP queue with a bad root.
pub fn icmp_messages_root_mismatch(n_messages: usize) -> Rep {
const PER_MESSAGE: i32 = -150;
Rep::new((0..n_messages).map(|_| PER_MESSAGE).sum(), "Polkadot: ICMP root mismatch")
}
} }
/// A gossip message. /// A gossip message.
@@ -144,12 +130,9 @@ pub enum GossipMessage {
/// Non-candidate statements should only be sent to peers who are aware of the candidate. /// Non-candidate statements should only be sent to peers who are aware of the candidate.
#[codec(index = "2")] #[codec(index = "2")]
Statement(GossipStatement), Statement(GossipStatement),
/// A packet of messages from one parachain to another.
#[codec(index = "3")]
ParachainMessages(GossipParachainMessages),
// TODO: https://github.com/paritytech/polkadot/issues/253 // TODO: https://github.com/paritytech/polkadot/issues/253
/// A packet containing one of the erasure-coding chunks of one candidate. /// A packet containing one of the erasure-coding chunks of one candidate.
#[codec(index = "4")] #[codec(index = "3")]
ErasureChunk(ErasureChunkMessage), ErasureChunk(ErasureChunkMessage),
} }
@@ -165,12 +148,6 @@ impl From<GossipStatement> for GossipMessage {
} }
} }
impl From<GossipParachainMessages> for GossipMessage {
fn from(messages: GossipParachainMessages) -> Self {
GossipMessage::ParachainMessages(messages)
}
}
/// A gossip message containing a statement. /// A gossip message containing a statement.
#[derive(Encode, Decode, Clone, PartialEq)] #[derive(Encode, Decode, Clone, PartialEq)]
pub struct GossipStatement { pub struct GossipStatement {
@@ -218,19 +195,6 @@ impl From<ErasureChunkMessage> for GossipMessage {
pub struct GossipParachainMessages { pub struct GossipParachainMessages {
/// The root of the message queue. /// The root of the message queue.
pub queue_root: Hash, pub queue_root: Hash,
/// The messages themselves.
pub messages: Vec<ParachainMessage>,
}
impl GossipParachainMessages {
// confirms that the queue-root in the struct correctly matches
// the messages.
fn queue_root_is_correct(&self) -> bool {
let root = polkadot_validation::message_queue_root(
self.messages.iter().map(|m| &m.0)
);
root == self.queue_root
}
} }
/// A versioned neighbor message. /// A versioned neighbor message.
@@ -283,23 +247,9 @@ impl<F, P> ChainContext for (F, P) where
fn leaf_unrouted_roots( fn leaf_unrouted_roots(
&self, &self,
&leaf: &Hash, _leaf: &Hash,
with_queue_root: &mut dyn FnMut(&Hash), _with_queue_root: &mut dyn FnMut(&Hash),
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let api = self.1.runtime_api();
let leaf_id = BlockId::Hash(leaf);
let active_parachains = api.active_parachains(&leaf_id)?;
// TODO: https://github.com/paritytech/polkadot/issues/467
for (para_id, _) in active_parachains {
if let Some(ingress) = api.ingress(&leaf_id, para_id, None)? {
for (_height, _from, queue_root) in ingress.iter() {
with_queue_root(queue_root);
}
}
}
Ok(()) Ok(())
} }
} }
@@ -325,7 +275,6 @@ pub fn register_validator<C: ChainContext + 'static, S: NetworkSpecialization<Bl
inner: RwLock::new(Inner { inner: RwLock::new(Inner {
peers: HashMap::new(), peers: HashMap::new(),
attestation_view: Default::default(), attestation_view: Default::default(),
message_routing_view: Default::default(),
availability_store: None, availability_store: None,
chain, chain,
}) })
@@ -350,8 +299,6 @@ pub fn register_validator<C: ChainContext + 'static, S: NetworkSpecialization<Bl
enum NewLeafAction { enum NewLeafAction {
// (who, message) // (who, message)
TargetedMessage(PeerId, GossipMessage), TargetedMessage(PeerId, GossipMessage),
// (topic, message)
Multicast(Hash, GossipMessage),
} }
/// Actions to take after noting a new block-DAG leaf. /// Actions to take after noting a new block-DAG leaf.
@@ -372,8 +319,6 @@ impl NewLeafActions {
match action { match action {
NewLeafAction::TargetedMessage(who, message) NewLeafAction::TargetedMessage(who, message)
=> gossip.send_message(who, message), => gossip.send_message(who, message),
NewLeafAction::Multicast(topic, message)
=> gossip.gossip_message(topic, message),
} }
} }
} }
@@ -428,7 +373,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
&self, &self,
relay_chain_leaf: Hash, relay_chain_leaf: Hash,
validation: MessageValidationData, validation: MessageValidationData,
lookup_queue_by_root: impl Fn(&Hash) -> Option<Vec<ParachainMessage>>,
) -> NewLeafActions { ) -> NewLeafActions {
// add an entry in attestation_view // add an entry in attestation_view
// prune any entries from attestation_view which are no longer leaves // prune any entries from attestation_view which are no longer leaves
@@ -441,7 +385,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
let &mut Inner { let &mut Inner {
ref chain, ref chain,
ref mut attestation_view, ref mut attestation_view,
ref mut message_routing_view,
.. ..
} = &mut *inner; } = &mut *inner;
@@ -449,10 +392,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
Some(Known::Leaf) => true, Some(Known::Leaf) => true,
_ => false, _ => false,
}); });
if let Err(e) = message_routing_view.update_leaves(chain, attestation_view.neighbor_info()) {
warn!("Unable to fully update leaf-state: {:?}", e);
}
} }
@@ -461,23 +400,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
|who, message| actions.push(NewLeafAction::TargetedMessage(who.clone(), message)) |who, message| actions.push(NewLeafAction::TargetedMessage(who.clone(), message))
); );
// feed any new unrouted queues into the propagation pool.
inner.message_routing_view.sweep_unknown_queues(|topic, queue_root|
match lookup_queue_by_root(queue_root) {
Some(messages) => {
let message = GossipMessage::from(GossipParachainMessages {
queue_root: *queue_root,
messages,
});
actions.push(NewLeafAction::Multicast(*topic, message));
true
}
None => false,
}
);
NewLeafActions { actions } NewLeafActions { actions }
} }
@@ -575,16 +497,9 @@ struct PeerData {
attestation: AttestationPeerData, attestation: AttestationPeerData,
} }
impl PeerData {
fn leaves(&self) -> impl Iterator<Item = &Hash> {
self.attestation.leaves()
}
}
struct Inner<C: ?Sized> { struct Inner<C: ?Sized> {
peers: HashMap<PeerId, PeerData>, peers: HashMap<PeerId, PeerData>,
attestation_view: AttestationView, attestation_view: AttestationView,
message_routing_view: MessageRoutingView,
availability_store: Option<av_store::Store>, availability_store: Option<av_store::Store>,
chain: C, chain: C,
} }
@@ -602,11 +517,7 @@ impl<C: ?Sized + ChainContext> Inner<C> {
let new_leaves = peer.attestation.update_leaves(&chain_heads); let new_leaves = peer.attestation.update_leaves(&chain_heads);
let new_attestation_topics = new_leaves.iter().cloned().map(attestation_topic); let new_attestation_topics = new_leaves.iter().cloned().map(attestation_topic);
// find all topics which are from the intersection of our leaves with the peer's new_attestation_topics.collect()
// new leaves.
let new_message_routing_topics = self.message_routing_view.intersection_topics(&new_leaves);
new_attestation_topics.chain(new_message_routing_topics).collect()
} else { } else {
Vec::new() Vec::new()
}; };
@@ -692,7 +603,6 @@ impl<C: ChainContext + ?Sized> MessageValidator<C> {
inner: RwLock::new(Inner { inner: RwLock::new(Inner {
peers: HashMap::new(), peers: HashMap::new(),
attestation_view: Default::default(), attestation_view: Default::default(),
message_routing_view: Default::default(),
availability_store: None, availability_store: None,
chain, chain,
}), }),
@@ -740,18 +650,6 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
} }
(res, cb) (res, cb)
} }
Ok(GossipMessage::ParachainMessages(messages)) => {
let (res, cb) = {
let mut inner = self.inner.write();
let inner = &mut *inner;
inner.message_routing_view.validate_queue_and_note_known(&messages)
};
if let GossipValidationResult::ProcessAndKeep(ref topic) = res {
context.broadcast_message(topic.clone(), data.to_vec(), false);
}
(res, cb)
}
Ok(GossipMessage::ErasureChunk(chunk)) => { Ok(GossipMessage::ErasureChunk(chunk)) => {
self.inner.write().validate_erasure_chunk_packet(chunk) self.inner.write().validate_erasure_chunk_packet(chunk)
} }
@@ -767,8 +665,7 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
Box::new(move |topic, _data| { Box::new(move |topic, _data| {
// check that messages from this topic are considered live by one of our protocols. // check that messages from this topic are considered live by one of our protocols.
// everything else is expired // everything else is expired
let live = inner.attestation_view.is_topic_live(&topic) let live = inner.attestation_view.is_topic_live(&topic);
|| !inner.message_routing_view.is_topic_live(&topic);
!live // = expired !live // = expired
}) })
@@ -780,7 +677,6 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
let &mut Inner { let &mut Inner {
ref mut peers, ref mut peers,
ref mut attestation_view, ref mut attestation_view,
ref mut message_routing_view,
.. ..
} = &mut *inner; } = &mut *inner;
@@ -806,13 +702,6 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
) )
}) })
} }
Ok(GossipMessage::ParachainMessages(_)) => match peer {
None => false,
Some(peer) => {
let their_leaves: LeavesVec = peer.leaves().cloned().collect();
message_routing_view.allowed_intersecting(&their_leaves, topic)
}
}
_ => false, _ => false,
} }
}) })
@@ -827,9 +716,8 @@ mod tests {
use parking_lot::Mutex; use parking_lot::Mutex;
use polkadot_primitives::parachain::{CandidateReceipt, HeadData}; use polkadot_primitives::parachain::{CandidateReceipt, HeadData};
use sp_core::crypto::UncheckedInto; use sp_core::crypto::UncheckedInto;
use sp_core::sr25519::{Public as Sr25519Public, Signature as Sr25519Signature}; use sp_core::sr25519::Signature as Sr25519Signature;
use polkadot_validation::GenericStatement; use polkadot_validation::GenericStatement;
use super::message_routing::queue_topic;
use crate::legacy::tests::TestChainContext; use crate::legacy::tests::TestChainContext;
@@ -867,22 +755,6 @@ mod tests {
} }
} }
impl NewLeafActions {
fn has_message(&self, who: PeerId, message: GossipMessage) -> bool {
let x = NewLeafAction::TargetedMessage(who, message);
self.actions.iter().find(|&m| m == &x).is_some()
}
fn has_multicast(&self, topic: Hash, message: GossipMessage) -> bool {
let x = NewLeafAction::Multicast(topic, message);
self.actions.iter().find(|&m| m == &x).is_some()
}
}
fn validator_id(raw: [u8; 32]) -> ValidatorId {
Sr25519Public::from_raw(raw).into()
}
#[test] #[test]
fn message_allowed() { fn message_allowed() {
let (tx, _rx) = mpsc::channel(); let (tx, _rx) = mpsc::channel();
@@ -933,7 +805,6 @@ mod tests {
head_data: HeadData(vec![9, 9, 9]), head_data: HeadData(vec![9, 9, 9]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
signature: Default::default(), signature: Default::default(),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [20u8; 32].into(), block_data_hash: [20u8; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -1095,12 +966,6 @@ mod tests {
let hash_a = [1u8; 32].into(); let hash_a = [1u8; 32].into();
let root_a = [11u8; 32].into(); let root_a = [11u8; 32].into();
let root_a_topic = queue_topic(root_a);
let root_a_messages = vec![
ParachainMessage(vec![1, 2, 3]),
ParachainMessage(vec![4, 5, 6]),
];
let chain = { let chain = {
let mut chain = TestChainContext::default(); let mut chain = TestChainContext::default();
@@ -1111,8 +976,6 @@ mod tests {
let validator = RegisteredMessageValidator::new_test(chain, report_handle); let validator = RegisteredMessageValidator::new_test(chain, report_handle);
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
let peer_a = PeerId::random(); let peer_a = PeerId::random();
let peer_b = PeerId::random(); let peer_b = PeerId::random();
@@ -1144,274 +1007,5 @@ mod tests {
], ],
); );
} }
// ensure that we attempt to multicast all relevant queues after noting a leaf.
{
let actions = validator.new_local_leaf(
hash_a,
MessageValidationData { authorities },
|root| if root == &root_a {
Some(root_a_messages.clone())
} else {
None
},
);
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
chain_heads: vec![hash_a],
})));
assert!(actions.has_multicast(root_a_topic, GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
})));
}
// ensure that we are allowed to multicast to a peer with same chain head,
// but not to one without.
{
let message = GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
}).encode();
let mut allowed = validator.inner.message_allowed();
let intent = MessageIntent::Broadcast;
assert!(allowed(&peer_a, intent, &root_a_topic, &message[..]));
assert!(!allowed(&peer_b, intent, &root_a_topic, &message[..]));
}
}
#[test]
fn multicasts_icmp_queues_on_neighbor_update() {
let (tx, _rx) = mpsc::channel();
let tx = Mutex::new(tx);
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
let hash_a = [1u8; 32].into();
let root_a = [11u8; 32].into();
let root_a_topic = queue_topic(root_a);
let root_a_messages = vec![
ParachainMessage(vec![1, 2, 3]),
ParachainMessage(vec![4, 5, 6]),
];
let chain = {
let mut chain = TestChainContext::default();
chain.known_map.insert(hash_a, Known::Leaf);
chain.ingress_roots.insert(hash_a, vec![root_a]);
chain
};
let validator = RegisteredMessageValidator::new_test(chain, report_handle);
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
let peer_a = PeerId::random();
let peer_b = PeerId::random();
let mut validator_context = MockValidatorContext::default();
validator.inner.new_peer(&mut validator_context, &peer_a, Roles::FULL);
validator.inner.new_peer(&mut validator_context, &peer_b, Roles::FULL);
assert!(validator_context.events.is_empty());
validator_context.clear();
// ensure that we attempt to multicast all relevant queues after noting a leaf.
{
let actions = validator.new_local_leaf(
hash_a,
MessageValidationData { authorities },
|root| if root == &root_a {
Some(root_a_messages.clone())
} else {
None
},
);
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
chain_heads: vec![hash_a],
})));
assert!(actions.has_multicast(root_a_topic, GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
})));
}
// ensure that we are not allowed to multicast to either peer, as they
// don't have the chain head.
{
let message = GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
});
let mut allowed = validator.inner.message_allowed();
let intent = MessageIntent::Broadcast;
assert!(!allowed(&peer_a, intent, &root_a_topic, &message.encode()));
assert!(!allowed(&peer_b, intent, &root_a_topic, &message.encode()));
}
// peer A gets updated to the chain head. now we'll attempt to broadcast
// all queues to it.
{
let message = GossipMessage::from(NeighborPacket {
chain_heads: vec![hash_a],
}).encode();
let res = validator.inner.validate(
&mut validator_context,
&peer_a,
&message[..],
);
match res {
GossipValidationResult::Discard => {},
_ => panic!("wrong result"),
}
assert_eq!(
validator_context.events,
vec![
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
ContextEvent::SendTopic(peer_a.clone(), root_a_topic, false),
],
);
}
// ensure that we are allowed to multicast to a peer with same chain head,
// but not to one without.
{
let message = GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
}).encode();
let mut allowed = validator.inner.message_allowed();
let intent = MessageIntent::Broadcast;
assert!(allowed(&peer_a, intent, &root_a_topic, &message[..]));
assert!(!allowed(&peer_b, intent, &root_a_topic, &message[..]));
}
}
#[test]
fn accepts_needed_unknown_icmp_message_queue() {
let (tx, _rx) = mpsc::channel();
let tx = Mutex::new(tx);
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
let hash_a = [1u8; 32].into();
let root_a_messages = vec![
ParachainMessage(vec![1, 2, 3]),
ParachainMessage(vec![4, 5, 6]),
];
let not_root_a_messages = vec![
ParachainMessage(vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
ParachainMessage(vec![4, 5, 6]),
];
let root_a = polkadot_validation::message_queue_root(
root_a_messages.iter().map(|m| &m.0)
);
let not_root_a = [69u8; 32].into();
let root_a_topic = queue_topic(root_a);
let chain = {
let mut chain = TestChainContext::default();
chain.known_map.insert(hash_a, Known::Leaf);
chain.ingress_roots.insert(hash_a, vec![root_a]);
chain
};
let validator = RegisteredMessageValidator::new_test(chain, report_handle);
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
let peer_a = PeerId::random();
let mut validator_context = MockValidatorContext::default();
validator.inner.new_peer(&mut validator_context, &peer_a, Roles::FULL);
assert!(validator_context.events.is_empty());
validator_context.clear();
let queue_messages = GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: root_a_messages.clone(),
});
let not_queue_messages = GossipMessage::from(GossipParachainMessages {
queue_root: root_a,
messages: not_root_a_messages.clone(),
});
let queue_messages_wrong_root = GossipMessage::from(GossipParachainMessages {
queue_root: not_root_a,
messages: root_a_messages.clone(),
});
// ensure that we attempt to multicast all relevant queues after noting a leaf.
{
let actions = validator.new_local_leaf(
hash_a,
MessageValidationData { authorities },
|_root| None,
);
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
chain_heads: vec![hash_a],
})));
// we don't know this queue! no broadcast :(
assert!(!actions.has_multicast(root_a_topic, queue_messages.clone()));
}
// rejects right queue with unknown root.
{
let res = validator.inner.validate(
&mut validator_context,
&peer_a,
&queue_messages_wrong_root.encode(),
);
match res {
GossipValidationResult::Discard => {},
_ => panic!("wrong result"),
}
assert_eq!(validator_context.events, Vec::new());
}
// rejects bad queue.
{
let res = validator.inner.validate(
&mut validator_context,
&peer_a,
&not_queue_messages.encode(),
);
match res {
GossipValidationResult::Discard => {},
_ => panic!("wrong result"),
}
assert_eq!(validator_context.events, Vec::new());
}
// accepts the right queue.
{
let res = validator.inner.validate(
&mut validator_context,
&peer_a,
&queue_messages.encode(),
);
match res {
GossipValidationResult::ProcessAndKeep(topic) if topic == root_a_topic => {},
_ => panic!("wrong result"),
}
assert_eq!(validator_context.events, vec![
ContextEvent::BroadcastMessage(root_a_topic, queue_messages.encode(), false),
]);
}
} }
} }
+1 -13
View File
@@ -31,7 +31,7 @@ use futures::prelude::*;
use polkadot_primitives::{Block, Hash, Header}; use polkadot_primitives::{Block, Hash, Header};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, CollatorId, CandidateReceipt, Collation, PoVBlock, Id as ParaId, CollatorId, CandidateReceipt, Collation, PoVBlock,
StructuredUnroutedIngress, ValidatorId, OutgoingMessages, ErasureChunk, ValidatorId, ErasureChunk,
}; };
use sc_network::{ use sc_network::{
PeerId, RequestId, Context, StatusMessage as GenericFullStatus, PeerId, RequestId, Context, StatusMessage as GenericFullStatus,
@@ -169,7 +169,6 @@ struct PoVBlockRequest {
candidate_hash: Hash, candidate_hash: Hash,
block_data_hash: Hash, block_data_hash: Hash,
sender: oneshot::Sender<PoVBlock>, sender: oneshot::Sender<PoVBlock>,
canon_roots: StructuredUnroutedIngress,
} }
impl PoVBlockRequest { impl PoVBlockRequest {
@@ -182,14 +181,8 @@ impl PoVBlockRequest {
return Err(self); return Err(self);
} }
match polkadot_validation::validate_incoming(&self.canon_roots, &pov_block.ingress) {
Ok(()) => {
let _ = self.sender.send(pov_block);
Ok(()) Ok(())
} }
Err(_) => Err(self)
}
}
} }
// ensures collator-protocol messages are sent in correct order. // ensures collator-protocol messages are sent in correct order.
@@ -300,7 +293,6 @@ impl PolkadotProtocol {
ctx: &mut dyn Context<Block>, ctx: &mut dyn Context<Block>,
candidate: &CandidateReceipt, candidate: &CandidateReceipt,
relay_parent: Hash, relay_parent: Hash,
canon_roots: StructuredUnroutedIngress,
) -> oneshot::Receiver<PoVBlock> { ) -> oneshot::Receiver<PoVBlock> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
@@ -310,7 +302,6 @@ impl PolkadotProtocol {
candidate_hash: candidate.hash(), candidate_hash: candidate.hash(),
block_data_hash: candidate.block_data_hash, block_data_hash: candidate.block_data_hash,
sender: tx, sender: tx,
canon_roots,
}); });
self.dispatch_pending_requests(ctx); self.dispatch_pending_requests(ctx);
@@ -617,7 +608,6 @@ impl Specialization<Block> for PolkadotProtocol {
validation_leaf: Default::default(), validation_leaf: Default::default(),
candidate_hash: Default::default(), candidate_hash: Default::default(),
block_data_hash: Default::default(), block_data_hash: Default::default(),
canon_roots: StructuredUnroutedIngress(Vec::new()),
sender, sender,
})); }));
} }
@@ -747,7 +737,6 @@ impl PolkadotProtocol {
relay_parent: Hash, relay_parent: Hash,
targets: HashSet<ValidatorId>, targets: HashSet<ValidatorId>,
collation: Collation, collation: Collation,
outgoing_targeted: OutgoingMessages,
) -> impl Future<Output = ()> { ) -> impl Future<Output = ()> {
debug!(target: "p_net", "Importing local collation on relay parent {:?} and parachain {:?}", debug!(target: "p_net", "Importing local collation on relay parent {:?} and parachain {:?}",
relay_parent, collation.info.parachain_index); relay_parent, collation.info.parachain_index);
@@ -776,7 +765,6 @@ impl PolkadotProtocol {
relay_parent, relay_parent,
parachain_id: collation_cloned.info.parachain_index, parachain_id: collation_cloned.info.parachain_index,
block_data: collation_cloned.pov.block_data.clone(), block_data: collation_cloned.pov.block_data.clone(),
outgoing_queues: Some(outgoing_targeted.clone().into()),
}).await; }).await;
} }
} }
+2 -5
View File
@@ -29,7 +29,7 @@ use polkadot_validation::{
}; };
use polkadot_primitives::{Block, Hash}; use polkadot_primitives::{Block, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
OutgoingMessages, CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock, ErasureChunk, CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock, ErasureChunk,
}; };
use sp_api::ProvideRuntimeApi; use sp_api::ProvideRuntimeApi;
@@ -205,7 +205,6 @@ impl<P: ProvideRuntimeApi<Block> + Send + Sync + 'static, T> Router<P, T> where
knowledge.lock().note_candidate( knowledge.lock().note_candidate(
candidate_hash, candidate_hash,
Some(validated.0.pov_block().clone()), Some(validated.0.pov_block().clone()),
validated.0.outgoing_messages().cloned(),
); );
// propagate the statement. // propagate the statement.
@@ -241,7 +240,6 @@ impl<P: ProvideRuntimeApi<Block> + Send, T> TableRouter for Router<P, T> where
&self, &self,
collation: Collation, collation: Collation,
receipt: CandidateReceipt, receipt: CandidateReceipt,
outgoing: OutgoingMessages,
chunks: (ValidatorIndex, &[ErasureChunk]) chunks: (ValidatorIndex, &[ErasureChunk])
) -> Self::SendLocalCollation { ) -> Self::SendLocalCollation {
// produce a signed statement // produce a signed statement
@@ -250,7 +248,6 @@ impl<P: ProvideRuntimeApi<Block> + Send, T> TableRouter for Router<P, T> where
let validated = Validated::collated_local( let validated = Validated::collated_local(
receipt, receipt,
collation.pov.clone(), collation.pov.clone(),
outgoing.clone(),
); );
let statement = GossipStatement::new( let statement = GossipStatement::new(
@@ -262,7 +259,7 @@ impl<P: ProvideRuntimeApi<Block> + Send, T> TableRouter for Router<P, T> where
); );
// give to network to make available. // give to network to make available.
self.fetcher.knowledge().lock().note_candidate(hash, Some(collation.pov), Some(outgoing)); self.fetcher.knowledge().lock().note_candidate(hash, Some(collation.pov));
self.network().gossip_message(self.attestation_topic, statement.into()); self.network().gossip_message(self.attestation_topic, statement.into());
for chunk in chunks.1 { for chunk in chunks.1 {
+1 -106
View File
@@ -20,20 +20,14 @@ use std::collections::HashMap;
use super::{PolkadotProtocol, Status, Message, FullStatus}; use super::{PolkadotProtocol, Status, Message, FullStatus};
use crate::legacy::validation::LeafWorkParams; use crate::legacy::validation::LeafWorkParams;
use polkadot_validation::GenericStatement;
use polkadot_primitives::{Block, Hash}; use polkadot_primitives::{Block, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{CollatorId, ValidatorId};
CandidateReceipt, HeadData, PoVBlock, BlockData, CollatorId, ValidatorId,
StructuredUnroutedIngress,
};
use sp_core::crypto::UncheckedInto; use sp_core::crypto::UncheckedInto;
use codec::Encode; use codec::Encode;
use sc_network::{ use sc_network::{
PeerId, Context, ReputationChange, config::Roles, specialization::NetworkSpecialization, PeerId, Context, ReputationChange, config::Roles, specialization::NetworkSpecialization,
}; };
use futures::executor::block_on;
mod validation; mod validation;
#[derive(Default)] #[derive(Default)]
@@ -94,13 +88,6 @@ impl crate::legacy::gossip::ChainContext for TestChainContext {
} }
} }
fn make_pov(block_data: Vec<u8>) -> PoVBlock {
PoVBlock {
block_data: BlockData(block_data),
ingress: polkadot_primitives::parachain::ConsolidatedIngress(Vec::new()),
}
}
fn make_status(status: &Status, roles: Roles) -> FullStatus { fn make_status(status: &Status, roles: Roles) -> FullStatus {
FullStatus { FullStatus {
version: 1, version: 1,
@@ -121,11 +108,6 @@ fn make_validation_leaf_work(parent_hash: Hash, local_key: ValidatorId) -> LeafW
} }
} }
fn on_message(protocol: &mut PolkadotProtocol, ctx: &mut TestContext, from: PeerId, message: Message) {
let encoded = message.encode();
protocol.on_message(ctx, from, encoded);
}
#[test] #[test]
fn sends_session_key() { fn sends_session_key() {
let mut protocol = PolkadotProtocol::new(None); let mut protocol = PolkadotProtocol::new(None);
@@ -158,93 +140,6 @@ fn sends_session_key() {
} }
} }
#[test]
fn fetches_from_those_with_knowledge() {
let mut protocol = PolkadotProtocol::new(None);
let peer_a = PeerId::random();
let peer_b = PeerId::random();
let parent_hash = [0; 32].into();
let local_key: ValidatorId = [1; 32].unchecked_into();
let block_data = BlockData(vec![1, 2, 3, 4]);
let block_data_hash = block_data.hash();
let candidate_receipt = CandidateReceipt {
parachain_index: 5.into(),
collator: [255; 32].unchecked_into(),
head_data: HeadData(vec![9, 9, 9]),
parent_head: HeadData(vec![]),
signature: Default::default(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash,
upward_messages: Vec::new(),
erasure_root: [1u8; 32].into(),
};
let candidate_hash = candidate_receipt.hash();
let a_key: ValidatorId = [3; 32].unchecked_into();
let b_key: ValidatorId = [4; 32].unchecked_into();
let status = Status { collating_for: None };
let params = make_validation_leaf_work(parent_hash, local_key.clone());
let session = protocol.new_validation_leaf_work(&mut TestContext::default(), params);
let knowledge = session.knowledge();
knowledge.lock().note_statement(a_key.clone(), &GenericStatement::Valid(candidate_hash));
let canon_roots = StructuredUnroutedIngress(Vec::new());
let recv = protocol.fetch_pov_block(
&mut TestContext::default(),
&candidate_receipt,
parent_hash,
canon_roots,
);
// connect peer A
{
let mut ctx = TestContext::default();
protocol.on_connect(&mut ctx, peer_a.clone(), make_status(&status, Roles::AUTHORITY));
assert!(ctx.has_message(peer_a.clone(), Message::ValidatorId(local_key)));
}
// peer A gives session key and gets asked for data.
{
let mut ctx = TestContext::default();
on_message(&mut protocol, &mut ctx, peer_a.clone(), Message::ValidatorId(a_key.clone()));
assert!(protocol.validators.contains_key(&a_key));
assert!(ctx.has_message(peer_a.clone(), Message::RequestPovBlock(1, parent_hash, candidate_hash)));
}
knowledge.lock().note_statement(b_key.clone(), &GenericStatement::Valid(candidate_hash));
// peer B connects and sends session key. request already assigned to A
{
let mut ctx = TestContext::default();
protocol.on_connect(&mut ctx, peer_b.clone(), make_status(&status, Roles::AUTHORITY));
on_message(&mut protocol, &mut ctx, peer_b.clone(), Message::ValidatorId(b_key.clone()));
assert!(!ctx.has_message(peer_b.clone(), Message::RequestPovBlock(2, parent_hash, candidate_hash)));
}
// peer A disconnects, triggering reassignment
{
let mut ctx = TestContext::default();
protocol.on_disconnect(&mut ctx, peer_a.clone());
assert!(!protocol.validators.contains_key(&a_key));
assert!(ctx.has_message(peer_b.clone(), Message::RequestPovBlock(2, parent_hash, candidate_hash)));
}
// peer B comes back with block data.
{
let mut ctx = TestContext::default();
let pov_block = make_pov(block_data.0);
on_message(&mut protocol, &mut ctx, peer_b, Message::PovBlock(2, Some(pov_block.clone())));
drop(protocol);
assert_eq!(block_on(recv).unwrap(), pov_block);
}
}
#[test] #[test]
fn remove_bad_collator() { fn remove_bad_collator() {
let mut protocol = PolkadotProtocol::new(None); let mut protocol = PolkadotProtocol::new(None);
@@ -28,8 +28,7 @@ use crate::legacy::{PolkadotProtocol, NetworkService, GossipService, GossipMessa
use polkadot_validation::{SharedTable, Network}; use polkadot_validation::{SharedTable, Network};
use polkadot_primitives::{Block, BlockNumber, Hash, Header, BlockId}; use polkadot_primitives::{Block, BlockNumber, Hash, Header, BlockId};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, Chain, DutyRoster, ParachainHost, TargetedMessage, Id as ParaId, Chain, DutyRoster, ParachainHost, TargetedMessage, ValidatorId, Status,
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, Status,
FeeSchedule, HeadData, Retriable, CollatorId, ErasureChunk, CandidateReceipt, FeeSchedule, HeadData, Retriable, CollatorId, ErasureChunk, CandidateReceipt,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -150,7 +149,6 @@ struct ApiData {
validators: Vec<ValidatorId>, validators: Vec<ValidatorId>,
duties: Vec<Chain>, duties: Vec<Chain>,
active_parachains: Vec<(ParaId, Option<(CollatorId, Retriable)>)>, active_parachains: Vec<(ParaId, Option<(CollatorId, Retriable)>)>,
ingress: HashMap<ParaId, StructuredUnroutedIngress>,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@@ -299,17 +297,6 @@ impl ParachainHost<Block> for RuntimeApi {
Ok(NativeOrEncoded::Native(Some(Vec::new()))) Ok(NativeOrEncoded::Native(Some(Vec::new())))
} }
fn ParachainHost_ingress_runtime_api_impl(
&self,
_at: &BlockId,
_: ExecutionContext,
id: Option<(ParaId, Option<BlockNumber>)>,
_: Vec<u8>,
) -> ClientResult<NativeOrEncoded<Option<StructuredUnroutedIngress>>> {
let (id, _) = id.unwrap();
Ok(NativeOrEncoded::Native(self.data.lock().ingress.get(&id).cloned()))
}
fn ParachainHost_get_heads_runtime_api_impl( fn ParachainHost_get_heads_runtime_api_impl(
&self, &self,
_at: &BlockId, _at: &BlockId,
@@ -362,34 +349,6 @@ fn build_network<SP: Spawn + Clone>(n: usize, spawner: SP)-> Built<SP> {
} }
} }
#[derive(Default)]
struct IngressBuilder {
egress: HashMap<(ParaId, ParaId), Vec<Vec<u8>>>,
}
impl IngressBuilder {
fn add_messages(&mut self, source: ParaId, messages: &[TargetedMessage]) {
for message in messages {
let target = message.target;
self.egress.entry((source, target)).or_insert_with(Vec::new).push(message.data.clone());
}
}
fn build(self) -> HashMap<ParaId, BlockIngressRoots> {
let mut map = HashMap::new();
for ((source, target), messages) in self.egress {
map.entry(target).or_insert_with(Vec::new)
.push((source, polkadot_validation::message_queue_root(&messages)));
}
for roots in map.values_mut() {
roots.sort_by_key(|&(para_id, _)| para_id);
}
map.into_iter().map(|(k, v)| (k, BlockIngressRoots(v))).collect()
}
}
#[derive(Clone)] #[derive(Clone)]
struct DummyGossipMessages; struct DummyGossipMessages;
+3 -27
View File
@@ -23,9 +23,9 @@ use sc_network::PeerId;
use polkadot_validation::{ use polkadot_validation::{
Network as ParachainNetwork, SharedTable, Collators, Statement, GenericStatement, SignedStatement, Network as ParachainNetwork, SharedTable, Collators, Statement, GenericStatement, SignedStatement,
}; };
use polkadot_primitives::{Block, BlockId, Hash}; use polkadot_primitives::{Block, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, Collation, OutgoingMessages, ParachainHost, CandidateReceipt, CollatorId, Id as ParaId, Collation, ParachainHost, CandidateReceipt, CollatorId,
ValidatorId, PoVBlock, ValidatorId, PoVBlock,
}; };
use sp_api::ProvideRuntimeApi; use sp_api::ProvideRuntimeApi;
@@ -49,8 +49,6 @@ use crate::legacy::gossip::{RegisteredMessageValidator, MessageValidationData};
use super::{NetworkService, PolkadotProtocol}; use super::{NetworkService, PolkadotProtocol};
pub use polkadot_validation::Incoming;
/// Params to instantiate validation work on a block-DAG leaf. /// Params to instantiate validation work on a block-DAG leaf.
pub struct LeafWorkParams { pub struct LeafWorkParams {
/// The local session key. /// The local session key.
@@ -123,8 +121,6 @@ impl<P, T> ValidationNetwork<P, T> where
let actions = network.new_local_leaf( let actions = network.new_local_leaf(
parent_hash, parent_hash,
MessageValidationData { authorities }, MessageValidationData { authorities },
|queue_root| spec.availability_store.as_ref()
.and_then(|store| store.queue_by_root(queue_root))
); );
actions.perform(&network); actions.perform(&network);
@@ -264,7 +260,6 @@ struct KnowledgeEntry {
knows_block_data: Vec<ValidatorId>, knows_block_data: Vec<ValidatorId>,
knows_outgoing: Vec<ValidatorId>, knows_outgoing: Vec<ValidatorId>,
pov: Option<PoVBlock>, pov: Option<PoVBlock>,
outgoing_messages: Option<OutgoingMessages>,
} }
/// Tracks knowledge of peers. /// Tracks knowledge of peers.
@@ -308,11 +303,9 @@ impl Knowledge {
&mut self, &mut self,
hash: Hash, hash: Hash,
pov: Option<PoVBlock>, pov: Option<PoVBlock>,
outgoing_messages: Option<OutgoingMessages>,
) { ) {
let entry = self.candidates.entry(hash).or_insert_with(Default::default); let entry = self.candidates.entry(hash).or_insert_with(Default::default);
entry.pov = entry.pov.take().or(pov); entry.pov = entry.pov.take().or(pov);
entry.outgoing_messages = entry.outgoing_messages.take().or(outgoing_messages);
} }
} }
@@ -567,32 +560,15 @@ impl<P: ProvideRuntimeApi<Block> + Send, T> LeafWorkDataFetcher<P, T> where
pub fn fetch_pov_block(&self, candidate: &CandidateReceipt) pub fn fetch_pov_block(&self, candidate: &CandidateReceipt)
-> Pin<Box<dyn Future<Output = Result<PoVBlock, io::Error>> + Send>> { -> Pin<Box<dyn Future<Output = Result<PoVBlock, io::Error>> + Send>> {
let parachain = candidate.parachain_index;
let parent_hash = self.parent_hash; let parent_hash = self.parent_hash;
let network = self.network.clone(); let network = self.network.clone();
let candidate = candidate.clone(); let candidate = candidate.clone();
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let canon_roots = self.api.runtime_api().ingress(
&BlockId::hash(parent_hash),
parachain,
None,
)
.map_err(|e|
format!(
"Cannot fetch ingress for parachain {:?} at {:?}: {:?}",
parachain,
parent_hash,
e,
)
);
async move { async move {
network.with_spec(move |spec, ctx| { network.with_spec(move |spec, ctx| {
if let Ok(Some(canon_roots)) = canon_roots { let inner_rx = spec.fetch_pov_block(ctx, &candidate, parent_hash);
let inner_rx = spec.fetch_pov_block(ctx, &candidate, parent_hash, canon_roots);
let _ = tx.send(inner_rx); let _ = tx.send(inner_rx);
}
}); });
let map_err = |_| io::Error::new( let map_err = |_| io::Error::new(
+1 -8
View File
@@ -28,11 +28,10 @@ use futures::prelude::*;
use futures::task::{Spawn, SpawnExt}; use futures::task::{Spawn, SpawnExt};
use log::{debug, trace}; use log::{debug, trace};
use av_store::Store as AvailabilityStore;
use polkadot_primitives::{ use polkadot_primitives::{
Hash, Block, Hash, Block,
parachain::{ parachain::{
PoVBlock, ValidatorId, ValidatorIndex, Collation, CandidateReceipt, OutgoingMessages, PoVBlock, ValidatorId, ValidatorIndex, Collation, CandidateReceipt,
ErasureChunk, ParachainHost, Id as ParaId, CollatorId, ErasureChunk, ParachainHost, Id as ParaId, CollatorId,
}, },
}; };
@@ -106,7 +105,6 @@ pub struct Service {
pub fn start<C, Api, SP>( pub fn start<C, Api, SP>(
service: Arc<PolkadotNetworkService>, service: Arc<PolkadotNetworkService>,
config: Config, config: Config,
availability_store: AvailabilityStore,
chain_context: C, chain_context: C,
api: Arc<Api>, api: Arc<Api>,
executor: SP, executor: SP,
@@ -129,7 +127,6 @@ pub fn start<C, Api, SP>(
executor.spawn(worker_loop( executor.spawn(worker_loop(
config, config,
service.clone(), service.clone(),
availability_store,
gossip_validator, gossip_validator,
worker_sender.clone(), worker_sender.clone(),
api, api,
@@ -563,7 +560,6 @@ impl ProtocolHandler {
async fn worker_loop<Api, Sp>( async fn worker_loop<Api, Sp>(
config: Config, config: Config,
service: Arc<PolkadotNetworkService>, service: Arc<PolkadotNetworkService>,
availability_store: AvailabilityStore,
gossip_handle: RegisteredMessageValidator, gossip_handle: RegisteredMessageValidator,
sender: mpsc::Sender<ServiceToWorkerMsg>, sender: mpsc::Sender<ServiceToWorkerMsg>,
api: Arc<Api>, api: Arc<Api>,
@@ -624,7 +620,6 @@ async fn worker_loop<Api, Sp>(
let new_leaf_actions = gossip_handle.new_local_leaf( let new_leaf_actions = gossip_handle.new_local_leaf(
relay_parent, relay_parent,
crate::legacy::gossip::MessageValidationData { authorities }, crate::legacy::gossip::MessageValidationData { authorities },
|queue_root| availability_store.queue_by_root(queue_root),
); );
new_leaf_actions.perform(&gossip_handle); new_leaf_actions.perform(&gossip_handle);
@@ -789,7 +784,6 @@ fn distribute_local_collation(
let validated = Validated::collated_local( let validated = Validated::collated_local(
receipt, receipt,
collation.pov.clone(), collation.pov.clone(),
OutgoingMessages { outgoing_messages: Vec::new() },
); );
let statement = crate::legacy::gossip::GossipStatement::new( let statement = crate::legacy::gossip::GossipStatement::new(
@@ -914,7 +908,6 @@ impl TableRouter for Router {
&self, &self,
collation: Collation, collation: Collation,
receipt: CandidateReceipt, receipt: CandidateReceipt,
_outgoing: OutgoingMessages,
chunks: (ValidatorIndex, &[ErasureChunk]), chunks: (ValidatorIndex, &[ErasureChunk]),
) -> Self::SendLocalCollation { ) -> Self::SendLocalCollation {
let message = ServiceToWorkerMsg::LocalCollation( let message = ServiceToWorkerMsg::LocalCollation(
-2
View File
@@ -65,8 +65,6 @@ pub struct ValidationParams {
pub block_data: Vec<u8>, pub block_data: Vec<u8>,
/// Previous head-data. /// Previous head-data.
pub parent_head: Vec<u8>, pub parent_head: Vec<u8>,
/// Incoming messages.
pub ingress: Vec<IncomingMessage>,
} }
/// The result of parachain validation. /// The result of parachain validation.
+1 -42
View File
@@ -18,7 +18,7 @@
use polkadot_parachain as parachain; use polkadot_parachain as parachain;
use crate::{DummyExt, parachain::{IncomingMessage, ValidationParams}}; use crate::{DummyExt, parachain::ValidationParams};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
/// Head data for this parachain. /// Head data for this parachain.
@@ -75,7 +75,6 @@ pub fn execute_good_on_parent() {
ValidationParams { ValidationParams {
parent_head: parent_head.encode(), parent_head: parent_head.encode(),
block_data: block_data.encode(), block_data: block_data.encode(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
@@ -111,7 +110,6 @@ fn execute_good_chain_on_parent() {
ValidationParams { ValidationParams {
parent_head: parent_head.encode(), parent_head: parent_head.encode(),
block_data: block_data.encode(), block_data: block_data.encode(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
@@ -147,47 +145,8 @@ fn execute_bad_on_parent() {
ValidationParams { ValidationParams {
parent_head: parent_head.encode(), parent_head: parent_head.encode(),
block_data: block_data.encode(), block_data: block_data.encode(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
).unwrap_err(); ).unwrap_err();
} }
#[test]
fn processes_messages() {
let parent_head = HeadData {
number: 0,
parent_hash: [0; 32],
post_state: hash_state(0),
};
let block_data = BlockData {
state: 0,
add: 512,
};
let bad_message_data = vec![1];
assert!(AddMessage::decode(&mut &bad_message_data[..]).is_err());
let ret = parachain::wasm_executor::validate_candidate(
TEST_CODE,
ValidationParams {
parent_head: parent_head.encode(),
block_data: block_data.encode(),
ingress: vec![
IncomingMessage { source: 1.into(), data: (AddMessage { amount: 256 }).encode() },
IncomingMessage { source: 2.into(), data: bad_message_data },
IncomingMessage { source: 3.into(), data: (AddMessage { amount: 256 }).encode() },
],
},
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest,
).unwrap();
let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap();
assert_eq!(new_head.number, 1);
assert_eq!(new_head.parent_hash, hash_head(&parent_head));
assert_eq!(new_head.post_state, hash_state(1024));
}
@@ -30,7 +30,6 @@ fn terminates_on_timeout() {
ValidationParams { ValidationParams {
parent_head: Default::default(), parent_head: Default::default(),
block_data: Vec::new(), block_data: Vec::new(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
@@ -53,7 +52,6 @@ fn parallel_execution() {
ValidationParams { ValidationParams {
parent_head: Default::default(), parent_head: Default::default(),
block_data: Vec::new(), block_data: Vec::new(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
@@ -63,7 +61,6 @@ fn parallel_execution() {
ValidationParams { ValidationParams {
parent_head: Default::default(), parent_head: Default::default(),
block_data: Vec::new(), block_data: Vec::new(),
ingress: Vec::new(),
}, },
DummyExt, DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest, parachain::wasm_executor::ExecutionMode::RemoteTest,
+1 -135
View File
@@ -20,7 +20,7 @@ use rstd::prelude::*;
use rstd::cmp::Ordering; use rstd::cmp::Ordering;
use parity_scale_codec::{Encode, Decode}; use parity_scale_codec::{Encode, Decode};
use bitvec::vec::BitVec; use bitvec::vec::BitVec;
use super::{Hash, Balance, BlockNumber}; use super::{Hash, Balance};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@@ -164,53 +164,6 @@ pub struct DutyRoster {
pub validator_duty: Vec<Chain>, pub validator_duty: Vec<Chain>,
} }
/// Outgoing message data for a parachain candidate.
///
/// This is data produced by evaluating the candidate. It contains
/// full records of all outgoing messages to other parachains.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "std", serde(deny_unknown_fields))]
pub struct OutgoingMessages {
/// The outgoing messages from the execution of the parachain.
///
/// This must be sorted in ascending order by parachain ID.
pub outgoing_messages: Vec<TargetedMessage>
}
impl OutgoingMessages {
/// Returns an iterator of slices of all outgoing message queues.
///
/// All messages in a given slice are guaranteed to have the same target.
pub fn message_queues(&'_ self) -> impl Iterator<Item=&'_ [TargetedMessage]> + '_ {
let mut outgoing = &self.outgoing_messages[..];
rstd::iter::from_fn(move || {
if outgoing.is_empty() { return None }
let target = outgoing[0].target;
let mut end = 1; // the index of the last matching item + 1.
loop {
match outgoing.get(end) {
None => break,
Some(x) => if x.target != target { break },
}
end += 1;
}
let item = &outgoing[..end];
outgoing = &outgoing[end..];
Some(item)
})
}
}
/// Messages by queue root that are stored in the availability store.
#[derive(PartialEq, Clone, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Encode, Debug))]
pub struct AvailableMessages(pub Vec<(Hash, Vec<Message>)>);
/// Compute a trie root for a set of messages, given the raw message data. /// Compute a trie root for a set of messages, given the raw message data.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn message_queue_root<A, I: IntoIterator<Item=A>>(messages: I) -> Hash pub fn message_queue_root<A, I: IntoIterator<Item=A>>(messages: I) -> Hash
@@ -219,19 +172,6 @@ pub fn message_queue_root<A, I: IntoIterator<Item=A>>(messages: I) -> Hash
trie::trie_types::Layout::<primitives::Blake2Hasher>::ordered_trie_root(messages) trie::trie_types::Layout::<primitives::Blake2Hasher>::ordered_trie_root(messages)
} }
#[cfg(feature = "std")]
impl From<OutgoingMessages> for AvailableMessages {
fn from(outgoing: OutgoingMessages) -> Self {
let queues = outgoing.message_queues().filter_map(|queue| {
let queue_root = message_queue_root(queue);
let queue_data = queue.iter().map(|msg| msg.clone().into()).collect();
Some((queue_root, queue_data))
}).collect();
AvailableMessages(queues)
}
}
/// Candidate receipt type. /// Candidate receipt type.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))] #[cfg_attr(feature = "std", derive(Debug))]
@@ -242,9 +182,6 @@ pub struct CollationInfo {
pub collator: CollatorId, pub collator: CollatorId,
/// Signature on blake2-256 of the block data by collator. /// Signature on blake2-256 of the block data by collator.
pub signature: CollatorSignature, pub signature: CollatorSignature,
/// Egress queue roots. Must be sorted lexicographically (ascending)
/// by parachain ID.
pub egress_queue_roots: Vec<(Id, Hash)>,
/// The head-data /// The head-data
pub head_data: HeadData, pub head_data: HeadData,
/// blake2-256 Hash of block data. /// blake2-256 Hash of block data.
@@ -259,7 +196,6 @@ impl From<CandidateReceipt> for CollationInfo {
parachain_index: receipt.parachain_index, parachain_index: receipt.parachain_index,
collator: receipt.collator, collator: receipt.collator,
signature: receipt.signature, signature: receipt.signature,
egress_queue_roots: receipt.egress_queue_roots,
head_data: receipt.head_data, head_data: receipt.head_data,
block_data_hash: receipt.block_data_hash, block_data_hash: receipt.block_data_hash,
upward_messages: receipt.upward_messages, upward_messages: receipt.upward_messages,
@@ -294,9 +230,6 @@ pub struct CandidateReceipt {
pub head_data: HeadData, pub head_data: HeadData,
/// The parent head-data. /// The parent head-data.
pub parent_head: HeadData, pub parent_head: HeadData,
/// Egress queue roots. Must be sorted lexicographically (ascending)
/// by parachain ID.
pub egress_queue_roots: Vec<(Id, Hash)>,
/// Fees paid from the chain to the relay chain validators /// Fees paid from the chain to the relay chain validators
pub fees: Balance, pub fees: Balance,
/// blake2-256 Hash of block data. /// blake2-256 Hash of block data.
@@ -337,7 +270,6 @@ impl PartialEq<CollationInfo> for CandidateReceipt {
self.parachain_index == info.parachain_index && self.parachain_index == info.parachain_index &&
self.collator == info.collator && self.collator == info.collator &&
self.signature == info.signature && self.signature == info.signature &&
self.egress_queue_roots == info.egress_queue_roots &&
self.head_data == info.head_data && self.head_data == info.head_data &&
self.block_data_hash == info.block_data_hash && self.block_data_hash == info.block_data_hash &&
self.upward_messages == info.upward_messages self.upward_messages == info.upward_messages
@@ -369,68 +301,8 @@ pub struct Collation {
pub struct PoVBlock { pub struct PoVBlock {
/// Block data. /// Block data.
pub block_data: BlockData, pub block_data: BlockData,
/// Ingress for the parachain.
pub ingress: ConsolidatedIngress,
} }
/// Parachain ingress queue message.
#[derive(PartialEq, Eq, Clone, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Encode, Debug))]
pub struct Message(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
impl AsRef<[u8]> for Message {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl From<TargetedMessage> for Message {
fn from(targeted: TargetedMessage) -> Self {
Message(targeted.data)
}
}
/// All ingress roots at one block.
///
/// This is an ordered vector of other parachain's egress queue roots from a specific block.
/// empty roots are omitted. Each parachain may appear once at most.
#[derive(Default, PartialEq, Eq, Clone, Encode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Decode))]
pub struct BlockIngressRoots(pub Vec<(Id, Hash)>);
/// All ingress roots, grouped by block number (ascending). To properly
/// interpret this struct, the user must have knowledge of which fork of the relay
/// chain all block numbers correspond to.
#[derive(Default, PartialEq, Eq, Clone, Encode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Decode))]
pub struct StructuredUnroutedIngress(pub Vec<(BlockNumber, BlockIngressRoots)>);
#[cfg(feature = "std")]
impl StructuredUnroutedIngress {
/// Get the length of all the ingress roots across all blocks.
pub fn len(&self) -> usize {
self.0.iter().fold(0, |a, (_, roots)| a + roots.0.len())
}
/// Returns an iterator over all ingress roots. The block number indicates
/// the height at which that root was posted to the relay chain. The parachain ID is the
/// message sender.
pub fn iter(&self) -> impl Iterator<Item=(BlockNumber, &Id, &Hash)> {
self.0.iter().flat_map(|&(n, ref roots)|
roots.0.iter().map(move |&(ref from, ref root)| (n, from, root))
)
}
}
/// Consolidated ingress queue data.
///
/// This is just an ordered vector of other parachains' egress queues,
/// obtained according to the routing rules. The same parachain may appear
/// more than once.
#[derive(Default, PartialEq, Eq, Clone, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Encode, Debug))]
pub struct ConsolidatedIngress(pub Vec<(Id, Vec<Message>)>);
/// Parachain block data. /// Parachain block data.
/// ///
/// contains everything required to validate para-block, may contain block and witness data /// contains everything required to validate para-block, may contain block and witness data
@@ -580,12 +452,6 @@ sp_api::decl_runtime_apis! {
fn parachain_status(id: Id) -> Option<Status>; fn parachain_status(id: Id) -> Option<Status>;
/// Get the given parachain's head code blob. /// Get the given parachain's head code blob.
fn parachain_code(id: Id) -> Option<Vec<u8>>; fn parachain_code(id: Id) -> Option<Vec<u8>>;
/// Get all the unrouted ingress roots at the given block that
/// are targeting the given parachain.
///
/// If `since` is provided, only messages since (including those in) that block
/// will be included.
fn ingress(to: Id, since: Option<BlockNumber>) -> Option<StructuredUnroutedIngress>;
/// Extract the heads that were set by this set of extrinsics. /// Extract the heads that were set by this set of extrinsics.
fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>>; fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>>;
} }
+10 -402
View File
@@ -18,19 +18,18 @@
use rstd::prelude::*; use rstd::prelude::*;
use rstd::result; use rstd::result;
use rstd::collections::btree_map::BTreeMap;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use sp_runtime::traits::{ use sp_runtime::traits::{
Hash as HashT, BlakeTwo256, Saturating, One, Zero, Dispatchable, Hash as HashT, BlakeTwo256, Saturating, One, Dispatchable,
AccountIdConversion, BadOrigin, AccountIdConversion, BadOrigin,
}; };
use frame_support::weights::SimpleDispatchInfo; use frame_support::weights::SimpleDispatchInfo;
use primitives::{ use primitives::{
Hash, Balance, Balance,
parachain::{ parachain::{
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, ParachainDispatchOrigin, self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, ParachainDispatchOrigin,
UpwardMessage, BlockIngressRoots, ValidatorId, ActiveParas, CollatorId, Retriable, UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable,
NEW_HEADS_IDENTIFIER, NEW_HEADS_IDENTIFIER,
}, },
}; };
@@ -66,11 +65,6 @@ impl<N: Saturating + One + PartialOrd + PartialEq + Clone> Iterator for BlockNum
} }
} }
// creates a range iterator between `low` and `high`. `low` must be <= `high`.
fn number_range<N>(low: N, high: N) -> BlockNumberRange<N> {
BlockNumberRange { low, high }
}
// wrapper trait because an associated type of `Currency<Self::AccountId,Balance=Balance>` // wrapper trait because an associated type of `Currency<Self::AccountId,Balance=Balance>`
// doesn't work.` // doesn't work.`
pub trait ParachainCurrency<AccountId> { pub trait ParachainCurrency<AccountId> {
@@ -147,12 +141,6 @@ pub enum Origin {
Parachain(ParaId), Parachain(ParaId),
} }
// result of <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node()
const EMPTY_TRIE_ROOT: [u8; 32] = [
3, 23, 10, 46, 117, 151, 183, 183, 227, 216, 76, 5, 57, 29, 19, 154,
98, 177, 87, 231, 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, 19, 20
];
/// Total number of individual messages allowed in the parachain -> relay-chain message queue. /// Total number of individual messages allowed in the parachain -> relay-chain message queue.
const MAX_QUEUE_COUNT: usize = 100; const MAX_QUEUE_COUNT: usize = 100;
/// Total size of messages allowed in the parachain -> relay-chain message queue before which no /// Total size of messages allowed in the parachain -> relay-chain message queue before which no
@@ -169,18 +157,6 @@ decl_storage! {
pub Code get(parachain_code): map hasher(blake2_256) ParaId => Option<Vec<u8>>; pub Code get(parachain_code): map hasher(blake2_256) ParaId => Option<Vec<u8>>;
/// The heads of the parachains registered at present. /// The heads of the parachains registered at present.
pub Heads get(parachain_head): map hasher(blake2_256) ParaId => Option<Vec<u8>>; pub Heads get(parachain_head): map hasher(blake2_256) ParaId => Option<Vec<u8>>;
/// The watermark heights of the parachains registered at present.
/// For every parachain, this is the block height from which all messages targeting
/// that parachain have been processed. Can be `None` only if the parachain doesn't exist.
pub Watermarks get(watermark): map hasher(blake2_256) ParaId => Option<T::BlockNumber>;
/// Unrouted ingress. Maps (BlockNumber, to_chain) pairs to [(from_chain, egress_root)].
///
/// There may be an entry under (i, p) in this map for every i between the parachain's
/// watermark and the current block.
pub UnroutedIngress:
map hasher(blake2_256) (T::BlockNumber, ParaId) => Option<Vec<(ParaId, Hash)>>;
/// Messages ready to be dispatched onto the relay chain. It is subject to /// Messages ready to be dispatched onto the relay chain. It is subject to
/// `MAX_MESSAGE_COUNT` and `WATERMARK_MESSAGE_SIZE`. /// `MAX_MESSAGE_COUNT` and `WATERMARK_MESSAGE_SIZE`.
pub RelayDispatchQueue: map hasher(blake2_256) ParaId => Vec<UpwardMessage>; pub RelayDispatchQueue: map hasher(blake2_256) ParaId => Vec<UpwardMessage>;
@@ -219,14 +195,6 @@ decl_error! {
QueueFull, QueueFull,
/// The message origin is invalid. /// The message origin is invalid.
InvalidMessageOrigin, InvalidMessageOrigin,
/// Egress routes should be in ascending order by parachain ID without duplicates.
EgressOutOfOrder,
/// A parachain cannot route a message to itself.
SelfAddressed,
/// The trie root cannot be empty.
EmptyTrieRoot,
/// Cannot route to a non-existing parachain.
DestinationDoesNotExist,
/// No validator group for parachain. /// No validator group for parachain.
NoValidatorGroup, NoValidatorGroup,
/// Not enough validity votes for candidate. /// Not enough validity votes for candidate.
@@ -292,7 +260,6 @@ decl_module! {
MAX_QUEUE_COUNT, MAX_QUEUE_COUNT,
WATERMARK_QUEUE_SIZE, WATERMARK_QUEUE_SIZE,
)?; )?;
Self::check_egress_queue_roots(&head, &active_parachains)?;
let id = head.parachain_index(); let id = head.parachain_index();
proceeded.push(id); proceeded.push(id);
@@ -301,12 +268,10 @@ decl_module! {
} }
let para_blocks = Self::check_candidates(&heads, &active_parachains)?; let para_blocks = Self::check_candidates(&heads, &active_parachains)?;
let current_number = <system::Module<T>>::block_number();
<attestations::Module<T>>::note_included(&heads, para_blocks); <attestations::Module<T>>::note_included(&heads, para_blocks);
Self::update_routing( Self::update_routing(
current_number,
&heads, &heads,
); );
@@ -351,12 +316,6 @@ impl<T: Trait> Module<T> {
) { ) {
<Code>::insert(id, code); <Code>::insert(id, code);
<Heads>::insert(id, initial_head_data); <Heads>::insert(id, initial_head_data);
// Because there are no ordering guarantees that inherents
// are applied before regular transactions, a parachain candidate could
// be registered before the `UpdateHeads` inherent is processed. If so, messages
// could be sent to a parachain in the block it is registered.
<Watermarks<T>>::insert(id, <system::Module<T>>::block_number().saturating_sub(One::one()));
} }
pub fn cleanup_para( pub fn cleanup_para(
@@ -364,19 +323,6 @@ impl<T: Trait> Module<T> {
) { ) {
<Code>::remove(id); <Code>::remove(id);
<Heads>::remove(id); <Heads>::remove(id);
let watermark = <Watermarks<T>>::take(id);
// clear all routing entries _to_. But not those _from_.
if let Some(watermark) = watermark {
let now = <system::Module<T>>::block_number();
// iterate over all blocks between watermark and now + 1 (since messages might
// have already been sent to `id` in this block.
for unrouted_block in number_range(watermark, now).map(|n| n.saturating_add(One::one())) {
<UnroutedIngress<T>>::remove(&(unrouted_block, id));
}
}
} }
/// Dispatch some messages from a parachain. /// Dispatch some messages from a parachain.
@@ -439,38 +385,14 @@ impl<T: Trait> Module<T> {
/// Update routing information from the parachain heads. This queues upwards /// Update routing information from the parachain heads. This queues upwards
/// messages to the relay chain as well. /// messages to the relay chain as well.
fn update_routing( fn update_routing(
now: T::BlockNumber,
heads: &[AttestedCandidate], heads: &[AttestedCandidate],
) { ) {
// TODO: per-chain watermark
// https://github.com/paritytech/polkadot/issues/286
let watermark = now.saturating_sub(One::one());
let mut ingress_update = BTreeMap::new();
// we sort them in order to provide a fast lookup to ensure we can avoid duplicates in the // we sort them in order to provide a fast lookup to ensure we can avoid duplicates in the
// needs_dispatch queue. // needs_dispatch queue.
let mut ordered_needs_dispatch = NeedsDispatch::get(); let mut ordered_needs_dispatch = NeedsDispatch::get();
for head in heads.iter() { for head in heads.iter() {
let id = head.parachain_index(); let id = head.parachain_index();
<Heads>::insert(id, &head.candidate.head_data.0);
let last_watermark = <Watermarks<T>>::mutate(id, |mark| {
rstd::mem::replace(mark, Some(watermark))
});
if let Some(last_watermark) = last_watermark {
// Discard routed ingress.
for routed_height in number_range(last_watermark, watermark) {
<UnroutedIngress<T>>::remove(&(routed_height, id));
}
}
// place our egress root to `to` into the ingress table for (now, `to`).
for &(to, root) in &head.candidate.egress_queue_roots {
ingress_update.entry(to).or_insert_with(Vec::new).push((id, root));
}
// Queue up upwards messages (from parachains to relay chain). // Queue up upwards messages (from parachains to relay chain).
Self::queue_upward_messages( Self::queue_upward_messages(
@@ -481,11 +403,6 @@ impl<T: Trait> Module<T> {
} }
NeedsDispatch::put(ordered_needs_dispatch); NeedsDispatch::put(ordered_needs_dispatch);
// apply the ingress update.
for (to, ingress_roots) in ingress_update {
<UnroutedIngress<T>>::insert((now, to), ingress_roots);
}
} }
/// Place any new upward messages into our queue for later dispatch. /// Place any new upward messages into our queue for later dispatch.
@@ -623,32 +540,6 @@ impl<T: Trait> Module<T> {
(DutyRoster { validator_duty: roles_val, }, orig_seed) (DutyRoster { validator_duty: roles_val, }, orig_seed)
} }
/// Calculate the ingress to a specific parachain.
/// If `since` is provided, only messages since (including those in) that block
/// will be included.
/// Complexity: O(n) in the number of blocks since the supplied block.
/// invoked off-chain.
///
/// Yields a structure containing all unrouted ingress to the parachain.
pub fn ingress(to: ParaId, since: Option<T::BlockNumber>) -> Option<Vec<(T::BlockNumber, BlockIngressRoots)>> {
let watermark = <Watermarks<T>>::get(to)?;
let now = <system::Module<T>>::block_number();
let watermark_since = watermark.saturating_add(One::one());
let since = rstd::cmp::max(since.unwrap_or(Zero::zero()), watermark_since);
if since > now {
return Some(Vec::new());
}
Some(number_range(since, now)
.filter_map(|unrouted_height| {
<UnroutedIngress<T>>::get(&(unrouted_height, to)).map(|roots| {
(unrouted_height, BlockIngressRoots(roots))
})
})
.collect())
}
/// Get the parachain status necessary for validation. /// Get the parachain status necessary for validation.
pub fn parachain_status(id: &parachain::Id) -> Option<parachain::Status> { pub fn parachain_status(id: &parachain::Id) -> Option<parachain::Status> {
let balance = T::ParachainCurrency::free_balance(*id); let balance = T::ParachainCurrency::free_balance(*id);
@@ -669,42 +560,6 @@ impl<T: Trait> Module<T> {
T::ActiveParachains::active_paras() T::ActiveParachains::active_paras()
} }
fn check_egress_queue_roots(
head: &AttestedCandidate,
active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)]
) -> DispatchResult {
let mut last_egress_id = None;
let mut iter = active_parachains.iter().map(|x| x.0);
for (egress_para_id, root) in &head.candidate.egress_queue_roots {
// egress routes should be ascending order by parachain ID without duplicate.
ensure!(
last_egress_id.as_ref().map_or(true, |x| x < &egress_para_id),
Error::<T>::EgressOutOfOrder,
);
// a parachain can't route to self
ensure!(
*egress_para_id != head.candidate.parachain_index,
Error::<T>::SelfAddressed,
);
// no empty trie roots
ensure!(
*root != EMPTY_TRIE_ROOT.into(),
Error::<T>::EmptyTrieRoot,
);
// can't route to a parachain which doesn't exist
ensure!(
iter.find(|x| x == egress_para_id).is_some(),
Error::<T>::DestinationDoesNotExist,
);
last_egress_id = Some(egress_para_id)
}
Ok(())
}
// check the attestations on these candidates. The candidates should have been checked // check the attestations on these candidates. The candidates should have been checked
// that each candidates' chain ID is valid. // that each candidates' chain ID is valid.
fn check_candidates( fn check_candidates(
@@ -984,12 +839,18 @@ mod tests {
}; };
use keyring::Sr25519Keyring; use keyring::Sr25519Keyring;
use frame_support::{ use frame_support::{
impl_outer_origin, impl_outer_dispatch, assert_ok, assert_err, assert_noop, parameter_types, impl_outer_origin, impl_outer_dispatch, assert_ok, assert_err, parameter_types,
}; };
use crate::parachains; use crate::parachains;
use crate::registrar; use crate::registrar;
use crate::slots; use crate::slots;
// result of <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node()
const EMPTY_TRIE_ROOT: [u8; 32] = [
3, 23, 10, 46, 117, 151, 183, 183, 227, 216, 76, 5, 57, 29, 19, 154,
98, 177, 87, 231, 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, 19, 20
];
impl_outer_origin! { impl_outer_origin! {
pub enum Origin for Test { pub enum Origin for Test {
parachains parachains
@@ -1312,25 +1173,6 @@ mod tests {
candidate.validator_indices = validator_indices; candidate.validator_indices = validator_indices;
} }
fn new_candidate_with_egress_roots(egress_queue_roots: Vec<(ParaId, H256)>) -> AttestedCandidate {
AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]),
egress_queue_roots,
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![],
erasure_root: [1u8; 32].into(),
}
}
}
fn new_candidate_with_upward_messages( fn new_candidate_with_upward_messages(
id: u32, id: u32,
upward_messages: Vec<(ParachainDispatchOrigin, Vec<u8>)> upward_messages: Vec<(ParachainDispatchOrigin, Vec<u8>)>
@@ -1344,7 +1186,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: upward_messages.into_iter() upward_messages: upward_messages.into_iter()
@@ -1755,7 +1596,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![], upward_messages: vec![],
@@ -1788,7 +1628,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![], upward_messages: vec![],
@@ -1805,7 +1644,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![2, 3, 4]), head_data: HeadData(vec![2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![], upward_messages: vec![],
@@ -1846,7 +1684,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![], upward_messages: vec![],
@@ -1885,7 +1722,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]), head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![], upward_messages: vec![],
@@ -1906,234 +1742,6 @@ mod tests {
}); });
} }
#[test]
fn ingress_works() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
(99u32.into(), vec![1, 2, 3], vec![4, 5, 6]),
];
new_test_ext(parachains).execute_with(|| {
assert_eq!(Parachains::ingress(ParaId::from(1), None), Some(Vec::new()));
assert_eq!(Parachains::ingress(ParaId::from(99), None), Some(Vec::new()));
init_block();
for i in 1..10 {
run_to_block(i);
let from_a = vec![(1.into(), [i as u8; 32].into())];
let parent_head = HeadData(if i == 1 {
vec![]
} else {
vec![1, 2, 3]
});
let mut candidate_a = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
parent_head: parent_head.clone(),
egress_queue_roots: from_a.clone(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![],
erasure_root: [1u8; 32].into(),
}
};
let from_b = vec![(99.into(), [i as u8; 32].into())];
let mut candidate_b = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 1.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
parent_head,
egress_queue_roots: from_b.clone(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![],
erasure_root: [1u8; 32].into(),
}
};
make_attestations(&mut candidate_a);
make_attestations(&mut candidate_b);
assert_ok!(Parachains::dispatch(
set_heads(vec![candidate_a, candidate_b]),
Origin::NONE,
));
}
run_to_block(10);
assert_ok!(Parachains::dispatch(
set_heads(vec![]),
Origin::NONE,
));
// parachain 1 has had a bunch of parachain candidates included,
// which raises the watermark.
assert_eq!(
Parachains::ingress(ParaId::from(1), None),
Some(vec![
(9, BlockIngressRoots(vec![
(0.into(), [9; 32].into())
]))
]),
);
// parachain 99 hasn't had any candidates included, so the
// ingress is piling up.
assert_eq!(
Parachains::ingress(ParaId::from(99), None),
Some((1..10).map(|i| (i, BlockIngressRoots(
vec![(1.into(), [i as u8; 32].into())]
))).collect::<Vec<_>>()),
);
assert_ok!(Registrar::deregister_para(Origin::ROOT, 1u32.into()));
// after deregistering, there is no ingress to 1, but unrouted messages
// from 1 stick around.
assert_eq!(Parachains::ingress(ParaId::from(1), None), None);
assert_eq!(Parachains::ingress(ParaId::from(99), None), Some((1..10).map(|i| (i, BlockIngressRoots(
vec![(1.into(), [i as u8; 32].into())]
))).collect::<Vec<_>>()));
run_to_block(11);
let mut candidate_c = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 99.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
parent_head: HeadData(vec![4, 5, 6]),
egress_queue_roots: Vec::new(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![],
erasure_root: [1u8; 32].into(),
}
};
make_attestations(&mut candidate_c);
assert_ok!(Parachains::dispatch(
set_heads(vec![candidate_c]),
Origin::NONE,
));
run_to_block(12);
// at the next block, ingress to 99 should be empty.
assert_eq!(Parachains::ingress(ParaId::from(99), None), Some(Vec::new()));
});
}
#[test]
fn egress_routed_to_non_existent_parachain_is_rejected() {
// That no parachain is routed to which doesn't exist
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
new_test_ext(parachains.clone()).execute_with(|| {
run_to_block(2);
// parachain 99 does not exist
let non_existent = vec![(99.into(), [1; 32].into())];
let mut candidate = new_candidate_with_egress_roots(non_existent);
make_attestations(&mut candidate);
assert_noop!(
Parachains::set_heads(Origin::NONE, vec![candidate.clone()]),
Error::<Test>::DestinationDoesNotExist
);
});
}
#[test]
fn egress_routed_to_self_is_rejected() {
// That the parachain doesn't route to self
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
new_test_ext(parachains.clone()).execute_with(|| {
run_to_block(2);
// parachain 0 is self
let to_self = vec![(0.into(), [1; 32].into())];
let mut candidate = new_candidate_with_egress_roots(to_self);
make_attestations(&mut candidate);
assert_noop!(
Parachains::set_heads(Origin::NONE, vec![candidate.clone()]),
Error::<Test>::SelfAddressed
);
});
}
#[test]
fn egress_queue_roots_out_of_order_rejected() {
// That the list of egress queue roots is in ascending order by `ParaId`.
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
new_test_ext(parachains.clone()).execute_with(|| {
run_to_block(2);
// parachain 0 is self
let out_of_order = vec![(1.into(), [1; 32].into()), ((0.into(), [1; 32].into()))];
let mut candidate = new_candidate_with_egress_roots(out_of_order);
make_attestations(&mut candidate);
assert_noop!(
Parachains::set_heads(Origin::NONE, vec![candidate.clone()]),
Error::<Test>::EgressOutOfOrder
);
});
}
#[test]
fn egress_queue_roots_empty_trie_roots_rejected() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
(2u32.into(), vec![], vec![]),
];
new_test_ext(parachains.clone()).execute_with(|| {
run_to_block(2);
// parachain 0 is self
let contains_empty_trie_root = vec![(1.into(), [1; 32].into()), ((2.into(), EMPTY_TRIE_ROOT.into()))];
let mut candidate = new_candidate_with_egress_roots(contains_empty_trie_root);
make_attestations(&mut candidate);
assert_noop!(
Parachains::set_heads(Origin::NONE, vec![candidate.clone()]),
Error::<Test>::EmptyTrieRoot
);
});
}
#[test] #[test]
fn empty_trie_root_const_is_blake2_hashed_null_node() { fn empty_trie_root_const_is_blake2_hashed_null_node() {
let hashed_null_node = <NodeCodec<Blake2Hasher> as trie_db::NodeCodec>::hashed_null_node(); let hashed_null_node = <NodeCodec<Blake2Hasher> as trie_db::NodeCodec>::hashed_null_node();
-4
View File
@@ -198,8 +198,6 @@ decl_storage! {
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn build<T: Trait>(config: &GenesisConfig<T>) { fn build<T: Trait>(config: &GenesisConfig<T>) {
use sp_runtime::traits::Zero;
let mut p = config.parachains.clone(); let mut p = config.parachains.clone();
p.sort_unstable_by_key(|&(ref id, _, _)| *id); p.sort_unstable_by_key(|&(ref id, _, _)| *id);
p.dedup_by_key(|&mut (ref id, _, _)| *id); p.dedup_by_key(|&mut (ref id, _, _)| *id);
@@ -213,7 +211,6 @@ fn build<T: Trait>(config: &GenesisConfig<T>) {
// no ingress -- a chain cannot be routed to until it is live. // no ingress -- a chain cannot be routed to until it is live.
<parachains::Code>::insert(&id, &code); <parachains::Code>::insert(&id, &code);
<parachains::Heads>::insert(&id, &genesis); <parachains::Heads>::insert(&id, &genesis);
<parachains::Watermarks<T>>::insert(&id, T::BlockNumber::zero());
// Save initial parachains in registrar // Save initial parachains in registrar
Paras::insert(id, ParaInfo { scheduling: Scheduling::Always }) Paras::insert(id, ParaInfo { scheduling: Scheduling::Always })
} }
@@ -907,7 +904,6 @@ mod tests {
signature: block_data_hash.using_encoded(|d| collator.sign(d)), signature: block_data_hash.using_encoded(|d| collator.sign(d)),
head_data: HeadData(head_data.to_vec()), head_data: HeadData(head_data.to_vec()),
parent_head: HeadData(parent_head.to_vec()), parent_head: HeadData(parent_head.to_vec()),
egress_queue_roots: vec![],
fees: 0, fees: 0,
block_data_hash, block_data_hash,
upward_messages: vec![], upward_messages: vec![],
-5
View File
@@ -770,11 +770,6 @@ sp_api::impl_runtime_apis! {
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> { fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
Parachains::parachain_code(&id) Parachains::parachain_code(&id)
} }
fn ingress(to: parachain::Id, since: Option<BlockNumber>)
-> Option<parachain::StructuredUnroutedIngress>
{
Parachains::ingress(to, since).map(parachain::StructuredUnroutedIngress)
}
fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>> { fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>> {
extrinsics extrinsics
.into_iter() .into_iter()
-5
View File
@@ -688,11 +688,6 @@ sp_api::impl_runtime_apis! {
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> { fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
Parachains::parachain_code(&id) Parachains::parachain_code(&id)
} }
fn ingress(to: parachain::Id, since: Option<BlockNumber>)
-> Option<parachain::StructuredUnroutedIngress>
{
Parachains::ingress(to, since).map(parachain::StructuredUnroutedIngress)
}
fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>> { fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>> {
extrinsics extrinsics
.into_iter() .into_iter()
@@ -24,9 +24,7 @@ use sp_core::Pair;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use primitives::{ use primitives::{
Hash, Hash,
parachain::{ parachain::{HeadData, BlockData, Id as ParaId, Status as ParachainStatus},
HeadData, BlockData, Id as ParaId, Message, OutgoingMessages, Status as ParachainStatus,
},
}; };
use collator::{ use collator::{
InvalidHead, ParachainContext, Network, BuildParachainContext, load_spec, Configuration, InvalidHead, ParachainContext, Network, BuildParachainContext, load_spec, Configuration,
@@ -57,13 +55,12 @@ struct AdderContext {
/// The parachain context. /// The parachain context.
impl ParachainContext for AdderContext { impl ParachainContext for AdderContext {
type ProduceCandidate = Ready<Result<(BlockData, HeadData, OutgoingMessages), InvalidHead>>; type ProduceCandidate = Ready<Result<(BlockData, HeadData), InvalidHead>>;
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>( fn produce_candidate(
&mut self, &mut self,
_relay_parent: Hash, _relay_parent: Hash,
status: ParachainStatus, status: ParachainStatus,
ingress: I,
) -> Self::ProduceCandidate ) -> Self::ProduceCandidate
{ {
let adder_head = match AdderHead::decode(&mut &status.head_data.0[..]) { let adder_head = match AdderHead::decode(&mut &status.head_data.0[..]) {
@@ -86,11 +83,7 @@ impl ParachainContext for AdderContext {
add: adder_head.number % 100, add: adder_head.number % 100,
}; };
let from_messages = ::adder::process_messages( let next_head = ::adder::execute(adder_head.hash(), adder_head, &next_body)
ingress.into_iter().map(|(_, msg)| msg.0)
);
let next_head = ::adder::execute(adder_head.hash(), adder_head, &next_body, from_messages)
.expect("good execution params; qed"); .expect("good execution params; qed");
let encoded_head = HeadData(next_head.encode()); let encoded_head = HeadData(next_head.encode());
@@ -100,7 +93,7 @@ impl ParachainContext for AdderContext {
next_head.number, next_body.state.overflowing_add(next_body.add).0); next_head.number, next_body.state.overflowing_add(next_body.add).0);
db.insert(next_head.clone(), next_body); db.insert(next_head.clone(), next_body);
ok((encoded_body, encoded_head, OutgoingMessages { outgoing_messages: Vec::new() })) ok((encoded_body, encoded_head))
} }
} }
@@ -90,7 +90,6 @@ pub fn execute(
parent_hash: [u8; 32], parent_hash: [u8; 32],
parent_head: HeadData, parent_head: HeadData,
block_data: &BlockData, block_data: &BlockData,
from_messages: u64,
) -> Result<HeadData, StateMismatch> { ) -> Result<HeadData, StateMismatch> {
debug_assert_eq!(parent_hash, parent_head.hash()); debug_assert_eq!(parent_hash, parent_head.hash());
@@ -99,7 +98,6 @@ pub fn execute(
} }
let new_state = block_data.state.overflowing_add(block_data.add).0; let new_state = block_data.state.overflowing_add(block_data.add).0;
let new_state = new_state.overflowing_add(from_messages).0;
Ok(HeadData { Ok(HeadData {
number: parent_head.number + 1, number: parent_head.number + 1,
@@ -48,13 +48,7 @@ pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
let parent_hash = tiny_keccak::keccak256(&params.parent_head[..]); let parent_hash = tiny_keccak::keccak256(&params.parent_head[..]);
// we also add based on incoming data from messages. ignoring unknown message match crate::execute(parent_hash, parent_head, &block_data) {
// kinds.
let from_messages = crate::process_messages(
params.ingress.iter().map(|incoming| &incoming.data[..])
);
match crate::execute(parent_hash, parent_head, &block_data, from_messages) {
Ok(new_head) => parachain::write_result( Ok(new_head) => parachain::write_result(
&ValidationResult { head_data: new_head.encode() } &ValidationResult { head_data: new_head.encode() }
), ),
+15 -179
View File
@@ -24,8 +24,8 @@ use std::sync::Arc;
use polkadot_primitives::{ use polkadot_primitives::{
BlakeTwo256, Block, Hash, HashT, BlockId, Balance, BlakeTwo256, Block, Hash, HashT, BlockId, Balance,
parachain::{ parachain::{
CollatorId, ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, CollationInfo, CollatorId, CandidateReceipt, CollationInfo,
ParachainHost, Id as ParaId, Collation, OutgoingMessages, FeeSchedule, ErasureChunk, ParachainHost, Id as ParaId, Collation, FeeSchedule, ErasureChunk,
HeadData, PoVBlock, HeadData, PoVBlock,
}, },
}; };
@@ -71,7 +71,7 @@ pub async fn collation_fetch<C: Collators, P>(
collators: C, collators: C,
client: Arc<P>, client: Arc<P>,
max_block_data_size: Option<u64>, max_block_data_size: Option<u64>,
) -> Result<(Collation, OutgoingMessages, HeadData, Balance),C::Error> ) -> Result<(Collation, HeadData, Balance),C::Error>
where where
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>, P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
C: Collators + Unpin, C: Collators + Unpin,
@@ -92,8 +92,8 @@ pub async fn collation_fetch<C: Collators, P>(
); );
match res { match res {
Ok((messages, parent_head, fees)) => { Ok((parent_head, fees)) => {
return Ok((collation, messages, parent_head, fees)) return Ok((collation, parent_head, fees))
} }
Err(e) => { Err(e) => {
debug!("Failed to validate parachain due to API error: {}", e); debug!("Failed to validate parachain due to API error: {}", e);
@@ -199,59 +199,6 @@ pub fn egress_roots(outgoing: &mut [TargetedMessage]) -> Vec<(ParaId, Hash)> {
egress_roots egress_roots
} }
fn check_egress(
mut outgoing: Vec<TargetedMessage>,
expected_egress_roots: &[(ParaId, Hash)],
) -> Result<OutgoingMessages, Error> {
// stable sort messages by parachain ID.
outgoing.sort_by_key(|msg| ParaId::from(msg.target));
{
let mut messages_iter = outgoing.iter().peekable();
let mut expected_egress_roots = expected_egress_roots.iter();
while let Some(batch_target) = messages_iter.peek().map(|o| o.target) {
let expected_root = match expected_egress_roots.next() {
None => return Err(Error::MissingEgressRoot {
expected: Some(batch_target),
got: None
}),
Some(&(id, ref root)) => if id == batch_target {
root
} else {
return Err(Error::MissingEgressRoot{
expected: Some(batch_target),
got: Some(id)
});
}
};
// we borrow the iterator mutably to ensure it advances so the
// next iteration of the loop starts with `messages_iter` pointing to
// the next batch.
let messages_to = messages_iter
.clone()
.take_while(|o| o.target == batch_target)
.map(|o| { let _ = messages_iter.next(); &o.data[..] });
let computed_root = message_queue_root(messages_to);
if &computed_root != expected_root {
return Err(Error::EgressRootMismatch {
id: batch_target,
expected: expected_root.clone(),
got: computed_root,
});
}
}
// also check that there are no more additional expected roots.
if let Some((next_target, _)) = expected_egress_roots.next() {
return Err(Error::MissingEgressRoot { expected: None, got: Some(*next_target) });
}
}
Ok(OutgoingMessages { outgoing_messages: outgoing })
}
struct ExternalitiesInner { struct ExternalitiesInner {
parachain_index: ParaId, parachain_index: ParaId,
outgoing: Vec<TargetedMessage>, outgoing: Vec<TargetedMessage>,
@@ -309,9 +256,8 @@ impl ExternalitiesInner {
fn final_checks( fn final_checks(
&mut self, &mut self,
upward_messages: &[UpwardMessage], upward_messages: &[UpwardMessage],
egress_queue_roots: &[(ParaId, Hash)],
fees_charged: Option<Balance>, fees_charged: Option<Balance>,
) -> Result<(OutgoingMessages, Balance), Error> { ) -> Result<Balance, Error> {
if self.upward != upward_messages { if self.upward != upward_messages {
return Err(Error::UpwardMessagesInvalid { return Err(Error::UpwardMessagesInvalid {
expected: upward_messages.to_vec(), expected: upward_messages.to_vec(),
@@ -328,12 +274,8 @@ impl ExternalitiesInner {
} }
} }
let messages = check_egress(
std::mem::replace(&mut self.outgoing, Vec::new()),
&egress_queue_roots[..],
)?;
Ok((messages, self.fees_charged)) Ok(self.fees_charged)
} }
} }
@@ -376,40 +318,6 @@ pub fn validate_chunk(
Ok(()) Ok(())
} }
/// Validate incoming messages against expected roots.
pub fn validate_incoming(
roots: &StructuredUnroutedIngress,
ingress: &ConsolidatedIngress,
) -> Result<(), Error> {
if roots.len() != ingress.0.len() {
return Err(Error::IngressCanonicalityMismatch {
expected: roots.0.len(),
got: ingress.0.len()
});
}
let all_iter = roots.iter().zip(&ingress.0);
for ((_, expected_from, root), (got_id, messages)) in all_iter {
if expected_from != got_id {
return Err(Error::IngressChainMismatch {
expected: *expected_from,
got: *got_id
});
}
let got_root = message_queue_root(messages.iter().map(|msg| &msg.0[..]));
if &got_root != root {
return Err(Error::IngressRootMismatch{
id: *expected_from,
expected: *root,
got: got_root
});
}
}
Ok(())
}
// A utility function that implements most of the collation validation logic. // A utility function that implements most of the collation validation logic.
// //
// Reused by `validate_collation` and `validate_receipt`. // Reused by `validate_collation` and `validate_receipt`.
@@ -422,13 +330,12 @@ fn do_validation<P>(
max_block_data_size: Option<u64>, max_block_data_size: Option<u64>,
fees_charged: Option<Balance>, fees_charged: Option<Balance>,
head_data: &HeadData, head_data: &HeadData,
queue_roots: &Vec<(ParaId, Hash)>,
upward_messages: &Vec<UpwardMessage>, upward_messages: &Vec<UpwardMessage>,
) -> Result<(OutgoingMessages, HeadData, Balance), Error> where ) -> Result<(HeadData, Balance), Error> where
P: ProvideRuntimeApi<Block>, P: ProvideRuntimeApi<Block>,
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>, P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
{ {
use parachain::{IncomingMessage, ValidationParams}; use parachain::ValidationParams;
if let Some(max_size) = max_block_data_size { if let Some(max_size) = max_block_data_size {
let block_data_size = pov_block.block_data.0.len() as u64; let block_data_size = pov_block.block_data.0.len() as u64;
@@ -444,22 +351,10 @@ fn do_validation<P>(
let chain_status = api.parachain_status(relay_parent, para_id)? let chain_status = api.parachain_status(relay_parent, para_id)?
.ok_or_else(|| Error::InactiveParachain(para_id))?; .ok_or_else(|| Error::InactiveParachain(para_id))?;
let roots = api.ingress(relay_parent, para_id, None)?
.ok_or_else(|| Error::InactiveParachain(para_id))?;
validate_incoming(&roots, &pov_block.ingress)?;
let params = ValidationParams { let params = ValidationParams {
parent_head: chain_status.head_data.0.clone(), parent_head: chain_status.head_data.0.clone(),
block_data: pov_block.block_data.0.clone(), block_data: pov_block.block_data.0.clone(),
ingress: pov_block.ingress.0.iter()
.flat_map(|&(source, ref messages)| {
messages.iter().map(move |msg| IncomingMessage {
source,
data: msg.0.clone(),
})
})
.collect()
}; };
let ext = Externalities::new(para_id.clone(), chain_status.balance, chain_status.fee_schedule); let ext = Externalities::new(para_id.clone(), chain_status.balance, chain_status.fee_schedule);
@@ -472,13 +367,12 @@ fn do_validation<P>(
) { ) {
Ok(result) => { Ok(result) => {
if result.head_data == head_data.0 { if result.head_data == head_data.0 {
let (messages, fees) = ext.0.lock().final_checks( let fees = ext.0.lock().final_checks(
upward_messages, upward_messages,
queue_roots,
fees_charged fees_charged
)?; )?;
Ok((messages, chain_status.head_data, fees)) Ok((chain_status.head_data, fees))
} else { } else {
Err(Error::WrongHeadData { Err(Error::WrongHeadData {
expected: head_data.0.clone(), expected: head_data.0.clone(),
@@ -500,7 +394,6 @@ pub fn produce_receipt_and_chunks(
n_validators: usize, n_validators: usize,
parent_head: HeadData, parent_head: HeadData,
pov: &PoVBlock, pov: &PoVBlock,
messages: &OutgoingMessages,
fees: Balance, fees: Balance,
info: &CollationInfo, info: &CollationInfo,
) -> Result<(CandidateReceipt, Vec<ErasureChunk>), Error> ) -> Result<(CandidateReceipt, Vec<ErasureChunk>), Error>
@@ -508,7 +401,6 @@ pub fn produce_receipt_and_chunks(
let erasure_chunks = erasure::obtain_chunks( let erasure_chunks = erasure::obtain_chunks(
n_validators, n_validators,
&pov.block_data, &pov.block_data,
Some(&messages.clone().into())
)?; )?;
let branches = erasure::branches(erasure_chunks.as_ref()); let branches = erasure::branches(erasure_chunks.as_ref());
@@ -532,7 +424,6 @@ pub fn produce_receipt_and_chunks(
signature: info.signature.clone(), signature: info.signature.clone(),
head_data: info.head_data.clone(), head_data: info.head_data.clone(),
parent_head, parent_head,
egress_queue_roots: info.egress_queue_roots.clone(),
fees, fees,
block_data_hash: info.block_data_hash.clone(), block_data_hash: info.block_data_hash.clone(),
upward_messages: info.upward_messages.clone(), upward_messages: info.upward_messages.clone(),
@@ -552,11 +443,11 @@ pub fn validate_receipt<P>(
pov_block: &PoVBlock, pov_block: &PoVBlock,
receipt: &CandidateReceipt, receipt: &CandidateReceipt,
max_block_data_size: Option<u64>, max_block_data_size: Option<u64>,
) -> Result<(OutgoingMessages, Vec<ErasureChunk>), Error> where ) -> Result<Vec<ErasureChunk>, Error> where
P: ProvideRuntimeApi<Block>, P: ProvideRuntimeApi<Block>,
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>, P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
{ {
let (messages, parent_head, _fees) = do_validation( let (parent_head, _fees) = do_validation(
client, client,
relay_parent, relay_parent,
pov_block, pov_block,
@@ -564,7 +455,6 @@ pub fn validate_receipt<P>(
max_block_data_size, max_block_data_size,
Some(receipt.fees), Some(receipt.fees),
&receipt.head_data, &receipt.head_data,
&receipt.egress_queue_roots,
&receipt.upward_messages, &receipt.upward_messages,
)?; )?;
@@ -583,7 +473,6 @@ pub fn validate_receipt<P>(
n_validators, n_validators,
parent_head, parent_head,
pov_block, pov_block,
&messages,
receipt.fees, receipt.fees,
&receipt.clone().into(), &receipt.clone().into(),
)?; )?;
@@ -595,7 +484,7 @@ pub fn validate_receipt<P>(
}); });
} }
Ok((messages, chunks)) Ok(chunks)
} }
/// Check whether a given collation is valid. Returns `Ok` on success, error otherwise. /// Check whether a given collation is valid. Returns `Ok` on success, error otherwise.
@@ -608,7 +497,7 @@ pub fn validate_collation<P>(
relay_parent: &BlockId, relay_parent: &BlockId,
collation: &Collation, collation: &Collation,
max_block_data_size: Option<u64>, max_block_data_size: Option<u64>,
) -> Result<(OutgoingMessages, HeadData, Balance), Error> where ) -> Result<(HeadData, Balance), Error> where
P: ProvideRuntimeApi<Block>, P: ProvideRuntimeApi<Block>,
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>, P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
{ {
@@ -624,7 +513,6 @@ pub fn validate_collation<P>(
max_block_data_size, max_block_data_size,
None, None,
&collation.info.head_data, &collation.info.head_data,
&collation.info.egress_queue_roots,
&collation.info.upward_messages, &collation.info.upward_messages,
) )
} }
@@ -636,50 +524,6 @@ mod tests {
use parachain::ParachainDispatchOrigin; use parachain::ParachainDispatchOrigin;
use polkadot_primitives::parachain::{CandidateReceipt, HeadData}; use polkadot_primitives::parachain::{CandidateReceipt, HeadData};
#[test]
fn compute_and_check_egress() {
let messages = vec![
TargetedMessage { target: 3.into(), data: vec![1, 1, 1] },
TargetedMessage { target: 1.into(), data: vec![1, 2, 3] },
TargetedMessage { target: 2.into(), data: vec![4, 5, 6] },
TargetedMessage { target: 1.into(), data: vec![7, 8, 9] },
];
let root_1 = message_queue_root(&[vec![1, 2, 3], vec![7, 8, 9]]);
let root_2 = message_queue_root(&[vec![4, 5, 6]]);
let root_3 = message_queue_root(&[vec![1, 1, 1]]);
assert!(check_egress(
messages.clone(),
&[(1.into(), root_1), (2.into(), root_2), (3.into(), root_3)],
).is_ok());
let egress_roots = egress_roots(&mut messages.clone()[..]);
assert!(check_egress(
messages.clone(),
&egress_roots[..],
).is_ok());
// missing root.
assert!(check_egress(
messages.clone(),
&[(1.into(), root_1), (3.into(), root_3)],
).is_err());
// extra root.
assert!(check_egress(
messages.clone(),
&[(1.into(), root_1), (2.into(), root_2), (3.into(), root_3), (4.into(), Default::default())],
).is_err());
// root mismatch.
assert!(check_egress(
messages.clone(),
&[(1.into(), root_2), (2.into(), root_1), (3.into(), root_3)],
).is_err());
}
#[test] #[test]
fn ext_rejects_local_message() { fn ext_rejects_local_message() {
let mut ext = ExternalitiesInner { let mut ext = ExternalitiesInner {
@@ -719,7 +563,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(Vec::new()), head_data: HeadData(Vec::new()),
parent_head: HeadData(Vec::new()), parent_head: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![ upward_messages: vec![
@@ -730,7 +573,6 @@ mod tests {
}; };
assert!(ext().final_checks( assert!(ext().final_checks(
&receipt.upward_messages, &receipt.upward_messages,
&receipt.egress_queue_roots,
Some(receipt.fees), Some(receipt.fees),
).is_err()); ).is_err());
let receipt = CandidateReceipt { let receipt = CandidateReceipt {
@@ -739,7 +581,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(Vec::new()), head_data: HeadData(Vec::new()),
parent_head: HeadData(Vec::new()), parent_head: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![ upward_messages: vec![
@@ -749,7 +590,6 @@ mod tests {
}; };
assert!(ext().final_checks( assert!(ext().final_checks(
&receipt.upward_messages, &receipt.upward_messages,
&receipt.egress_queue_roots,
Some(receipt.fees), Some(receipt.fees),
).is_err()); ).is_err());
let receipt = CandidateReceipt { let receipt = CandidateReceipt {
@@ -758,7 +598,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(Vec::new()), head_data: HeadData(Vec::new()),
parent_head: HeadData(Vec::new()), parent_head: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![ upward_messages: vec![
@@ -768,7 +607,6 @@ mod tests {
}; };
assert!(ext().final_checks( assert!(ext().final_checks(
&receipt.upward_messages, &receipt.upward_messages,
&receipt.egress_queue_roots,
Some(receipt.fees), Some(receipt.fees),
).is_err()); ).is_err());
let receipt = CandidateReceipt { let receipt = CandidateReceipt {
@@ -777,7 +615,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(Vec::new()), head_data: HeadData(Vec::new()),
parent_head: HeadData(Vec::new()), parent_head: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0, fees: 0,
block_data_hash: Default::default(), block_data_hash: Default::default(),
upward_messages: vec![ upward_messages: vec![
@@ -787,7 +624,6 @@ mod tests {
}; };
assert!(ext().final_checks( assert!(ext().final_checks(
&receipt.upward_messages, &receipt.upward_messages,
&receipt.egress_queue_roots,
Some(receipt.fees), Some(receipt.fees),
).is_ok()); ).is_ok());
} }
+2 -18
View File
@@ -38,7 +38,7 @@ use codec::Encode;
use polkadot_primitives::Hash; use polkadot_primitives::Hash;
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, Chain, DutyRoster, CandidateReceipt, Id as ParaId, Chain, DutyRoster, CandidateReceipt,
Statement as PrimitiveStatement, Message, OutgoingMessages, Statement as PrimitiveStatement,
Collation, PoVBlock, ErasureChunk, ValidatorSignature, ValidatorIndex, Collation, PoVBlock, ErasureChunk, ValidatorSignature, ValidatorIndex,
ValidatorPair, ValidatorId, ValidatorPair, ValidatorId,
}; };
@@ -48,7 +48,7 @@ use futures::prelude::*;
pub use self::block_production::ProposerFactory; pub use self::block_production::ProposerFactory;
pub use self::collation::{ pub use self::collation::{
validate_collation, validate_incoming, message_queue_root, egress_roots, Collators, validate_collation, message_queue_root, egress_roots, Collators,
produce_receipt_and_chunks, produce_receipt_and_chunks,
}; };
pub use self::error::Error; pub use self::error::Error;
@@ -70,9 +70,6 @@ pub mod collation;
pub mod validation_service; pub mod validation_service;
pub mod block_production; pub mod block_production;
/// Incoming messages; a series of sorted (ParaId, Message) pairs.
pub type Incoming = Vec<(ParaId, Vec<Message>)>;
/// A handle to a statement table router. /// A handle to a statement table router.
/// ///
/// This is expected to be a lightweight, shared type like an `Arc`. /// This is expected to be a lightweight, shared type like an `Arc`.
@@ -92,7 +89,6 @@ pub trait TableRouter: Clone {
&self, &self,
collation: Collation, collation: Collation,
receipt: CandidateReceipt, receipt: CandidateReceipt,
outgoing: OutgoingMessages,
chunks: (ValidatorIndex, &[ErasureChunk]), chunks: (ValidatorIndex, &[ErasureChunk]),
) -> Self::SendLocalCollation; ) -> Self::SendLocalCollation;
@@ -218,18 +214,6 @@ pub fn make_group_info(
} }
/// Compute the (target, root, messages) of all outgoing queues.
pub fn outgoing_queues(outgoing_targeted: &'_ OutgoingMessages)
-> impl Iterator<Item=(ParaId, Hash, Vec<Message>)> + '_
{
outgoing_targeted.message_queues().filter_map(|queue| {
let target = queue.get(0)?.target;
let queue_root = message_queue_root(queue);
let queue_data = queue.iter().map(|msg| msg.clone().into()).collect();
Some((target, queue_root, queue_data))
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+20 -56
View File
@@ -24,7 +24,7 @@ use availability_store::{Store as AvailabilityStore};
use table::{self, Table, Context as TableContextTrait}; use table::{self, Table, Context as TableContextTrait};
use polkadot_primitives::{Block, BlockId, Hash}; use polkadot_primitives::{Block, BlockId, Hash};
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
Id as ParaId, OutgoingMessages, CandidateReceipt, ValidatorPair, ValidatorId, Id as ParaId, CandidateReceipt, ValidatorPair, ValidatorId,
AttestedCandidate, ParachainHost, PoVBlock, ValidatorIndex, ErasureChunk, AttestedCandidate, ParachainHost, PoVBlock, ValidatorIndex, ErasureChunk,
}; };
@@ -91,7 +91,7 @@ impl TableContext {
} }
pub(crate) enum Validation { pub(crate) enum Validation {
Valid(PoVBlock, OutgoingMessages), Valid(PoVBlock),
Invalid(PoVBlock), // should take proof. Invalid(PoVBlock), // should take proof.
} }
@@ -222,10 +222,10 @@ impl Validated {
/// Note that we've validated a candidate with given hash and it is good. /// Note that we've validated a candidate with given hash and it is good.
/// outgoing message required. /// outgoing message required.
pub fn known_good(hash: Hash, collation: PoVBlock, outgoing: OutgoingMessages) -> Self { pub fn known_good(hash: Hash, collation: PoVBlock) -> Self {
Validated { Validated {
statement: GenericStatement::Valid(hash), statement: GenericStatement::Valid(hash),
result: Validation::Valid(collation, outgoing), result: Validation::Valid(collation),
} }
} }
@@ -234,26 +234,17 @@ impl Validated {
pub fn collated_local( pub fn collated_local(
receipt: CandidateReceipt, receipt: CandidateReceipt,
collation: PoVBlock, collation: PoVBlock,
outgoing: OutgoingMessages,
) -> Self { ) -> Self {
Validated { Validated {
statement: GenericStatement::Candidate(receipt), statement: GenericStatement::Candidate(receipt),
result: Validation::Valid(collation, outgoing), result: Validation::Valid(collation),
} }
} }
/// Get a reference to the proof-of-validation block. /// Get a reference to the proof-of-validation block.
pub fn pov_block(&self) -> &PoVBlock { pub fn pov_block(&self) -> &PoVBlock {
match self.result { match self.result {
Validation::Valid(ref b, _) | Validation::Invalid(ref b) => b, Validation::Valid(ref b) | Validation::Invalid(ref b) => b,
}
}
/// Get a reference to the outgoing messages data, if any.
pub fn outgoing_messages(&self) -> Option<&OutgoingMessages> {
match self.result {
Validation::Valid(_, ref ex) => Some(ex),
Validation::Invalid(_) => None,
} }
} }
} }
@@ -273,7 +264,7 @@ impl<Fetch: Future + Unpin> ParachainWork<Fetch> {
pub fn prime<P: ProvideRuntimeApi<Block>>(self, api: Arc<P>) pub fn prime<P: ProvideRuntimeApi<Block>>(self, api: Arc<P>)
-> PrimedParachainWork< -> PrimedParachainWork<
Fetch, Fetch,
impl Send + FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<(OutgoingMessages, ErasureChunk), ()> + Unpin, impl Send + FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<ErasureChunk, ()> + Unpin,
> >
where where
P: Send + Sync + 'static, P: Send + Sync + 'static,
@@ -292,8 +283,8 @@ impl<Fetch: Future + Unpin> ParachainWork<Fetch> {
); );
match res { match res {
Ok((messages, mut chunks)) => { Ok(mut chunks) => {
Ok((messages, chunks.swap_remove(local_index))) Ok(chunks.swap_remove(local_index))
} }
Err(e) => { Err(e) => {
debug!(target: "validation", "Encountered bad collation: {}", e); debug!(target: "validation", "Encountered bad collation: {}", e);
@@ -307,7 +298,7 @@ impl<Fetch: Future + Unpin> ParachainWork<Fetch> {
/// Prime the parachain work with a custom validation function. /// Prime the parachain work with a custom validation function.
pub fn prime_with<F>(self, validate: F) -> PrimedParachainWork<Fetch, F> pub fn prime_with<F>(self, validate: F) -> PrimedParachainWork<Fetch, F>
where F: FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<(OutgoingMessages, ErasureChunk), ()> where F: FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<ErasureChunk, ()>
{ {
PrimedParachainWork { inner: self, validate } PrimedParachainWork { inner: self, validate }
} }
@@ -327,7 +318,7 @@ pub struct PrimedParachainWork<Fetch, F> {
impl<Fetch, F, Err> PrimedParachainWork<Fetch, F> impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
where where
Fetch: Future<Output=Result<PoVBlock,Err>> + Unpin, Fetch: Future<Output=Result<PoVBlock,Err>> + Unpin,
F: FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<(OutgoingMessages, ErasureChunk), ()> + Unpin, F: FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<ErasureChunk, ()> + Unpin,
Err: From<::std::io::Error>, Err: From<::std::io::Error>,
{ {
pub async fn validate(mut self) -> Result<(Validated, Option<ErasureChunk>), Err> { pub async fn validate(mut self) -> Result<(Validated, Option<ErasureChunk>), Err> {
@@ -353,7 +344,7 @@ impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
}, },
None, None,
)), )),
Ok((outgoing_targeted, our_chunk)) => { Ok(our_chunk) => {
self.inner.availability_store.add_erasure_chunk( self.inner.availability_store.add_erasure_chunk(
self.inner.relay_parent, self.inner.relay_parent,
candidate.clone(), candidate.clone(),
@@ -363,7 +354,7 @@ impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
Ok(( Ok((
Validated { Validated {
statement: GenericStatement::Valid(candidate_hash), statement: GenericStatement::Valid(candidate_hash),
result: Validation::Valid(pov_block, outgoing_targeted), result: Validation::Valid(pov_block),
}, },
Some(our_chunk), Some(our_chunk),
)) ))
@@ -580,9 +571,7 @@ mod tests {
use super::*; use super::*;
use sp_keyring::Sr25519Keyring; use sp_keyring::Sr25519Keyring;
use primitives::crypto::UncheckedInto; use primitives::crypto::UncheckedInto;
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{BlockData, Collation, HeadData};
AvailableMessages, BlockData, ConsolidatedIngress, Collation, HeadData,
};
use polkadot_erasure_coding::{self as erasure}; use polkadot_erasure_coding::{self as erasure};
use availability_store::ProvideGossipMessages; use availability_store::ProvideGossipMessages;
use futures::future; use futures::future;
@@ -592,7 +581,6 @@ mod tests {
fn pov_block_with_data(data: Vec<u8>) -> PoVBlock { fn pov_block_with_data(data: Vec<u8>) -> PoVBlock {
PoVBlock { PoVBlock {
block_data: BlockData(data), block_data: BlockData(data),
ingress: ConsolidatedIngress(Vec::new()),
} }
} }
@@ -627,7 +615,6 @@ mod tests {
&self, &self,
_collation: Collation, _collation: Collation,
_candidate: CandidateReceipt, _candidate: CandidateReceipt,
_outgoing: OutgoingMessages,
_chunks: (ValidatorIndex, &[ErasureChunk]) _chunks: (ValidatorIndex, &[ErasureChunk])
) -> Self::SendLocalCollation { future::ready(Ok(())) } ) -> Self::SendLocalCollation { future::ready(Ok(())) }
@@ -671,7 +658,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [2; 32].into(), block_data_hash: [2; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -728,7 +714,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [2; 32].into(), block_data_hash: [2; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -766,7 +751,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash, block_data_hash,
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -792,24 +776,17 @@ mod tests {
max_block_data_size: None, max_block_data_size: None,
}; };
let validated = block_on(producer.prime_with(|_, _, _| Ok(( let validated = block_on(producer.prime_with(|_, _, _| Ok(
OutgoingMessages { outgoing_messages: Vec::new() },
ErasureChunk { ErasureChunk {
chunk: vec![1, 2, 3], chunk: vec![1, 2, 3],
index: local_index as u32, index: local_index as u32,
proof: vec![], proof: vec![],
}, }
))).validate()).unwrap(); )).validate()).unwrap();
assert_eq!(validated.0.pov_block(), &pov_block); assert_eq!(validated.0.pov_block(), &pov_block);
assert_eq!(validated.0.statement, GenericStatement::Valid(hash)); assert_eq!(validated.0.statement, GenericStatement::Valid(hash));
if let Some(messages) = validated.0.outgoing_messages() {
let available_messages: AvailableMessages = messages.clone().into();
for (root, queue) in available_messages.0 {
assert_eq!(store.queue_by_root(&root), Some(queue));
}
}
assert!(store.get_erasure_chunk(&relay_parent, block_data_hash, local_index).is_some()); assert!(store.get_erasure_chunk(&relay_parent, block_data_hash, local_index).is_some());
assert!(store.get_erasure_chunk(&relay_parent, block_data_hash, local_index + 1).is_none()); assert!(store.get_erasure_chunk(&relay_parent, block_data_hash, local_index + 1).is_none());
} }
@@ -823,7 +800,6 @@ mod tests {
let block_data_hash = pov_block.block_data.hash(); let block_data_hash = pov_block.block_data.hash();
let local_index = 0; let local_index = 0;
let n_validators = 2; let n_validators = 2;
let ex = Some(AvailableMessages(Vec::new()));
let candidate = CandidateReceipt { let candidate = CandidateReceipt {
parachain_index: para_id, parachain_index: para_id,
@@ -831,14 +807,13 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [2; 32].into(), block_data_hash: [2; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
erasure_root: [1u8; 32].into(), erasure_root: [1u8; 32].into(),
}; };
let chunks = erasure::obtain_chunks(n_validators, &pov_block.block_data, ex.as_ref()).unwrap(); let chunks = erasure::obtain_chunks(n_validators, &pov_block.block_data).unwrap();
store.add_validator_index_and_n_validators( store.add_validator_index_and_n_validators(
&relay_parent, &relay_parent,
@@ -857,23 +832,16 @@ mod tests {
max_block_data_size: None, max_block_data_size: None,
}; };
let validated = block_on(producer.prime_with(|_, _, _| Ok(( let validated = block_on(producer.prime_with(|_, _, _| Ok(
OutgoingMessages { outgoing_messages: Vec::new() },
ErasureChunk { ErasureChunk {
chunk: chunks[local_index].clone(), chunk: chunks[local_index].clone(),
index: local_index as u32, index: local_index as u32,
proof: vec![], proof: vec![],
}, },
))).validate()).unwrap(); )).validate()).unwrap();
assert_eq!(validated.0.pov_block(), &pov_block); assert_eq!(validated.0.pov_block(), &pov_block);
if let Some(messages) = validated.0.outgoing_messages() {
let available_messages: AvailableMessages = messages.clone().into();
for (root, queue) in available_messages.0 {
assert_eq!(store.queue_by_root(&root), Some(queue));
}
}
// This works since there are only two validators and one erasure chunk should be // This works since there are only two validators and one erasure chunk should be
// enough to reconstruct the block data. // enough to reconstruct the block data.
assert_eq!(store.block_data(relay_parent, block_data_hash).unwrap(), pov_block.block_data); assert_eq!(store.block_data(relay_parent, block_data_hash).unwrap(), pov_block.block_data);
@@ -915,7 +883,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [2; 32].into(), block_data_hash: [2; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -953,7 +920,6 @@ mod tests {
let para_id = ParaId::from(1); let para_id = ParaId::from(1);
let pov_block = pov_block_with_data(vec![1, 2, 3]); let pov_block = pov_block_with_data(vec![1, 2, 3]);
let outgoing_messages = OutgoingMessages { outgoing_messages: Vec::new() };
let parent_hash = Default::default(); let parent_hash = Default::default();
let local_key = Sr25519Keyring::Alice.pair(); let local_key = Sr25519Keyring::Alice.pair();
@@ -983,7 +949,6 @@ mod tests {
signature: Default::default(), signature: Default::default(),
head_data: HeadData(vec![1, 2, 3, 4]), head_data: HeadData(vec![1, 2, 3, 4]),
parent_head: HeadData(vec![]), parent_head: HeadData(vec![]),
egress_queue_roots: Vec::new(),
fees: 1_000_000, fees: 1_000_000,
block_data_hash: [2; 32].into(), block_data_hash: [2; 32].into(),
upward_messages: Vec::new(), upward_messages: Vec::new(),
@@ -994,7 +959,6 @@ mod tests {
let signed_statement = shared_table.import_validated(Validated::collated_local( let signed_statement = shared_table.import_validated(Validated::collated_local(
candidate, candidate,
pov_block, pov_block,
outgoing_messages,
)).unwrap(); )).unwrap();
assert!(shared_table.inner.lock().validated.get(&hash).expect("validation has started").is_done()); assert!(shared_table.inner.lock().validated.get(&hash).expect("validation has started").is_done());
@@ -391,12 +391,11 @@ impl<C, N, P, SP> ParachainValidationInstances<C, N, P, SP> where
); );
collation_work.then(move |result| match result { collation_work.then(move |result| match result {
Ok((collation, outgoing_targeted, parent_head, fees_charged)) => { Ok((collation, parent_head, fees_charged)) => {
match crate::collation::produce_receipt_and_chunks( match crate::collation::produce_receipt_and_chunks(
authorities_num, authorities_num,
parent_head, parent_head,
&collation.pov, &collation.pov,
&outgoing_targeted,
fees_charged, fees_charged,
&collation.info, &collation.info,
) { ) {
@@ -421,7 +420,6 @@ impl<C, N, P, SP> ParachainValidationInstances<C, N, P, SP> where
router.local_collation( router.local_collation(
collation, collation,
receipt, receipt,
outgoing_targeted,
(local_id, &chunks), (local_id, &chunks),
).map_err(|e| warn!(target: "validation", "Failed to send local collation: {:?}", e)) ).map_err(|e| warn!(target: "validation", "Failed to send local collation: {:?}", e))
}); });