Unverified Commit 89a7d8dc authored by Gavin Wood's avatar Gavin Wood Committed by GitHub
Browse files

Allow parachains to send messages (#274)



* Slots module

* Integrate slots

* More drafting

* Minor updates

* Update parachains to use trati

* More build fixes

* Full code now compiles

* Add renew bid function

* Implement calculate_winner

* Warning remove

* Update gitignore

* Test framework

* Tests

* Further testing

* More tests, new parameterisation.

* Fix and new test

* Thread-safe tests

* Test off-boarding and a fix.

* Test onboarding

* Allow late onboarding.

* Another test and fix

* Avoid println in nostd

* Compact representation of paraids

* Introduce documentation.

* Introduce events.

* Additional test and fix

* Additional test

* Tidy up line lengths.

* Remove printlns

* Use later substrate utils.

* Allow parachains to send messages.

* Fix build/test

* Make slots work with latest substrate

* Update runtime/src/slot_range.rs

Co-Authored-By: asynchronous rob's avatarRobert Habermeier <rphmeier@gmail.com>

* Update runtime/src/slots.rs

Co-Authored-By: Shawn Tabrizi's avatarShawn Tabrizi <shawntabrizi@gmail.com>

* Update runtime/src/slots.rs

Co-Authored-By: Shawn Tabrizi's avatarShawn Tabrizi <shawntabrizi@gmail.com>

* Polish logic

* Rewind to earlier substrate master

* Remove dead code.

* Fix build

* Update substrate ref to master

* Update to new inherent digests API

* address grumbles

* fix

* Fix a warning.

* Reworded a comment.

* Check that receipt matches expectations

* Add test for final checks

* Split out queuing logic.

* Test final piece of queuing logic

* Fix up docs.

* More docs fixes
parent 027299b1
This diff is collapsed.
......@@ -152,10 +152,10 @@ pub fn collate<'a, R, P>(
collator: key.public(),
signature,
head_data,
balance_uploads: Vec::new(),
egress_queue_roots,
fees: 0,
block_data_hash,
upward_messages: Vec::new(),
};
Ok(parachain::Collation {
......
......@@ -277,10 +277,10 @@ mod tests {
collator: primary.into(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: [3; 32].into(),
upward_messages: Vec::new(),
},
pov: make_pov(vec![4, 5, 6]),
});
......@@ -304,10 +304,10 @@ mod tests {
collator: primary,
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: [3; 32].into(),
upward_messages: Vec::new(),
},
pov: make_pov(vec![4, 5, 6]),
});
......
......@@ -643,10 +643,10 @@ mod tests {
collator: [255; 32].unchecked_into(),
head_data: HeadData(vec![9, 9, 9]),
signature: Default::default(),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash: [20u8; 32].into(),
upward_messages: Vec::new(),
};
let statement = GossipMessage::Statement(GossipStatement {
......
......@@ -69,7 +69,7 @@ mod benefit {
type FullStatus = GenericFullStatus<Block>;
/// Specialization of the network service for the polkadot protocol.
pub type NetworkService = ::substrate_network::Service<Block, PolkadotProtocol>;
pub type NetworkService = ::substrate_network::NetworkService<Block, PolkadotProtocol>;
/// Status of a Polkadot node.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
......
......@@ -158,10 +158,10 @@ fn fetches_from_those_with_knowledge() {
collator: [255; 32].unchecked_into(),
head_data: HeadData(vec![9, 9, 9]),
signature: Default::default(),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash,
upward_messages: Vec::new(),
};
let candidate_hash = candidate_receipt.hash();
......@@ -242,10 +242,10 @@ fn fetches_available_block_data() {
collator: [255; 32].unchecked_into(),
head_data: HeadData(vec![9, 9, 9]),
signature: Default::default(),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash,
upward_messages: Vec::new(),
};
let candidate_hash = candidate_receipt.hash();
......
......@@ -197,3 +197,37 @@ pub struct MessageRef<'a> {
/// Underlying data of the message.
pub data: &'a [u8],
}
/// Which origin a parachain's message to the relay chain should be dispatched from.
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(u8)]
pub enum ParachainDispatchOrigin {
/// As a simple `Origin::Signed`, using `ParaId::account_id` as its value. This is good when
/// interacting with standard modules such as `balances`.
Signed,
/// As the special `Origin::Parachain(ParaId)`. This is good when interacting with parachain-
/// aware modules which need to succinctly verify that the origin is a parachain.
Parachain,
}
impl core::convert::TryFrom<u8> for ParachainDispatchOrigin {
type Error = ();
fn try_from(x: u8) -> core::result::Result<ParachainDispatchOrigin, ()> {
const SIGNED: u8 = ParachainDispatchOrigin::Signed as u8;
const PARACHAIN: u8 = ParachainDispatchOrigin::Parachain as u8;
Ok(match x {
SIGNED => ParachainDispatchOrigin::Signed,
PARACHAIN => ParachainDispatchOrigin::Parachain,
_ => return Err(()),
})
}
}
/// A reference to an upward message.
pub struct UpwardMessageRef<'a> {
/// The origin type.
pub origin: ParachainDispatchOrigin,
/// Underlying data of the message.
pub data: &'a [u8],
}
......@@ -17,11 +17,14 @@
//! Utilities for writing parachain WASM.
use codec::{Encode, Decode};
use super::{ValidationParams, ValidationResult, MessageRef};
use super::{
ValidationParams, ValidationResult, MessageRef, UpwardMessageRef, ParachainDispatchOrigin
};
mod ll {
extern "C" {
pub(super) fn ext_post_message(target: u32, data_ptr: *const u8, data_len: u32);
pub(super) fn ext_post_upward_message(origin: u32, data_ptr: *const u8, data_len: u32);
}
}
......@@ -61,3 +64,11 @@ pub fn post_message(message: MessageRef) {
unsafe { ll::ext_post_message(message.target.into_inner(), data_ptr, data_len as u32) }
}
/// Post a message to this parachain's relay chain.
pub fn post_upward_message(message: UpwardMessageRef) {
let data_ptr = message.as_ptr();
let data_len = message.len();
unsafe { ll::ext_post_upward_message(message.origin as u8 as u32, data_ptr, data_len as u32) }
}
......@@ -20,20 +20,21 @@
//! Assuming the parameters are correct, this module provides a wrapper around
//! a WASM VM for re-execution of a parachain candidate.
use std::{cell::RefCell, fmt, convert::TryInto};
use codec::{Decode, Encode};
use wasmi::{self, Module, ModuleInstance, Trap, MemoryInstance, MemoryDescriptor, MemoryRef, ModuleImportResolver};
use wasmi::{memory_units, RuntimeValue, Externals, Error as WasmError, ValueType};
use wasmi::memory_units::{Bytes, Pages, RoundUpTo};
use super::{ValidationParams, ValidationResult, MessageRef};
use std::cell::RefCell;
use std::fmt;
use wasmi::{
self, Module, ModuleInstance, Trap, MemoryInstance, MemoryDescriptor, MemoryRef,
ModuleImportResolver, RuntimeValue, Externals, Error as WasmError, ValueType,
memory_units::{self, Bytes, Pages, RoundUpTo}
};
use super::{ValidationParams, ValidationResult, MessageRef, UpwardMessageRef};
mod ids {
/// Post a message to another parachain.
pub const POST_MESSAGE: usize = 1;
/// Post a message to this parachain's relay chain.
pub const POST_UPWARDS_MESSAGE: usize = 2;
}
error_chain! {
......@@ -67,6 +68,9 @@ pub enum ExternalitiesError {
pub trait Externalities {
/// Called when a message is to be posted to another parachain.
fn post_message(&mut self, message: MessageRef) -> Result<(), ExternalitiesError>;
/// Called when a message is to be posted to the parachain's relay chain.
fn post_upward_message(&mut self, message: UpwardMessageRef) -> Result<(), ExternalitiesError>;
}
impl fmt::Display for ExternalitiesError {
......@@ -96,7 +100,23 @@ impl ModuleImportResolver for Resolver {
"ext_post_message" => {
let index = ids::POST_MESSAGE;
let (params, ret_ty): (&[ValueType], Option<ValueType>) =
(&[ValueType::I32, ValueType::I32, ValueType::I32], None);
(&[ValueType::I32, ValueType::I32, ValueType::I32], None);
if signature.params() != params && signature.return_type() != ret_ty {
Err(WasmError::Instantiation(
format!("Export {} has a bad signature", field_name)
))
} else {
Ok(wasmi::FuncInstance::alloc_host(
wasmi::Signature::new(&params[..], ret_ty),
index,
))
}
}
"ext_upwards_post_message" => {
let index = ids::POST_UPWARDS_MESSAGE;
let (params, ret_ty): (&[ValueType], Option<ValueType>) =
(&[ValueType::I32, ValueType::I32], None);
if signature.params() != params && signature.return_type() != ret_ty {
Err(WasmError::Instantiation(
......@@ -172,6 +192,31 @@ impl<'a, E: 'a + Externalities> ValidationExternals<'a, E> {
}
})
}
/// Signature: post_upward_message(u32, *const u8, u32) -> None
/// usage: post_upward_message(origin, data ptr, data len).
/// Origin is the integer representation of the dispatch origin.
/// Data is the raw data of the message.
fn ext_post_upward_message(&mut self, args: ::wasmi::RuntimeArgs) -> Result<(), Trap> {
let origin: u32 = args.nth_checked(0)?;
let data_ptr: u32 = args.nth_checked(1)?;
let data_len: u32 = args.nth_checked(2)?;
let (data_ptr, data_len) = (data_ptr as usize, data_len as usize);
self.memory.with_direct_access(|mem| {
if mem.len() < (data_ptr + data_len) {
Err(Trap::new(wasmi::TrapKind::MemoryAccessOutOfBounds))
} else {
let origin = (origin as u8).try_into()
.map_err(|_| Trap::new(wasmi::TrapKind::UnexpectedSignature))?;
let message = UpwardMessageRef { origin, data: &mem[data_ptr..][..data_len] };
let res = self.externalities.post_upward_message(message);
res.map_err(|e| Trap::new(wasmi::TrapKind::Host(
Box::new(e) as Box<_>
)))
}
})
}
}
impl<'a, E: 'a + Externalities> Externals for ValidationExternals<'a, E> {
......@@ -182,6 +227,7 @@ impl<'a, E: 'a + Externalities> Externals for ValidationExternals<'a, E> {
) -> Result<Option<RuntimeValue>, Trap> {
match index {
ids::POST_MESSAGE => self.ext_post_message(args).map(|_| None),
ids::POST_UPWARDS_MESSAGE => self.ext_post_upward_message(args).map(|_| None),
_ => panic!("no externality at given index"),
}
}
......
......@@ -22,7 +22,7 @@ extern crate parity_codec as codec;
extern crate polkadot_parachain as parachain;
extern crate tiny_keccak;
use parachain::{MessageRef, IncomingMessage, ValidationParams};
use parachain::{MessageRef, UpwardMessageRef, IncomingMessage, ValidationParams};
use parachain::wasm_executor::{Externalities, ExternalitiesError};
use codec::{Decode, Encode};
......@@ -57,6 +57,9 @@ impl Externalities for DummyExt {
fn post_message(&mut self, _message: MessageRef) -> Result<(), ExternalitiesError> {
Ok(())
}
fn post_upward_message(&mut self, _message: UpwardMessageRef) -> Result<(), ExternalitiesError> {
Ok(())
}
}
const TEST_CODE: &[u8] = include_bytes!("res/adder.wasm");
......
......@@ -19,7 +19,7 @@
use rstd::prelude::*;
use rstd::cmp::Ordering;
use parity_codec::{Encode, Decode};
use super::Hash;
use super::{Hash, Balance};
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
......@@ -28,7 +28,7 @@ use serde::{Serialize, Deserialize};
use primitives::bytes;
use primitives::ed25519;
pub use polkadot_parachain::{Id, AccountIdConversion};
pub use polkadot_parachain::{Id, AccountIdConversion, ParachainDispatchOrigin};
/// Identity that collators use.
pub type CollatorId = ed25519::Public;
......@@ -108,6 +108,16 @@ pub struct Extrinsic {
pub outgoing_messages: Vec<OutgoingMessage>
}
/// A message from a parachain to its Relay Chain.
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct UpwardMessage {
/// The origin for the message to be sent from.
pub origin: ParachainDispatchOrigin,
/// The message data.
pub data: Vec<u8>,
}
/// Candidate receipt type.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
......@@ -120,15 +130,15 @@ pub struct CandidateReceipt {
pub signature: CollatorSignature,
/// The head-data
pub head_data: HeadData,
/// Balance uploads to the relay chain.
pub balance_uploads: Vec<(super::AccountId, u64)>,
/// 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: u64,
pub fees: Balance,
/// blake2-256 Hash of block data.
pub block_data_hash: Hash,
/// Messages destined to be interpreted by the Relay chain itself.
pub upward_messages: Vec<UpwardMessage>,
}
impl CandidateReceipt {
......
......@@ -172,6 +172,7 @@ impl<T: Trait> ValidateUnsigned for Module<T> {
requires: vec![],
provides: vec![],
longevity: TransactionLongevity::max_value(),
propagate: true,
}
}
_ => TransactionValidity::Invalid(INVALID_CALL)
......@@ -345,6 +346,7 @@ mod tests {
requires: vec![],
provides: vec![],
longevity: TransactionLongevity::max_value(),
propagate: true,
}
);
assert_eq!(
......
......@@ -200,7 +200,10 @@ impl grandpa::Trait for Runtime {
type Event = Event;
}
impl parachains::Trait for Runtime {}
impl parachains::Trait for Runtime {
type Origin = Origin;
type Call = Call;
}
parameter_types!{
pub const LeasePeriod: BlockNumber = 100000;
......@@ -245,7 +248,7 @@ construct_runtime!(
CouncilMotions: council_motions::{Module, Call, Storage, Event<T>, Origin},
CouncilSeats: council_seats::{Config<T>},
Treasury: treasury,
Parachains: parachains::{Module, Call, Storage, Config<T>, Inherent},
Parachains: parachains::{Module, Call, Storage, Config<T>, Inherent, Origin},
Slots: slots::{Module, Call, Storage, Event<T>},
Sudo: sudo,
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -21,13 +21,12 @@
use std::sync::Arc;
use polkadot_primitives::{Block, Hash, BlockId, parachain::CollatorId};
use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic, OutgoingMessage};
use polkadot_primitives::parachain::{
use polkadot_primitives::{Block, Hash, BlockId, parachain::CollatorId, parachain::{
ConsolidatedIngress, ConsolidatedIngressRoots, CandidateReceipt, ParachainHost,
};
Id as ParaId, Collation, Extrinsic, OutgoingMessage, UpwardMessage
}};
use runtime_primitives::traits::ProvideRuntimeApi;
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef};
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef, UpwardMessageRef};
use error_chain::bail;
use futures::prelude::*;
......@@ -190,6 +189,10 @@ error_chain! {
description("Block data is too big."),
display("Block data is too big (maximum allowed size: {}, actual size: {})", max_size, size),
}
UpwardMessagesInvalid(expected: Vec<UpwardMessage>, got: Vec<UpwardMessage>) {
description("Parachain validation produced wrong relay-chain messages."),
display("Parachain validation produced wrong relay-chain messages (expected: {:?}, got {:?})", expected, got),
}
}
}
......@@ -275,6 +278,7 @@ fn check_extrinsic(
struct Externalities {
parachain_index: ParaId,
outgoing: Vec<OutgoingMessage>,
upward: Vec<UpwardMessage>,
}
impl wasm_executor::Externalities for Externalities {
......@@ -293,14 +297,35 @@ impl wasm_executor::Externalities for Externalities {
Ok(())
}
fn post_upward_message(&mut self, message: UpwardMessageRef)
-> Result<(), ExternalitiesError>
{
// TODO: https://github.com/paritytech/polkadot/issues/92
// check per-message and per-byte fees for the parachain.
self.upward.push(UpwardMessage {
origin: message.origin,
data: message.data.to_vec(),
});
Ok(())
}
}
impl Externalities {
// Performs final checks of validity, producing the extrinsic data.
fn final_checks(
self,
candidate: &CandidateReceipt,
) -> Result<Extrinsic, Error> {
if &self.upward != &candidate.upward_messages {
bail!(ErrorKind::UpwardMessagesInvalid(
candidate.upward_messages.clone(),
self.upward.clone(),
));
}
check_extrinsic(
self.outgoing,
&candidate.egress_queue_roots[..],
......@@ -382,6 +407,7 @@ pub fn validate_collation<P>(
let mut ext = Externalities {
parachain_index: collation.receipt.parachain_index.clone(),
outgoing: Vec::new(),
upward: Vec::new(),
};
match wasm_executor::validate_candidate(&validation_code, params, &mut ext) {
......@@ -403,6 +429,8 @@ pub fn validate_collation<P>(
mod tests {
use super::*;
use parachain::wasm_executor::Externalities as ExternalitiesTrait;
use parachain::ParachainDispatchOrigin;
use polkadot_primitives::parachain::{Statement::Candidate, CandidateReceipt, HeadData};
#[test]
fn compute_and_check_egress() {
......@@ -453,9 +481,74 @@ mod tests {
let mut ext = Externalities {
parachain_index: 5.into(),
outgoing: Vec::new(),
upward: Vec::new(),
};
assert!(ext.post_message(MessageRef { target: 1.into(), data: &[] }).is_ok());
assert!(ext.post_message(MessageRef { target: 5.into(), data: &[] }).is_err());
}
#[test]
fn ext_checks_upward_messages() {
let ext = || Externalities {
parachain_index: 5.into(),
outgoing: Vec::new(),
upward: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Parachain },
],
};
let receipt = CandidateReceipt {
parachain_index: 5.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Signed },
UpwardMessage{ data: vec![69], origin: ParachainDispatchOrigin::Parachain },
],
};
assert!(ext().final_checks(&receipt).is_err());
let receipt = CandidateReceipt {
parachain_index: 5.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Signed },
],
};
assert!(ext().final_checks(&receipt).is_err());
let receipt = CandidateReceipt {
parachain_index: 5.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![
UpwardMessage{ data: vec![69], origin: ParachainDispatchOrigin::Parachain },
],
};
assert!(ext().final_checks(&receipt).is_err());
let receipt = CandidateReceipt {
parachain_index: 5.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(Vec::new()),
egress_queue_roots: Vec::new(),
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![
UpwardMessage{ data: vec![42], origin: ParachainDispatchOrigin::Parachain },
],
};
assert!(ext().final_checks(&receipt).is_ok());
}
}
......@@ -48,7 +48,7 @@ use polkadot_primitives::parachain::{
Collation, PoVBlock,
};
use primitives::{Pair, ed25519};
use runtime_primitives::{traits::{ProvideRuntimeApi, Header as HeaderT}, ApplyError};
use runtime_primitives::{traits::{ProvideRuntimeApi, Header as HeaderT, Block as BlockT}, ApplyError};
use tokio::runtime::TaskExecutor;
use tokio::timer::{Delay, Interval};
use transaction_pool::txpool::{Pool, ChainApi as PoolChainApi};
......@@ -570,7 +570,11 @@ impl<C, TxApi> consensus::Proposer<Block> for Proposer<C, TxApi> where
type Error = Error;
type Create = Either<CreateProposal<C, TxApi>, future::FutureResult<Block, Error>>;
fn propose(&self, inherent_data: InherentData, max_duration: Duration) -> Self::Create {
fn propose(&self,
inherent_data: InherentData,
_digest: <<Block as BlockT>::Header as HeaderT>::Digest,
max_duration: Duration,
) -> Self::Create {
const ATTEMPT_PROPOSE_EVERY: Duration = Duration::from_millis(100);
const SLOT_DURATION_DENOMINATOR: u64 = 3; // wait up to 1/3 of the slot for candidates.
......@@ -712,7 +716,7 @@ impl<C, TxApi> CreateProposal<C, TxApi> where
let runtime_api = self.client.runtime_api();
let mut block_builder = BlockBuilder::at_block(&self.parent_id, &*self.client, false)?;
let mut block_builder = BlockBuilder::at_block(&self.parent_id, &*self.client, false, Default::default())?;
{
let inherents = runtime_api.inherent_extrinsics(&self.parent_id, inherent_data)?;
......
......@@ -630,10 +630,10 @@ mod tests {
collator: [1; 32].unchecked_into(),
signature: Default::default(),
head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash: [2; 32].into(),
upward_messages: Vec::new(),
};
let candidate_statement = GenericStatement::Candidate(candidate);
......@@ -684,10 +684,10 @@ mod tests {
collator: [1; 32].unchecked_into(),
signature: Default::default(),
head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash: [2; 32].into(),
upward_messages: Vec::new(),
};
let candidate_statement = GenericStatement::Candidate(candidate);
......@@ -717,10 +717,10 @@ mod tests {
collator: [1; 32].unchecked_into(),
signature: Default::default(),
head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash: [2; 32].into(),
upward_messages: Vec::new(),
};
let hash = candidate.hash();
......@@ -758,10 +758,10 @@ mod tests {
collator: [1; 32].unchecked_into(),
signature: Default::default(),