Downward & Upward messages (#1266)

* Downward messages, the front-end.

* Move types around to make them accessible from Parachains

* Fix compilation

* Fix branch

* Make it compile for Cumulus

* Update the branch names

* Add default generic parameter

* Implement `Partialeq`

* Move upward messages into the `ValidationResult`

* Support disabling of the runtime api

* Update branch

* Adds support for handling downward messages

* Implement sending XCMP messages as up/downward messages

* service: update to latest ServiceBuilder changes

* Make it compile

* Initial commit

Forked at: ef2aa428d7
Parent branch: origin/master

* Update substrate branch to cecton-update-polkadot-substrate

* Update substrate & polkadot to cumulus-branch

* Reset branch

* Update primitives/src/parachain.rs

Co-authored-by: Robert Habermeier <rphmeier@gmail.com>

* Update runtime/common/src/parachains.rs

Co-authored-by: Robert Habermeier <rphmeier@gmail.com>

* Update runtime/common/src/parachains.rs

Co-authored-by: Robert Habermeier <rphmeier@gmail.com>

* Minor fixes

* Fix wasm build

Co-authored-by: Gav Wood <gavin@parity.io>
Co-authored-by: André Silva <andre.beat@gmail.com>
Co-authored-by: Cecile Tonglet <cecile.tonglet@cecton.com>
Co-authored-by: Robert Habermeier <rphmeier@gmail.com>
This commit is contained in:
Bastian Köcher
2020-07-01 16:20:38 +02:00
committed by GitHub
parent 3b357fadd5
commit 934f27d92b
37 changed files with 460 additions and 377 deletions
+14 -1
View File
@@ -4319,6 +4319,16 @@ dependencies = [
"tokio 0.2.21", "tokio 0.2.21",
] ]
[[package]]
name = "polkadot-core-primitives"
version = "0.7.30"
dependencies = [
"parity-scale-codec",
"sp-core",
"sp-runtime",
"sp-std",
]
[[package]] [[package]]
name = "polkadot-erasure-coding" name = "polkadot-erasure-coding"
version = "0.8.13" version = "0.8.13"
@@ -4446,13 +4456,13 @@ dependencies = [
"log 0.4.8", "log 0.4.8",
"parity-scale-codec", "parity-scale-codec",
"parking_lot 0.10.2", "parking_lot 0.10.2",
"polkadot-core-primitives",
"sc-executor", "sc-executor",
"serde", "serde",
"shared_memory", "shared_memory",
"sp-core", "sp-core",
"sp-externalities", "sp-externalities",
"sp-io", "sp-io",
"sp-runtime-interface",
"sp-std", "sp-std",
"sp-wasm-interface", "sp-wasm-interface",
] ]
@@ -4464,6 +4474,7 @@ dependencies = [
"bitvec", "bitvec",
"frame-system", "frame-system",
"parity-scale-codec", "parity-scale-codec",
"polkadot-core-primitives",
"polkadot-parachain", "polkadot-parachain",
"pretty_assertions", "pretty_assertions",
"serde", "serde",
@@ -8043,6 +8054,7 @@ dependencies = [
"parity-scale-codec", "parity-scale-codec",
"polkadot-parachain", "polkadot-parachain",
"sp-io", "sp-io",
"sp-std",
"substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0", "tiny-keccak 1.5.0",
] ]
@@ -8070,6 +8082,7 @@ dependencies = [
"parity-scale-codec", "parity-scale-codec",
"polkadot-parachain", "polkadot-parachain",
"sp-io", "sp-io",
"sp-std",
"substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0", "tiny-keccak 1.5.0",
] ]
+1
View File
@@ -26,6 +26,7 @@ members = [
"availability-store", "availability-store",
"cli", "cli",
"collator", "collator",
"core-primitives",
"erasure-coding", "erasure-coding",
"network", "network",
"network/test", "network/test",
+2 -2
View File
@@ -33,7 +33,7 @@ use polkadot_primitives::{
}, },
}; };
use sp_runtime::traits::HashFor; use sp_runtime::traits::HashFor;
use sp_blockchain::{Result as ClientResult}; use sp_blockchain::Result as ClientResult;
use client::{ use client::{
BlockchainEvents, BlockBackend, BlockchainEvents, BlockBackend,
}; };
@@ -58,7 +58,7 @@ use worker::{
Worker, WorkerHandle, IncludedParachainBlocks, WorkerMsg, MakeAvailable, Chunks Worker, WorkerHandle, IncludedParachainBlocks, WorkerMsg, MakeAvailable, Chunks
}; };
use store::{Store as InnerStore}; use store::Store as InnerStore;
const LOG_TARGET: &str = "availability"; const LOG_TARGET: &str = "availability";
+1 -1
View File
@@ -18,7 +18,7 @@
use kvdb_rocksdb::{Database, DatabaseConfig}; use kvdb_rocksdb::{Database, DatabaseConfig};
use kvdb::{KeyValueDB, DBTransaction}; use kvdb::{KeyValueDB, DBTransaction};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use polkadot_erasure_coding::{self as erasure}; use polkadot_erasure_coding as erasure;
use polkadot_primitives::{ use polkadot_primitives::{
Hash, Hash,
parachain::{ parachain::{
+7 -1
View File
@@ -56,7 +56,7 @@ use sc_client_api::{StateBackend, BlockchainEvents};
use sp_blockchain::HeaderBackend; use sp_blockchain::HeaderBackend;
use sp_core::Pair; use sp_core::Pair;
use polkadot_primitives::{ use polkadot_primitives::{
BlockId, Hash, Block, BlockId, Hash, Block, DownwardMessage,
parachain::{ parachain::{
self, BlockData, DutyRoster, HeadData, Id as ParaId, self, BlockData, DutyRoster, HeadData, Id as ParaId,
PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationSchedule, PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationSchedule,
@@ -150,6 +150,7 @@ pub trait ParachainContext: Clone {
relay_parent: Hash, relay_parent: Hash,
global_validation: GlobalValidationSchedule, global_validation: GlobalValidationSchedule,
local_validation: LocalValidationData, local_validation: LocalValidationData,
downward_messages: Vec<DownwardMessage>,
) -> Self::ProduceCandidate; ) -> Self::ProduceCandidate;
} }
@@ -159,6 +160,7 @@ pub async fn collate<P>(
local_id: ParaId, local_id: ParaId,
global_validation: GlobalValidationSchedule, global_validation: GlobalValidationSchedule,
local_validation_data: LocalValidationData, local_validation_data: LocalValidationData,
downward_messages: Vec<DownwardMessage>,
mut para_context: P, mut para_context: P,
key: Arc<CollatorPair>, key: Arc<CollatorPair>,
) -> Option<parachain::Collation> ) -> Option<parachain::Collation>
@@ -170,6 +172,7 @@ pub async fn collate<P>(
relay_parent, relay_parent,
global_validation, global_validation,
local_validation_data, local_validation_data,
downward_messages,
).await?; ).await?;
let pov_block = PoVBlock { let pov_block = PoVBlock {
@@ -317,6 +320,7 @@ fn build_collator_service<P, C, R, Extrinsic>(
Some(local_validation) => local_validation, Some(local_validation) => local_validation,
None => return future::Either::Left(future::ok(())), None => return future::Either::Left(future::ok(())),
}; };
let downward_messages = try_fr!(api.downward_messages(&id, para_id));
let validators = try_fr!(api.validators(&id)); let validators = try_fr!(api.validators(&id));
@@ -331,6 +335,7 @@ fn build_collator_service<P, C, R, Extrinsic>(
para_id, para_id,
global_validation, global_validation,
local_validation, local_validation,
downward_messages,
parachain_context, parachain_context,
key, key,
).map(move |collation| { ).map(move |collation| {
@@ -470,6 +475,7 @@ mod tests {
_relay_parent: Hash, _relay_parent: Hash,
_global: GlobalValidationSchedule, _global: GlobalValidationSchedule,
_local_validation: LocalValidationData, _local_validation: LocalValidationData,
_: Vec<DownwardMessage>,
) -> Self::ProduceCandidate { ) -> Self::ProduceCandidate {
// send messages right back. // send messages right back.
future::ready(Some(( future::ready(Some((
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "polkadot-core-primitives"
version = "0.7.30"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = [ "derive" ] }
[features]
default = [ "std" ]
std = [
"sp-core/std",
"sp-runtime/std",
"sp-std/std",
"codec/std",
]
+96
View File
@@ -0,0 +1,96 @@
// Copyright 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/>.
#![cfg_attr(not(feature = "std"), no_std)]
//! Core Polkadot types.
//!
//! These core Polkadot types are used by the relay chain and the Parachains.
use sp_runtime::{generic, MultiSignature, traits::{Verify, BlakeTwo256, IdentifyAccount}};
/// The block number type used by Polkadot.
/// 32-bits will allow for 136 years of blocks assuming 1 block per second.
pub type BlockNumber = u32;
/// An instant or duration in time.
pub type Moment = u64;
/// Alias to type for a signature for a transaction on the relay chain. This allows one of several
/// kinds of underlying crypto to be used, so isn't a fixed size when encoded.
pub type Signature = MultiSignature;
/// Alias to the public key used for this chain, actually a `MultiSigner`. Like the signature, this
/// also isn't a fixed size when encoded, as different cryptos have different size public keys.
pub type AccountPublic = <Signature as Verify>::Signer;
/// Alias to the opaque account ID type for this chain, actually a `AccountId32`. This is always
/// 32 bytes.
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them.
pub type AccountIndex = u32;
/// Identifier for a chain. 32-bit should be plenty.
pub type ChainId = u32;
/// A hash of some data used by the relay chain.
pub type Hash = sp_core::H256;
/// Index of a transaction in the relay chain. 32-bit should be plenty.
pub type Nonce = u32;
/// The balance of an account.
/// 128-bits (or 38 significant decimal figures) will allow for 10m currency (10^7) at a resolution
/// to all for one second's worth of an annualised 50% reward be paid to a unit holder (10^11 unit
/// denomination), or 10^18 total atomic units, to grow at 50%/year for 51 years (10^9 multiplier)
/// for an eventual total of 10^27 units (27 significant decimal figures).
/// We round denomination to 10^12 (12 sdf), and leave the other redundancy at the upper end so
/// that 32 bits may be multiplied with a balance in 128 bits without worrying about overflow.
pub type Balance = u128;
/// Header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Block ID.
pub type BlockId = generic::BlockId<Block>;
/// Opaque, encoded, unchecked extrinsic.
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
/// The information that goes alongside a transfer_into_parachain operation. Entirely opaque, it
/// will generally be used for identifying the reason for the transfer. Typically it will hold the
/// destination account to which the transfer should be credited. If still more information is
/// needed, then this should be a hash with the pre-image presented via an off-chain mechanism on
/// the parachain.
pub type Remark = [u8; 32];
/// These are special "control" messages that can be passed from the Relaychain to a parachain.
/// They should be handled by all parachains.
#[derive(codec::Encode, codec::Decode, Clone, sp_runtime::RuntimeDebug, PartialEq)]
pub enum DownwardMessage<AccountId = crate::AccountId> {
/// Some funds were transferred into the parachain's account. The hash is the identifier that
/// was given with the transfer.
TransferInto(AccountId, Balance, Remark),
/// An opaque blob of data. The relay chain must somehow know how to form this so that the
/// destination parachain does something sensible.
///
/// NOTE: Be very careful not to allow users to place arbitrary size information in here.
Opaque(sp_std::vec::Vec<u8>),
/// XCMP message for the Parachain.
XCMPMessage(sp_std::vec::Vec<u8>),
}
+3
View File
@@ -185,6 +185,9 @@ sp_api::mock_impl_runtime_apis! {
parent_hash: Default::default(), parent_hash: Default::default(),
} }
} }
fn downward_messages(_: ParaId) -> Vec<polkadot_primitives::DownwardMessage> {
Vec::new()
}
} }
} }
+3 -3
View File
@@ -13,11 +13,11 @@ codec = { package = "parity-scale-codec", version = "1.1.0", default-features =
sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-wasm-interface = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-wasm-interface = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
polkadot-core-primitives = { path = "../core-primitives", default-features = false }
# all optional crates. # all optional crates.
derive_more = { version = "0.99.2", optional = true } derive_more = { version = "0.99.2", optional = true }
serde = { version = "1.0.102", default-features = false, features = [ "derive" ], optional = true } serde = { version = "1.0.102", default-features = false, features = [ "derive" ], optional = true }
sp-runtime-interface = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true, default-features = false }
sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sp-externalities = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true }
@@ -29,7 +29,7 @@ shared_memory = { version = "0.10.0", optional = true }
[features] [features]
default = ["std"] default = ["std"]
wasm-api = ["sp-runtime-interface"] wasm-api = []
std = [ std = [
"codec/std", "codec/std",
"derive_more", "derive_more",
@@ -39,8 +39,8 @@ std = [
"sp-core/std", "sp-core/std",
"parking_lot", "parking_lot",
"log", "log",
"sp-runtime-interface/std",
"sp-externalities", "sp-externalities",
"sc-executor", "sc-executor",
"sp-io", "sp-io",
"polkadot-core-primitives/std",
] ]
+10 -9
View File
@@ -28,9 +28,8 @@ use serde::{Serialize, Deserialize};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use sp_core::bytes; use sp_core::bytes;
/// The block number of the relay chain. /// Block number type used by the relay chain.
/// 32-bits will allow for 136 years of blocks assuming 1 block per second. pub use polkadot_core_primitives::BlockNumber as RelayChainBlockNumber;
pub type RelayChainBlockNumber = u32;
/// Parachain head data included in the chain. /// Parachain head data included in the chain.
#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug)] #[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug)]
@@ -186,10 +185,6 @@ impl sp_std::convert::TryFrom<u8> for ParachainDispatchOrigin {
/// A message from a parachain to its Relay Chain. /// A message from a parachain to its Relay Chain.
#[derive(Clone, PartialEq, Eq, Encode, Decode)] #[derive(Clone, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(
any(feature = "std", feature = "wasm-api"),
derive(sp_runtime_interface::pass_by::PassByCodec,
))]
#[cfg_attr(feature = "std", derive(Debug))] #[cfg_attr(feature = "std", derive(Debug))]
pub struct UpwardMessage { pub struct UpwardMessage {
/// The origin for the message to be sent from. /// The origin for the message to be sent from.
@@ -212,13 +207,13 @@ pub struct ValidationParams {
/// The maximum head-data size permitted, in bytes. /// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32, pub max_head_data_size: u32,
/// The current relay-chain block number. /// The current relay-chain block number.
pub relay_chain_height: RelayChainBlockNumber, pub relay_chain_height: polkadot_core_primitives::BlockNumber,
/// Whether a code upgrade is allowed or not, and at which height the upgrade /// Whether a code upgrade is allowed or not, and at which height the upgrade
/// would be applied after, if so. The parachain logic should apply any upgrade /// would be applied after, if so. The parachain logic should apply any upgrade
/// issued in this block after the first block /// issued in this block after the first block
/// with `relay_chain_height` at least this value, if `Some`. if `None`, issue /// with `relay_chain_height` at least this value, if `Some`. if `None`, issue
/// no upgrade. /// no upgrade.
pub code_upgrade_allowed: Option<RelayChainBlockNumber>, pub code_upgrade_allowed: Option<polkadot_core_primitives::BlockNumber>,
} }
/// The result of parachain validation. /// The result of parachain validation.
@@ -230,4 +225,10 @@ pub struct ValidationResult {
pub head_data: HeadData, pub head_data: HeadData,
/// An update to the validation code that should be scheduled in the relay chain. /// An update to the validation code that should be scheduled in the relay chain.
pub new_validation_code: Option<ValidationCode>, pub new_validation_code: Option<ValidationCode>,
/// Upward messages send by the Parachain.
pub upward_messages: Vec<UpwardMessage>,
/// Number of downward messages that were processed by the Parachain.
///
/// It is expected that the Parachain processes them from first to last.
pub processed_downward_messages: u32,
} }
-23
View File
@@ -16,29 +16,6 @@
//! Utilities for writing parachain WASM. //! Utilities for writing parachain WASM.
#[cfg(any(feature = "std", all(not(feature = "std"), feature = "wasm-api")))]
use crate::primitives::UpwardMessage;
#[cfg(any(feature = "std", all(not(feature = "std"), feature = "wasm-api")))]
use sp_runtime_interface::runtime_interface;
#[cfg(feature = "std")]
use sp_externalities::ExternalitiesExt;
/// The parachain api for posting messages.
// Either activate on `std` to get access to the `HostFunctions` or when `wasm-api` is given and on
// `no_std`.
#[cfg(any(feature = "std", all(not(feature = "std"), feature = "wasm-api")))]
#[runtime_interface]
pub trait Parachain {
/// Post a message to this parachain's relay chain.
#[allow(dead_code)]
fn post_upward_message(&mut self, msg: UpwardMessage) {
self.extension::<crate::wasm_executor::ParachainExt>()
.expect("No `ParachainExt` associated with the current context.")
.post_upward_message(msg)
.expect("Failed to post upward message")
}
}
/// Load the validation params from memory when implementing a Rust parachain. /// Load the validation params from memory when implementing a Rust parachain.
/// ///
/// Offset and length must have been provided by the validation /// Offset and length must have been provided by the validation
+8 -32
View File
@@ -21,12 +21,12 @@
//! a WASM VM for re-execution of a parachain candidate. //! a WASM VM for re-execution of a parachain candidate.
use std::any::{TypeId, Any}; use std::any::{TypeId, Any};
use crate::primitives::{ValidationParams, ValidationResult, UpwardMessage}; use crate::primitives::{ValidationParams, ValidationResult};
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use sp_core::storage::ChildInfo; use sp_core::storage::ChildInfo;
use sp_core::traits::CallInWasm; use sp_core::traits::CallInWasm;
use sp_wasm_interface::HostFunctions as _;
use sp_externalities::Extensions; use sp_externalities::Extensions;
use sp_wasm_interface::HostFunctions as _;
#[cfg(not(any(target_os = "android", target_os = "unknown")))] #[cfg(not(any(target_os = "android", target_os = "unknown")))]
pub use validation_host::{run_worker, ValidationPool, EXECUTION_TIMEOUT_SEC}; pub use validation_host::{run_worker, ValidationPool, EXECUTION_TIMEOUT_SEC};
@@ -37,18 +37,6 @@ mod validation_host;
const MAX_RUNTIME_MEM: usize = 1024 * 1024 * 1024; // 1 GiB const MAX_RUNTIME_MEM: usize = 1024 * 1024 * 1024; // 1 GiB
const MAX_CODE_MEM: usize = 16 * 1024 * 1024; // 16 MiB const MAX_CODE_MEM: usize = 16 * 1024 * 1024; // 16 MiB
sp_externalities::decl_extension! {
/// The extension that is registered at the `Externalities` when validating a parachain state
/// transition.
pub(crate) struct ParachainExt(Box<dyn Externalities>);
}
impl ParachainExt {
pub fn new<T: Externalities + 'static>(ext: T) -> Self {
Self(Box::new(ext))
}
}
/// A stub validation-pool defined when compiling for Android or WASM. /// A stub validation-pool defined when compiling for Android or WASM.
#[cfg(any(target_os = "android", target_os = "unknown"))] #[cfg(any(target_os = "android", target_os = "unknown"))]
#[derive(Clone)] #[derive(Clone)]
@@ -124,32 +112,25 @@ impl std::error::Error for Error {
} }
} }
/// Externalities for parachain validation.
pub trait Externalities: Send {
/// Called when a message is to be posted to the parachain's relay chain.
fn post_upward_message(&mut self, message: UpwardMessage) -> Result<(), String>;
}
/// Validate a candidate under the given validation code. /// Validate a candidate under the given validation code.
/// ///
/// This will fail if the validation code is not a proper parachain validation module. /// This will fail if the validation code is not a proper parachain validation module.
pub fn validate_candidate<E: Externalities + 'static>( pub fn validate_candidate(
validation_code: &[u8], validation_code: &[u8],
params: ValidationParams, params: ValidationParams,
ext: E,
options: ExecutionMode<'_>, options: ExecutionMode<'_>,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, Error> {
match options { match options {
ExecutionMode::Local => { ExecutionMode::Local => {
validate_candidate_internal(validation_code, &params.encode(), ext) validate_candidate_internal(validation_code, &params.encode())
}, },
#[cfg(not(any(target_os = "android", target_os = "unknown")))] #[cfg(not(any(target_os = "android", target_os = "unknown")))]
ExecutionMode::Remote(pool) => { ExecutionMode::Remote(pool) => {
pool.validate_candidate(validation_code, params, ext, false) pool.validate_candidate(validation_code, params, false)
}, },
#[cfg(not(any(target_os = "android", target_os = "unknown")))] #[cfg(not(any(target_os = "android", target_os = "unknown")))]
ExecutionMode::RemoteTest(pool) => { ExecutionMode::RemoteTest(pool) => {
pool.validate_candidate(validation_code, params, ext, true) pool.validate_candidate(validation_code, params, true)
}, },
#[cfg(any(target_os = "android", target_os = "unknown"))] #[cfg(any(target_os = "android", target_os = "unknown"))]
ExecutionMode::Remote(pool) => ExecutionMode::Remote(pool) =>
@@ -165,21 +146,16 @@ pub fn validate_candidate<E: Externalities + 'static>(
} }
/// The host functions provided by the wasm executor to the parachain wasm blob. /// The host functions provided by the wasm executor to the parachain wasm blob.
type HostFunctions = ( type HostFunctions = sp_io::SubstrateHostFunctions;
sp_io::SubstrateHostFunctions,
crate::wasm_api::parachain::HostFunctions,
);
/// Validate a candidate under the given validation code. /// Validate a candidate under the given validation code.
/// ///
/// This will fail if the validation code is not a proper parachain validation module. /// This will fail if the validation code is not a proper parachain validation module.
pub fn validate_candidate_internal<E: Externalities + 'static>( pub fn validate_candidate_internal(
validation_code: &[u8], validation_code: &[u8],
encoded_call_data: &[u8], encoded_call_data: &[u8],
externalities: E,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, Error> {
let mut extensions = Extensions::new(); let mut extensions = Extensions::new();
extensions.register(ParachainExt::new(externalities));
extensions.register(sp_core::traits::TaskExecutorExt(sp_core::tasks::executor())); extensions.register(sp_core::traits::TaskExecutorExt(sp_core::tasks::executor()));
let mut ext = ValidationExternalities(extensions); let mut ext = ValidationExternalities(extensions);
@@ -16,18 +16,15 @@
#![cfg(not(any(target_os = "android", target_os = "unknown")))] #![cfg(not(any(target_os = "android", target_os = "unknown")))]
use std::{process, env, sync::Arc, sync::atomic, mem}; use std::{process, env, sync::Arc, sync::atomic};
use codec::{Decode, Encode, EncodeAppend}; use codec::{Decode, Encode};
use crate::primitives::{ValidationParams, ValidationResult, UpwardMessage}; use crate::primitives::{ValidationParams, ValidationResult};
use super::{validate_candidate_internal, Error, Externalities}; use super::{validate_candidate_internal, Error};
use super::{MAX_CODE_MEM, MAX_RUNTIME_MEM}; use super::{MAX_CODE_MEM, MAX_RUNTIME_MEM};
use shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet}; use shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet};
use parking_lot::Mutex; use parking_lot::Mutex;
use log::{debug, trace}; use log::{debug, trace};
// Message data limit
const MAX_MESSAGE_MEM: usize = 16 * 1024 * 1024; // 16 MiB
const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"]; const WORKER_ARGS_TEST: &[&'static str] = &["--nocapture", "validation_worker"];
/// CLI Argument to start in validation worker mode. /// CLI Argument to start in validation worker mode.
const WORKER_ARG: &'static str = "validation-worker"; const WORKER_ARG: &'static str = "validation-worker";
@@ -40,27 +37,6 @@ pub const EXECUTION_TIMEOUT_SEC: u64 = 30;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const EXECUTION_TIMEOUT_SEC: u64 = 5; pub const EXECUTION_TIMEOUT_SEC: u64 = 5;
#[derive(Default)]
struct WorkerExternalitiesInner {
up_data: Vec<u8>,
}
#[derive(Default, Clone)]
struct WorkerExternalities {
inner: Arc<Mutex<WorkerExternalitiesInner>>,
}
impl Externalities for WorkerExternalities {
fn post_upward_message(&mut self, message: UpwardMessage) -> Result<(), String> {
let mut inner = self.inner.lock();
inner.up_data = <Vec::<UpwardMessage> as EncodeAppend>::append_or_new(
mem::replace(&mut inner.up_data, Vec::new()),
std::iter::once(message),
).map_err(|e| e.what())?;
Ok(())
}
}
enum Event { enum Event {
CandidateReady = 0, CandidateReady = 0,
ResultReady = 1, ResultReady = 1,
@@ -87,21 +63,20 @@ impl ValidationPool {
/// free validation host. /// free validation host.
/// ///
/// This will fail if the validation code is not a proper parachain validation module. /// This will fail if the validation code is not a proper parachain validation module.
pub fn validate_candidate<E: Externalities>( pub fn validate_candidate(
&self, &self,
validation_code: &[u8], validation_code: &[u8],
params: ValidationParams, params: ValidationParams,
externalities: E,
test_mode: bool, test_mode: bool,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, Error> {
for host in self.hosts.iter() { for host in self.hosts.iter() {
if let Some(mut host) = host.try_lock() { if let Some(mut host) = host.try_lock() {
return host.validate_candidate(validation_code, params, externalities, test_mode); return host.validate_candidate(validation_code, params, test_mode);
} }
} }
// all workers are busy, just wait for the first one // all workers are busy, just wait for the first one
self.hosts[0].lock().validate_candidate(validation_code, params, externalities, test_mode) self.hosts[0].lock().validate_candidate(validation_code, params, test_mode)
} }
} }
@@ -116,8 +91,6 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> {
} }
}; };
let worker_ext = WorkerExternalities::default();
let exit = Arc::new(atomic::AtomicBool::new(false)); let exit = Arc::new(atomic::AtomicBool::new(false));
// spawn parent monitor thread // spawn parent monitor thread
let watch_exit = exit.clone(); let watch_exit = exit.clone();
@@ -166,21 +139,11 @@ pub fn run_worker(mem_id: &str) -> Result<(), String> {
let (call_data, _) = rest.split_at_mut(MAX_RUNTIME_MEM); let (call_data, _) = rest.split_at_mut(MAX_RUNTIME_MEM);
let (call_data, _) = call_data.split_at_mut(header.params_size as usize); let (call_data, _) = call_data.split_at_mut(header.params_size as usize);
let result = validate_candidate_internal(code, call_data, worker_ext.clone()); let result = validate_candidate_internal(code, call_data);
debug!("{} Candidate validated: {:?}", process::id(), result); debug!("{} Candidate validated: {:?}", process::id(), result);
match result { match result {
Ok(r) => { Ok(r) => ValidationResultHeader::Ok(r),
let inner = worker_ext.inner.lock();
let up_data = &inner.up_data;
let up_len = up_data.len();
if up_len > MAX_MESSAGE_MEM {
ValidationResultHeader::Error("Message data is too large".into())
} else {
ValidationResultHeader::Ok(r)
}
},
Err(e) => ValidationResultHeader::Error(e.to_string()), Err(e) => ValidationResultHeader::Error(e.to_string()),
} }
}; };
@@ -226,7 +189,7 @@ impl Drop for ValidationHost {
impl ValidationHost { impl ValidationHost {
fn create_memory() -> Result<SharedMem, Error> { fn create_memory() -> Result<SharedMem, Error> {
let mem_size = MAX_RUNTIME_MEM + MAX_CODE_MEM + MAX_MESSAGE_MEM + 1024; let mem_size = MAX_RUNTIME_MEM + MAX_CODE_MEM + 1024;
let mem_config = SharedMemConf::default() let mem_config = SharedMemConf::default()
.set_size(mem_size) .set_size(mem_size)
.add_lock(shared_memory::LockType::Mutex, 0, mem_size)? .add_lock(shared_memory::LockType::Mutex, 0, mem_size)?
@@ -268,11 +231,10 @@ impl ValidationHost {
/// Validate a candidate under the given validation code. /// Validate a candidate under the given validation code.
/// ///
/// This will fail if the validation code is not a proper parachain validation module. /// This will fail if the validation code is not a proper parachain validation module.
pub fn validate_candidate<E: Externalities>( pub fn validate_candidate(
&mut self, &mut self,
validation_code: &[u8], validation_code: &[u8],
params: ValidationParams, params: ValidationParams,
mut externalities: E,
test_mode: bool, test_mode: bool,
) -> Result<ValidationResult, Error> { ) -> Result<ValidationResult, Error> {
if validation_code.len() > MAX_CODE_MEM { if validation_code.len() > MAX_CODE_MEM {
@@ -322,24 +284,11 @@ impl ValidationHost {
{ {
debug!("{} Reading results", self.id); debug!("{} Reading results", self.id);
let data: &[u8] = &**memory.wlock_as_slice(0)?; let data: &[u8] = &**memory.wlock_as_slice(0)?;
let (header_buf, rest) = data.split_at(1024); let (header_buf, _) = data.split_at(1024);
let (_, rest) = rest.split_at(MAX_CODE_MEM);
let (_, message_data) = rest.split_at(MAX_RUNTIME_MEM);
let mut header_buf: &[u8] = header_buf; let mut header_buf: &[u8] = header_buf;
let mut message_data: &[u8] = message_data;
let header = ValidationResultHeader::decode(&mut header_buf).unwrap(); let header = ValidationResultHeader::decode(&mut header_buf).unwrap();
match header { match header {
ValidationResultHeader::Ok(result) => { ValidationResultHeader::Ok(result) => Ok(result),
let upwards = Vec::<UpwardMessage>::decode(&mut message_data)
.map_err(|e|
Error::External(
format!("Could not decode upward messages: {}", e.what())
)
)?;
upwards.into_iter().try_for_each(|msg| externalities.post_upward_message(msg))?;
Ok(result)
}
ValidationResultHeader::Error(message) => { ValidationResultHeader::Error(message) => {
debug!("{} Validation error: {}", self.id, message); debug!("{} Validation error: {}", self.id, message);
Err(Error::External(message).into()) Err(Error::External(message).into())
@@ -9,6 +9,7 @@ build = "build.rs"
[dependencies] [dependencies]
parachain = { package = "polkadot-parachain", path = "../../", default-features = false, features = [ "wasm-api" ] } parachain = { package = "polkadot-parachain", path = "../../", default-features = false, features = [ "wasm-api" ] }
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
tiny-keccak = "1.5.0" tiny-keccak = "1.5.0"
dlmalloc = { version = "0.1.3", features = [ "global" ] } dlmalloc = { version = "0.1.3", features = [ "global" ] }
@@ -20,4 +21,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.
[features] [features]
default = [ "std" ] default = [ "std" ]
std = ["parachain/std"] std = [
"parachain/std",
"sp-std/std",
]
@@ -23,7 +23,7 @@ use adder::{HeadData as AdderHead, BlockData as AdderBody};
use sp_core::Pair; use sp_core::Pair;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use primitives::{ use primitives::{
Hash, Hash, DownwardMessage,
parachain::{HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationSchedule}, parachain::{HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationSchedule},
}; };
use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli}; use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli};
@@ -60,6 +60,7 @@ impl ParachainContext for AdderContext {
_relay_parent: Hash, _relay_parent: Hash,
_global_validation: GlobalValidationSchedule, _global_validation: GlobalValidationSchedule,
local_validation: LocalValidationData, local_validation: LocalValidationData,
_: Vec<DownwardMessage>,
) -> Self::ProduceCandidate ) -> Self::ProduceCandidate
{ {
let adder_head = match AdderHead::decode(&mut &local_validation.parent_head.0[..]).ok() { let adder_head = match AdderHead::decode(&mut &local_validation.parent_head.0[..]).ok() {
@@ -21,22 +21,6 @@ use core::{intrinsics, panic};
use parachain::primitives::{ValidationResult, HeadData as GenericHeadData}; use parachain::primitives::{ValidationResult, HeadData as GenericHeadData};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
#[panic_handler]
#[no_mangle]
pub fn panic(_info: &panic::PanicInfo) -> ! {
unsafe {
intrinsics::abort()
}
}
#[alloc_error_handler]
#[no_mangle]
pub fn oom(_: core::alloc::Layout) -> ! {
unsafe {
intrinsics::abort();
}
}
#[no_mangle] #[no_mangle]
pub extern fn validate_block(params: *const u8, len: usize) -> u64 { pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
let params = unsafe { parachain::load_params(params, len) }; let params = unsafe { parachain::load_params(params, len) };
@@ -53,6 +37,8 @@ pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
&ValidationResult { &ValidationResult {
head_data: GenericHeadData(new_head.encode()), head_data: GenericHeadData(new_head.encode()),
new_validation_code: None, new_validation_code: None,
upward_messages: sp_std::vec::Vec::new(),
processed_downward_messages: 0,
} }
), ),
Err(_) => panic!("execution failure"), Err(_) => panic!("execution failure"),
@@ -9,6 +9,7 @@ build = "build.rs"
[dependencies] [dependencies]
parachain = { package = "polkadot-parachain", path = "../../", default-features = false, features = [ "wasm-api" ] } parachain = { package = "polkadot-parachain", path = "../../", default-features = false, features = [ "wasm-api" ] }
codec = { package = "parity-scale-codec", version = "1.1.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.1.0", default-features = false, features = ["derive"] }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
tiny-keccak = "1.5.0" tiny-keccak = "1.5.0"
dlmalloc = { version = "0.1.3", features = [ "global" ] } dlmalloc = { version = "0.1.3", features = [ "global" ] }
@@ -20,4 +21,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.
[features] [features]
default = [ "std" ] default = [ "std" ]
std = ["parachain/std"] std = [
"parachain/std",
"sp-std/std",
]
@@ -21,22 +21,6 @@ use core::{intrinsics, panic};
use parachain::primitives::{ValidationResult, HeadData as GenericHeadData}; use parachain::primitives::{ValidationResult, HeadData as GenericHeadData};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
#[panic_handler]
#[no_mangle]
pub fn panic(_info: &panic::PanicInfo) -> ! {
unsafe {
intrinsics::abort()
}
}
#[alloc_error_handler]
#[no_mangle]
pub fn oom(_: core::alloc::Layout) -> ! {
unsafe {
intrinsics::abort();
}
}
#[no_mangle] #[no_mangle]
pub extern fn validate_block(params: *const u8, len: usize) -> u64 { pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
let params = unsafe { parachain::load_params(params, len) }; let params = unsafe { parachain::load_params(params, len) };
@@ -64,6 +48,8 @@ pub extern fn validate_block(params: *const u8, len: usize) -> u64 {
&ValidationResult { &ValidationResult {
head_data: GenericHeadData(output.head_data.encode()), head_data: GenericHeadData(output.head_data.encode()),
new_validation_code: output.new_validation_code, new_validation_code: output.new_validation_code,
upward_messages: sp_std::vec::Vec::new(),
processed_downward_messages: 0,
} }
), ),
Err(_) => panic!("execution failure"), Err(_) => panic!("execution failure"),
@@ -16,15 +16,11 @@
//! Basic parachain that adds a number as part of its state. //! Basic parachain that adds a number as part of its state.
use crate::{ use parachain::primitives::{
DummyExt,
parachain,
parachain::primitives::{
RelayChainBlockNumber, RelayChainBlockNumber,
BlockData as GenericBlockData, BlockData as GenericBlockData,
HeadData as GenericHeadData, HeadData as GenericHeadData,
ValidationParams, ValidationParams,
},
}; };
use codec::{Decode, Encode}; use codec::{Decode, Encode};
@@ -83,7 +79,6 @@ pub fn execute_good_on_parent() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -123,7 +118,6 @@ fn execute_good_chain_on_parent() {
relay_chain_height: number as RelayChainBlockNumber + 1, relay_chain_height: number as RelayChainBlockNumber + 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -164,7 +158,6 @@ fn execute_bad_on_parent() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap_err(); ).unwrap_err();
} }
@@ -16,15 +16,10 @@
//! Basic parachain that adds a number as part of its state. //! Basic parachain that adds a number as part of its state.
use parachain; use parachain::primitives::{
use crate::{
DummyExt,
parachain::primitives::{
BlockData as GenericBlockData, BlockData as GenericBlockData,
HeadData as GenericHeadData, HeadData as GenericHeadData,
ValidationParams, ValidationCode, ValidationParams, ValidationCode,
},
}; };
use codec::{Decode, Encode}; use codec::{Decode, Encode};
use code_upgrader::{hash_state, HeadData, BlockData, State}; use code_upgrader::{hash_state, HeadData, BlockData, State};
@@ -56,7 +51,6 @@ pub fn execute_good_no_upgrade() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -93,7 +87,6 @@ pub fn execute_good_with_upgrade() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: Some(20), code_upgrade_allowed: Some(20),
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -137,7 +130,6 @@ pub fn code_upgrade_not_allowed() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
} }
@@ -168,7 +160,6 @@ pub fn applies_code_upgrade_after_delay() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: Some(2), code_upgrade_allowed: Some(2),
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -204,7 +195,6 @@ pub fn applies_code_upgrade_after_delay() {
relay_chain_height: 2, relay_chain_height: 2,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
).unwrap(); ).unwrap();
@@ -18,16 +18,7 @@ mod adder;
mod code_upgrader; mod code_upgrader;
mod wasm_executor; mod wasm_executor;
use parachain::{ use parachain::wasm_executor::run_worker;
self, primitives::UpwardMessage, wasm_executor::{Externalities, run_worker},
};
struct DummyExt;
impl Externalities for DummyExt {
fn post_upward_message(&mut self, _: UpwardMessage) -> Result<(), String> {
Ok(())
}
}
// This is not an actual test, but rather an entry point for out-of process WASM executor. // This is not an actual test, but rather an entry point for out-of process WASM executor.
// When executing tests the executor spawns currently executing binary, which happens to be test binary. // When executing tests the executor spawns currently executing binary, which happens to be test binary.
@@ -16,9 +16,8 @@
//! Basic parachain that adds a number as part of its state. //! Basic parachain that adds a number as part of its state.
use parachain; use crate::adder;
use crate::{adder, DummyExt}; use parachain::{
use crate::parachain::{
primitives::{BlockData, ValidationParams}, primitives::{BlockData, ValidationParams},
wasm_executor::EXECUTION_TIMEOUT_SEC, wasm_executor::EXECUTION_TIMEOUT_SEC,
}; };
@@ -40,7 +39,6 @@ fn terminates_on_timeout() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
); );
match result { match result {
@@ -70,7 +68,6 @@ fn parallel_execution() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool2), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool2),
).ok()); ).ok());
let _ = parachain::wasm_executor::validate_candidate( let _ = parachain::wasm_executor::validate_candidate(
@@ -83,7 +80,6 @@ fn parallel_execution() {
relay_chain_height: 1, relay_chain_height: 1,
code_upgrade_allowed: None, code_upgrade_allowed: None,
}, },
DummyExt,
parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), parachain::wasm_executor::ExecutionMode::RemoteTest(&pool),
); );
thread.join().unwrap(); thread.join().unwrap();
+5 -2
View File
@@ -8,7 +8,6 @@ edition = "2018"
serde = { version = "1.0.102", optional = true, features = ["derive"] } serde = { version = "1.0.102", optional = true, features = ["derive"] }
parity-scale-codec = { version = "1.3.0", default-features = false, features = ["bit-vec", "derive"] } parity-scale-codec = { version = "1.3.0", default-features = false, features = ["bit-vec", "derive"] }
primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
system = { package = "frame-system", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
application-crypto = { package = "sp-application-crypto", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } application-crypto = { package = "sp-application-crypto", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -17,8 +16,10 @@ sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate",
sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
polkadot-parachain = { path = "../parachain", default-features = false } polkadot-parachain = { path = "../parachain", default-features = false }
polkadot-core-primitives = { path = "../core-primitives", default-features = false }
trie = { package = "sp-trie", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } trie = { package = "sp-trie", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] } bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[dev-dependencies] [dev-dependencies]
sp-serializer = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-serializer = { git = "https://github.com/paritytech/substrate", branch = "master" }
@@ -27,9 +28,9 @@ pretty_assertions = "0.5.1"
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"application-crypto/std",
"parity-scale-codec/std", "parity-scale-codec/std",
"primitives/std", "primitives/std",
"system/std",
"inherents/std", "inherents/std",
"trie/std", "trie/std",
"sp-api/std", "sp-api/std",
@@ -39,5 +40,7 @@ std = [
"runtime_primitives/std", "runtime_primitives/std",
"serde", "serde",
"polkadot-parachain/std", "polkadot-parachain/std",
"polkadot-core-primitives/std",
"bitvec/std", "bitvec/std",
"frame-system/std",
] ]
+2 -51
View File
@@ -20,63 +20,14 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use runtime_primitives::{generic, MultiSignature};
pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT, Verify, IdentifyAccount}; pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT, Verify, IdentifyAccount};
pub use polkadot_core_primitives::*;
pub mod inclusion_inherent; pub mod inclusion_inherent;
pub mod parachain; pub mod parachain;
pub use parity_scale_codec::Compact; pub use parity_scale_codec::Compact;
/// An index to a block.
pub type BlockNumber = polkadot_parachain::primitives::RelayChainBlockNumber;
/// An instant or duration in time.
pub type Moment = u64;
/// Alias to type for a signature for a transaction on the relay chain. This allows one of several
/// kinds of underlying crypto to be used, so isn't a fixed size when encoded.
pub type Signature = MultiSignature;
/// Alias to the public key used for this chain, actually a `MultiSigner`. Like the signature, this
/// also isn't a fixed size when encoded, as different cryptos have different size public keys.
pub type AccountPublic = <Signature as Verify>::Signer;
/// Alias to the opaque account ID type for this chain, actually a `AccountId32`. This is always
/// 32 bytes.
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them.
pub type AccountIndex = u32;
/// Identifier for a chain. 32-bit should be plenty.
pub type ChainId = u32;
/// A hash of some data used by the relay chain.
pub type Hash = primitives::H256;
/// Index of a transaction in the relay chain. 32-bit should be plenty.
pub type Nonce = u32;
/// The balance of an account.
/// 128-bits (or 38 significant decimal figures) will allow for 10m currency (10^7) at a resolution
/// to all for one second's worth of an annualised 50% reward be paid to a unit holder (10^11 unit
/// denomination), or 10^18 total atomic units, to grow at 50%/year for 51 years (10^9 multiplier)
/// for an eventual total of 10^27 units (27 significant decimal figures).
/// We round denomination to 10^12 (12 sdf), and leave the other redundancy at the upper end so
/// that 32 bits may be multiplied with a balance in 128 bits without worrying about overflow.
pub type Balance = u128;
/// Header type.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Block type.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Block ID.
pub type BlockId = generic::BlockId<Block>;
/// Opaque, encoded, unchecked extrinsic.
pub use runtime_primitives::OpaqueExtrinsic as UncheckedExtrinsic;
/// Custom validity errors used in Polkadot while validating transactions. /// Custom validity errors used in Polkadot while validating transactions.
#[repr(u8)] #[repr(u8)]
pub enum ValidityError { pub enum ValidityError {
@@ -118,7 +69,7 @@ pub mod fisherman {
/// An `AppCrypto` type to allow submitting signed transactions using the fisherman /// An `AppCrypto` type to allow submitting signed transactions using the fisherman
/// application key as signer. /// application key as signer.
pub struct FishermanAppCrypto; pub struct FishermanAppCrypto;
impl system::offchain::AppCrypto<<Signature as Verify>::Signer, Signature> for FishermanAppCrypto { impl frame_system::offchain::AppCrypto<<Signature as Verify>::Signer, Signature> for FishermanAppCrypto {
type RuntimeAppPublic = FishermanId; type RuntimeAppPublic = FishermanId;
type GenericSignature = primitives::sr25519::Signature; type GenericSignature = primitives::sr25519::Signature;
type GenericPublic = primitives::sr25519::Public; type GenericPublic = primitives::sr25519::Public;
+7
View File
@@ -32,6 +32,7 @@ use primitives::RuntimeDebug;
use runtime_primitives::traits::{AppVerify, Block as BlockT}; use runtime_primitives::traits::{AppVerify, Block as BlockT};
use inherents::InherentIdentifier; use inherents::InherentIdentifier;
use application_crypto::KeyTypeId; use application_crypto::KeyTypeId;
use polkadot_core_primitives::DownwardMessage;
pub use polkadot_parachain::primitives::{ pub use polkadot_parachain::primitives::{
Id, ParachainDispatchOrigin, LOWEST_USER_ID, UpwardMessage, HeadData, BlockData, Id, ParachainDispatchOrigin, LOWEST_USER_ID, UpwardMessage, HeadData, BlockData,
@@ -220,6 +221,10 @@ pub struct CandidateCommitments<H = Hash> {
pub erasure_root: H, pub erasure_root: H,
/// New validation code. /// New validation code.
pub new_validation_code: Option<ValidationCode>, pub new_validation_code: Option<ValidationCode>,
/// Number of `DownwardMessage`'s that were processed by the Parachain.
///
/// It is expected that the Parachain processes them from first to last.
pub processed_downward_messages: u32,
} }
/// Get a collator signature payload on a relay-parent, block-data combo. /// Get a collator signature payload on a relay-parent, block-data combo.
@@ -814,6 +819,8 @@ sp_api::decl_runtime_apis! {
-> Option<Vec<AbridgedCandidateReceipt>>; -> Option<Vec<AbridgedCandidateReceipt>>;
/// Get a `SigningContext` with current `SessionIndex` and parent hash. /// Get a `SigningContext` with current `SessionIndex` and parent hash.
fn signing_context() -> SigningContext; fn signing_context() -> SigningContext;
/// Get the `DownwardMessage`'s for the given parachain.
fn downward_messages(id: Id) -> Vec<DownwardMessage>;
} }
} }
+137 -12
View File
@@ -34,7 +34,7 @@ use sp_staking::{
}; };
use frame_support::{ use frame_support::{
traits::KeyOwnerProofSystem, traits::KeyOwnerProofSystem,
dispatch::{IsSubType}, dispatch::IsSubType,
weights::{DispatchClass, Weight}, weights::{DispatchClass, Weight},
}; };
use primitives::{ use primitives::{
@@ -47,14 +47,13 @@ use primitives::{
LocalValidationData, Scheduling, ValidityAttestation, NEW_HEADS_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, LocalValidationData, Scheduling, ValidityAttestation, NEW_HEADS_IDENTIFIER, PARACHAIN_KEY_TYPE_ID,
ValidatorSignature, SigningContext, HeadData, ValidationCode, ValidatorSignature, SigningContext, HeadData, ValidationCode,
}, },
Remark, DownwardMessage
}; };
use frame_support::{ use frame_support::{
Parameter, dispatch::DispatchResult, decl_storage, decl_module, decl_error, ensure, Parameter, dispatch::DispatchResult, decl_storage, decl_module, decl_error, ensure,
traits::{Currency, Get, WithdrawReason, ExistenceRequirement, Randomness}, traits::{Currency, Get, WithdrawReason, ExistenceRequirement, Randomness},
}; };
use sp_runtime::{ use sp_runtime::transaction_validity::InvalidTransaction;
transaction_validity::InvalidTransaction,
};
use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier}; use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier};
@@ -91,6 +90,18 @@ impl<N: Saturating + One + PartialOrd + PartialEq + Clone> Iterator for BlockNum
pub trait ParachainCurrency<AccountId> { pub trait ParachainCurrency<AccountId> {
fn free_balance(para_id: ParaId) -> Balance; fn free_balance(para_id: ParaId) -> Balance;
fn deduct(para_id: ParaId, amount: Balance) -> DispatchResult; fn deduct(para_id: ParaId, amount: Balance) -> DispatchResult;
fn transfer_in(
source: &AccountId,
dest: ParaId,
amount: Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult;
fn transfer_out(
source: ParaId,
dest: &AccountId,
amount: Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult;
} }
impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where
@@ -102,6 +113,8 @@ impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where
T::free_balance(&para_account).into() T::free_balance(&para_account).into()
} }
// TODO: this should really be the same API as `withdraw`, having NegativeImbalance as an
// associated type.
fn deduct(para_id: ParaId, amount: Balance) -> DispatchResult { fn deduct(para_id: ParaId, amount: Balance) -> DispatchResult {
let para_account = para_id.into_account(); let para_account = para_id.into_account();
@@ -115,6 +128,24 @@ impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where
Ok(()) Ok(())
} }
fn transfer_in(
source: &AccountId,
dest: ParaId,
amount: Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult {
T::transfer(source, &dest.into_account(), amount.into(), existence_requirement)
}
fn transfer_out(
source: ParaId,
dest: &AccountId,
amount: Balance,
existence_requirement: ExistenceRequirement,
) -> DispatchResult {
T::transfer(&source.into_account(), dest, amount.into(), existence_requirement)
}
} }
/// Interface to the persistent (stash) identities of the current validators. /// Interface to the persistent (stash) identities of the current validators.
@@ -226,12 +257,14 @@ pub trait Trait: CreateSignedTransaction<Call<Self>> + attestations::Trait + ses
type AuthorityId: system::offchain::AppCrypto<Self::Public, Self::Signature>; type AuthorityId: system::offchain::AppCrypto<Self::Public, Self::Signature>;
/// The outer origin type. /// The outer origin type.
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>; type Origin: From<Origin>
+ From<<Self as system::Trait>::Origin>
+ Into<result::Result<Origin, <Self as Trait>::Origin>>;
/// The outer call dispatch type. /// The outer call dispatch type.
type Call: Parameter + Dispatchable<Origin=<Self as Trait>::Origin> + From<Call<Self>>; type Call: Parameter + Dispatchable<Origin=<Self as Trait>::Origin> + From<Call<Self>>;
/// Some way of interacting with balances for fees. /// Some way of interacting with balances for fees and transfers.
type ParachainCurrency: ParachainCurrency<Self::AccountId>; type ParachainCurrency: ParachainCurrency<Self::AccountId>;
/// Polkadot in practice will always use the `BlockNumber` type. /// Polkadot in practice will always use the `BlockNumber` type.
@@ -345,6 +378,8 @@ impl<Offender: Clone> Offence<Offender> for DoubleVoteOffence<Offender> {
} }
} }
/// Total number of individual messages allowed in the relay-chain -> parachain message queue.
const MAX_DOWNWARD_QUEUE_COUNT: usize = 10;
/// Total number of individual messages allowed in the parachain -> relay-chain message queue. /// Total number of individual messages allowed in the parachain -> relay-chain message queue.
const MAX_QUEUE_COUNT: usize = 100; const MAX_QUEUE_COUNT: usize = 100;
/// Total size of messages allowed in the parachain -> relay-chain message queue before which no /// Total size of messages allowed in the parachain -> relay-chain message queue before which no
@@ -445,8 +480,7 @@ impl<N: Ord + Copy> ParaPastCodeMeta<N> {
} }
decl_storage! { decl_storage! {
trait Store for Module<T: Trait> as Parachains trait Store for Module<T: Trait> as Parachains {
{
/// All authorities' keys at the moment. /// All authorities' keys at the moment.
pub Authorities get(fn authorities): Vec<ValidatorId>; pub Authorities get(fn authorities): Vec<ValidatorId>;
/// The active code of a currently-registered parachain. /// The active code of a currently-registered parachain.
@@ -484,6 +518,9 @@ decl_storage! {
/// ///
/// `None` if not yet updated. /// `None` if not yet updated.
pub DidUpdate: Option<Vec<ParaId>>; pub DidUpdate: Option<Vec<ParaId>>;
/// Messages waiting to be delivered from the Relay chain into the parachain.
pub DownwardMessageQueue: map hasher(twox_64_concat) ParaId => Vec<DownwardMessage<T::AccountId>>;
} }
add_extra_genesis { add_extra_genesis {
config(authorities): Vec<ValidatorId>; config(authorities): Vec<ValidatorId>;
@@ -531,6 +568,8 @@ decl_error! {
CannotPayFees, CannotPayFees,
/// Unexpected relay-parent for a candidate receipt. /// Unexpected relay-parent for a candidate receipt.
UnexpectedRelayParent, UnexpectedRelayParent,
/// Downward message queue is full for the Parachain.
DownwardMessageQueueFull,
} }
} }
@@ -596,6 +635,11 @@ decl_module! {
WATERMARK_QUEUE_SIZE, WATERMARK_QUEUE_SIZE,
)?; )?;
Self::remove_processed_downward_messages(
id,
head.candidate.commitments.processed_downward_messages as usize,
);
let id = head.parachain_index(); let id = head.parachain_index();
proceeded.push(id); proceeded.push(id);
last_id = Some(id); last_id = Some(id);
@@ -668,6 +712,36 @@ decl_module! {
Ok(()) Ok(())
} }
/// Transfer some tokens into a parachain and leave a message in the downward queue for it.
#[weight = 100_000]
pub fn transfer_to_parachain(
origin,
to: ParaId,
amount: Balance,
remark: Remark,
) {
let who = ensure_signed(origin)?;
let downward_queue_count = DownwardMessageQueue::<T>::decode_len(to).unwrap_or(0);
ensure!(downward_queue_count < MAX_DOWNWARD_QUEUE_COUNT, Error::<T>::DownwardMessageQueueFull);
T::ParachainCurrency::transfer_in(&who, to, amount, ExistenceRequirement::AllowDeath)?;
DownwardMessageQueue::<T>::append(to, DownwardMessage::TransferInto(who, amount, remark));
}
/// Send a XCMP message to the given parachain.
///
/// The origin must be another parachain.
#[weight = 100_000]
pub fn send_xcmp_message(
origin,
to: ParaId,
msg: Vec<u8>,
) {
ensure_parachain(<T as Trait>::Origin::from(origin))?;
let downward_queue_count = DownwardMessageQueue::<T>::decode_len(to).unwrap_or(0);
ensure!(downward_queue_count < MAX_DOWNWARD_QUEUE_COUNT, Error::<T>::DownwardMessageQueueFull);
DownwardMessageQueue::<T>::append(to, DownwardMessage::XCMPMessage(msg));
}
} }
} }
@@ -814,11 +888,11 @@ impl<T: Trait> Module<T> {
if let Ok(message_call) = <T as Trait>::Call::decode(&mut &data[..]) { if let Ok(message_call) = <T as Trait>::Call::decode(&mut &data[..]) {
let origin: <T as Trait>::Origin = match origin { let origin: <T as Trait>::Origin = match origin {
ParachainDispatchOrigin::Signed => ParachainDispatchOrigin::Signed =>
system::RawOrigin::Signed(id.into_account()).into(), <T as Trait>::Origin::from(<T as system::Trait>::Origin::from(system::RawOrigin::Signed(id.into_account()))),
ParachainDispatchOrigin::Parachain => ParachainDispatchOrigin::Parachain =>
Origin::Parachain(id).into(), Origin::Parachain(id).into(),
ParachainDispatchOrigin::Root => ParachainDispatchOrigin::Root =>
system::RawOrigin::Root.into(), <T as Trait>::Origin::from(<T as system::Trait>::Origin::from(system::RawOrigin::Root)),
}; };
let _ok = message_call.dispatch(origin).is_ok(); let _ok = message_call.dispatch(origin).is_ok();
// Not much to do with the result as it is. It's up to the parachain to ensure that the // Not much to do with the result as it is. It's up to the parachain to ensure that the
@@ -862,6 +936,17 @@ impl<T: Trait> Module<T> {
Ok(()) Ok(())
} }
/// Remove processed downward messages from the `DownwardMessageQueue`.
fn remove_processed_downward_messages(id: ParaId, processed: usize) {
DownwardMessageQueue::<T>::mutate(id, |v| {
if processed > v.len() {
v.clear();
} else {
*v = v.split_off(processed);
}
});
}
/// Update routing information from the parachain heads. This queues upwards /// Update routing information from the parachain heads. This queues upwards
/// messages to the relay chain as well. /// messages to the relay chain as well.
fn update_routing( fn update_routing(
@@ -1079,6 +1164,11 @@ impl<T: Trait> Module<T> {
}) })
} }
/// Returns the `DownwardMessage`'s for the given parachain.
pub fn downward_messages(id: ParaId) -> Vec<DownwardMessage<T::AccountId>> {
DownwardMessageQueue::<T>::get(id)
}
/// Get the local validation data for a particular parent w.r.t. the current /// Get the local validation data for a particular parent w.r.t. the current
/// block height. /// block height.
pub fn current_local_validation_data(id: &ParaId) -> Option<LocalValidationData> { pub fn current_local_validation_data(id: &ParaId) -> Option<LocalValidationData> {
@@ -1113,8 +1203,7 @@ impl<T: Trait> Module<T> {
schedule: &GlobalValidationSchedule, schedule: &GlobalValidationSchedule,
attested_candidates: &[AttestedCandidate], attested_candidates: &[AttestedCandidate],
active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)] active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)]
) -> sp_std::result::Result<IncludedBlocks<T>, sp_runtime::DispatchError> ) -> sp_std::result::Result<IncludedBlocks<T>, sp_runtime::DispatchError> {
{
// returns groups of slices that have the same chain ID. // returns groups of slices that have the same chain ID.
// assumes the inner slice is sorted by id. // assumes the inner slice is sorted by id.
struct GroupedDutyIter<'a> { struct GroupedDutyIter<'a> {
@@ -3511,4 +3600,40 @@ mod tests {
} }
}); });
} }
#[test]
fn downward_message_removal_works() {
let id = ParaId::from(0);
// That the list of egress queue roots is in ascending order by `ParaId`.
let parachains = vec![
(id, vec![].into(), vec![].into()),
];
new_test_ext(parachains).execute_with(|| {
run_to_block(2);
DownwardMessageQueue::<Test>::insert(
&id,
vec![
DownwardMessage::Opaque(vec![1]),
DownwardMessage::Opaque(vec![2]),
DownwardMessage::Opaque(vec![3])
]
);
let mut raw_candidate = raw_candidate(id);
raw_candidate.commitments.processed_downward_messages = 2;
let candidate = make_blank_attested(raw_candidate);
let mut candidates = vec![candidate];
candidates.iter_mut().for_each(make_attestations);
assert_ok!(Parachains::set_heads(Origin::none(), candidates));
assert_eq!(
vec![DownwardMessage::Opaque(vec![3])],
DownwardMessageQueue::<Test>::get(&id),
);
});
}
} }
+1
View File
@@ -1074,6 +1074,7 @@ mod tests {
upward_messages: vec![], upward_messages: vec![],
erasure_root: [1; 32].into(), erasure_root: [1; 32].into(),
new_validation_code: None, new_validation_code: None,
processed_downward_messages: 0,
}, },
}; };
let (candidate, _) = candidate.abridge(); let (candidate, _) = candidate.abridge();
+7 -1
View File
@@ -66,9 +66,9 @@ utility = { package = "pallet-utility", git = "https://github.com/paritytech/sub
vesting = { package = "pallet-vesting", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } vesting = { package = "pallet-vesting", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
pallet-offences-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } pallet-offences-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
pallet-session-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } pallet-session-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
hex-literal = { version = "0.2.1", optional = true } hex-literal = { version = "0.2.1", optional = true }
runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false } runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false }
@@ -170,3 +170,9 @@ runtime-benchmarks = [
"pallet-session-benchmarking", "pallet-session-benchmarking",
"hex-literal", "hex-literal",
] ]
# When enabled, the runtime api will not be build.
#
# This is required by Cumulus to access certain types of the
# runtime without clashing with the runtime api exported functions
# in WASM.
disable-runtime-api = []
+7
View File
@@ -88,7 +88,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
authoring_version: 2, authoring_version: 2,
spec_version: 2013, spec_version: 2013,
impl_version: 0, impl_version: 0,
#[cfg(not(feature = "disable-runtime-api"))]
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
#[cfg(feature = "disable-runtime-api")]
apis: version::create_apis_vec![[]],
transaction_version: 2, transaction_version: 2,
}; };
@@ -1005,6 +1008,7 @@ pub type Executive = executive::Executive<
/// The payload being signed in the transactions. /// The payload being signed in the transactions.
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>; pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
#[cfg(not(feature = "disable-runtime-api"))]
sp_api::impl_runtime_apis! { sp_api::impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime { impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion { fn version() -> RuntimeVersion {
@@ -1103,6 +1107,9 @@ sp_api::impl_runtime_apis! {
fn signing_context() -> SigningContext { fn signing_context() -> SigningContext {
Parachains::signing_context() Parachains::signing_context()
} }
fn downward_messages(id: parachain::Id) -> Vec<primitives::DownwardMessage> {
Parachains::downward_messages(id)
}
} }
impl fg_primitives::GrandpaApi<Block> for Runtime { impl fg_primitives::GrandpaApi<Block> for Runtime {
+7 -1
View File
@@ -60,8 +60,8 @@ system_rpc_runtime_api = { package = "frame-system-rpc-runtime-api", git = "http
timestamp = { package = "pallet-timestamp", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } timestamp = { package = "pallet-timestamp", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
treasury = { package = "pallet-treasury", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } treasury = { package = "pallet-treasury", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sudo = { package = "pallet-sudo", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sudo = { package = "pallet-sudo", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
utility = { package = "pallet-utility", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
vesting = { package = "pallet-vesting", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } vesting = { package = "pallet-vesting", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
utility = { package = "pallet-utility", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
@@ -164,3 +164,9 @@ runtime-benchmarks = [
"pallet-session-benchmarking", "pallet-session-benchmarking",
"hex-literal", "hex-literal",
] ]
# When enabled, the runtime api will not be build.
#
# This is required by Cumulus to access certain types of the
# runtime without clashing with the runtime api exported functions
# in WASM.
disable-runtime-api = []
+7
View File
@@ -93,7 +93,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
authoring_version: 0, authoring_version: 0,
spec_version: 13, spec_version: 13,
impl_version: 0, impl_version: 0,
#[cfg(not(feature = "disable-runtime-api"))]
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
#[cfg(feature = "disable-runtime-api")]
apis: version::create_apis_vec![[]],
transaction_version: 2, transaction_version: 2,
}; };
@@ -991,6 +994,7 @@ pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<R
/// The payload being signed in transactions. /// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>; pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
#[cfg(not(feature = "disable-runtime-api"))]
sp_api::impl_runtime_apis! { sp_api::impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime { impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion { fn version() -> RuntimeVersion {
@@ -1089,6 +1093,9 @@ sp_api::impl_runtime_apis! {
fn signing_context() -> SigningContext { fn signing_context() -> SigningContext {
Parachains::signing_context() Parachains::signing_context()
} }
fn downward_messages(id: parachain::Id) -> Vec<primitives::DownwardMessage> {
Parachains::downward_messages(id)
}
} }
impl fg_primitives::GrandpaApi<Block> for Runtime { impl fg_primitives::GrandpaApi<Block> for Runtime {
+3
View File
@@ -656,6 +656,9 @@ sp_api::impl_runtime_apis! {
fn signing_context() -> SigningContext { fn signing_context() -> SigningContext {
Parachains::signing_context() Parachains::signing_context()
} }
fn downward_messages(id: parachain::Id) -> Vec<primitives::DownwardMessage> {
Parachains::downward_messages(id)
}
} }
impl fg_primitives::GrandpaApi<Block> for Runtime { impl fg_primitives::GrandpaApi<Block> for Runtime {
+6
View File
@@ -174,3 +174,9 @@ runtime-benchmarks = [
"pallet-session-benchmarking", "pallet-session-benchmarking",
"hex-literal", "hex-literal",
] ]
# When enabled, the runtime api will not be build.
#
# This is required by Cumulus to access certain types of the
# runtime without clashing with the runtime api exported functions
# in WASM.
disable-runtime-api = []
+7
View File
@@ -83,7 +83,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
authoring_version: 2, authoring_version: 2,
spec_version: 33, spec_version: 33,
impl_version: 0, impl_version: 0,
#[cfg(not(feature = "disable-runtime-api"))]
apis: RUNTIME_API_VERSIONS, apis: RUNTIME_API_VERSIONS,
#[cfg(feature = "disable-runtime-api")]
apis: version::create_apis_vec![[]],
transaction_version: 2, transaction_version: 2,
}; };
@@ -753,6 +756,7 @@ pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<R
/// The payload being signed in transactions. /// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>; pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
#[cfg(not(feature = "disable-runtime-api"))]
sp_api::impl_runtime_apis! { sp_api::impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime { impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion { fn version() -> RuntimeVersion {
@@ -851,6 +855,9 @@ sp_api::impl_runtime_apis! {
fn signing_context() -> SigningContext { fn signing_context() -> SigningContext {
Parachains::signing_context() Parachains::signing_context()
} }
fn downward_messages(id: parachain::Id) -> Vec<primitives::DownwardMessage> {
Parachains::downward_messages(id)
}
} }
impl fg_primitives::GrandpaApi<Block> for Runtime { impl fg_primitives::GrandpaApi<Block> for Runtime {
+4 -1
View File
@@ -81,7 +81,10 @@ pub enum Error {
/// Block data is too big /// Block data is too big
#[display(fmt = "Block data is too big (maximum allowed size: {}, actual size: {})", size, max_size)] #[display(fmt = "Block data is too big (maximum allowed size: {}, actual size: {})", size, max_size)]
BlockDataTooBig { size: u64, max_size: u64 }, BlockDataTooBig { size: u64, max_size: u64 },
Join(tokio::task::JoinError) Join(tokio::task::JoinError),
/// Could not cover fee for an operation e.g. for sending `UpwardMessage`.
#[display(fmt = "Parachain could not cover fee for an operation e.g. for sending an `UpwardMessage`.")]
CouldNotCoverFee,
} }
impl std::error::Error for Error { impl std::error::Error for Error {
+49 -94
View File
@@ -17,8 +17,6 @@
//! The pipeline of validation functions a parachain block must pass through before //! The pipeline of validation functions a parachain block must pass through before
//! it can be voted for. //! it can be voted for.
use std::sync::Arc;
use codec::Encode; use codec::Encode;
use polkadot_erasure_coding as erasure; use polkadot_erasure_coding as erasure;
use polkadot_primitives::parachain::{ use polkadot_primitives::parachain::{
@@ -33,7 +31,6 @@ use parachain::{
}; };
use runtime_primitives::traits::{BlakeTwo256, Hash as HashT}; use runtime_primitives::traits::{BlakeTwo256, Hash as HashT};
use sp_api::ProvideRuntimeApi; use sp_api::ProvideRuntimeApi;
use parking_lot::Mutex;
use crate::Error; use crate::Error;
pub use parachain::wasm_executor::ValidationPool; pub use parachain::wasm_executor::ValidationPool;
@@ -67,67 +64,6 @@ pub fn basic_checks(
Ok(()) Ok(())
} }
struct ExternalitiesInner {
upward: Vec<UpwardMessage>,
fees_charged: Balance,
free_balance: Balance,
fee_schedule: FeeSchedule,
}
impl wasm_executor::Externalities for ExternalitiesInner {
fn post_upward_message(&mut self, message: UpwardMessage) -> Result<(), String> {
self.apply_message_fee(message.data.len())?;
self.upward.push(message);
Ok(())
}
}
impl ExternalitiesInner {
fn new(free_balance: Balance, fee_schedule: FeeSchedule) -> Self {
Self {
free_balance,
fee_schedule,
fees_charged: 0,
upward: Vec::new(),
}
}
fn apply_message_fee(&mut self, message_len: usize) -> Result<(), String> {
let fee = self.fee_schedule.compute_message_fee(message_len);
let new_fees_charged = self.fees_charged.saturating_add(fee);
if new_fees_charged > self.free_balance {
Err("could not cover fee.".into())
} else {
self.fees_charged = new_fees_charged;
Ok(())
}
}
// Returns the noted outputs of execution so far - upward messages and balances.
fn outputs(self) -> (Vec<UpwardMessage>, Balance) {
(self.upward, self.fees_charged)
}
}
#[derive(Clone)]
struct Externalities(Arc<Mutex<ExternalitiesInner>>);
impl Externalities {
fn new(free_balance: Balance, fee_schedule: FeeSchedule) -> Self {
Self(Arc::new(Mutex::new(
ExternalitiesInner::new(free_balance, fee_schedule)
)))
}
}
impl wasm_executor::Externalities for Externalities {
fn post_upward_message(&mut self, message: UpwardMessage) -> Result<(), String> {
self.0.lock().post_upward_message(message)
}
}
/// Data from a fully-outputted validation of a parachain candidate. This contains /// Data from a fully-outputted validation of a parachain candidate. This contains
/// all outputs and commitments of the validation as well as all additional data to make available. /// all outputs and commitments of the validation as well as all additional data to make available.
pub struct FullOutput { pub struct FullOutput {
@@ -163,6 +99,7 @@ pub struct ValidatedCandidate<'a> {
local_validation: &'a LocalValidationData, local_validation: &'a LocalValidationData,
upward_messages: Vec<UpwardMessage>, upward_messages: Vec<UpwardMessage>,
fees: Balance, fees: Balance,
processed_downward_messages: u32,
} }
impl<'a> ValidatedCandidate<'a> { impl<'a> ValidatedCandidate<'a> {
@@ -175,6 +112,7 @@ impl<'a> ValidatedCandidate<'a> {
local_validation, local_validation,
upward_messages, upward_messages,
fees, fees,
processed_downward_messages,
} = self; } = self;
let omitted_validation = OmittedValidationData { let omitted_validation = OmittedValidationData {
@@ -212,6 +150,7 @@ impl<'a> ValidatedCandidate<'a> {
fees, fees,
erasure_root, erasure_root,
new_validation_code: None, new_validation_code: None,
processed_downward_messages,
}; };
Ok(FullOutput { Ok(FullOutput {
@@ -223,6 +162,27 @@ impl<'a> ValidatedCandidate<'a> {
} }
} }
/// Validate that the given `UpwardMessage`s are covered by the given `free_balance`.
///
/// Will return an error if the `free_balance` does not cover the required fees to the
/// given `msgs`. On success it returns the fees that need to be charged for the `msgs`.
fn validate_upward_messages(
msgs: &[UpwardMessage],
fee_schedule: FeeSchedule,
free_balance: Balance,
) -> Result<Balance, Error> {
msgs.iter().try_fold(Balance::from(0u128), |fees_charged, msg| {
let fees = fee_schedule.compute_message_fee(msg.data.len());
let fees_charged = fees_charged.saturating_add(fees);
if fees_charged > free_balance {
Err(Error::CouldNotCoverFee)
} else {
Ok(fees_charged)
}
})
}
/// Does full checks of a collation, with provided PoV-block and contextual data. /// Does full checks of a collation, with provided PoV-block and contextual data.
pub fn validate<'a>( pub fn validate<'a>(
validation_pool: Option<&'_ ValidationPool>, validation_pool: Option<&'_ ValidationPool>,
@@ -258,28 +218,26 @@ pub fn validate<'a>(
.map(ExecutionMode::Remote) .map(ExecutionMode::Remote)
.unwrap_or(ExecutionMode::Local); .unwrap_or(ExecutionMode::Local);
let ext = Externalities::new(local_validation.balance, fee_schedule);
match wasm_executor::validate_candidate( match wasm_executor::validate_candidate(
&validation_code.0, &validation_code.0,
params, params,
ext.clone(),
execution_mode, execution_mode,
) { ) {
Ok(result) => { Ok(result) => {
if result.head_data == collation.head_data { if result.head_data == collation.head_data {
let (upward_messages, fees) = Arc::try_unwrap(ext.0) let fees = validate_upward_messages(
.map_err(|_| "<non-unique>") &result.upward_messages,
.expect("Wasm executor drops passed externalities on completion; \ fee_schedule,
call has concluded; qed") local_validation.balance,
.into_inner() )?;
.outputs();
Ok(ValidatedCandidate { Ok(ValidatedCandidate {
pov_block, pov_block,
global_validation, global_validation,
local_validation, local_validation,
upward_messages, upward_messages: result.upward_messages,
fees, fees,
processed_downward_messages: result.processed_downward_messages,
}) })
} else { } else {
Err(Error::HeadDataMismatch) Err(Error::HeadDataMismatch)
@@ -368,37 +326,34 @@ pub fn full_output_validation_with_api<P>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use parachain::wasm_executor::Externalities as ExternalitiesTrait;
use parachain::primitives::ParachainDispatchOrigin; use parachain::primitives::ParachainDispatchOrigin;
fn add_msg(size: usize, msgs: &mut Vec<UpwardMessage>) {
let msg = UpwardMessage { data: vec![0; size], origin: ParachainDispatchOrigin::Parachain };
msgs.push(msg);
}
#[test] #[test]
fn ext_checks_fees_and_updates_correctly() { fn validate_upward_messages_works() {
let mut ext = ExternalitiesInner { let fee_schedule = FeeSchedule {
upward: vec![
UpwardMessage { data: vec![42], origin: ParachainDispatchOrigin::Parachain },
],
fees_charged: 0,
free_balance: 1_000_000,
fee_schedule: FeeSchedule {
base: 1000, base: 1000,
per_byte: 10, per_byte: 10,
},
}; };
let free_balance = 1_000_000;
let mut msgs = Vec::new();
ext.apply_message_fee(100).unwrap(); add_msg(100, &mut msgs);
assert_eq!(ext.fees_charged, 2000); assert_eq!(2000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
ext.post_upward_message(UpwardMessage { add_msg(100, &mut msgs);
origin: ParachainDispatchOrigin::Signed, assert_eq!(4000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
data: vec![0u8; 100],
}).unwrap();
assert_eq!(ext.fees_charged, 4000);
add_msg((1_000_000 - 4000 - 1000) / 10, &mut msgs);
ext.apply_message_fee((1_000_000 - 4000 - 1000) / 10).unwrap(); assert_eq!(1_000_000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap());
assert_eq!(ext.fees_charged, 1_000_000);
// cannot pay fee. // cannot pay fee.
assert!(ext.apply_message_fee(1).is_err()); add_msg(1, &mut msgs);
let err = validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap_err();
assert!(matches!(err, Error::CouldNotCoverFee));
} }
} }
@@ -704,6 +704,9 @@ mod tests {
fn signing_context() -> SigningContext { fn signing_context() -> SigningContext {
Default::default() Default::default()
} }
fn downward_messages(_: ParaId) -> Vec<polkadot_primitives::DownwardMessage> {
Vec::new()
}
} }
} }