From 934f27d92b744d12e3d9894b740684ff6c2a5052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 1 Jul 2020 16:20:38 +0200 Subject: [PATCH] Downward & Upward messages (#1266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: ef2aa428d7aaca2651427d19f3b288e8b182006b 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 * Update runtime/common/src/parachains.rs Co-authored-by: Robert Habermeier * Update runtime/common/src/parachains.rs Co-authored-by: Robert Habermeier * Minor fixes * Fix wasm build Co-authored-by: Gav Wood Co-authored-by: André Silva Co-authored-by: Cecile Tonglet Co-authored-by: Robert Habermeier --- polkadot/Cargo.lock | 15 +- polkadot/Cargo.toml | 1 + polkadot/availability-store/src/lib.rs | 4 +- polkadot/availability-store/src/store.rs | 2 +- polkadot/collator/src/lib.rs | 8 +- polkadot/core-primitives/Cargo.toml | 20 +++ polkadot/core-primitives/src/lib.rs | 96 +++++++++++ polkadot/network/src/protocol/tests.rs | 3 + polkadot/parachain/Cargo.toml | 6 +- polkadot/parachain/src/primitives.rs | 19 +-- polkadot/parachain/src/wasm_api.rs | 23 --- polkadot/parachain/src/wasm_executor/mod.rs | 40 +---- .../src/wasm_executor/validation_host.rs | 77 ++------- .../test-parachains/adder/Cargo.toml | 6 +- .../adder/collator/src/main.rs | 3 +- .../adder/src/wasm_validation.rs | 18 +-- .../test-parachains/code-upgrader/Cargo.toml | 6 +- .../code-upgrader/src/wasm_validation.rs | 18 +-- .../test-parachains/tests/adder/mod.rs | 17 +- .../tests/code_upgrader/mod.rs | 18 +-- .../parachain/test-parachains/tests/lib.rs | 11 +- .../tests/wasm_executor/mod.rs | 8 +- polkadot/primitives/Cargo.toml | 7 +- polkadot/primitives/src/lib.rs | 53 +------ polkadot/primitives/src/parachain.rs | 7 + polkadot/runtime/common/src/parachains.rs | 149 ++++++++++++++++-- polkadot/runtime/common/src/registrar.rs | 1 + polkadot/runtime/kusama/Cargo.toml | 8 +- polkadot/runtime/kusama/src/lib.rs | 7 + polkadot/runtime/polkadot/Cargo.toml | 8 +- polkadot/runtime/polkadot/src/lib.rs | 7 + polkadot/runtime/test-runtime/src/lib.rs | 3 + polkadot/runtime/westend/Cargo.toml | 6 + polkadot/runtime/westend/src/lib.rs | 7 + polkadot/validation/src/error.rs | 5 +- polkadot/validation/src/pipeline.rs | 147 ++++++----------- .../validation/src/validation_service/mod.rs | 3 + 37 files changed, 460 insertions(+), 377 deletions(-) create mode 100644 polkadot/core-primitives/Cargo.toml create mode 100644 polkadot/core-primitives/src/lib.rs diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 9463440019..e9756cfd53 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -4319,6 +4319,16 @@ dependencies = [ "tokio 0.2.21", ] +[[package]] +name = "polkadot-core-primitives" +version = "0.7.30" +dependencies = [ + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "polkadot-erasure-coding" version = "0.8.13" @@ -4446,13 +4456,13 @@ dependencies = [ "log 0.4.8", "parity-scale-codec", "parking_lot 0.10.2", + "polkadot-core-primitives", "sc-executor", "serde", "shared_memory", "sp-core", "sp-externalities", "sp-io", - "sp-runtime-interface", "sp-std", "sp-wasm-interface", ] @@ -4464,6 +4474,7 @@ dependencies = [ "bitvec", "frame-system", "parity-scale-codec", + "polkadot-core-primitives", "polkadot-parachain", "pretty_assertions", "serde", @@ -8043,6 +8054,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "sp-io", + "sp-std", "substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0", ] @@ -8070,6 +8082,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "sp-io", + "sp-std", "substrate-wasm-builder-runner 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0", ] diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index 4a72ae8dc6..f6632f77a1 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -26,6 +26,7 @@ members = [ "availability-store", "cli", "collator", + "core-primitives", "erasure-coding", "network", "network/test", diff --git a/polkadot/availability-store/src/lib.rs b/polkadot/availability-store/src/lib.rs index 8e9feea687..281441fb5e 100644 --- a/polkadot/availability-store/src/lib.rs +++ b/polkadot/availability-store/src/lib.rs @@ -33,7 +33,7 @@ use polkadot_primitives::{ }, }; use sp_runtime::traits::HashFor; -use sp_blockchain::{Result as ClientResult}; +use sp_blockchain::Result as ClientResult; use client::{ BlockchainEvents, BlockBackend, }; @@ -58,7 +58,7 @@ use worker::{ Worker, WorkerHandle, IncludedParachainBlocks, WorkerMsg, MakeAvailable, Chunks }; -use store::{Store as InnerStore}; +use store::Store as InnerStore; const LOG_TARGET: &str = "availability"; diff --git a/polkadot/availability-store/src/store.rs b/polkadot/availability-store/src/store.rs index e3b1e35929..851beabc6a 100644 --- a/polkadot/availability-store/src/store.rs +++ b/polkadot/availability-store/src/store.rs @@ -18,7 +18,7 @@ use kvdb_rocksdb::{Database, DatabaseConfig}; use kvdb::{KeyValueDB, DBTransaction}; use codec::{Encode, Decode}; -use polkadot_erasure_coding::{self as erasure}; +use polkadot_erasure_coding as erasure; use polkadot_primitives::{ Hash, parachain::{ diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs index f790d70557..51f3255a74 100644 --- a/polkadot/collator/src/lib.rs +++ b/polkadot/collator/src/lib.rs @@ -56,7 +56,7 @@ use sc_client_api::{StateBackend, BlockchainEvents}; use sp_blockchain::HeaderBackend; use sp_core::Pair; use polkadot_primitives::{ - BlockId, Hash, Block, + BlockId, Hash, Block, DownwardMessage, parachain::{ self, BlockData, DutyRoster, HeadData, Id as ParaId, PoVBlock, ValidatorId, CollatorPair, LocalValidationData, GlobalValidationSchedule, @@ -150,6 +150,7 @@ pub trait ParachainContext: Clone { relay_parent: Hash, global_validation: GlobalValidationSchedule, local_validation: LocalValidationData, + downward_messages: Vec, ) -> Self::ProduceCandidate; } @@ -159,6 +160,7 @@ pub async fn collate

( local_id: ParaId, global_validation: GlobalValidationSchedule, local_validation_data: LocalValidationData, + downward_messages: Vec, mut para_context: P, key: Arc, ) -> Option @@ -170,6 +172,7 @@ pub async fn collate

( relay_parent, global_validation, local_validation_data, + downward_messages, ).await?; let pov_block = PoVBlock { @@ -317,6 +320,7 @@ fn build_collator_service( Some(local_validation) => local_validation, 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)); @@ -331,6 +335,7 @@ fn build_collator_service( para_id, global_validation, local_validation, + downward_messages, parachain_context, key, ).map(move |collation| { @@ -470,6 +475,7 @@ mod tests { _relay_parent: Hash, _global: GlobalValidationSchedule, _local_validation: LocalValidationData, + _: Vec, ) -> Self::ProduceCandidate { // send messages right back. future::ready(Some(( diff --git a/polkadot/core-primitives/Cargo.toml b/polkadot/core-primitives/Cargo.toml new file mode 100644 index 0000000000..b0f5971e2a --- /dev/null +++ b/polkadot/core-primitives/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "polkadot-core-primitives" +version = "0.7.30" +authors = ["Parity Technologies "] +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", +] diff --git a/polkadot/core-primitives/src/lib.rs b/polkadot/core-primitives/src/lib.rs new file mode 100644 index 0000000000..d91ed3cfc1 --- /dev/null +++ b/polkadot/core-primitives/src/lib.rs @@ -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 . + +#![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 = ::Signer; + +/// Alias to the opaque account ID type for this chain, actually a `AccountId32`. This is always +/// 32 bytes. +pub type AccountId = ::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; +/// Block type. +pub type Block = generic::Block; +/// Block ID. +pub type BlockId = generic::BlockId; + +/// 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 { + /// 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), + /// XCMP message for the Parachain. + XCMPMessage(sp_std::vec::Vec), +} diff --git a/polkadot/network/src/protocol/tests.rs b/polkadot/network/src/protocol/tests.rs index a1cad24be3..0e78479fb2 100644 --- a/polkadot/network/src/protocol/tests.rs +++ b/polkadot/network/src/protocol/tests.rs @@ -185,6 +185,9 @@ sp_api::mock_impl_runtime_apis! { parent_hash: Default::default(), } } + fn downward_messages(_: ParaId) -> Vec { + Vec::new() + } } } diff --git a/polkadot/parachain/Cargo.toml b/polkadot/parachain/Cargo.toml index e401e2baa5..fb1e1ce503 100644 --- a/polkadot/parachain/Cargo.toml +++ b/polkadot/parachain/Cargo.toml @@ -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-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 } +polkadot-core-primitives = { path = "../core-primitives", default-features = false } # all optional crates. derive_more = { version = "0.99.2", 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 } sc-executor = { 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] default = ["std"] -wasm-api = ["sp-runtime-interface"] +wasm-api = [] std = [ "codec/std", "derive_more", @@ -39,8 +39,8 @@ std = [ "sp-core/std", "parking_lot", "log", - "sp-runtime-interface/std", "sp-externalities", "sc-executor", "sp-io", + "polkadot-core-primitives/std", ] diff --git a/polkadot/parachain/src/primitives.rs b/polkadot/parachain/src/primitives.rs index 54c6907f42..f6b69cd894 100644 --- a/polkadot/parachain/src/primitives.rs +++ b/polkadot/parachain/src/primitives.rs @@ -28,9 +28,8 @@ use serde::{Serialize, Deserialize}; #[cfg(feature = "std")] use sp_core::bytes; -/// The block number of the relay chain. -/// 32-bits will allow for 136 years of blocks assuming 1 block per second. -pub type RelayChainBlockNumber = u32; +/// Block number type used by the relay chain. +pub use polkadot_core_primitives::BlockNumber as RelayChainBlockNumber; /// Parachain head data included in the chain. #[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug)] @@ -186,10 +185,6 @@ impl sp_std::convert::TryFrom for ParachainDispatchOrigin { /// A message from a parachain to its Relay Chain. #[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))] pub struct UpwardMessage { /// The origin for the message to be sent from. @@ -212,13 +207,13 @@ pub struct ValidationParams { /// The maximum head-data size permitted, in bytes. pub max_head_data_size: u32, /// 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 /// would be applied after, if so. The parachain logic should apply any upgrade /// issued in this block after the first block /// with `relay_chain_height` at least this value, if `Some`. if `None`, issue /// no upgrade. - pub code_upgrade_allowed: Option, + pub code_upgrade_allowed: Option, } /// The result of parachain validation. @@ -230,4 +225,10 @@ pub struct ValidationResult { pub head_data: HeadData, /// An update to the validation code that should be scheduled in the relay chain. pub new_validation_code: Option, + /// Upward messages send by the Parachain. + pub upward_messages: Vec, + /// 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, } diff --git a/polkadot/parachain/src/wasm_api.rs b/polkadot/parachain/src/wasm_api.rs index f5a82f5781..9c7eac25f1 100644 --- a/polkadot/parachain/src/wasm_api.rs +++ b/polkadot/parachain/src/wasm_api.rs @@ -16,29 +16,6 @@ //! 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::() - .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. /// /// Offset and length must have been provided by the validation diff --git a/polkadot/parachain/src/wasm_executor/mod.rs b/polkadot/parachain/src/wasm_executor/mod.rs index 2410da844f..e617a4c474 100644 --- a/polkadot/parachain/src/wasm_executor/mod.rs +++ b/polkadot/parachain/src/wasm_executor/mod.rs @@ -21,12 +21,12 @@ //! a WASM VM for re-execution of a parachain candidate. use std::any::{TypeId, Any}; -use crate::primitives::{ValidationParams, ValidationResult, UpwardMessage}; +use crate::primitives::{ValidationParams, ValidationResult}; use codec::{Decode, Encode}; use sp_core::storage::ChildInfo; use sp_core::traits::CallInWasm; -use sp_wasm_interface::HostFunctions as _; use sp_externalities::Extensions; +use sp_wasm_interface::HostFunctions as _; #[cfg(not(any(target_os = "android", target_os = "unknown")))] 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_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); -} - -impl ParachainExt { - pub fn new(ext: T) -> Self { - Self(Box::new(ext)) - } -} - /// A stub validation-pool defined when compiling for Android or WASM. #[cfg(any(target_os = "android", target_os = "unknown"))] #[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. /// /// This will fail if the validation code is not a proper parachain validation module. -pub fn validate_candidate( +pub fn validate_candidate( validation_code: &[u8], params: ValidationParams, - ext: E, options: ExecutionMode<'_>, ) -> Result { match options { ExecutionMode::Local => { - validate_candidate_internal(validation_code, ¶ms.encode(), ext) + validate_candidate_internal(validation_code, ¶ms.encode()) }, #[cfg(not(any(target_os = "android", target_os = "unknown")))] 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")))] 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"))] ExecutionMode::Remote(pool) => @@ -165,21 +146,16 @@ pub fn validate_candidate( } /// The host functions provided by the wasm executor to the parachain wasm blob. -type HostFunctions = ( - sp_io::SubstrateHostFunctions, - crate::wasm_api::parachain::HostFunctions, -); +type HostFunctions = sp_io::SubstrateHostFunctions; /// Validate a candidate under the given validation code. /// /// This will fail if the validation code is not a proper parachain validation module. -pub fn validate_candidate_internal( +pub fn validate_candidate_internal( validation_code: &[u8], encoded_call_data: &[u8], - externalities: E, ) -> Result { let mut extensions = Extensions::new(); - extensions.register(ParachainExt::new(externalities)); extensions.register(sp_core::traits::TaskExecutorExt(sp_core::tasks::executor())); let mut ext = ValidationExternalities(extensions); diff --git a/polkadot/parachain/src/wasm_executor/validation_host.rs b/polkadot/parachain/src/wasm_executor/validation_host.rs index 0f123349e3..d780d5b86b 100644 --- a/polkadot/parachain/src/wasm_executor/validation_host.rs +++ b/polkadot/parachain/src/wasm_executor/validation_host.rs @@ -16,18 +16,15 @@ #![cfg(not(any(target_os = "android", target_os = "unknown")))] -use std::{process, env, sync::Arc, sync::atomic, mem}; -use codec::{Decode, Encode, EncodeAppend}; -use crate::primitives::{ValidationParams, ValidationResult, UpwardMessage}; -use super::{validate_candidate_internal, Error, Externalities}; +use std::{process, env, sync::Arc, sync::atomic}; +use codec::{Decode, Encode}; +use crate::primitives::{ValidationParams, ValidationResult}; +use super::{validate_candidate_internal, Error}; use super::{MAX_CODE_MEM, MAX_RUNTIME_MEM}; use shared_memory::{SharedMem, SharedMemConf, EventState, WriteLockable, EventWait, EventSet}; use parking_lot::Mutex; 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"]; /// CLI Argument to start in validation worker mode. const WORKER_ARG: &'static str = "validation-worker"; @@ -40,27 +37,6 @@ pub const EXECUTION_TIMEOUT_SEC: u64 = 30; #[cfg(not(debug_assertions))] pub const EXECUTION_TIMEOUT_SEC: u64 = 5; -#[derive(Default)] -struct WorkerExternalitiesInner { - up_data: Vec, -} - -#[derive(Default, Clone)] -struct WorkerExternalities { - inner: Arc>, -} - -impl Externalities for WorkerExternalities { - fn post_upward_message(&mut self, message: UpwardMessage) -> Result<(), String> { - let mut inner = self.inner.lock(); - inner.up_data = 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 { CandidateReady = 0, ResultReady = 1, @@ -87,21 +63,20 @@ impl ValidationPool { /// free validation host. /// /// This will fail if the validation code is not a proper parachain validation module. - pub fn validate_candidate( + pub fn validate_candidate( &self, validation_code: &[u8], params: ValidationParams, - externalities: E, test_mode: bool, ) -> Result { for host in self.hosts.iter() { 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 - 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)); // spawn parent monitor thread 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, _) = 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); match result { - 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) - } - }, + Ok(r) => ValidationResultHeader::Ok(r), Err(e) => ValidationResultHeader::Error(e.to_string()), } }; @@ -226,7 +189,7 @@ impl Drop for ValidationHost { impl ValidationHost { fn create_memory() -> Result { - 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() .set_size(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. /// /// This will fail if the validation code is not a proper parachain validation module. - pub fn validate_candidate( + pub fn validate_candidate( &mut self, validation_code: &[u8], params: ValidationParams, - mut externalities: E, test_mode: bool, ) -> Result { if validation_code.len() > MAX_CODE_MEM { @@ -322,24 +284,11 @@ impl ValidationHost { { debug!("{} Reading results", self.id); let data: &[u8] = &**memory.wlock_as_slice(0)?; - let (header_buf, rest) = data.split_at(1024); - let (_, rest) = rest.split_at(MAX_CODE_MEM); - let (_, message_data) = rest.split_at(MAX_RUNTIME_MEM); + let (header_buf, _) = data.split_at(1024); let mut header_buf: &[u8] = header_buf; - let mut message_data: &[u8] = message_data; let header = ValidationResultHeader::decode(&mut header_buf).unwrap(); match header { - ValidationResultHeader::Ok(result) => { - let upwards = Vec::::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::Ok(result) => Ok(result), ValidationResultHeader::Error(message) => { debug!("{} Validation error: {}", self.id, message); Err(Error::External(message).into()) diff --git a/polkadot/parachain/test-parachains/adder/Cargo.toml b/polkadot/parachain/test-parachains/adder/Cargo.toml index 7f04dc5946..2cfb77566d 100644 --- a/polkadot/parachain/test-parachains/adder/Cargo.toml +++ b/polkadot/parachain/test-parachains/adder/Cargo.toml @@ -9,6 +9,7 @@ build = "build.rs" [dependencies] 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"] } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tiny-keccak = "1.5.0" dlmalloc = { version = "0.1.3", features = [ "global" ] } @@ -20,4 +21,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1. [features] default = [ "std" ] -std = ["parachain/std"] +std = [ + "parachain/std", + "sp-std/std", +] diff --git a/polkadot/parachain/test-parachains/adder/collator/src/main.rs b/polkadot/parachain/test-parachains/adder/collator/src/main.rs index a32b6e4ca4..326549fda7 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/main.rs @@ -23,7 +23,7 @@ use adder::{HeadData as AdderHead, BlockData as AdderBody}; use sp_core::Pair; use codec::{Encode, Decode}; use primitives::{ - Hash, + Hash, DownwardMessage, parachain::{HeadData, BlockData, Id as ParaId, LocalValidationData, GlobalValidationSchedule}, }; use collator::{ParachainContext, Network, BuildParachainContext, Cli, SubstrateCli}; @@ -60,6 +60,7 @@ impl ParachainContext for AdderContext { _relay_parent: Hash, _global_validation: GlobalValidationSchedule, local_validation: LocalValidationData, + _: Vec, ) -> Self::ProduceCandidate { let adder_head = match AdderHead::decode(&mut &local_validation.parent_head.0[..]).ok() { diff --git a/polkadot/parachain/test-parachains/adder/src/wasm_validation.rs b/polkadot/parachain/test-parachains/adder/src/wasm_validation.rs index eaa5101ba9..c0f3b56dc8 100644 --- a/polkadot/parachain/test-parachains/adder/src/wasm_validation.rs +++ b/polkadot/parachain/test-parachains/adder/src/wasm_validation.rs @@ -21,22 +21,6 @@ use core::{intrinsics, panic}; use parachain::primitives::{ValidationResult, HeadData as GenericHeadData}; 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] pub extern fn validate_block(params: *const u8, len: usize) -> u64 { let params = unsafe { parachain::load_params(params, len) }; @@ -53,6 +37,8 @@ pub extern fn validate_block(params: *const u8, len: usize) -> u64 { &ValidationResult { head_data: GenericHeadData(new_head.encode()), new_validation_code: None, + upward_messages: sp_std::vec::Vec::new(), + processed_downward_messages: 0, } ), Err(_) => panic!("execution failure"), diff --git a/polkadot/parachain/test-parachains/code-upgrader/Cargo.toml b/polkadot/parachain/test-parachains/code-upgrader/Cargo.toml index 4dfcde274a..6f673adbf8 100644 --- a/polkadot/parachain/test-parachains/code-upgrader/Cargo.toml +++ b/polkadot/parachain/test-parachains/code-upgrader/Cargo.toml @@ -9,6 +9,7 @@ build = "build.rs" [dependencies] 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"] } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tiny-keccak = "1.5.0" dlmalloc = { version = "0.1.3", features = [ "global" ] } @@ -20,4 +21,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1. [features] default = [ "std" ] -std = ["parachain/std"] +std = [ + "parachain/std", + "sp-std/std", +] diff --git a/polkadot/parachain/test-parachains/code-upgrader/src/wasm_validation.rs b/polkadot/parachain/test-parachains/code-upgrader/src/wasm_validation.rs index 8ebc3ae3c6..3f4d0c44f8 100644 --- a/polkadot/parachain/test-parachains/code-upgrader/src/wasm_validation.rs +++ b/polkadot/parachain/test-parachains/code-upgrader/src/wasm_validation.rs @@ -21,22 +21,6 @@ use core::{intrinsics, panic}; use parachain::primitives::{ValidationResult, HeadData as GenericHeadData}; 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] pub extern fn validate_block(params: *const u8, len: usize) -> u64 { let params = unsafe { parachain::load_params(params, len) }; @@ -64,6 +48,8 @@ pub extern fn validate_block(params: *const u8, len: usize) -> u64 { &ValidationResult { head_data: GenericHeadData(output.head_data.encode()), new_validation_code: output.new_validation_code, + upward_messages: sp_std::vec::Vec::new(), + processed_downward_messages: 0, } ), Err(_) => panic!("execution failure"), diff --git a/polkadot/parachain/test-parachains/tests/adder/mod.rs b/polkadot/parachain/test-parachains/tests/adder/mod.rs index d97db998fd..b0b53dc4dc 100644 --- a/polkadot/parachain/test-parachains/tests/adder/mod.rs +++ b/polkadot/parachain/test-parachains/tests/adder/mod.rs @@ -16,15 +16,11 @@ //! Basic parachain that adds a number as part of its state. -use crate::{ - DummyExt, - parachain, - parachain::primitives::{ - RelayChainBlockNumber, - BlockData as GenericBlockData, - HeadData as GenericHeadData, - ValidationParams, - }, +use parachain::primitives::{ + RelayChainBlockNumber, + BlockData as GenericBlockData, + HeadData as GenericHeadData, + ValidationParams, }; use codec::{Decode, Encode}; @@ -83,7 +79,6 @@ pub fn execute_good_on_parent() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); @@ -123,7 +118,6 @@ fn execute_good_chain_on_parent() { relay_chain_height: number as RelayChainBlockNumber + 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); @@ -164,7 +158,6 @@ fn execute_bad_on_parent() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap_err(); } diff --git a/polkadot/parachain/test-parachains/tests/code_upgrader/mod.rs b/polkadot/parachain/test-parachains/tests/code_upgrader/mod.rs index c59e44fc12..89f6065cf1 100644 --- a/polkadot/parachain/test-parachains/tests/code_upgrader/mod.rs +++ b/polkadot/parachain/test-parachains/tests/code_upgrader/mod.rs @@ -16,15 +16,10 @@ //! Basic parachain that adds a number as part of its state. -use parachain; - -use crate::{ - DummyExt, - parachain::primitives::{ - BlockData as GenericBlockData, - HeadData as GenericHeadData, - ValidationParams, ValidationCode, - }, +use parachain::primitives::{ + BlockData as GenericBlockData, + HeadData as GenericHeadData, + ValidationParams, ValidationCode, }; use codec::{Decode, Encode}; use code_upgrader::{hash_state, HeadData, BlockData, State}; @@ -56,7 +51,6 @@ pub fn execute_good_no_upgrade() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); @@ -93,7 +87,6 @@ pub fn execute_good_with_upgrade() { relay_chain_height: 1, code_upgrade_allowed: Some(20), }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); @@ -137,7 +130,6 @@ pub fn code_upgrade_not_allowed() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); } @@ -168,7 +160,6 @@ pub fn applies_code_upgrade_after_delay() { relay_chain_height: 1, code_upgrade_allowed: Some(2), }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); @@ -204,7 +195,6 @@ pub fn applies_code_upgrade_after_delay() { relay_chain_height: 2, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ).unwrap(); diff --git a/polkadot/parachain/test-parachains/tests/lib.rs b/polkadot/parachain/test-parachains/tests/lib.rs index cf6d63fd2a..692c04c10c 100644 --- a/polkadot/parachain/test-parachains/tests/lib.rs +++ b/polkadot/parachain/test-parachains/tests/lib.rs @@ -18,16 +18,7 @@ mod adder; mod code_upgrader; mod wasm_executor; -use parachain::{ - self, primitives::UpwardMessage, wasm_executor::{Externalities, run_worker}, -}; - -struct DummyExt; -impl Externalities for DummyExt { - fn post_upward_message(&mut self, _: UpwardMessage) -> Result<(), String> { - Ok(()) - } -} +use parachain::wasm_executor::run_worker; // 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. diff --git a/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs b/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs index c6cf2407b3..6ba8ddefec 100644 --- a/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs +++ b/polkadot/parachain/test-parachains/tests/wasm_executor/mod.rs @@ -16,9 +16,8 @@ //! Basic parachain that adds a number as part of its state. -use parachain; -use crate::{adder, DummyExt}; -use crate::parachain::{ +use crate::adder; +use parachain::{ primitives::{BlockData, ValidationParams}, wasm_executor::EXECUTION_TIMEOUT_SEC, }; @@ -40,7 +39,6 @@ fn terminates_on_timeout() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ); match result { @@ -70,7 +68,6 @@ fn parallel_execution() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool2), ).ok()); let _ = parachain::wasm_executor::validate_candidate( @@ -83,7 +80,6 @@ fn parallel_execution() { relay_chain_height: 1, code_upgrade_allowed: None, }, - DummyExt, parachain::wasm_executor::ExecutionMode::RemoteTest(&pool), ); thread.join().unwrap(); diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index d327429b64..f3bc264c8c 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" serde = { version = "1.0.102", optional = true, features = ["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 } -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 } 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 } @@ -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 } runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master", 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 } 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] sp-serializer = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -27,9 +28,9 @@ pretty_assertions = "0.5.1" [features] default = ["std"] std = [ + "application-crypto/std", "parity-scale-codec/std", "primitives/std", - "system/std", "inherents/std", "trie/std", "sp-api/std", @@ -39,5 +40,7 @@ std = [ "runtime_primitives/std", "serde", "polkadot-parachain/std", + "polkadot-core-primitives/std", "bitvec/std", + "frame-system/std", ] diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 24ed6e1eb3..6f0a0986c9 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -20,63 +20,14 @@ #![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 polkadot_core_primitives::*; pub mod inclusion_inherent; pub mod parachain; 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 = ::Signer; - -/// Alias to the opaque account ID type for this chain, actually a `AccountId32`. This is always -/// 32 bytes. -pub type AccountId = ::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; -/// Block type. -pub type Block = generic::Block; -/// Block ID. -pub type BlockId = generic::BlockId; - -/// Opaque, encoded, unchecked extrinsic. -pub use runtime_primitives::OpaqueExtrinsic as UncheckedExtrinsic; - /// Custom validity errors used in Polkadot while validating transactions. #[repr(u8)] pub enum ValidityError { @@ -118,7 +69,7 @@ pub mod fisherman { /// An `AppCrypto` type to allow submitting signed transactions using the fisherman /// application key as signer. pub struct FishermanAppCrypto; - impl system::offchain::AppCrypto<::Signer, Signature> for FishermanAppCrypto { + impl frame_system::offchain::AppCrypto<::Signer, Signature> for FishermanAppCrypto { type RuntimeAppPublic = FishermanId; type GenericSignature = primitives::sr25519::Signature; type GenericPublic = primitives::sr25519::Public; diff --git a/polkadot/primitives/src/parachain.rs b/polkadot/primitives/src/parachain.rs index 52306174be..33a0f0363d 100644 --- a/polkadot/primitives/src/parachain.rs +++ b/polkadot/primitives/src/parachain.rs @@ -32,6 +32,7 @@ use primitives::RuntimeDebug; use runtime_primitives::traits::{AppVerify, Block as BlockT}; use inherents::InherentIdentifier; use application_crypto::KeyTypeId; +use polkadot_core_primitives::DownwardMessage; pub use polkadot_parachain::primitives::{ Id, ParachainDispatchOrigin, LOWEST_USER_ID, UpwardMessage, HeadData, BlockData, @@ -220,6 +221,10 @@ pub struct CandidateCommitments { pub erasure_root: H, /// New validation code. pub new_validation_code: Option, + /// 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. @@ -814,6 +819,8 @@ sp_api::decl_runtime_apis! { -> Option>; /// Get a `SigningContext` with current `SessionIndex` and parent hash. fn signing_context() -> SigningContext; + /// Get the `DownwardMessage`'s for the given parachain. + fn downward_messages(id: Id) -> Vec; } } diff --git a/polkadot/runtime/common/src/parachains.rs b/polkadot/runtime/common/src/parachains.rs index ef67adb031..0753f74e63 100644 --- a/polkadot/runtime/common/src/parachains.rs +++ b/polkadot/runtime/common/src/parachains.rs @@ -34,7 +34,7 @@ use sp_staking::{ }; use frame_support::{ traits::KeyOwnerProofSystem, - dispatch::{IsSubType}, + dispatch::IsSubType, weights::{DispatchClass, Weight}, }; use primitives::{ @@ -47,14 +47,13 @@ use primitives::{ LocalValidationData, Scheduling, ValidityAttestation, NEW_HEADS_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, ValidatorSignature, SigningContext, HeadData, ValidationCode, }, + Remark, DownwardMessage }; use frame_support::{ Parameter, dispatch::DispatchResult, decl_storage, decl_module, decl_error, ensure, traits::{Currency, Get, WithdrawReason, ExistenceRequirement, Randomness}, }; -use sp_runtime::{ - transaction_validity::InvalidTransaction, -}; +use sp_runtime::transaction_validity::InvalidTransaction; use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier}; @@ -91,6 +90,18 @@ impl Iterator for BlockNum pub trait ParachainCurrency { fn free_balance(para_id: ParaId) -> Balance; 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> ParachainCurrency for T where @@ -102,6 +113,8 @@ impl> ParachainCurrency for T where T::free_balance(¶_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 { let para_account = para_id.into_account(); @@ -115,6 +128,24 @@ impl> ParachainCurrency for T where 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. @@ -226,12 +257,14 @@ pub trait Trait: CreateSignedTransaction> + attestations::Trait + ses type AuthorityId: system::offchain::AppCrypto; /// The outer origin type. - type Origin: From + From>; + type Origin: From + + From<::Origin> + + Into::Origin>>; /// The outer call dispatch type. type Call: Parameter + Dispatchable::Origin> + From>; - /// Some way of interacting with balances for fees. + /// Some way of interacting with balances for fees and transfers. type ParachainCurrency: ParachainCurrency; /// Polkadot in practice will always use the `BlockNumber` type. @@ -345,6 +378,8 @@ impl Offence for DoubleVoteOffence { } } +/// 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. const MAX_QUEUE_COUNT: usize = 100; /// Total size of messages allowed in the parachain -> relay-chain message queue before which no @@ -445,8 +480,7 @@ impl ParaPastCodeMeta { } decl_storage! { - trait Store for Module as Parachains - { + trait Store for Module as Parachains { /// All authorities' keys at the moment. pub Authorities get(fn authorities): Vec; /// The active code of a currently-registered parachain. @@ -484,6 +518,9 @@ decl_storage! { /// /// `None` if not yet updated. pub DidUpdate: Option>; + + /// Messages waiting to be delivered from the Relay chain into the parachain. + pub DownwardMessageQueue: map hasher(twox_64_concat) ParaId => Vec>; } add_extra_genesis { config(authorities): Vec; @@ -531,6 +568,8 @@ decl_error! { CannotPayFees, /// Unexpected relay-parent for a candidate receipt. UnexpectedRelayParent, + /// Downward message queue is full for the Parachain. + DownwardMessageQueueFull, } } @@ -596,6 +635,11 @@ decl_module! { WATERMARK_QUEUE_SIZE, )?; + Self::remove_processed_downward_messages( + id, + head.candidate.commitments.processed_downward_messages as usize, + ); + let id = head.parachain_index(); proceeded.push(id); last_id = Some(id); @@ -668,6 +712,36 @@ decl_module! { 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::::decode_len(to).unwrap_or(0); + ensure!(downward_queue_count < MAX_DOWNWARD_QUEUE_COUNT, Error::::DownwardMessageQueueFull); + T::ParachainCurrency::transfer_in(&who, to, amount, ExistenceRequirement::AllowDeath)?; + DownwardMessageQueue::::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, + ) { + ensure_parachain(::Origin::from(origin))?; + let downward_queue_count = DownwardMessageQueue::::decode_len(to).unwrap_or(0); + ensure!(downward_queue_count < MAX_DOWNWARD_QUEUE_COUNT, Error::::DownwardMessageQueueFull); + DownwardMessageQueue::::append(to, DownwardMessage::XCMPMessage(msg)); + } } } @@ -814,11 +888,11 @@ impl Module { if let Ok(message_call) = ::Call::decode(&mut &data[..]) { let origin: ::Origin = match origin { ParachainDispatchOrigin::Signed => - system::RawOrigin::Signed(id.into_account()).into(), + ::Origin::from(::Origin::from(system::RawOrigin::Signed(id.into_account()))), ParachainDispatchOrigin::Parachain => Origin::Parachain(id).into(), ParachainDispatchOrigin::Root => - system::RawOrigin::Root.into(), + ::Origin::from(::Origin::from(system::RawOrigin::Root)), }; 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 @@ -862,6 +936,17 @@ impl Module { Ok(()) } + /// Remove processed downward messages from the `DownwardMessageQueue`. + fn remove_processed_downward_messages(id: ParaId, processed: usize) { + DownwardMessageQueue::::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 /// messages to the relay chain as well. fn update_routing( @@ -1079,6 +1164,11 @@ impl Module { }) } + /// Returns the `DownwardMessage`'s for the given parachain. + pub fn downward_messages(id: ParaId) -> Vec> { + DownwardMessageQueue::::get(id) + } + /// Get the local validation data for a particular parent w.r.t. the current /// block height. pub fn current_local_validation_data(id: &ParaId) -> Option { @@ -1113,8 +1203,7 @@ impl Module { schedule: &GlobalValidationSchedule, attested_candidates: &[AttestedCandidate], active_parachains: &[(ParaId, Option<(CollatorId, Retriable)>)] - ) -> sp_std::result::Result, sp_runtime::DispatchError> - { + ) -> sp_std::result::Result, sp_runtime::DispatchError> { // returns groups of slices that have the same chain ID. // assumes the inner slice is sorted by id. 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::::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::::get(&id), + ); + }); + } } diff --git a/polkadot/runtime/common/src/registrar.rs b/polkadot/runtime/common/src/registrar.rs index b5fbddb49e..248371d256 100644 --- a/polkadot/runtime/common/src/registrar.rs +++ b/polkadot/runtime/common/src/registrar.rs @@ -1074,6 +1074,7 @@ mod tests { upward_messages: vec![], erasure_root: [1; 32].into(), new_validation_code: None, + processed_downward_messages: 0, }, }; let (candidate, _) = candidate.abridge(); diff --git a/polkadot/runtime/kusama/Cargo.toml b/polkadot/runtime/kusama/Cargo.toml index b5494ab625..dbe3934dd3 100644 --- a/polkadot/runtime/kusama/Cargo.toml +++ b/polkadot/runtime/kusama/Cargo.toml @@ -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 } 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-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 } runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false } @@ -170,3 +170,9 @@ runtime-benchmarks = [ "pallet-session-benchmarking", "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 = [] diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs index 81755c5f4e..8e91f8f829 100644 --- a/polkadot/runtime/kusama/src/lib.rs +++ b/polkadot/runtime/kusama/src/lib.rs @@ -88,7 +88,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { authoring_version: 2, spec_version: 2013, impl_version: 0, + #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, + #[cfg(feature = "disable-runtime-api")] + apis: version::create_apis_vec![[]], transaction_version: 2, }; @@ -1005,6 +1008,7 @@ pub type Executive = executive::Executive< /// The payload being signed in the transactions. pub type SignedPayload = generic::SignedPayload; +#[cfg(not(feature = "disable-runtime-api"))] sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -1103,6 +1107,9 @@ sp_api::impl_runtime_apis! { fn signing_context() -> SigningContext { Parachains::signing_context() } + fn downward_messages(id: parachain::Id) -> Vec { + Parachains::downward_messages(id) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/polkadot/runtime/polkadot/Cargo.toml b/polkadot/runtime/polkadot/Cargo.toml index 471efb0bac..96ec48d318 100644 --- a/polkadot/runtime/polkadot/Cargo.toml +++ b/polkadot/runtime/polkadot/Cargo.toml @@ -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 } 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 } -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 } +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-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } @@ -164,3 +164,9 @@ runtime-benchmarks = [ "pallet-session-benchmarking", "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 = [] diff --git a/polkadot/runtime/polkadot/src/lib.rs b/polkadot/runtime/polkadot/src/lib.rs index 90b9ad6395..dd9a365545 100644 --- a/polkadot/runtime/polkadot/src/lib.rs +++ b/polkadot/runtime/polkadot/src/lib.rs @@ -93,7 +93,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { authoring_version: 0, spec_version: 13, impl_version: 0, + #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, + #[cfg(feature = "disable-runtime-api")] + apis: version::create_apis_vec![[]], transaction_version: 2, }; @@ -991,6 +994,7 @@ pub type Executive = executive::Executive; +#[cfg(not(feature = "disable-runtime-api"))] sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -1089,6 +1093,9 @@ sp_api::impl_runtime_apis! { fn signing_context() -> SigningContext { Parachains::signing_context() } + fn downward_messages(id: parachain::Id) -> Vec { + Parachains::downward_messages(id) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8b79ff22d6..84bf487382 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -656,6 +656,9 @@ sp_api::impl_runtime_apis! { fn signing_context() -> SigningContext { Parachains::signing_context() } + fn downward_messages(id: parachain::Id) -> Vec { + Parachains::downward_messages(id) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index deab7e0109..9e6af55afc 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -174,3 +174,9 @@ runtime-benchmarks = [ "pallet-session-benchmarking", "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 = [] diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 6e8bd9ad3c..7aaa05eaaf 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -83,7 +83,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { authoring_version: 2, spec_version: 33, impl_version: 0, + #[cfg(not(feature = "disable-runtime-api"))] apis: RUNTIME_API_VERSIONS, + #[cfg(feature = "disable-runtime-api")] + apis: version::create_apis_vec![[]], transaction_version: 2, }; @@ -753,6 +756,7 @@ pub type Executive = executive::Executive; +#[cfg(not(feature = "disable-runtime-api"))] sp_api::impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -851,6 +855,9 @@ sp_api::impl_runtime_apis! { fn signing_context() -> SigningContext { Parachains::signing_context() } + fn downward_messages(id: parachain::Id) -> Vec { + Parachains::downward_messages(id) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/polkadot/validation/src/error.rs b/polkadot/validation/src/error.rs index 834270151f..83b51ed236 100644 --- a/polkadot/validation/src/error.rs +++ b/polkadot/validation/src/error.rs @@ -81,7 +81,10 @@ pub enum Error { /// Block data is too big #[display(fmt = "Block data is too big (maximum allowed size: {}, actual size: {})", size, max_size)] 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 { diff --git a/polkadot/validation/src/pipeline.rs b/polkadot/validation/src/pipeline.rs index 52a724fca9..b29285716d 100644 --- a/polkadot/validation/src/pipeline.rs +++ b/polkadot/validation/src/pipeline.rs @@ -17,8 +17,6 @@ //! The pipeline of validation functions a parachain block must pass through before //! it can be voted for. -use std::sync::Arc; - use codec::Encode; use polkadot_erasure_coding as erasure; use polkadot_primitives::parachain::{ @@ -33,7 +31,6 @@ use parachain::{ }; use runtime_primitives::traits::{BlakeTwo256, Hash as HashT}; use sp_api::ProvideRuntimeApi; -use parking_lot::Mutex; use crate::Error; pub use parachain::wasm_executor::ValidationPool; @@ -67,67 +64,6 @@ pub fn basic_checks( Ok(()) } -struct ExternalitiesInner { - upward: Vec, - 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, Balance) { - (self.upward, self.fees_charged) - } -} - -#[derive(Clone)] -struct Externalities(Arc>); - -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 /// all outputs and commitments of the validation as well as all additional data to make available. pub struct FullOutput { @@ -163,6 +99,7 @@ pub struct ValidatedCandidate<'a> { local_validation: &'a LocalValidationData, upward_messages: Vec, fees: Balance, + processed_downward_messages: u32, } impl<'a> ValidatedCandidate<'a> { @@ -175,6 +112,7 @@ impl<'a> ValidatedCandidate<'a> { local_validation, upward_messages, fees, + processed_downward_messages, } = self; let omitted_validation = OmittedValidationData { @@ -212,6 +150,7 @@ impl<'a> ValidatedCandidate<'a> { fees, erasure_root, new_validation_code: None, + processed_downward_messages, }; 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 { + 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. pub fn validate<'a>( validation_pool: Option<&'_ ValidationPool>, @@ -258,28 +218,26 @@ pub fn validate<'a>( .map(ExecutionMode::Remote) .unwrap_or(ExecutionMode::Local); - let ext = Externalities::new(local_validation.balance, fee_schedule); match wasm_executor::validate_candidate( &validation_code.0, params, - ext.clone(), execution_mode, ) { Ok(result) => { if result.head_data == collation.head_data { - let (upward_messages, fees) = Arc::try_unwrap(ext.0) - .map_err(|_| "") - .expect("Wasm executor drops passed externalities on completion; \ - call has concluded; qed") - .into_inner() - .outputs(); + let fees = validate_upward_messages( + &result.upward_messages, + fee_schedule, + local_validation.balance, + )?; Ok(ValidatedCandidate { pov_block, global_validation, local_validation, - upward_messages, + upward_messages: result.upward_messages, fees, + processed_downward_messages: result.processed_downward_messages, }) } else { Err(Error::HeadDataMismatch) @@ -368,37 +326,34 @@ pub fn full_output_validation_with_api

( #[cfg(test)] mod tests { use super::*; - use parachain::wasm_executor::Externalities as ExternalitiesTrait; use parachain::primitives::ParachainDispatchOrigin; + fn add_msg(size: usize, msgs: &mut Vec) { + let msg = UpwardMessage { data: vec![0; size], origin: ParachainDispatchOrigin::Parachain }; + msgs.push(msg); + } + #[test] - fn ext_checks_fees_and_updates_correctly() { - let mut ext = ExternalitiesInner { - upward: vec![ - UpwardMessage { data: vec![42], origin: ParachainDispatchOrigin::Parachain }, - ], - fees_charged: 0, - free_balance: 1_000_000, - fee_schedule: FeeSchedule { - base: 1000, - per_byte: 10, - }, + fn validate_upward_messages_works() { + let fee_schedule = FeeSchedule { + base: 1000, + per_byte: 10, }; + let free_balance = 1_000_000; + let mut msgs = Vec::new(); - ext.apply_message_fee(100).unwrap(); - assert_eq!(ext.fees_charged, 2000); + add_msg(100, &mut msgs); + assert_eq!(2000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap()); - ext.post_upward_message(UpwardMessage { - origin: ParachainDispatchOrigin::Signed, - data: vec![0u8; 100], - }).unwrap(); - assert_eq!(ext.fees_charged, 4000); + add_msg(100, &mut msgs); + assert_eq!(4000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap()); - - ext.apply_message_fee((1_000_000 - 4000 - 1000) / 10).unwrap(); - assert_eq!(ext.fees_charged, 1_000_000); + add_msg((1_000_000 - 4000 - 1000) / 10, &mut msgs); + assert_eq!(1_000_000, validate_upward_messages(&msgs, fee_schedule, free_balance).unwrap()); // 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)); } } diff --git a/polkadot/validation/src/validation_service/mod.rs b/polkadot/validation/src/validation_service/mod.rs index a19e2f0e3b..650950f24e 100644 --- a/polkadot/validation/src/validation_service/mod.rs +++ b/polkadot/validation/src/validation_service/mod.rs @@ -704,6 +704,9 @@ mod tests { fn signing_context() -> SigningContext { Default::default() } + fn downward_messages(_: ParaId) -> Vec { + Vec::new() + } } }