mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 03:31:05 +00:00
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:
Generated
+14
-1
@@ -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",
|
||||
]
|
||||
|
||||
@@ -26,6 +26,7 @@ members = [
|
||||
"availability-store",
|
||||
"cli",
|
||||
"collator",
|
||||
"core-primitives",
|
||||
"erasure-coding",
|
||||
"network",
|
||||
"network/test",
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate;
|
||||
}
|
||||
|
||||
@@ -159,6 +160,7 @@ pub async fn collate<P>(
|
||||
local_id: ParaId,
|
||||
global_validation: GlobalValidationSchedule,
|
||||
local_validation_data: LocalValidationData,
|
||||
downward_messages: Vec<DownwardMessage>,
|
||||
mut para_context: P,
|
||||
key: Arc<CollatorPair>,
|
||||
) -> Option<parachain::Collation>
|
||||
@@ -170,6 +172,7 @@ pub async fn collate<P>(
|
||||
relay_parent,
|
||||
global_validation,
|
||||
local_validation_data,
|
||||
downward_messages,
|
||||
).await?;
|
||||
|
||||
let pov_block = PoVBlock {
|
||||
@@ -317,6 +320,7 @@ fn build_collator_service<P, C, R, Extrinsic>(
|
||||
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<P, C, R, Extrinsic>(
|
||||
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<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate {
|
||||
// send messages right back.
|
||||
future::ready(Some((
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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>),
|
||||
}
|
||||
@@ -185,6 +185,9 @@ sp_api::mock_impl_runtime_apis! {
|
||||
parent_hash: Default::default(),
|
||||
}
|
||||
}
|
||||
fn downward_messages(_: ParaId) -> Vec<polkadot_primitives::DownwardMessage> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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<u8> 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<RelayChainBlockNumber>,
|
||||
pub code_upgrade_allowed: Option<polkadot_core_primitives::BlockNumber>,
|
||||
}
|
||||
|
||||
/// 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<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,
|
||||
}
|
||||
|
||||
@@ -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::<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.
|
||||
///
|
||||
/// Offset and length must have been provided by the validation
|
||||
|
||||
@@ -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<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.
|
||||
#[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<E: Externalities + 'static>(
|
||||
pub fn validate_candidate(
|
||||
validation_code: &[u8],
|
||||
params: ValidationParams,
|
||||
ext: E,
|
||||
options: ExecutionMode<'_>,
|
||||
) -> Result<ValidationResult, Error> {
|
||||
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<E: Externalities + 'static>(
|
||||
}
|
||||
|
||||
/// 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<E: Externalities + 'static>(
|
||||
pub fn validate_candidate_internal(
|
||||
validation_code: &[u8],
|
||||
encoded_call_data: &[u8],
|
||||
externalities: E,
|
||||
) -> Result<ValidationResult, Error> {
|
||||
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);
|
||||
|
||||
@@ -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<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 {
|
||||
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<E: Externalities>(
|
||||
pub fn validate_candidate(
|
||||
&self,
|
||||
validation_code: &[u8],
|
||||
params: ValidationParams,
|
||||
externalities: E,
|
||||
test_mode: bool,
|
||||
) -> Result<ValidationResult, Error> {
|
||||
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<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()
|
||||
.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<E: Externalities>(
|
||||
pub fn validate_candidate(
|
||||
&mut self,
|
||||
validation_code: &[u8],
|
||||
params: ValidationParams,
|
||||
mut externalities: E,
|
||||
test_mode: bool,
|
||||
) -> Result<ValidationResult, Error> {
|
||||
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::<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::Ok(result) => Ok(result),
|
||||
ValidationResultHeader::Error(message) => {
|
||||
debug!("{} Validation error: {}", self.id, message);
|
||||
Err(Error::External(message).into())
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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<DownwardMessage>,
|
||||
) -> Self::ProduceCandidate
|
||||
{
|
||||
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 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"),
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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 = <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.
|
||||
#[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<<Signature as Verify>::Signer, Signature> for FishermanAppCrypto {
|
||||
impl frame_system::offchain::AppCrypto<<Signature as Verify>::Signer, Signature> for FishermanAppCrypto {
|
||||
type RuntimeAppPublic = FishermanId;
|
||||
type GenericSignature = primitives::sr25519::Signature;
|
||||
type GenericPublic = primitives::sr25519::Public;
|
||||
|
||||
@@ -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<H = Hash> {
|
||||
pub erasure_root: H,
|
||||
/// New validation code.
|
||||
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.
|
||||
@@ -814,6 +819,8 @@ sp_api::decl_runtime_apis! {
|
||||
-> Option<Vec<AbridgedCandidateReceipt>>;
|
||||
/// 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<DownwardMessage>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<N: Saturating + One + PartialOrd + PartialEq + Clone> Iterator for BlockNum
|
||||
pub trait ParachainCurrency<AccountId> {
|
||||
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<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(¶_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<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> 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<Call<Self>> + attestations::Trait + ses
|
||||
type AuthorityId: system::offchain::AppCrypto<Self::Public, Self::Signature>;
|
||||
|
||||
/// 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.
|
||||
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>;
|
||||
|
||||
/// 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.
|
||||
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<N: Ord + Copy> ParaPastCodeMeta<N> {
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Parachains
|
||||
{
|
||||
trait Store for Module<T: Trait> as Parachains {
|
||||
/// All authorities' keys at the moment.
|
||||
pub Authorities get(fn authorities): Vec<ValidatorId>;
|
||||
/// The active code of a currently-registered parachain.
|
||||
@@ -484,6 +518,9 @@ decl_storage! {
|
||||
///
|
||||
/// `None` if not yet updated.
|
||||
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 {
|
||||
config(authorities): Vec<ValidatorId>;
|
||||
@@ -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::<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[..]) {
|
||||
let origin: <T as Trait>::Origin = match origin {
|
||||
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 =>
|
||||
Origin::Parachain(id).into(),
|
||||
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();
|
||||
// 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(())
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// messages to the relay chain as well.
|
||||
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
|
||||
/// block height.
|
||||
pub fn current_local_validation_data(id: &ParaId) -> Option<LocalValidationData> {
|
||||
@@ -1113,8 +1203,7 @@ impl<T: Trait> Module<T> {
|
||||
schedule: &GlobalValidationSchedule,
|
||||
attested_candidates: &[AttestedCandidate],
|
||||
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.
|
||||
// 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::<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),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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<Call, SignedExtra>;
|
||||
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
sp_api::impl_runtime_apis! {
|
||||
impl sp_api::Core<Block> 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<primitives::DownwardMessage> {
|
||||
Parachains::downward_messages(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fg_primitives::GrandpaApi<Block> for Runtime {
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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<Runtime, Block, system::ChainContext<R
|
||||
/// The payload being signed in transactions.
|
||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
sp_api::impl_runtime_apis! {
|
||||
impl sp_api::Core<Block> 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<primitives::DownwardMessage> {
|
||||
Parachains::downward_messages(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fg_primitives::GrandpaApi<Block> for Runtime {
|
||||
|
||||
@@ -656,6 +656,9 @@ sp_api::impl_runtime_apis! {
|
||||
fn signing_context() -> SigningContext {
|
||||
Parachains::signing_context()
|
||||
}
|
||||
fn downward_messages(id: parachain::Id) -> Vec<primitives::DownwardMessage> {
|
||||
Parachains::downward_messages(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fg_primitives::GrandpaApi<Block> for Runtime {
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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<Runtime, Block, system::ChainContext<R
|
||||
/// The payload being signed in transactions.
|
||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||
|
||||
#[cfg(not(feature = "disable-runtime-api"))]
|
||||
sp_api::impl_runtime_apis! {
|
||||
impl sp_api::Core<Block> 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<primitives::DownwardMessage> {
|
||||
Parachains::downward_messages(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl fg_primitives::GrandpaApi<Block> for Runtime {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<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
|
||||
/// 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<UpwardMessage>,
|
||||
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<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.
|
||||
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(|_| "<non-unique>")
|
||||
.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<P>(
|
||||
#[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<UpwardMessage>) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,6 +704,9 @@ mod tests {
|
||||
fn signing_context() -> SigningContext {
|
||||
Default::default()
|
||||
}
|
||||
fn downward_messages(_: ParaId) -> Vec<polkadot_primitives::DownwardMessage> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user