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