mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
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:
@@ -28,8 +28,7 @@ use keystore::KeyStorePtr;
|
||||
use polkadot_primitives::{
|
||||
Hash, Block,
|
||||
parachain::{
|
||||
Id as ParaId, BlockData, CandidateReceipt, Message, AvailableMessages, ErasureChunk,
|
||||
ParachainHost,
|
||||
Id as ParaId, BlockData, CandidateReceipt, ErasureChunk, ParachainHost
|
||||
},
|
||||
};
|
||||
use sp_runtime::traits::{BlakeTwo256, Hash as HashT, HasherFor};
|
||||
@@ -126,10 +125,6 @@ pub struct Data {
|
||||
pub parachain_id: ParaId,
|
||||
/// Block data.
|
||||
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.
|
||||
@@ -384,9 +379,4 @@ impl Store {
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ use codec::{Encode, Decode};
|
||||
use polkadot_erasure_coding::{self as erasure};
|
||||
use polkadot_primitives::{
|
||||
Hash,
|
||||
parachain::{
|
||||
BlockData, CandidateReceipt, Message, ErasureChunk
|
||||
},
|
||||
parachain::{BlockData, CandidateReceipt, ErasureChunk},
|
||||
};
|
||||
|
||||
use log::{trace, warn};
|
||||
@@ -130,18 +128,6 @@ impl Store {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -287,14 +273,13 @@ impl Store {
|
||||
columns::DATA,
|
||||
&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,
|
||||
v.iter().map(|chunk| (chunk.chunk.as_ref(), chunk.index as usize))) {
|
||||
self.make_available(Data {
|
||||
relay_parent: *relay_parent,
|
||||
parachain_id: receipt.parachain_index,
|
||||
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> {
|
||||
self.query_inner(columns::META, &block_to_candidate_key(&block_hash))
|
||||
}
|
||||
@@ -414,8 +394,8 @@ impl Store {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use polkadot_erasure_coding::{self as erasure};
|
||||
use polkadot_primitives::parachain::{Id as ParaId, AvailableMessages};
|
||||
use polkadot_erasure_coding as erasure;
|
||||
use polkadot_primitives::parachain::Id as ParaId;
|
||||
|
||||
#[test]
|
||||
fn finalization_removes_unneeded() {
|
||||
@@ -444,14 +424,12 @@ mod tests {
|
||||
relay_parent,
|
||||
parachain_id: para_id_1,
|
||||
block_data: block_data_1.clone(),
|
||||
outgoing_queues: None,
|
||||
}).unwrap();
|
||||
|
||||
store.make_available(Data {
|
||||
relay_parent,
|
||||
parachain_id: para_id_2,
|
||||
block_data: block_data_2.clone(),
|
||||
outgoing_queues: None,
|
||||
}).unwrap();
|
||||
|
||||
let candidate_1 = CandidateReceipt {
|
||||
@@ -460,7 +438,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: Default::default(),
|
||||
parent_head: Default::default(),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: block_data_1.hash(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -473,7 +450,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: Default::default(),
|
||||
parent_head: Default::default(),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: block_data_2.hash(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -516,34 +492,12 @@ mod tests {
|
||||
let para_id = 5.into();
|
||||
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();
|
||||
store.make_available(Data {
|
||||
relay_parent,
|
||||
parachain_id: para_id,
|
||||
block_data: block_data.clone(),
|
||||
outgoing_queues: Some(outgoing_queues),
|
||||
block_data,
|
||||
}).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]
|
||||
@@ -554,21 +508,10 @@ mod tests {
|
||||
let block_data_hash = block_data.hash();
|
||||
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(
|
||||
n_validators,
|
||||
&block_data,
|
||||
outgoing_queues.as_ref()).unwrap();
|
||||
).unwrap();
|
||||
|
||||
let branches = erasure::branches(erasure_chunks.as_ref());
|
||||
|
||||
@@ -578,7 +521,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: Default::default(),
|
||||
parent_head: Default::default(),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: block_data.hash(),
|
||||
upward_messages: Vec::new(),
|
||||
|
||||
@@ -35,7 +35,7 @@ use consensus_common::{
|
||||
use polkadot_primitives::{Block, BlockId, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
CandidateReceipt, ParachainHost, ValidatorId,
|
||||
ValidatorPair, AvailableMessages, BlockData, ErasureChunk,
|
||||
ValidatorPair, ErasureChunk, PoVBlock,
|
||||
};
|
||||
use futures::{prelude::*, future::select, channel::{mpsc, oneshot}, task::{Spawn, SpawnExt}};
|
||||
use keystore::KeyStorePtr;
|
||||
@@ -90,7 +90,7 @@ pub(crate) struct ParachainBlocks {
|
||||
/// The relay parent of the block these parachain blocks belong to.
|
||||
pub relay_parent: Hash,
|
||||
/// The blocks themselves.
|
||||
pub blocks: Vec<(CandidateReceipt, Option<(BlockData, AvailableMessages)>)>,
|
||||
pub blocks: Vec<(CandidateReceipt, Option<PoVBlock>)>,
|
||||
/// A sender to signal the result asynchronously.
|
||||
pub result: oneshot::Sender<Result<(), Error>>,
|
||||
}
|
||||
@@ -367,7 +367,7 @@ where
|
||||
runtime_handle: &Handle,
|
||||
sender: &mut mpsc::UnboundedSender<WorkerMsg>,
|
||||
relay_parent: Hash,
|
||||
blocks: Vec<(CandidateReceipt, Option<(BlockData, AvailableMessages)>)>,
|
||||
blocks: Vec<(CandidateReceipt, Option<PoVBlock>)>,
|
||||
) -> Result<(), Error> {
|
||||
let hashes: Vec<_> = blocks.iter().map(|(c, _)| c.hash()).collect();
|
||||
|
||||
@@ -375,7 +375,7 @@ where
|
||||
for (candidate, block) in blocks.into_iter() {
|
||||
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?
|
||||
}
|
||||
|
||||
|
||||
+16
-166
@@ -48,7 +48,6 @@ use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::{future, Future, Stream, FutureExt, TryFutureExt, StreamExt, task::Spawn};
|
||||
use log::warn;
|
||||
@@ -57,18 +56,17 @@ use sp_core::{Pair, Blake2Hasher};
|
||||
use polkadot_primitives::{
|
||||
BlockId, Hash, Block,
|
||||
parachain::{
|
||||
self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId,
|
||||
OutgoingMessages, PoVBlock, Status as ParachainStatus, ValidatorId, CollatorPair,
|
||||
self, BlockData, DutyRoster, HeadData, Id as ParaId,
|
||||
PoVBlock, Status as ParachainStatus, ValidatorId, CollatorPair,
|
||||
}
|
||||
};
|
||||
use polkadot_cli::{
|
||||
ProvideRuntimeApi, AbstractService, ParachainHost, IsKusama,
|
||||
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_network::legacy::validation::Incoming;
|
||||
pub use polkadot_validation::SignedStatement;
|
||||
pub use polkadot_primitives::parachain::CollatorId;
|
||||
pub use sc_network::PeerId;
|
||||
@@ -111,14 +109,14 @@ pub struct InvalidHead;
|
||||
|
||||
/// Collation errors.
|
||||
#[derive(Debug)]
|
||||
pub enum Error<R> {
|
||||
pub enum Error {
|
||||
/// Error on the relay-chain side of things.
|
||||
Polkadot(R),
|
||||
Polkadot(String),
|
||||
/// Error on the collator side of things.
|
||||
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 {
|
||||
match *self {
|
||||
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 is expected to be a lightweight, shared type like an Arc.
|
||||
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
|
||||
/// and the last parachain head.
|
||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||
fn produce_candidate(
|
||||
&mut self,
|
||||
relay_parent: Hash,
|
||||
status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> 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.
|
||||
pub async fn collate<R, P>(
|
||||
pub async fn collate<P>(
|
||||
relay_parent: Hash,
|
||||
local_id: ParaId,
|
||||
parachain_status: ParachainStatus,
|
||||
relay_context: R,
|
||||
mut para_context: P,
|
||||
key: Arc<CollatorPair>,
|
||||
)
|
||||
-> Result<(parachain::Collation, OutgoingMessages), Error<R::Error>>
|
||||
-> Result<parachain::Collation, Error>
|
||||
where
|
||||
R: RelayChainContext,
|
||||
P: ParachainContext,
|
||||
P::ProduceCandidate: Send,
|
||||
{
|
||||
let ingress = relay_context.unrouted_egress(local_id).await.map_err(Error::Polkadot)?;
|
||||
|
||||
let (block_data, head_data, mut outgoing) = para_context.produce_candidate(
|
||||
let (block_data, head_data) = para_context.produce_candidate(
|
||||
relay_parent,
|
||||
parachain_status,
|
||||
ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg)))
|
||||
).map_err(Error::Collator).await?;
|
||||
|
||||
let block_data_hash = block_data.hash();
|
||||
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 {
|
||||
parachain_index: local_id,
|
||||
collator: key.public(),
|
||||
signature,
|
||||
egress_queue_roots,
|
||||
head_data,
|
||||
block_data_hash,
|
||||
upward_messages: Vec::new(),
|
||||
@@ -230,47 +204,10 @@ pub async fn collate<R, P>(
|
||||
info,
|
||||
pov: PoVBlock {
|
||||
block_data,
|
||||
ingress,
|
||||
},
|
||||
};
|
||||
|
||||
Ok((collation, outgoing))
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
Ok(collation)
|
||||
}
|
||||
|
||||
/// Run the collator node using the given `service`.
|
||||
@@ -378,7 +315,6 @@ fn run_collator_node<S, P, Extrinsic>(
|
||||
let client = client.clone();
|
||||
let key = key.clone();
|
||||
let parachain_context = parachain_context.clone();
|
||||
let validation_network = validation_network.clone();
|
||||
|
||||
let work = future::lazy(move |_| {
|
||||
let api = client.runtime_api();
|
||||
@@ -395,27 +331,19 @@ fn run_collator_node<S, P, Extrinsic>(
|
||||
try_fr!(api.duty_roster(&id)),
|
||||
);
|
||||
|
||||
let context = ApiContext {
|
||||
network: validation_network,
|
||||
parent_hash: relay_parent,
|
||||
validators,
|
||||
};
|
||||
|
||||
let collation_work = collate(
|
||||
relay_parent,
|
||||
para_id,
|
||||
status,
|
||||
context,
|
||||
parachain_context,
|
||||
key,
|
||||
).map_ok(move |(collation, outgoing)| {
|
||||
).map_ok(move |collation| {
|
||||
network.with_spec(move |spec, ctx| {
|
||||
let res = spec.add_local_collation(
|
||||
ctx,
|
||||
relay_parent,
|
||||
targets,
|
||||
collation,
|
||||
outgoing,
|
||||
);
|
||||
|
||||
tokio::spawn(res.boxed());
|
||||
@@ -514,104 +442,26 @@ pub fn run_collator<P>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use polkadot_primitives::parachain::{TargetedMessage, FeeSchedule};
|
||||
use polkadot_primitives::parachain::FeeSchedule;
|
||||
use keyring::Sr25519Keyring;
|
||||
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(¶_id) {
|
||||
Some(ingress) => Box::new(future::ok(ingress.clone())),
|
||||
None => Box::new(future::pending()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct 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,
|
||||
_relay_parent: Hash,
|
||||
_status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> Self::ProduceCandidate {
|
||||
// send messages right back.
|
||||
future::ok((
|
||||
BlockData(vec![1, 2, 3, 4, 5,]),
|
||||
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)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
use codec::{Encode, Decode};
|
||||
use reed_solomon::galois_16::{self, ReedSolomon};
|
||||
use primitives::{Hash as H256, BlakeTwo256, HashT};
|
||||
use primitives::parachain::{BlockData, AvailableMessages};
|
||||
use primitives::parachain::BlockData;
|
||||
use sp_core::Blake2Hasher;
|
||||
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.
|
||||
///
|
||||
/// 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>
|
||||
{
|
||||
let params = code_params(n_validators)?;
|
||||
let encoded = (block_data, outgoing).encode();
|
||||
let encoded = block_data.encode();
|
||||
|
||||
if encoded.is_empty() {
|
||||
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.
|
||||
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)>
|
||||
{
|
||||
let params = code_params(n_validators)?;
|
||||
@@ -402,11 +402,9 @@ mod tests {
|
||||
#[test]
|
||||
fn round_trip_block_data() {
|
||||
let block_data = BlockData((0..255).collect());
|
||||
let ex = Some(AvailableMessages(Vec::new()));
|
||||
let chunks = obtain_chunks(
|
||||
10,
|
||||
&block_data,
|
||||
ex.as_ref(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(chunks.len(), 10);
|
||||
@@ -422,18 +420,16 @@ mod tests {
|
||||
].iter().cloned(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(reconstructed, (block_data, ex));
|
||||
assert_eq!(reconstructed, block_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_valid_branches() {
|
||||
let block_data = BlockData(vec![2; 256]);
|
||||
let ex = Some(AvailableMessages(Vec::new()));
|
||||
|
||||
let chunks = obtain_chunks(
|
||||
10,
|
||||
&block_data,
|
||||
ex.as_ref(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(chunks.len(), 10);
|
||||
|
||||
@@ -236,15 +236,12 @@ impl CollatorPool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_core::crypto::UncheckedInto;
|
||||
use polkadot_primitives::parachain::{
|
||||
CandidateReceipt, BlockData, PoVBlock, HeadData, ConsolidatedIngress,
|
||||
};
|
||||
use polkadot_primitives::parachain::{CandidateReceipt, BlockData, PoVBlock, HeadData};
|
||||
use futures::executor::block_on;
|
||||
|
||||
fn make_pov(block_data: Vec<u8>) -> PoVBlock {
|
||||
PoVBlock {
|
||||
block_data: BlockData(block_data),
|
||||
ingress: ConsolidatedIngress(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +291,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: [3; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -324,7 +320,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: [3; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
|
||||
@@ -93,11 +93,6 @@ impl PeerData {
|
||||
pub(super) fn knowledge_at_mut(&mut self, parent_hash: &Hash) -> Option<&mut Knowledge> {
|
||||
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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
//! 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.
|
||||
|
||||
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 sc_network::{config::Roles, Context, PeerId, ReputationChange};
|
||||
use sc_network::{NetworkService as SubstrateNetworkService, specialization::NetworkSpecialization};
|
||||
@@ -60,7 +60,7 @@ use sc_network_gossip::{
|
||||
use polkadot_validation::{SignedStatement};
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
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 codec::{Decode, Encode};
|
||||
@@ -72,15 +72,12 @@ use std::sync::Arc;
|
||||
use arrayvec::ArrayVec;
|
||||
use futures::prelude::*;
|
||||
use parking_lot::RwLock;
|
||||
use log::warn;
|
||||
|
||||
use crate::legacy::{GossipMessageStream, NetworkService, GossipService, PolkadotProtocol, router::attestation_topic};
|
||||
|
||||
use attestation::{View as AttestationView, PeerData as AttestationPeerData};
|
||||
use message_routing::{View as MessageRoutingView};
|
||||
|
||||
mod attestation;
|
||||
mod message_routing;
|
||||
|
||||
/// The engine ID of the polkadot attestation system.
|
||||
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");
|
||||
/// When a peer sends us a previously-unknown 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 {
|
||||
@@ -119,19 +114,10 @@ mod cost {
|
||||
pub const BAD_SIGNATURE: Rep = Rep::new(-500, "Polkadot: Bad signature");
|
||||
/// A peer sent us a bad neighbor packet.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
@@ -144,12 +130,9 @@ pub enum GossipMessage {
|
||||
/// Non-candidate statements should only be sent to peers who are aware of the candidate.
|
||||
#[codec(index = "2")]
|
||||
Statement(GossipStatement),
|
||||
/// A packet of messages from one parachain to another.
|
||||
#[codec(index = "3")]
|
||||
ParachainMessages(GossipParachainMessages),
|
||||
// TODO: https://github.com/paritytech/polkadot/issues/253
|
||||
/// A packet containing one of the erasure-coding chunks of one candidate.
|
||||
#[codec(index = "4")]
|
||||
#[codec(index = "3")]
|
||||
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.
|
||||
#[derive(Encode, Decode, Clone, PartialEq)]
|
||||
pub struct GossipStatement {
|
||||
@@ -218,19 +195,6 @@ impl From<ErasureChunkMessage> for GossipMessage {
|
||||
pub struct GossipParachainMessages {
|
||||
/// The root of the message queue.
|
||||
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.
|
||||
@@ -283,23 +247,9 @@ impl<F, P> ChainContext for (F, P) where
|
||||
|
||||
fn leaf_unrouted_roots(
|
||||
&self,
|
||||
&leaf: &Hash,
|
||||
with_queue_root: &mut dyn FnMut(&Hash),
|
||||
_leaf: &Hash,
|
||||
_with_queue_root: &mut dyn FnMut(&Hash),
|
||||
) -> 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(())
|
||||
}
|
||||
}
|
||||
@@ -325,7 +275,6 @@ pub fn register_validator<C: ChainContext + 'static, S: NetworkSpecialization<Bl
|
||||
inner: RwLock::new(Inner {
|
||||
peers: HashMap::new(),
|
||||
attestation_view: Default::default(),
|
||||
message_routing_view: Default::default(),
|
||||
availability_store: None,
|
||||
chain,
|
||||
})
|
||||
@@ -350,8 +299,6 @@ pub fn register_validator<C: ChainContext + 'static, S: NetworkSpecialization<Bl
|
||||
enum NewLeafAction {
|
||||
// (who, message)
|
||||
TargetedMessage(PeerId, GossipMessage),
|
||||
// (topic, message)
|
||||
Multicast(Hash, GossipMessage),
|
||||
}
|
||||
|
||||
/// Actions to take after noting a new block-DAG leaf.
|
||||
@@ -372,8 +319,6 @@ impl NewLeafActions {
|
||||
match action {
|
||||
NewLeafAction::TargetedMessage(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,
|
||||
relay_chain_leaf: Hash,
|
||||
validation: MessageValidationData,
|
||||
lookup_queue_by_root: impl Fn(&Hash) -> Option<Vec<ParachainMessage>>,
|
||||
) -> NewLeafActions {
|
||||
// add an entry in attestation_view
|
||||
// prune any entries from attestation_view which are no longer leaves
|
||||
@@ -441,7 +385,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
|
||||
let &mut Inner {
|
||||
ref chain,
|
||||
ref mut attestation_view,
|
||||
ref mut message_routing_view,
|
||||
..
|
||||
} = &mut *inner;
|
||||
|
||||
@@ -449,10 +392,6 @@ impl<S: NetworkSpecialization<Block>> RegisteredMessageValidator<S> {
|
||||
Some(Known::Leaf) => true,
|
||||
_ => 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))
|
||||
);
|
||||
|
||||
// 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 }
|
||||
}
|
||||
|
||||
@@ -575,16 +497,9 @@ struct PeerData {
|
||||
attestation: AttestationPeerData,
|
||||
}
|
||||
|
||||
impl PeerData {
|
||||
fn leaves(&self) -> impl Iterator<Item = &Hash> {
|
||||
self.attestation.leaves()
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner<C: ?Sized> {
|
||||
peers: HashMap<PeerId, PeerData>,
|
||||
attestation_view: AttestationView,
|
||||
message_routing_view: MessageRoutingView,
|
||||
availability_store: Option<av_store::Store>,
|
||||
chain: C,
|
||||
}
|
||||
@@ -602,11 +517,7 @@ impl<C: ?Sized + ChainContext> Inner<C> {
|
||||
let new_leaves = peer.attestation.update_leaves(&chain_heads);
|
||||
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 leaves.
|
||||
let new_message_routing_topics = self.message_routing_view.intersection_topics(&new_leaves);
|
||||
|
||||
new_attestation_topics.chain(new_message_routing_topics).collect()
|
||||
new_attestation_topics.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
@@ -692,7 +603,6 @@ impl<C: ChainContext + ?Sized> MessageValidator<C> {
|
||||
inner: RwLock::new(Inner {
|
||||
peers: HashMap::new(),
|
||||
attestation_view: Default::default(),
|
||||
message_routing_view: Default::default(),
|
||||
availability_store: None,
|
||||
chain,
|
||||
}),
|
||||
@@ -740,18 +650,6 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
|
||||
}
|
||||
(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)) => {
|
||||
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| {
|
||||
// check that messages from this topic are considered live by one of our protocols.
|
||||
// everything else is expired
|
||||
let live = inner.attestation_view.is_topic_live(&topic)
|
||||
|| !inner.message_routing_view.is_topic_live(&topic);
|
||||
let live = inner.attestation_view.is_topic_live(&topic);
|
||||
|
||||
!live // = expired
|
||||
})
|
||||
@@ -780,7 +677,6 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
|
||||
let &mut Inner {
|
||||
ref mut peers,
|
||||
ref mut attestation_view,
|
||||
ref mut message_routing_view,
|
||||
..
|
||||
} = &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,
|
||||
}
|
||||
})
|
||||
@@ -827,9 +716,8 @@ mod tests {
|
||||
use parking_lot::Mutex;
|
||||
use polkadot_primitives::parachain::{CandidateReceipt, HeadData};
|
||||
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 super::message_routing::queue_topic;
|
||||
|
||||
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]
|
||||
fn message_allowed() {
|
||||
let (tx, _rx) = mpsc::channel();
|
||||
@@ -933,7 +805,6 @@ mod tests {
|
||||
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: [20u8; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -1095,12 +966,6 @@ mod tests {
|
||||
|
||||
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();
|
||||
@@ -1111,8 +976,6 @@ mod tests {
|
||||
|
||||
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();
|
||||
|
||||
@@ -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,
|
||||
¬_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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use futures::prelude::*;
|
||||
use polkadot_primitives::{Block, Hash, Header};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, CollatorId, CandidateReceipt, Collation, PoVBlock,
|
||||
StructuredUnroutedIngress, ValidatorId, OutgoingMessages, ErasureChunk,
|
||||
ValidatorId, ErasureChunk,
|
||||
};
|
||||
use sc_network::{
|
||||
PeerId, RequestId, Context, StatusMessage as GenericFullStatus,
|
||||
@@ -169,7 +169,6 @@ struct PoVBlockRequest {
|
||||
candidate_hash: Hash,
|
||||
block_data_hash: Hash,
|
||||
sender: oneshot::Sender<PoVBlock>,
|
||||
canon_roots: StructuredUnroutedIngress,
|
||||
}
|
||||
|
||||
impl PoVBlockRequest {
|
||||
@@ -182,13 +181,7 @@ impl PoVBlockRequest {
|
||||
return Err(self);
|
||||
}
|
||||
|
||||
match polkadot_validation::validate_incoming(&self.canon_roots, &pov_block.ingress) {
|
||||
Ok(()) => {
|
||||
let _ = self.sender.send(pov_block);
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(self)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +293,6 @@ impl PolkadotProtocol {
|
||||
ctx: &mut dyn Context<Block>,
|
||||
candidate: &CandidateReceipt,
|
||||
relay_parent: Hash,
|
||||
canon_roots: StructuredUnroutedIngress,
|
||||
) -> oneshot::Receiver<PoVBlock> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -310,7 +302,6 @@ impl PolkadotProtocol {
|
||||
candidate_hash: candidate.hash(),
|
||||
block_data_hash: candidate.block_data_hash,
|
||||
sender: tx,
|
||||
canon_roots,
|
||||
});
|
||||
|
||||
self.dispatch_pending_requests(ctx);
|
||||
@@ -617,7 +608,6 @@ impl Specialization<Block> for PolkadotProtocol {
|
||||
validation_leaf: Default::default(),
|
||||
candidate_hash: Default::default(),
|
||||
block_data_hash: Default::default(),
|
||||
canon_roots: StructuredUnroutedIngress(Vec::new()),
|
||||
sender,
|
||||
}));
|
||||
}
|
||||
@@ -747,7 +737,6 @@ impl PolkadotProtocol {
|
||||
relay_parent: Hash,
|
||||
targets: HashSet<ValidatorId>,
|
||||
collation: Collation,
|
||||
outgoing_targeted: OutgoingMessages,
|
||||
) -> impl Future<Output = ()> {
|
||||
debug!(target: "p_net", "Importing local collation on relay parent {:?} and parachain {:?}",
|
||||
relay_parent, collation.info.parachain_index);
|
||||
@@ -776,7 +765,6 @@ impl PolkadotProtocol {
|
||||
relay_parent,
|
||||
parachain_id: collation_cloned.info.parachain_index,
|
||||
block_data: collation_cloned.pov.block_data.clone(),
|
||||
outgoing_queues: Some(outgoing_targeted.clone().into()),
|
||||
}).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ use polkadot_validation::{
|
||||
};
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
OutgoingMessages, CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock, ErasureChunk,
|
||||
CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock, ErasureChunk,
|
||||
};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
|
||||
@@ -203,9 +203,8 @@ impl<P: ProvideRuntimeApi<Block> + Send + Sync + 'static, T> Router<P, T> where
|
||||
Ok(validated) => {
|
||||
// store the data before broadcasting statements, so other peers can fetch.
|
||||
knowledge.lock().note_candidate(
|
||||
candidate_hash,
|
||||
Some(validated.0.pov_block().clone()),
|
||||
validated.0.outgoing_messages().cloned(),
|
||||
candidate_hash,
|
||||
Some(validated.0.pov_block().clone()),
|
||||
);
|
||||
|
||||
// propagate the statement.
|
||||
@@ -241,7 +240,6 @@ impl<P: ProvideRuntimeApi<Block> + Send, T> TableRouter for Router<P, T> where
|
||||
&self,
|
||||
collation: Collation,
|
||||
receipt: CandidateReceipt,
|
||||
outgoing: OutgoingMessages,
|
||||
chunks: (ValidatorIndex, &[ErasureChunk])
|
||||
) -> Self::SendLocalCollation {
|
||||
// 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(
|
||||
receipt,
|
||||
collation.pov.clone(),
|
||||
outgoing.clone(),
|
||||
);
|
||||
|
||||
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.
|
||||
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());
|
||||
|
||||
for chunk in chunks.1 {
|
||||
|
||||
@@ -20,20 +20,14 @@ use std::collections::HashMap;
|
||||
use super::{PolkadotProtocol, Status, Message, FullStatus};
|
||||
use crate::legacy::validation::LeafWorkParams;
|
||||
|
||||
use polkadot_validation::GenericStatement;
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
CandidateReceipt, HeadData, PoVBlock, BlockData, CollatorId, ValidatorId,
|
||||
StructuredUnroutedIngress,
|
||||
};
|
||||
use polkadot_primitives::parachain::{CollatorId, ValidatorId};
|
||||
use sp_core::crypto::UncheckedInto;
|
||||
use codec::Encode;
|
||||
use sc_network::{
|
||||
PeerId, Context, ReputationChange, config::Roles, specialization::NetworkSpecialization,
|
||||
};
|
||||
|
||||
use futures::executor::block_on;
|
||||
|
||||
mod validation;
|
||||
|
||||
#[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 {
|
||||
FullStatus {
|
||||
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]
|
||||
fn sends_session_key() {
|
||||
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]
|
||||
fn remove_bad_collator() {
|
||||
let mut protocol = PolkadotProtocol::new(None);
|
||||
|
||||
@@ -28,8 +28,7 @@ use crate::legacy::{PolkadotProtocol, NetworkService, GossipService, GossipMessa
|
||||
use polkadot_validation::{SharedTable, Network};
|
||||
use polkadot_primitives::{Block, BlockNumber, Hash, Header, BlockId};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Chain, DutyRoster, ParachainHost, TargetedMessage,
|
||||
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, Status,
|
||||
Id as ParaId, Chain, DutyRoster, ParachainHost, TargetedMessage, ValidatorId, Status,
|
||||
FeeSchedule, HeadData, Retriable, CollatorId, ErasureChunk, CandidateReceipt,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
@@ -150,7 +149,6 @@ struct ApiData {
|
||||
validators: Vec<ValidatorId>,
|
||||
duties: Vec<Chain>,
|
||||
active_parachains: Vec<(ParaId, Option<(CollatorId, Retriable)>)>,
|
||||
ingress: HashMap<ParaId, StructuredUnroutedIngress>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
@@ -299,17 +297,6 @@ impl ParachainHost<Block> for RuntimeApi {
|
||||
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(
|
||||
&self,
|
||||
_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)]
|
||||
struct DummyGossipMessages;
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ use sc_network::PeerId;
|
||||
use polkadot_validation::{
|
||||
Network as ParachainNetwork, SharedTable, Collators, Statement, GenericStatement, SignedStatement,
|
||||
};
|
||||
use polkadot_primitives::{Block, BlockId, Hash};
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Collation, OutgoingMessages, ParachainHost, CandidateReceipt, CollatorId,
|
||||
Id as ParaId, Collation, ParachainHost, CandidateReceipt, CollatorId,
|
||||
ValidatorId, PoVBlock,
|
||||
};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
@@ -49,8 +49,6 @@ use crate::legacy::gossip::{RegisteredMessageValidator, MessageValidationData};
|
||||
|
||||
use super::{NetworkService, PolkadotProtocol};
|
||||
|
||||
pub use polkadot_validation::Incoming;
|
||||
|
||||
/// Params to instantiate validation work on a block-DAG leaf.
|
||||
pub struct LeafWorkParams {
|
||||
/// The local session key.
|
||||
@@ -123,8 +121,6 @@ impl<P, T> ValidationNetwork<P, T> where
|
||||
let actions = network.new_local_leaf(
|
||||
parent_hash,
|
||||
MessageValidationData { authorities },
|
||||
|queue_root| spec.availability_store.as_ref()
|
||||
.and_then(|store| store.queue_by_root(queue_root))
|
||||
);
|
||||
|
||||
actions.perform(&network);
|
||||
@@ -264,7 +260,6 @@ struct KnowledgeEntry {
|
||||
knows_block_data: Vec<ValidatorId>,
|
||||
knows_outgoing: Vec<ValidatorId>,
|
||||
pov: Option<PoVBlock>,
|
||||
outgoing_messages: Option<OutgoingMessages>,
|
||||
}
|
||||
|
||||
/// Tracks knowledge of peers.
|
||||
@@ -308,11 +303,9 @@ impl Knowledge {
|
||||
&mut self,
|
||||
hash: Hash,
|
||||
pov: Option<PoVBlock>,
|
||||
outgoing_messages: Option<OutgoingMessages>,
|
||||
) {
|
||||
let entry = self.candidates.entry(hash).or_insert_with(Default::default);
|
||||
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)
|
||||
-> Pin<Box<dyn Future<Output = Result<PoVBlock, io::Error>> + Send>> {
|
||||
|
||||
let parachain = candidate.parachain_index;
|
||||
let parent_hash = self.parent_hash;
|
||||
let network = self.network.clone();
|
||||
let candidate = candidate.clone();
|
||||
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 {
|
||||
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, canon_roots);
|
||||
let _ = tx.send(inner_rx);
|
||||
}
|
||||
let inner_rx = spec.fetch_pov_block(ctx, &candidate, parent_hash);
|
||||
let _ = tx.send(inner_rx);
|
||||
});
|
||||
|
||||
let map_err = |_| io::Error::new(
|
||||
|
||||
@@ -28,11 +28,10 @@ use futures::prelude::*;
|
||||
use futures::task::{Spawn, SpawnExt};
|
||||
use log::{debug, trace};
|
||||
|
||||
use av_store::Store as AvailabilityStore;
|
||||
use polkadot_primitives::{
|
||||
Hash, Block,
|
||||
parachain::{
|
||||
PoVBlock, ValidatorId, ValidatorIndex, Collation, CandidateReceipt, OutgoingMessages,
|
||||
PoVBlock, ValidatorId, ValidatorIndex, Collation, CandidateReceipt,
|
||||
ErasureChunk, ParachainHost, Id as ParaId, CollatorId,
|
||||
},
|
||||
};
|
||||
@@ -106,7 +105,6 @@ pub struct Service {
|
||||
pub fn start<C, Api, SP>(
|
||||
service: Arc<PolkadotNetworkService>,
|
||||
config: Config,
|
||||
availability_store: AvailabilityStore,
|
||||
chain_context: C,
|
||||
api: Arc<Api>,
|
||||
executor: SP,
|
||||
@@ -129,7 +127,6 @@ pub fn start<C, Api, SP>(
|
||||
executor.spawn(worker_loop(
|
||||
config,
|
||||
service.clone(),
|
||||
availability_store,
|
||||
gossip_validator,
|
||||
worker_sender.clone(),
|
||||
api,
|
||||
@@ -563,7 +560,6 @@ impl ProtocolHandler {
|
||||
async fn worker_loop<Api, Sp>(
|
||||
config: Config,
|
||||
service: Arc<PolkadotNetworkService>,
|
||||
availability_store: AvailabilityStore,
|
||||
gossip_handle: RegisteredMessageValidator,
|
||||
sender: mpsc::Sender<ServiceToWorkerMsg>,
|
||||
api: Arc<Api>,
|
||||
@@ -624,7 +620,6 @@ async fn worker_loop<Api, Sp>(
|
||||
let new_leaf_actions = gossip_handle.new_local_leaf(
|
||||
relay_parent,
|
||||
crate::legacy::gossip::MessageValidationData { authorities },
|
||||
|queue_root| availability_store.queue_by_root(queue_root),
|
||||
);
|
||||
|
||||
new_leaf_actions.perform(&gossip_handle);
|
||||
@@ -789,7 +784,6 @@ fn distribute_local_collation(
|
||||
let validated = Validated::collated_local(
|
||||
receipt,
|
||||
collation.pov.clone(),
|
||||
OutgoingMessages { outgoing_messages: Vec::new() },
|
||||
);
|
||||
|
||||
let statement = crate::legacy::gossip::GossipStatement::new(
|
||||
@@ -914,7 +908,6 @@ impl TableRouter for Router {
|
||||
&self,
|
||||
collation: Collation,
|
||||
receipt: CandidateReceipt,
|
||||
_outgoing: OutgoingMessages,
|
||||
chunks: (ValidatorIndex, &[ErasureChunk]),
|
||||
) -> Self::SendLocalCollation {
|
||||
let message = ServiceToWorkerMsg::LocalCollation(
|
||||
|
||||
@@ -65,8 +65,6 @@ pub struct ValidationParams {
|
||||
pub block_data: Vec<u8>,
|
||||
/// Previous head-data.
|
||||
pub parent_head: Vec<u8>,
|
||||
/// Incoming messages.
|
||||
pub ingress: Vec<IncomingMessage>,
|
||||
}
|
||||
|
||||
/// The result of parachain validation.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use polkadot_parachain as parachain;
|
||||
|
||||
use crate::{DummyExt, parachain::{IncomingMessage, ValidationParams}};
|
||||
use crate::{DummyExt, parachain::ValidationParams};
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
/// Head data for this parachain.
|
||||
@@ -75,7 +75,6 @@ pub fn execute_good_on_parent() {
|
||||
ValidationParams {
|
||||
parent_head: parent_head.encode(),
|
||||
block_data: block_data.encode(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
@@ -111,7 +110,6 @@ fn execute_good_chain_on_parent() {
|
||||
ValidationParams {
|
||||
parent_head: parent_head.encode(),
|
||||
block_data: block_data.encode(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
@@ -147,47 +145,8 @@ fn execute_bad_on_parent() {
|
||||
ValidationParams {
|
||||
parent_head: parent_head.encode(),
|
||||
block_data: block_data.encode(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
).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 {
|
||||
parent_head: Default::default(),
|
||||
block_data: Vec::new(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
@@ -53,7 +52,6 @@ fn parallel_execution() {
|
||||
ValidationParams {
|
||||
parent_head: Default::default(),
|
||||
block_data: Vec::new(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
@@ -63,7 +61,6 @@ fn parallel_execution() {
|
||||
ValidationParams {
|
||||
parent_head: Default::default(),
|
||||
block_data: Vec::new(),
|
||||
ingress: Vec::new(),
|
||||
},
|
||||
DummyExt,
|
||||
parachain::wasm_executor::ExecutionMode::RemoteTest,
|
||||
|
||||
@@ -20,7 +20,7 @@ use rstd::prelude::*;
|
||||
use rstd::cmp::Ordering;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use bitvec::vec::BitVec;
|
||||
use super::{Hash, Balance, BlockNumber};
|
||||
use super::{Hash, Balance};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
@@ -164,53 +164,6 @@ pub struct DutyRoster {
|
||||
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.
|
||||
#[cfg(feature = "std")]
|
||||
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)
|
||||
}
|
||||
|
||||
#[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.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -242,9 +182,6 @@ pub struct CollationInfo {
|
||||
pub collator: CollatorId,
|
||||
/// Signature on blake2-256 of the block data by collator.
|
||||
pub signature: CollatorSignature,
|
||||
/// Egress queue roots. Must be sorted lexicographically (ascending)
|
||||
/// by parachain ID.
|
||||
pub egress_queue_roots: Vec<(Id, Hash)>,
|
||||
/// The head-data
|
||||
pub head_data: HeadData,
|
||||
/// blake2-256 Hash of block data.
|
||||
@@ -259,7 +196,6 @@ impl From<CandidateReceipt> for CollationInfo {
|
||||
parachain_index: receipt.parachain_index,
|
||||
collator: receipt.collator,
|
||||
signature: receipt.signature,
|
||||
egress_queue_roots: receipt.egress_queue_roots,
|
||||
head_data: receipt.head_data,
|
||||
block_data_hash: receipt.block_data_hash,
|
||||
upward_messages: receipt.upward_messages,
|
||||
@@ -294,9 +230,6 @@ pub struct CandidateReceipt {
|
||||
pub head_data: HeadData,
|
||||
/// The parent head-data.
|
||||
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
|
||||
pub fees: Balance,
|
||||
/// blake2-256 Hash of block data.
|
||||
@@ -337,7 +270,6 @@ impl PartialEq<CollationInfo> for CandidateReceipt {
|
||||
self.parachain_index == info.parachain_index &&
|
||||
self.collator == info.collator &&
|
||||
self.signature == info.signature &&
|
||||
self.egress_queue_roots == info.egress_queue_roots &&
|
||||
self.head_data == info.head_data &&
|
||||
self.block_data_hash == info.block_data_hash &&
|
||||
self.upward_messages == info.upward_messages
|
||||
@@ -369,68 +301,8 @@ pub struct Collation {
|
||||
pub struct PoVBlock {
|
||||
/// Block data.
|
||||
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.
|
||||
///
|
||||
/// 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>;
|
||||
/// Get the given parachain's head code blob.
|
||||
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.
|
||||
fn get_heads(extrinsics: Vec<<Block as BlockT>::Extrinsic>) -> Option<Vec<CandidateReceipt>>;
|
||||
}
|
||||
|
||||
@@ -18,19 +18,18 @@
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::result;
|
||||
use rstd::collections::btree_map::BTreeMap;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use sp_runtime::traits::{
|
||||
Hash as HashT, BlakeTwo256, Saturating, One, Zero, Dispatchable,
|
||||
Hash as HashT, BlakeTwo256, Saturating, One, Dispatchable,
|
||||
AccountIdConversion, BadOrigin,
|
||||
};
|
||||
use frame_support::weights::SimpleDispatchInfo;
|
||||
use primitives::{
|
||||
Hash, Balance,
|
||||
Balance,
|
||||
parachain::{
|
||||
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, ParachainDispatchOrigin,
|
||||
UpwardMessage, BlockIngressRoots, ValidatorId, ActiveParas, CollatorId, Retriable,
|
||||
UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable,
|
||||
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>`
|
||||
// doesn't work.`
|
||||
pub trait ParachainCurrency<AccountId> {
|
||||
@@ -147,12 +141,6 @@ pub enum Origin {
|
||||
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.
|
||||
const MAX_QUEUE_COUNT: usize = 100;
|
||||
/// 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>>;
|
||||
/// The heads of the parachains registered at present.
|
||||
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
|
||||
/// `MAX_MESSAGE_COUNT` and `WATERMARK_MESSAGE_SIZE`.
|
||||
pub RelayDispatchQueue: map hasher(blake2_256) ParaId => Vec<UpwardMessage>;
|
||||
@@ -219,14 +195,6 @@ decl_error! {
|
||||
QueueFull,
|
||||
/// The message origin is invalid.
|
||||
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.
|
||||
NoValidatorGroup,
|
||||
/// Not enough validity votes for candidate.
|
||||
@@ -292,7 +260,6 @@ decl_module! {
|
||||
MAX_QUEUE_COUNT,
|
||||
WATERMARK_QUEUE_SIZE,
|
||||
)?;
|
||||
Self::check_egress_queue_roots(&head, &active_parachains)?;
|
||||
|
||||
let id = head.parachain_index();
|
||||
proceeded.push(id);
|
||||
@@ -301,15 +268,13 @@ decl_module! {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Self::update_routing(
|
||||
current_number,
|
||||
&heads,
|
||||
);
|
||||
|
||||
|
||||
Self::dispatch_upward_messages(
|
||||
MAX_QUEUE_COUNT,
|
||||
WATERMARK_QUEUE_SIZE,
|
||||
@@ -351,12 +316,6 @@ impl<T: Trait> Module<T> {
|
||||
) {
|
||||
<Code>::insert(id, code);
|
||||
<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(
|
||||
@@ -364,19 +323,6 @@ impl<T: Trait> Module<T> {
|
||||
) {
|
||||
<Code>::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.
|
||||
@@ -415,8 +361,8 @@ impl<T: Trait> Module<T> {
|
||||
upward_messages.len() + count as usize == 1
|
||||
// ...or...
|
||||
|| (
|
||||
// ...the total messages in the queue ends up being no greater than the
|
||||
// limit...
|
||||
// ...the total messages in the queue ends up being no greater than the
|
||||
// limit...
|
||||
upward_messages.len() + count as usize <= max_queue_count
|
||||
&&
|
||||
// ...and the total size of the payloads in the queue ends up being no
|
||||
@@ -439,38 +385,14 @@ impl<T: Trait> Module<T> {
|
||||
/// Update routing information from the parachain heads. This queues upwards
|
||||
/// messages to the relay chain as well.
|
||||
fn update_routing(
|
||||
now: T::BlockNumber,
|
||||
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
|
||||
// needs_dispatch queue.
|
||||
let mut ordered_needs_dispatch = NeedsDispatch::get();
|
||||
|
||||
for head in heads.iter() {
|
||||
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).
|
||||
Self::queue_upward_messages(
|
||||
@@ -481,11 +403,6 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -623,32 +540,6 @@ impl<T: Trait> Module<T> {
|
||||
(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.
|
||||
pub fn parachain_status(id: ¶chain::Id) -> Option<parachain::Status> {
|
||||
let balance = T::ParachainCurrency::free_balance(*id);
|
||||
@@ -669,42 +560,6 @@ impl<T: Trait> Module<T> {
|
||||
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
|
||||
// that each candidates' chain ID is valid.
|
||||
fn check_candidates(
|
||||
@@ -984,12 +839,18 @@ mod tests {
|
||||
};
|
||||
use keyring::Sr25519Keyring;
|
||||
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::registrar;
|
||||
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! {
|
||||
pub enum Origin for Test {
|
||||
parachains
|
||||
@@ -1312,25 +1173,6 @@ mod tests {
|
||||
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(
|
||||
id: u32,
|
||||
upward_messages: Vec<(ParachainDispatchOrigin, Vec<u8>)>
|
||||
@@ -1344,7 +1186,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: upward_messages.into_iter()
|
||||
@@ -1755,7 +1596,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
@@ -1788,7 +1628,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
@@ -1805,7 +1644,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
@@ -1846,7 +1684,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![],
|
||||
@@ -1885,7 +1722,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
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]
|
||||
fn empty_trie_root_const_is_blake2_hashed_null_node() {
|
||||
let hashed_null_node = <NodeCodec<Blake2Hasher> as trie_db::NodeCodec>::hashed_null_node();
|
||||
|
||||
@@ -198,8 +198,6 @@ decl_storage! {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn build<T: Trait>(config: &GenesisConfig<T>) {
|
||||
use sp_runtime::traits::Zero;
|
||||
|
||||
let mut p = config.parachains.clone();
|
||||
p.sort_unstable_by_key(|&(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.
|
||||
<parachains::Code>::insert(&id, &code);
|
||||
<parachains::Heads>::insert(&id, &genesis);
|
||||
<parachains::Watermarks<T>>::insert(&id, T::BlockNumber::zero());
|
||||
// Save initial parachains in registrar
|
||||
Paras::insert(id, ParaInfo { scheduling: Scheduling::Always })
|
||||
}
|
||||
@@ -907,7 +904,6 @@ mod tests {
|
||||
signature: block_data_hash.using_encoded(|d| collator.sign(d)),
|
||||
head_data: HeadData(head_data.to_vec()),
|
||||
parent_head: HeadData(parent_head.to_vec()),
|
||||
egress_queue_roots: vec![],
|
||||
fees: 0,
|
||||
block_data_hash,
|
||||
upward_messages: vec![],
|
||||
|
||||
@@ -770,11 +770,6 @@ sp_api::impl_runtime_apis! {
|
||||
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
|
||||
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>> {
|
||||
extrinsics
|
||||
.into_iter()
|
||||
|
||||
@@ -688,11 +688,6 @@ sp_api::impl_runtime_apis! {
|
||||
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
|
||||
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>> {
|
||||
extrinsics
|
||||
.into_iter()
|
||||
|
||||
@@ -24,9 +24,7 @@ use sp_core::Pair;
|
||||
use codec::{Encode, Decode};
|
||||
use primitives::{
|
||||
Hash,
|
||||
parachain::{
|
||||
HeadData, BlockData, Id as ParaId, Message, OutgoingMessages, Status as ParachainStatus,
|
||||
},
|
||||
parachain::{HeadData, BlockData, Id as ParaId, Status as ParachainStatus},
|
||||
};
|
||||
use collator::{
|
||||
InvalidHead, ParachainContext, Network, BuildParachainContext, load_spec, Configuration,
|
||||
@@ -57,13 +55,12 @@ struct AdderContext {
|
||||
|
||||
/// The parachain context.
|
||||
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,
|
||||
_relay_parent: Hash,
|
||||
status: ParachainStatus,
|
||||
ingress: I,
|
||||
) -> Self::ProduceCandidate
|
||||
{
|
||||
let adder_head = match AdderHead::decode(&mut &status.head_data.0[..]) {
|
||||
@@ -86,11 +83,7 @@ impl ParachainContext for AdderContext {
|
||||
add: adder_head.number % 100,
|
||||
};
|
||||
|
||||
let from_messages = ::adder::process_messages(
|
||||
ingress.into_iter().map(|(_, msg)| msg.0)
|
||||
);
|
||||
|
||||
let next_head = ::adder::execute(adder_head.hash(), adder_head, &next_body, from_messages)
|
||||
let next_head = ::adder::execute(adder_head.hash(), adder_head, &next_body)
|
||||
.expect("good execution params; qed");
|
||||
|
||||
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);
|
||||
|
||||
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_head: HeadData,
|
||||
block_data: &BlockData,
|
||||
from_messages: u64,
|
||||
) -> Result<HeadData, StateMismatch> {
|
||||
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 = new_state.overflowing_add(from_messages).0;
|
||||
|
||||
Ok(HeadData {
|
||||
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(¶ms.parent_head[..]);
|
||||
|
||||
// we also add based on incoming data from messages. ignoring unknown message
|
||||
// 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) {
|
||||
match crate::execute(parent_hash, parent_head, &block_data) {
|
||||
Ok(new_head) => parachain::write_result(
|
||||
&ValidationResult { head_data: new_head.encode() }
|
||||
),
|
||||
|
||||
@@ -24,8 +24,8 @@ use std::sync::Arc;
|
||||
use polkadot_primitives::{
|
||||
BlakeTwo256, Block, Hash, HashT, BlockId, Balance,
|
||||
parachain::{
|
||||
CollatorId, ConsolidatedIngress, StructuredUnroutedIngress, CandidateReceipt, CollationInfo,
|
||||
ParachainHost, Id as ParaId, Collation, OutgoingMessages, FeeSchedule, ErasureChunk,
|
||||
CollatorId, CandidateReceipt, CollationInfo,
|
||||
ParachainHost, Id as ParaId, Collation, FeeSchedule, ErasureChunk,
|
||||
HeadData, PoVBlock,
|
||||
},
|
||||
};
|
||||
@@ -71,7 +71,7 @@ pub async fn collation_fetch<C: Collators, P>(
|
||||
collators: C,
|
||||
client: Arc<P>,
|
||||
max_block_data_size: Option<u64>,
|
||||
) -> Result<(Collation, OutgoingMessages, HeadData, Balance),C::Error>
|
||||
) -> Result<(Collation, HeadData, Balance),C::Error>
|
||||
where
|
||||
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
|
||||
C: Collators + Unpin,
|
||||
@@ -92,8 +92,8 @@ pub async fn collation_fetch<C: Collators, P>(
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok((messages, parent_head, fees)) => {
|
||||
return Ok((collation, messages, parent_head, fees))
|
||||
Ok((parent_head, fees)) => {
|
||||
return Ok((collation, parent_head, fees))
|
||||
}
|
||||
Err(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
|
||||
}
|
||||
|
||||
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 {
|
||||
parachain_index: ParaId,
|
||||
outgoing: Vec<TargetedMessage>,
|
||||
@@ -309,9 +256,8 @@ impl ExternalitiesInner {
|
||||
fn final_checks(
|
||||
&mut self,
|
||||
upward_messages: &[UpwardMessage],
|
||||
egress_queue_roots: &[(ParaId, Hash)],
|
||||
fees_charged: Option<Balance>,
|
||||
) -> Result<(OutgoingMessages, Balance), Error> {
|
||||
) -> Result<Balance, Error> {
|
||||
if self.upward != upward_messages {
|
||||
return Err(Error::UpwardMessagesInvalid {
|
||||
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(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
//
|
||||
// Reused by `validate_collation` and `validate_receipt`.
|
||||
@@ -422,13 +330,12 @@ fn do_validation<P>(
|
||||
max_block_data_size: Option<u64>,
|
||||
fees_charged: Option<Balance>,
|
||||
head_data: &HeadData,
|
||||
queue_roots: &Vec<(ParaId, Hash)>,
|
||||
upward_messages: &Vec<UpwardMessage>,
|
||||
) -> Result<(OutgoingMessages, HeadData, Balance), Error> where
|
||||
) -> Result<(HeadData, Balance), Error> where
|
||||
P: ProvideRuntimeApi<Block>,
|
||||
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
|
||||
{
|
||||
use parachain::{IncomingMessage, ValidationParams};
|
||||
use parachain::ValidationParams;
|
||||
|
||||
if let Some(max_size) = max_block_data_size {
|
||||
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)?
|
||||
.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 {
|
||||
parent_head: chain_status.head_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);
|
||||
@@ -472,13 +367,12 @@ fn do_validation<P>(
|
||||
) {
|
||||
Ok(result) => {
|
||||
if result.head_data == head_data.0 {
|
||||
let (messages, fees) = ext.0.lock().final_checks(
|
||||
let fees = ext.0.lock().final_checks(
|
||||
upward_messages,
|
||||
queue_roots,
|
||||
fees_charged
|
||||
)?;
|
||||
|
||||
Ok((messages, chain_status.head_data, fees))
|
||||
Ok((chain_status.head_data, fees))
|
||||
} else {
|
||||
Err(Error::WrongHeadData {
|
||||
expected: head_data.0.clone(),
|
||||
@@ -500,7 +394,6 @@ pub fn produce_receipt_and_chunks(
|
||||
n_validators: usize,
|
||||
parent_head: HeadData,
|
||||
pov: &PoVBlock,
|
||||
messages: &OutgoingMessages,
|
||||
fees: Balance,
|
||||
info: &CollationInfo,
|
||||
) -> Result<(CandidateReceipt, Vec<ErasureChunk>), Error>
|
||||
@@ -508,7 +401,6 @@ pub fn produce_receipt_and_chunks(
|
||||
let erasure_chunks = erasure::obtain_chunks(
|
||||
n_validators,
|
||||
&pov.block_data,
|
||||
Some(&messages.clone().into())
|
||||
)?;
|
||||
|
||||
let branches = erasure::branches(erasure_chunks.as_ref());
|
||||
@@ -532,7 +424,6 @@ pub fn produce_receipt_and_chunks(
|
||||
signature: info.signature.clone(),
|
||||
head_data: info.head_data.clone(),
|
||||
parent_head,
|
||||
egress_queue_roots: info.egress_queue_roots.clone(),
|
||||
fees,
|
||||
block_data_hash: info.block_data_hash.clone(),
|
||||
upward_messages: info.upward_messages.clone(),
|
||||
@@ -552,11 +443,11 @@ pub fn validate_receipt<P>(
|
||||
pov_block: &PoVBlock,
|
||||
receipt: &CandidateReceipt,
|
||||
max_block_data_size: Option<u64>,
|
||||
) -> Result<(OutgoingMessages, Vec<ErasureChunk>), Error> where
|
||||
) -> Result<Vec<ErasureChunk>, Error> where
|
||||
P: ProvideRuntimeApi<Block>,
|
||||
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
|
||||
{
|
||||
let (messages, parent_head, _fees) = do_validation(
|
||||
let (parent_head, _fees) = do_validation(
|
||||
client,
|
||||
relay_parent,
|
||||
pov_block,
|
||||
@@ -564,7 +455,6 @@ pub fn validate_receipt<P>(
|
||||
max_block_data_size,
|
||||
Some(receipt.fees),
|
||||
&receipt.head_data,
|
||||
&receipt.egress_queue_roots,
|
||||
&receipt.upward_messages,
|
||||
)?;
|
||||
|
||||
@@ -583,7 +473,6 @@ pub fn validate_receipt<P>(
|
||||
n_validators,
|
||||
parent_head,
|
||||
pov_block,
|
||||
&messages,
|
||||
receipt.fees,
|
||||
&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.
|
||||
@@ -608,7 +497,7 @@ pub fn validate_collation<P>(
|
||||
relay_parent: &BlockId,
|
||||
collation: &Collation,
|
||||
max_block_data_size: Option<u64>,
|
||||
) -> Result<(OutgoingMessages, HeadData, Balance), Error> where
|
||||
) -> Result<(HeadData, Balance), Error> where
|
||||
P: ProvideRuntimeApi<Block>,
|
||||
P::Api: ParachainHost<Block, Error = sp_blockchain::Error>,
|
||||
{
|
||||
@@ -624,7 +513,6 @@ pub fn validate_collation<P>(
|
||||
max_block_data_size,
|
||||
None,
|
||||
&collation.info.head_data,
|
||||
&collation.info.egress_queue_roots,
|
||||
&collation.info.upward_messages,
|
||||
)
|
||||
}
|
||||
@@ -636,50 +524,6 @@ mod tests {
|
||||
use parachain::ParachainDispatchOrigin;
|
||||
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]
|
||||
fn ext_rejects_local_message() {
|
||||
let mut ext = ExternalitiesInner {
|
||||
@@ -719,7 +563,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(Vec::new()),
|
||||
parent_head: HeadData(Vec::new()),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![
|
||||
@@ -730,7 +573,6 @@ mod tests {
|
||||
};
|
||||
assert!(ext().final_checks(
|
||||
&receipt.upward_messages,
|
||||
&receipt.egress_queue_roots,
|
||||
Some(receipt.fees),
|
||||
).is_err());
|
||||
let receipt = CandidateReceipt {
|
||||
@@ -739,7 +581,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(Vec::new()),
|
||||
parent_head: HeadData(Vec::new()),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![
|
||||
@@ -749,7 +590,6 @@ mod tests {
|
||||
};
|
||||
assert!(ext().final_checks(
|
||||
&receipt.upward_messages,
|
||||
&receipt.egress_queue_roots,
|
||||
Some(receipt.fees),
|
||||
).is_err());
|
||||
let receipt = CandidateReceipt {
|
||||
@@ -758,7 +598,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(Vec::new()),
|
||||
parent_head: HeadData(Vec::new()),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![
|
||||
@@ -768,7 +607,6 @@ mod tests {
|
||||
};
|
||||
assert!(ext().final_checks(
|
||||
&receipt.upward_messages,
|
||||
&receipt.egress_queue_roots,
|
||||
Some(receipt.fees),
|
||||
).is_err());
|
||||
let receipt = CandidateReceipt {
|
||||
@@ -777,7 +615,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(Vec::new()),
|
||||
parent_head: HeadData(Vec::new()),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 0,
|
||||
block_data_hash: Default::default(),
|
||||
upward_messages: vec![
|
||||
@@ -787,7 +624,6 @@ mod tests {
|
||||
};
|
||||
assert!(ext().final_checks(
|
||||
&receipt.upward_messages,
|
||||
&receipt.egress_queue_roots,
|
||||
Some(receipt.fees),
|
||||
).is_ok());
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ use codec::Encode;
|
||||
use polkadot_primitives::Hash;
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Chain, DutyRoster, CandidateReceipt,
|
||||
Statement as PrimitiveStatement, Message, OutgoingMessages,
|
||||
Statement as PrimitiveStatement,
|
||||
Collation, PoVBlock, ErasureChunk, ValidatorSignature, ValidatorIndex,
|
||||
ValidatorPair, ValidatorId,
|
||||
};
|
||||
@@ -48,7 +48,7 @@ use futures::prelude::*;
|
||||
|
||||
pub use self::block_production::ProposerFactory;
|
||||
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,
|
||||
};
|
||||
pub use self::error::Error;
|
||||
@@ -70,9 +70,6 @@ pub mod collation;
|
||||
pub mod validation_service;
|
||||
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.
|
||||
///
|
||||
/// This is expected to be a lightweight, shared type like an `Arc`.
|
||||
@@ -92,7 +89,6 @@ pub trait TableRouter: Clone {
|
||||
&self,
|
||||
collation: Collation,
|
||||
receipt: CandidateReceipt,
|
||||
outgoing: OutgoingMessages,
|
||||
chunks: (ValidatorIndex, &[ErasureChunk]),
|
||||
) -> 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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -24,7 +24,7 @@ use availability_store::{Store as AvailabilityStore};
|
||||
use table::{self, Table, Context as TableContextTrait};
|
||||
use polkadot_primitives::{Block, BlockId, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, OutgoingMessages, CandidateReceipt, ValidatorPair, ValidatorId,
|
||||
Id as ParaId, CandidateReceipt, ValidatorPair, ValidatorId,
|
||||
AttestedCandidate, ParachainHost, PoVBlock, ValidatorIndex, ErasureChunk,
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ impl TableContext {
|
||||
}
|
||||
|
||||
pub(crate) enum Validation {
|
||||
Valid(PoVBlock, OutgoingMessages),
|
||||
Valid(PoVBlock),
|
||||
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.
|
||||
/// outgoing message required.
|
||||
pub fn known_good(hash: Hash, collation: PoVBlock, outgoing: OutgoingMessages) -> Self {
|
||||
pub fn known_good(hash: Hash, collation: PoVBlock) -> Self {
|
||||
Validated {
|
||||
statement: GenericStatement::Valid(hash),
|
||||
result: Validation::Valid(collation, outgoing),
|
||||
result: Validation::Valid(collation),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,26 +234,17 @@ impl Validated {
|
||||
pub fn collated_local(
|
||||
receipt: CandidateReceipt,
|
||||
collation: PoVBlock,
|
||||
outgoing: OutgoingMessages,
|
||||
) -> Self {
|
||||
Validated {
|
||||
statement: GenericStatement::Candidate(receipt),
|
||||
result: Validation::Valid(collation, outgoing),
|
||||
result: Validation::Valid(collation),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the proof-of-validation block.
|
||||
pub fn pov_block(&self) -> &PoVBlock {
|
||||
match self.result {
|
||||
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,
|
||||
Validation::Valid(ref b) | Validation::Invalid(ref b) => b,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,7 +264,7 @@ impl<Fetch: Future + Unpin> ParachainWork<Fetch> {
|
||||
pub fn prime<P: ProvideRuntimeApi<Block>>(self, api: Arc<P>)
|
||||
-> PrimedParachainWork<
|
||||
Fetch,
|
||||
impl Send + FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<(OutgoingMessages, ErasureChunk), ()> + Unpin,
|
||||
impl Send + FnMut(&BlockId, &PoVBlock, &CandidateReceipt) -> Result<ErasureChunk, ()> + Unpin,
|
||||
>
|
||||
where
|
||||
P: Send + Sync + 'static,
|
||||
@@ -292,8 +283,8 @@ impl<Fetch: Future + Unpin> ParachainWork<Fetch> {
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok((messages, mut chunks)) => {
|
||||
Ok((messages, chunks.swap_remove(local_index)))
|
||||
Ok(mut chunks) => {
|
||||
Ok(chunks.swap_remove(local_index))
|
||||
}
|
||||
Err(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.
|
||||
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 }
|
||||
}
|
||||
@@ -327,7 +318,7 @@ pub struct PrimedParachainWork<Fetch, F> {
|
||||
impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
|
||||
where
|
||||
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>,
|
||||
{
|
||||
pub async fn validate(mut self) -> Result<(Validated, Option<ErasureChunk>), Err> {
|
||||
@@ -353,7 +344,7 @@ impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
|
||||
},
|
||||
None,
|
||||
)),
|
||||
Ok((outgoing_targeted, our_chunk)) => {
|
||||
Ok(our_chunk) => {
|
||||
self.inner.availability_store.add_erasure_chunk(
|
||||
self.inner.relay_parent,
|
||||
candidate.clone(),
|
||||
@@ -363,7 +354,7 @@ impl<Fetch, F, Err> PrimedParachainWork<Fetch, F>
|
||||
Ok((
|
||||
Validated {
|
||||
statement: GenericStatement::Valid(candidate_hash),
|
||||
result: Validation::Valid(pov_block, outgoing_targeted),
|
||||
result: Validation::Valid(pov_block),
|
||||
},
|
||||
Some(our_chunk),
|
||||
))
|
||||
@@ -580,9 +571,7 @@ mod tests {
|
||||
use super::*;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use primitives::crypto::UncheckedInto;
|
||||
use polkadot_primitives::parachain::{
|
||||
AvailableMessages, BlockData, ConsolidatedIngress, Collation, HeadData,
|
||||
};
|
||||
use polkadot_primitives::parachain::{BlockData, Collation, HeadData};
|
||||
use polkadot_erasure_coding::{self as erasure};
|
||||
use availability_store::ProvideGossipMessages;
|
||||
use futures::future;
|
||||
@@ -592,7 +581,6 @@ mod tests {
|
||||
fn pov_block_with_data(data: Vec<u8>) -> PoVBlock {
|
||||
PoVBlock {
|
||||
block_data: BlockData(data),
|
||||
ingress: ConsolidatedIngress(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +615,6 @@ mod tests {
|
||||
&self,
|
||||
_collation: Collation,
|
||||
_candidate: CandidateReceipt,
|
||||
_outgoing: OutgoingMessages,
|
||||
_chunks: (ValidatorIndex, &[ErasureChunk])
|
||||
) -> Self::SendLocalCollation { future::ready(Ok(())) }
|
||||
|
||||
@@ -671,7 +658,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [2; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -728,7 +714,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [2; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -766,7 +751,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash,
|
||||
upward_messages: Vec::new(),
|
||||
@@ -792,24 +776,17 @@ mod tests {
|
||||
max_block_data_size: None,
|
||||
};
|
||||
|
||||
let validated = block_on(producer.prime_with(|_, _, _| Ok((
|
||||
OutgoingMessages { outgoing_messages: Vec::new() },
|
||||
ErasureChunk {
|
||||
chunk: vec![1, 2, 3],
|
||||
index: local_index as u32,
|
||||
proof: vec![],
|
||||
},
|
||||
))).validate()).unwrap();
|
||||
let validated = block_on(producer.prime_with(|_, _, _| Ok(
|
||||
ErasureChunk {
|
||||
chunk: vec![1, 2, 3],
|
||||
index: local_index as u32,
|
||||
proof: vec![],
|
||||
}
|
||||
)).validate()).unwrap();
|
||||
|
||||
assert_eq!(validated.0.pov_block(), &pov_block);
|
||||
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 + 1).is_none());
|
||||
}
|
||||
@@ -823,7 +800,6 @@ mod tests {
|
||||
let block_data_hash = pov_block.block_data.hash();
|
||||
let local_index = 0;
|
||||
let n_validators = 2;
|
||||
let ex = Some(AvailableMessages(Vec::new()));
|
||||
|
||||
let candidate = CandidateReceipt {
|
||||
parachain_index: para_id,
|
||||
@@ -831,14 +807,13 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [2; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
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(
|
||||
&relay_parent,
|
||||
@@ -857,23 +832,16 @@ mod tests {
|
||||
max_block_data_size: None,
|
||||
};
|
||||
|
||||
let validated = block_on(producer.prime_with(|_, _, _| Ok((
|
||||
OutgoingMessages { outgoing_messages: Vec::new() },
|
||||
let validated = block_on(producer.prime_with(|_, _, _| Ok(
|
||||
ErasureChunk {
|
||||
chunk: chunks[local_index].clone(),
|
||||
index: local_index as u32,
|
||||
proof: vec![],
|
||||
},
|
||||
))).validate()).unwrap();
|
||||
)).validate()).unwrap();
|
||||
|
||||
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
|
||||
// enough to reconstruct the 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(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [2; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -953,7 +920,6 @@ mod tests {
|
||||
|
||||
let para_id = ParaId::from(1);
|
||||
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 local_key = Sr25519Keyring::Alice.pair();
|
||||
@@ -983,7 +949,6 @@ mod tests {
|
||||
signature: Default::default(),
|
||||
head_data: HeadData(vec![1, 2, 3, 4]),
|
||||
parent_head: HeadData(vec![]),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [2; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
@@ -994,7 +959,6 @@ mod tests {
|
||||
let signed_statement = shared_table.import_validated(Validated::collated_local(
|
||||
candidate,
|
||||
pov_block,
|
||||
outgoing_messages,
|
||||
)).unwrap();
|
||||
|
||||
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 {
|
||||
Ok((collation, outgoing_targeted, parent_head, fees_charged)) => {
|
||||
Ok((collation, parent_head, fees_charged)) => {
|
||||
match crate::collation::produce_receipt_and_chunks(
|
||||
authorities_num,
|
||||
parent_head,
|
||||
&collation.pov,
|
||||
&outgoing_targeted,
|
||||
fees_charged,
|
||||
&collation.info,
|
||||
) {
|
||||
@@ -421,7 +420,6 @@ impl<C, N, P, SP> ParachainValidationInstances<C, N, P, SP> where
|
||||
router.local_collation(
|
||||
collation,
|
||||
receipt,
|
||||
outgoing_targeted,
|
||||
(local_id, &chunks),
|
||||
).map_err(|e| warn!(target: "validation", "Failed to send local collation: {:?}", e))
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user