From e3a5c33b842e79b3a0c711a5690727623c63c952 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 8 Sep 2020 13:05:04 +0200 Subject: [PATCH 1/7] DMP: data structures and plumbing --- core-primitives/src/lib.rs | 25 +++++++++---------- node/collation-generation/src/lib.rs | 2 ++ node/core/av-store/src/tests.rs | 1 + node/core/backing/src/lib.rs | 7 ++++++ node/core/candidate-selection/src/lib.rs | 2 ++ node/core/candidate-validation/src/lib.rs | 2 ++ .../availability-distribution/src/tests.rs | 1 + node/primitives/src/lib.rs | 2 ++ parachain/src/primitives.rs | 5 ++++ parachain/test-parachains/tests/adder/mod.rs | 3 +++ .../tests/wasm_executor/mod.rs | 3 +++ primitives/src/v1.rs | 13 +++++++++- runtime/parachains/src/util.rs | 2 ++ 13 files changed, 54 insertions(+), 14 deletions(-) diff --git a/core-primitives/src/lib.rs b/core-primitives/src/lib.rs index ffb346467d9e..d3f567f66a00 100644 --- a/core-primitives/src/lib.rs +++ b/core-primitives/src/lib.rs @@ -79,20 +79,19 @@ pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; /// 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. +/// A message sent from the relay-chain down to a parachain. +/// +/// The size of the message is limited by the `config.max_downward_message_size` parameter. +pub type DownwardMessage = sp_std::vec::Vec; + +/// A wrapped version of `DownwardMessage`. The difference is that it has attached the block number when +/// the message was sent. #[derive(codec::Encode, codec::Decode, Clone, sp_runtime::RuntimeDebug, PartialEq)] -pub enum DownwardMessage { - /// Some funds were transferred into the parachain's account. The hash is the identifier that - /// was given with the transfer. - TransferInto(AccountId, Balance, Remark), - /// An opaque blob of data. The relay chain must somehow know how to form this so that the - /// destination parachain does something sensible. - /// - /// NOTE: Be very careful not to allow users to place arbitrary size information in here. - Opaque(sp_std::vec::Vec), - /// XCMP message for the Parachain. - XCMPMessage(sp_std::vec::Vec), +pub struct InboundDownwardMessage { + /// The block number at which this messages was put into the downward message queue. + pub sent_at: BlockNumber, + /// The actual downward message to processes. + pub msg: DownwardMessage, } /// V1 primitives. diff --git a/node/collation-generation/src/lib.rs b/node/collation-generation/src/lib.rs index d51e5bb78b7b..f5cd7da28228 100644 --- a/node/collation-generation/src/lib.rs +++ b/node/collation-generation/src/lib.rs @@ -277,6 +277,7 @@ async fn handle_new_activations( new_validation_code: collation.new_validation_code, head_data: collation.head_data, erasure_root, + processed_downward_messages: collation.processed_downward_messages, }; let ccr = CandidateReceipt { @@ -387,6 +388,7 @@ mod tests { proof_of_validity: PoV { block_data: BlockData(Vec::new()), }, + processed_downward_messages: Default::default(), } } diff --git a/node/core/av-store/src/tests.rs b/node/core/av-store/src/tests.rs index 680eb74b930b..6c1a950f067a 100644 --- a/node/core/av-store/src/tests.rs +++ b/node/core/av-store/src/tests.rs @@ -70,6 +70,7 @@ impl Default for TestState { parent_head: HeadData(vec![7, 8, 9]), block_number: 5, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }; let pruning_config = PruningConfig { diff --git a/node/core/backing/src/lib.rs b/node/core/backing/src/lib.rs index 6c0979618160..ca17d26acfdb 100644 --- a/node/core/backing/src/lib.rs +++ b/node/core/backing/src/lib.rs @@ -682,6 +682,7 @@ impl CandidateBackingJob { erasure_root, new_validation_code: outputs.new_validation_code, head_data: outputs.head_data, + processed_downward_messages: outputs.processed_downward_messages, }; let res = match with_commitments(commitments) { @@ -977,12 +978,14 @@ mod tests { parent_head: HeadData(vec![7, 8, 9]), block_number: Default::default(), hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, transient: TransientValidationData { max_code_size: 1000, max_head_data_size: 1000, balance: Default::default(), code_upgrade_allowed: None, + dmq_length: 0, }, }; @@ -1159,6 +1162,7 @@ mod tests { upward_messages: Vec::new(), fees: Default::default(), new_validation_code: None, + processed_downward_messages: 0, }, test_state.validation_data.persisted), )).unwrap(); } @@ -1278,6 +1282,7 @@ mod tests { upward_messages: Vec::new(), fees: Default::default(), new_validation_code: None, + processed_downward_messages: 0, }, test_state.validation_data.persisted), )).unwrap(); } @@ -1416,6 +1421,7 @@ mod tests { upward_messages: Vec::new(), fees: Default::default(), new_validation_code: None, + processed_downward_messages: 0, }, test_state.validation_data.persisted), )).unwrap(); } @@ -1571,6 +1577,7 @@ mod tests { upward_messages: Vec::new(), fees: Default::default(), new_validation_code: None, + processed_downward_messages: 0, }, test_state.validation_data.persisted), )).unwrap(); } diff --git a/node/core/candidate-selection/src/lib.rs b/node/core/candidate-selection/src/lib.rs index 1e8cf4900221..e2270601cf4c 100644 --- a/node/core/candidate-selection/src/lib.rs +++ b/node/core/candidate-selection/src/lib.rs @@ -494,11 +494,13 @@ mod tests { upward_messages: Vec::new(), fees: 0, new_validation_code: None, + processed_downward_messages: 0, }, PersistedValidationData { parent_head: HeadData(parent_head_data), block_number: 123, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, ) } diff --git a/node/core/candidate-validation/src/lib.rs b/node/core/candidate-validation/src/lib.rs index 632e9b55774d..e749a42ffa9b 100644 --- a/node/core/candidate-validation/src/lib.rs +++ b/node/core/candidate-validation/src/lib.rs @@ -468,6 +468,7 @@ fn validate_candidate_exhaustive( parent_head: persisted_validation_data.parent_head.clone(), block_data: pov.block_data.clone(), relay_chain_height: persisted_validation_data.block_number, + dmq_mqc_head: persisted_validation_data.dmq_mqc_head, hrmp_mqc_heads: persisted_validation_data.hrmp_mqc_heads.clone(), }; @@ -491,6 +492,7 @@ fn validate_candidate_exhaustive( upward_messages: res.upward_messages, fees: 0, new_validation_code: res.new_validation_code, + processed_downward_messages: res.processed_downward_messages, }; Ok(ValidationResult::Valid(outputs, persisted_validation_data)) } diff --git a/node/network/availability-distribution/src/tests.rs b/node/network/availability-distribution/src/tests.rs index 2387c943cfa8..8012da546b09 100644 --- a/node/network/availability-distribution/src/tests.rs +++ b/node/network/availability-distribution/src/tests.rs @@ -217,6 +217,7 @@ impl Default for TestState { parent_head: HeadData(vec![7, 8, 9]), block_number: Default::default(), hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }; let validator_index = Some((validators.len() - 1) as ValidatorIndex); diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 4c2fe7715600..c8de99f4b89d 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -263,6 +263,8 @@ pub struct Collation { pub head_data: HeadData, /// Proof to verify the state transition of the parachain. pub proof_of_validity: PoV, + /// The number of messages processed from the DMQ. + pub processed_downward_messages: u32, } /// Configuration for the collation generator diff --git a/parachain/src/primitives.rs b/parachain/src/primitives.rs index 361ff2eff628..83edc4e0f2a6 100644 --- a/parachain/src/primitives.rs +++ b/parachain/src/primitives.rs @@ -248,6 +248,11 @@ pub struct ValidationParams { pub block_data: BlockData, /// The current relay-chain block number. pub relay_chain_height: RelayChainBlockNumber, + /// The MQC head for the DMQ. + /// + /// The DMQ MQC head will be used by the validation function to authorize the downward messages + /// passed by the collator. + pub dmq_mqc_head: Hash, /// The list of MQC heads for the inbound HRMP channels paired with the sender para ids. This /// vector is sorted ascending by the para id and doesn't contain multiple entries with the same /// sender. diff --git a/parachain/test-parachains/tests/adder/mod.rs b/parachain/test-parachains/tests/adder/mod.rs index 2d8d228c8696..b0d905988c19 100644 --- a/parachain/test-parachains/tests/adder/mod.rs +++ b/parachain/test-parachains/tests/adder/mod.rs @@ -97,6 +97,7 @@ fn execute_good_on_parent(execution_mode: ExecutionMode) { block_data: GenericBlockData(block_data.encode()), relay_chain_height: 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode, sp_core::testing::TaskExecutor::new(), @@ -135,6 +136,7 @@ fn execute_good_chain_on_parent() { block_data: GenericBlockData(block_data.encode()), relay_chain_height: number as RelayChainBlockNumber + 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode, sp_core::testing::TaskExecutor::new(), @@ -174,6 +176,7 @@ fn execute_bad_on_parent() { block_data: GenericBlockData(block_data.encode()), relay_chain_height: 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode, sp_core::testing::TaskExecutor::new(), diff --git a/parachain/test-parachains/tests/wasm_executor/mod.rs b/parachain/test-parachains/tests/wasm_executor/mod.rs index 600b25b9eedc..ad60e4e04e32 100644 --- a/parachain/test-parachains/tests/wasm_executor/mod.rs +++ b/parachain/test-parachains/tests/wasm_executor/mod.rs @@ -43,6 +43,7 @@ fn terminates_on_timeout() { parent_head: Default::default(), relay_chain_height: 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode, sp_core::testing::TaskExecutor::new(), @@ -71,6 +72,7 @@ fn parallel_execution() { parent_head: Default::default(), relay_chain_height: 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode, sp_core::testing::TaskExecutor::new(), @@ -82,6 +84,7 @@ fn parallel_execution() { parent_head: Default::default(), relay_chain_height: 1, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), }, &execution_mode2, sp_core::testing::TaskExecutor::new(), diff --git a/primitives/src/v1.rs b/primitives/src/v1.rs index b0118ba32593..d865e81b44d2 100644 --- a/primitives/src/v1.rs +++ b/primitives/src/v1.rs @@ -31,7 +31,7 @@ pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT}; pub use polkadot_core_primitives::v1::{ BlockNumber, Moment, Signature, AccountPublic, AccountId, AccountIndex, ChainId, Hash, Nonce, Balance, Header, Block, BlockId, UncheckedExtrinsic, - Remark, DownwardMessage, + Remark, DownwardMessage, InboundDownwardMessage, }; // Export some polkadot-parachain primitives @@ -266,6 +266,11 @@ pub struct PersistedValidationData { /// vector is sorted ascending by the para id and doesn't contain multiple entries with the same /// sender. pub hrmp_mqc_heads: Vec<(Id, Hash)>, + /// The MQC head for the DMQ. + /// + /// The DMQ MQC head will be used by the validation function to authorize the downward messages + /// passed by the collator. + pub dmq_mqc_head: Hash, } impl PersistedValidationData { @@ -301,6 +306,8 @@ pub struct TransientValidationData { /// which case the code upgrade should be applied at the end of the signaling /// block. pub code_upgrade_allowed: Option, + /// The number of messages pending of the downward message queue. + pub dmq_length: u32, } /// Outputs of validating a candidate. @@ -315,6 +322,8 @@ pub struct ValidationOutputs { pub fees: Balance, /// The new validation code submitted by the execution, if any. pub new_validation_code: Option, + /// The number of messages processed from the DMQ. + pub processed_downward_messages: u32, } /// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation. @@ -331,6 +340,8 @@ pub struct CandidateCommitments { pub new_validation_code: Option, /// The head-data produced as a result of execution. pub head_data: HeadData, + /// The number of messages processed from the DMQ. + pub processed_downward_messages: u32, } impl CandidateCommitments { diff --git a/runtime/parachains/src/util.rs b/runtime/parachains/src/util.rs index 028d65c5d957..dfb02e13e245 100644 --- a/runtime/parachains/src/util.rs +++ b/runtime/parachains/src/util.rs @@ -35,6 +35,7 @@ pub fn make_persisted_validation_data( parent_head: >::para_head(¶_id)?, block_number: relay_parent_number, hrmp_mqc_heads: Vec::new(), + dmq_mqc_head: Default::default(), // TODO: will be fixed in the following commits }) } @@ -67,5 +68,6 @@ pub fn make_transient_validation_data( max_head_data_size: config.max_head_data_size, balance: 0, code_upgrade_allowed, + dmq_length: 0, // TODO: will be fixed in the following commits }) } From 19806c1551603f4a8f8ee6f7c660664ac1f2bed6 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 8 Sep 2020 13:06:58 +0200 Subject: [PATCH 2/7] DMP: Implement DMP logic in the router module DMP: Integrate DMP parts into the inclusion module --- runtime/parachains/src/inclusion.rs | 21 ++- runtime/parachains/src/mock.rs | 2 - runtime/parachains/src/router.rs | 57 ++++++- runtime/parachains/src/router/dmp.rs | 212 +++++++++++++++++++++++++++ runtime/parachains/src/util.rs | 10 +- 5 files changed, 289 insertions(+), 13 deletions(-) create mode 100644 runtime/parachains/src/router/dmp.rs diff --git a/runtime/parachains/src/inclusion.rs b/runtime/parachains/src/inclusion.rs index d10d3475292a..2d11cbec588d 100644 --- a/runtime/parachains/src/inclusion.rs +++ b/runtime/parachains/src/inclusion.rs @@ -36,7 +36,7 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use sp_staking::SessionIndex; use sp_runtime::{DispatchError, traits::{One, Saturating}}; -use crate::{configuration, paras, scheduler::CoreAssignment}; +use crate::{configuration, paras, router, scheduler::CoreAssignment}; /// A bitfield signed by a validator indicating that it is keeping its piece of the erasure-coding /// for any backed candidates referred to by a `1` bit available. @@ -86,7 +86,7 @@ impl CandidatePendingAvailability { } pub trait Trait: - frame_system::Trait + paras::Trait + configuration::Trait + frame_system::Trait + paras::Trait + router::Trait + configuration::Trait { type Event: From> + Into<::Event>; } @@ -153,6 +153,8 @@ decl_error! { ValidationDataHashMismatch, /// Internal error only returned when compiled with debug assertions. InternalError, + /// The downward message queue is not processed correctly. + IncorrectDownwardMessageHandling, } } @@ -561,6 +563,12 @@ impl Module { ); } + // enact the messaging facet of the candidate. + weight += >::prune_dmq( + receipt.descriptor.para_id, + commitments.processed_downward_messages, + ); + Self::deposit_event( Event::::CandidateIncluded(plain, commitments.head_data.clone()) ); @@ -703,7 +711,14 @@ impl CandidateCheckContext { ); } - // TODO: messaging acceptance criteria rules will go here. + // check if the candidate passes the messaging acceptance criteria + ensure!( + >::check_processed_downward_messages( + para_id, + candidate.candidate.commitments.processed_downward_messages, + ), + Error::::IncorrectDownwardMessageHandling, + ); Ok(()) } diff --git a/runtime/parachains/src/mock.rs b/runtime/parachains/src/mock.rs index cf9f9eed411a..490c8083ad8f 100644 --- a/runtime/parachains/src/mock.rs +++ b/runtime/parachains/src/mock.rs @@ -128,8 +128,6 @@ pub type Configuration = crate::configuration::Module; pub type Paras = crate::paras::Module; /// Mocked router. -// TODO: Will be used in the follow ups. -#[allow(dead_code)] pub type Router = crate::router::Module; /// Mocked scheduler. diff --git a/runtime/parachains/src/router.rs b/runtime/parachains/src/router.rs index e066bbbafc8d..74b588ddcfb0 100644 --- a/runtime/parachains/src/router.rs +++ b/runtime/parachains/src/router.rs @@ -23,7 +23,9 @@ use crate::{configuration, initializer}; use sp_std::prelude::*; use frame_support::{decl_error, decl_module, decl_storage, weights::Weight}; -use primitives::v1::{Id as ParaId}; +use primitives::v1::{Id as ParaId, InboundDownwardMessage, Hash}; + +mod dmp; pub trait Trait: frame_system::Trait + configuration::Trait {} @@ -32,6 +34,23 @@ decl_storage! { /// Paras that are to be cleaned up at the end of the session. /// The entries are sorted ascending by the para id. OutgoingParas: Vec; + + /* + * Downward Message Passing (DMP) + * + * Storage layout required for implementation of DMP. + */ + + /// The downward messages addressed for a certain para. + DownwardMessageQueues: map hasher(twox_64_concat) ParaId => Vec>; + /// A mapping that stores the downward message queue MQC head for each para. + /// + /// Each link in this chain has a form: + /// `(prev_head, B, H(M))`, where + /// - `prev_head`: is the previous head hash or zero if none. + /// - `B`: is the relay-chain block number in which a message was appended. + /// - `H(M)`: is the hash of the message being appended. + DownwardMessageQueueHeads: map hasher(twox_64_concat) ParaId => Hash; } } @@ -60,8 +79,8 @@ impl Module { _notification: &initializer::SessionChangeNotification, ) { let outgoing = OutgoingParas::take(); - for _outgoing_para in outgoing { - + for outgoing_para in outgoing { + Self::clean_dmp_after_outgoing(outgoing_para); } } @@ -77,4 +96,36 @@ impl Module { #[cfg(test)] mod tests { + use super::*; + use primitives::v1::BlockNumber; + use frame_support::traits::{OnFinalize, OnInitialize}; + + use crate::mock::{System, Router, GenesisConfig as MockGenesisConfig}; + + pub(crate) fn run_to_block(to: BlockNumber, new_session: Option>) { + while System::block_number() < to { + let b = System::block_number(); + Router::initializer_finalize(); + System::on_finalize(b); + + System::on_initialize(b + 1); + System::set_block_number(b + 1); + + if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) { + Router::initializer_on_new_session(&Default::default()); + } + Router::initializer_initialize(b + 1); + } + } + + pub(crate) fn default_genesis_config() -> MockGenesisConfig { + MockGenesisConfig { + configuration: crate::configuration::GenesisConfig { + config: crate::configuration::HostConfiguration { + ..Default::default() + }, + }, + ..Default::default() + } + } } diff --git a/runtime/parachains/src/router/dmp.rs b/runtime/parachains/src/router/dmp.rs new file mode 100644 index 000000000000..ddd3c5aeb202 --- /dev/null +++ b/runtime/parachains/src/router/dmp.rs @@ -0,0 +1,212 @@ +// 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 . + +use super::{Trait, Module, Store}; +use frame_support::{StorageMap, weights::Weight, traits::Get}; +use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; +use primitives::v1::{Id as ParaId, DownwardMessage, InboundDownwardMessage, Hash}; + +/// Routines and getters related to downward message passing. +impl Module { + pub(crate) fn clean_dmp_after_outgoing(outgoing_para: ParaId) { + ::DownwardMessageQueues::remove(&outgoing_para); + ::DownwardMessageQueueHeads::remove(&outgoing_para); + } + + /// Enqueue a downward message to a specific recipient para. + pub fn queue_downward_message(para: ParaId, msg: DownwardMessage) { + let inbound = InboundDownwardMessage { + msg, + sent_at: >::block_number(), + }; + + // obtain the new link in the MQC and update the head. + ::DownwardMessageQueueHeads::mutate(para, |head| { + let new_head = + BlakeTwo256::hash_of(&(*head, inbound.sent_at, T::Hashing::hash_of(&inbound.msg))); + *head = new_head; + }); + + ::DownwardMessageQueues::mutate(para, |v| { + v.push(inbound); + }); + } + + /// Checks if the number of processed downward messages is valid, i.e.: + /// + /// - if there are pending messages then `processed_downward_messages` should be at least 1, + /// - `processed_downward_messages` should not be greater than the number of pending messages. + /// + /// Returns true if all checks have been passed. + pub(crate) fn check_processed_downward_messages( + para: ParaId, + processed_downward_messages: u32, + ) -> bool { + let dmq_length = Self::dmq_length(para); + + if dmq_length > 0 && processed_downward_messages == 0 { + return false; + } + if dmq_length < processed_downward_messages { + return false; + } + + true + } + + /// Prunes the specified number of messages from the downward message queue of the given para. + pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) -> Weight { + ::DownwardMessageQueues::mutate(para, |q| { + let processed_downward_messages = processed_downward_messages as usize; + if processed_downward_messages > q.len() { + // reaching this branch is unexpected due to the constraint established by + // `check_processed_downward_messages`. But better be safe than sorry. + q.clear(); + } else { + *q = q.split_off(processed_downward_messages); + } + }); + T::DbWeight::get().reads_writes(1, 1) + } + + /// Returns the Head of Message Queue Chain for the given para or `None` if there is none + /// associated with it. + pub(crate) fn dmq_mqc_head(para: ParaId) -> Hash { + ::DownwardMessageQueueHeads::get(¶) + } + + /// Returns the number of pending downward messages addressed to the given para. + /// + /// Returns 0 if the para doesn't have an associated downward message queue. + pub(crate) fn dmq_length(para: ParaId) -> u32 { + ::DownwardMessageQueues::decode_len(¶) + .unwrap_or(0) + .saturated_into::() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{Router, new_test_ext}; + use crate::router::{ + OutgoingParas, + tests::{ + default_genesis_config, + run_to_block, + }, + }; + use frame_support::StorageValue; + + #[test] + fn scheduled_cleanup_performed() { + let a = ParaId::from(1312); + let b = ParaId::from(228); + let c = ParaId::from(123); + + new_test_ext(default_genesis_config()).execute_with(|| { + run_to_block(1, None); + + // enqueue downward messages to A, B and C. + Router::queue_downward_message(a, vec![1, 2, 3]); + Router::queue_downward_message(b, vec![4, 5, 6]); + Router::queue_downward_message(c, vec![7, 8, 9]); + + Router::schedule_para_cleanup(a); + + // run to block without session change. + run_to_block(2, None); + + assert!(!::DownwardMessageQueues::get(&a).is_empty()); + assert!(!::DownwardMessageQueues::get(&b).is_empty()); + assert!(!::DownwardMessageQueues::get(&c).is_empty()); + + Router::schedule_para_cleanup(b); + + // run to block changing the session. + run_to_block(3, Some(vec![3])); + + assert!(::DownwardMessageQueues::get(&a).is_empty()); + assert!(::DownwardMessageQueues::get(&b).is_empty()); + assert!(!::DownwardMessageQueues::get(&c).is_empty()); + + // verify that the outgoing paras are emptied. + assert!(OutgoingParas::get().is_empty()) + }); + } + + #[test] + fn dmq_length_and_head_updated_properly() { + let a = ParaId::from(1312); + let b = ParaId::from(228); + + new_test_ext(default_genesis_config()).execute_with(|| { + assert_eq!(Router::dmq_length(a), 0); + assert_eq!(Router::dmq_length(b), 0); + + Router::queue_downward_message(a, vec![1, 2, 3]); + + assert_eq!(Router::dmq_length(a), 1); + assert_eq!(Router::dmq_length(b), 0); + assert!(!Router::dmq_mqc_head(a).is_zero()); + assert!(Router::dmq_mqc_head(b).is_zero()); + }); + } + + #[test] + fn check_processed_downward_messages() { + let a = ParaId::from(1312); + + new_test_ext(default_genesis_config()).execute_with(|| { + // processed_downward_messages=0 is allowed when the DMQ is empty. + assert!(Router::check_processed_downward_messages(a, 0)); + + Router::queue_downward_message(a, vec![1, 2, 3]); + Router::queue_downward_message(a, vec![4, 5, 6]); + Router::queue_downward_message(a, vec![7, 8, 9]); + + // 0 doesn't pass if the DMQ has msgs. + assert!(!Router::check_processed_downward_messages(a, 0)); + // a candidate can consume up to 3 messages + assert!(Router::check_processed_downward_messages(a, 1)); + assert!(Router::check_processed_downward_messages(a, 2)); + assert!(Router::check_processed_downward_messages(a, 3)); + // there is no 4 messages in the queue + assert!(!Router::check_processed_downward_messages(a, 4)); + }); + } + + #[test] + fn dmq_pruning() { + let a = ParaId::from(1312); + + new_test_ext(default_genesis_config()).execute_with(|| { + assert_eq!(Router::dmq_length(a), 0); + + Router::queue_downward_message(a, vec![1, 2, 3]); + Router::queue_downward_message(a, vec![4, 5, 6]); + Router::queue_downward_message(a, vec![7, 8, 9]); + assert_eq!(Router::dmq_length(a), 3); + + // pruning 0 elements shouldn't change anything. + Router::prune_dmq(a, 0); + assert_eq!(Router::dmq_length(a), 3); + + Router::prune_dmq(a, 2); + assert_eq!(Router::dmq_length(a), 1); + }); + } +} diff --git a/runtime/parachains/src/util.rs b/runtime/parachains/src/util.rs index dfb02e13e245..0f6becfd6005 100644 --- a/runtime/parachains/src/util.rs +++ b/runtime/parachains/src/util.rs @@ -21,12 +21,12 @@ use sp_runtime::traits::{One, Saturating}; use primitives::v1::{Id as ParaId, PersistedValidationData, TransientValidationData}; use sp_std::prelude::*; -use crate::{configuration, paras}; +use crate::{configuration, paras, router}; /// Make the persisted validation data for a particular parachain. /// /// This ties together the storage of several modules. -pub fn make_persisted_validation_data( +pub fn make_persisted_validation_data( para_id: ParaId, ) -> Option> { let relay_parent_number = >::block_number() - One::one(); @@ -35,14 +35,14 @@ pub fn make_persisted_validation_data( parent_head: >::para_head(¶_id)?, block_number: relay_parent_number, hrmp_mqc_heads: Vec::new(), - dmq_mqc_head: Default::default(), // TODO: will be fixed in the following commits + dmq_mqc_head: >::dmq_mqc_head(para_id), }) } /// Make the transient validation data for a particular parachain. /// /// This ties together the storage of several modules. -pub fn make_transient_validation_data( +pub fn make_transient_validation_data( para_id: ParaId, ) -> Option> { let config = >::config(); @@ -68,6 +68,6 @@ pub fn make_transient_validation_data( max_head_data_size: config.max_head_data_size, balance: 0, code_upgrade_allowed, - dmq_length: 0, // TODO: will be fixed in the following commits + dmq_length: >::dmq_length(para_id), }) } From 2410d90d1491c3cbe45efebf1780c73485426644 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Wed, 9 Sep 2020 11:55:18 +0200 Subject: [PATCH 3/7] DMP: Introduce the max size limit for the size of a downward message --- runtime/parachains/src/configuration.rs | 21 +++++++ runtime/parachains/src/router.rs | 8 ++- runtime/parachains/src/router/dmp.rs | 79 ++++++++++++++++++++----- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 9b51404fc06b..14a57ded184e 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -58,6 +58,13 @@ pub struct HostConfiguration { pub thread_availability_period: BlockNumber, /// The amount of blocks ahead to schedule parachains and parathreads. pub scheduling_lookahead: u32, + /// The maximum size of a message that can be put in a downward message queue. + /// + /// Since we require receiving at least one DMP message the obvious upper bound of the size is + /// the PoV size. Of course, there is a lot of other different things that a parachain may + /// decide to do with its PoV so this value in practice will be picked as a fraction of the PoV + /// size. + pub max_downward_message_size: u32, } pub trait Trait: frame_system::Trait { } @@ -190,6 +197,16 @@ decl_module! { }); Ok(()) } + + /// Set the critical downward message size. + #[weight = (1_000, DispatchClass::Operational)] + pub fn set_max_downward_message_size(origin, new: u32) -> DispatchResult { + ensure_root(origin)?; + Self::update_config_member(|config| { + sp_std::mem::replace(&mut config.max_downward_message_size, new) != new + }); + Ok(()) + } } } @@ -268,6 +285,7 @@ mod tests { chain_availability_period: 10, thread_availability_period: 8, scheduling_lookahead: 3, + max_downward_message_size: 2048, }; assert!(::PendingConfig::get().is_none()); @@ -305,6 +323,9 @@ mod tests { Configuration::set_scheduling_lookahead( Origin::root(), new_config.scheduling_lookahead, ).unwrap(); + Configuration::set_max_downward_message_size( + Origin::root(), new_config.max_downward_message_size, + ).unwrap(); assert_eq!(::PendingConfig::get(), Some(new_config)); }) diff --git a/runtime/parachains/src/router.rs b/runtime/parachains/src/router.rs index 74b588ddcfb0..ad12a33bc8c7 100644 --- a/runtime/parachains/src/router.rs +++ b/runtime/parachains/src/router.rs @@ -20,13 +20,18 @@ //! routing the messages at their destinations and informing the parachains about the incoming //! messages. -use crate::{configuration, initializer}; +use crate::{ + configuration, + initializer, +}; use sp_std::prelude::*; use frame_support::{decl_error, decl_module, decl_storage, weights::Weight}; use primitives::v1::{Id as ParaId, InboundDownwardMessage, Hash}; mod dmp; +pub use dmp::QueueDownwardMessageError; + pub trait Trait: frame_system::Trait + configuration::Trait {} decl_storage! { @@ -122,6 +127,7 @@ mod tests { MockGenesisConfig { configuration: crate::configuration::GenesisConfig { config: crate::configuration::HostConfiguration { + max_downward_message_size: 1024, ..Default::default() }, }, diff --git a/runtime/parachains/src/router/dmp.rs b/runtime/parachains/src/router/dmp.rs index ddd3c5aeb202..1cc1d172a375 100644 --- a/runtime/parachains/src/router/dmp.rs +++ b/runtime/parachains/src/router/dmp.rs @@ -15,10 +15,17 @@ // along with Polkadot. If not, see . use super::{Trait, Module, Store}; +use crate::configuration::HostConfiguration; use frame_support::{StorageMap, weights::Weight, traits::Get}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; use primitives::v1::{Id as ParaId, DownwardMessage, InboundDownwardMessage, Hash}; +#[cfg_attr(test, derive(Debug))] +pub enum QueueDownwardMessageError { + /// The message being sent exceeds the configured critical message size. + ExceedsCriticalMessageSize, +} + /// Routines and getters related to downward message passing. impl Module { pub(crate) fn clean_dmp_after_outgoing(outgoing_para: ParaId) { @@ -27,7 +34,19 @@ impl Module { } /// Enqueue a downward message to a specific recipient para. - pub fn queue_downward_message(para: ParaId, msg: DownwardMessage) { + /// + /// When encoded, the message should not exceed the `config.max_downward_message_size`. + /// Otherwise, the message won't be sent and `Err` will be returned. + pub fn queue_downward_message( + config: &HostConfiguration, + para: ParaId, + msg: DownwardMessage, + ) -> Result<(), QueueDownwardMessageError> { + let serialized_len = msg.len() as u32; + if serialized_len > config.max_downward_message_size { + return Err(QueueDownwardMessageError::ExceedsCriticalMessageSize); + } + let inbound = InboundDownwardMessage { msg, sent_at: >::block_number(), @@ -43,6 +62,8 @@ impl Module { ::DownwardMessageQueues::mutate(para, |v| { v.push(inbound); }); + + Ok(()) } /// Checks if the number of processed downward messages is valid, i.e.: @@ -101,15 +122,20 @@ impl Module { #[cfg(test)] mod tests { use super::*; - use crate::mock::{Router, new_test_ext}; + use crate::mock::{Configuration, Router, new_test_ext}; use crate::router::{ OutgoingParas, - tests::{ - default_genesis_config, - run_to_block, - }, + tests::{default_genesis_config, run_to_block}, }; use frame_support::StorageValue; + use codec::Encode; + + fn queue_downward_message( + para_id: ParaId, + msg: DownwardMessage, + ) -> Result<(), QueueDownwardMessageError> { + Router::queue_downward_message(&Configuration::config(), para_id, msg) + } #[test] fn scheduled_cleanup_performed() { @@ -121,9 +147,9 @@ mod tests { run_to_block(1, None); // enqueue downward messages to A, B and C. - Router::queue_downward_message(a, vec![1, 2, 3]); - Router::queue_downward_message(b, vec![4, 5, 6]); - Router::queue_downward_message(c, vec![7, 8, 9]); + queue_downward_message(a, vec![1, 2, 3]).unwrap(); + queue_downward_message(b, vec![4, 5, 6]).unwrap(); + queue_downward_message(c, vec![7, 8, 9]).unwrap(); Router::schedule_para_cleanup(a); @@ -157,7 +183,7 @@ mod tests { assert_eq!(Router::dmq_length(a), 0); assert_eq!(Router::dmq_length(b), 0); - Router::queue_downward_message(a, vec![1, 2, 3]); + queue_downward_message(a, vec![1, 2, 3]).unwrap(); assert_eq!(Router::dmq_length(a), 1); assert_eq!(Router::dmq_length(b), 0); @@ -174,9 +200,9 @@ mod tests { // processed_downward_messages=0 is allowed when the DMQ is empty. assert!(Router::check_processed_downward_messages(a, 0)); - Router::queue_downward_message(a, vec![1, 2, 3]); - Router::queue_downward_message(a, vec![4, 5, 6]); - Router::queue_downward_message(a, vec![7, 8, 9]); + queue_downward_message(a, vec![1, 2, 3]).unwrap(); + queue_downward_message(a, vec![4, 5, 6]).unwrap(); + queue_downward_message(a, vec![7, 8, 9]).unwrap(); // 0 doesn't pass if the DMQ has msgs. assert!(!Router::check_processed_downward_messages(a, 0)); @@ -196,9 +222,9 @@ mod tests { new_test_ext(default_genesis_config()).execute_with(|| { assert_eq!(Router::dmq_length(a), 0); - Router::queue_downward_message(a, vec![1, 2, 3]); - Router::queue_downward_message(a, vec![4, 5, 6]); - Router::queue_downward_message(a, vec![7, 8, 9]); + queue_downward_message(a, vec![1, 2, 3]).unwrap(); + queue_downward_message(a, vec![4, 5, 6]).unwrap(); + queue_downward_message(a, vec![7, 8, 9]).unwrap(); assert_eq!(Router::dmq_length(a), 3); // pruning 0 elements shouldn't change anything. @@ -209,4 +235,25 @@ mod tests { assert_eq!(Router::dmq_length(a), 1); }); } + + #[test] + fn queue_downward_message_critical() { + let a = ParaId::from(1312); + + let mut genesis = default_genesis_config(); + genesis.configuration.config.max_downward_message_size = 7; + + new_test_ext(genesis).execute_with(|| { + let smol = [0; 3].to_vec(); + let big = [0; 8].to_vec(); + + // still within limits + assert_eq!(smol.encode().len(), 4); + assert!(queue_downward_message(a, smol).is_ok()); + + // that's too big + assert_eq!(big.encode().len(), 9); + assert!(queue_downward_message(a, big).is_err()); + }); + } } From 0121c2e4b80ba27a86713c3198f3b2e8c4c877a1 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Wed, 14 Oct 2020 13:38:19 +0200 Subject: [PATCH 4/7] DMP: Runtime API for accessing inbound messages --- node/core/runtime-api/src/lib.rs | 61 ++++++++++++++++++- node/subsystem/src/messages.rs | 7 ++- primitives/src/v1.rs | 5 ++ runtime/kusama/src/lib.rs | 6 ++ runtime/parachains/src/inclusion.rs | 5 +- runtime/parachains/src/router/dmp.rs | 8 +++ runtime/parachains/src/runtime_api_impl/v1.rs | 10 ++- runtime/polkadot/src/lib.rs | 6 ++ runtime/rococo-v1/src/lib.rs | 6 ++ runtime/test-runtime/src/lib.rs | 6 ++ runtime/westend/src/lib.rs | 6 ++ 11 files changed, 122 insertions(+), 4 deletions(-) diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index 2f568673cd15..f3ba18992890 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -126,6 +126,7 @@ fn make_runtime_api_request( query!(candidate_pending_availability(para), sender), Request::CandidateEvents(sender) => query!(candidate_events(), sender), Request::ValidatorDiscovery(ids, sender) => query!(validator_discovery(ids), sender), + Request::DmqContents(id, sender) => query!(dmq_contents(id), sender), } } @@ -175,7 +176,7 @@ mod tests { use polkadot_primitives::v1::{ ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, PersistedValidationData, Id as ParaId, OccupiedCoreAssumption, ValidationData, SessionIndex, ValidationCode, - CommittedCandidateReceipt, CandidateEvent, AuthorityDiscoveryId, + CommittedCandidateReceipt, CandidateEvent, AuthorityDiscoveryId, InboundDownwardMessage, }; use polkadot_node_subsystem_test_helpers as test_helpers; use sp_core::testing::TaskExecutor; @@ -194,6 +195,7 @@ mod tests { validation_outputs_results: HashMap, candidate_pending_availability: HashMap, candidate_events: Vec, + dmq_contents: HashMap>, } impl ProvideRuntimeApi for MockRuntimeApi { @@ -282,6 +284,13 @@ mod tests { fn validator_discovery(ids: Vec) -> Vec> { vec![None; ids.len()] } + + fn dmq_contents( + &self, + recipient: ParaId, + ) -> Vec { + self.dmq_contents.get(&recipient).map(|q| q.clone()).unwrap_or_default() + } } } @@ -614,4 +623,54 @@ mod tests { futures::executor::block_on(future::join(subsystem_task, test_task)); } + + #[test] + fn requests_dmq_contents() { + let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new()); + let mut runtime_api = MockRuntimeApi::default(); + let relay_parent = [1; 32].into(); + let para_a = 5.into(); + let para_b = 6.into(); + + runtime_api.dmq_contents.insert(para_a, vec![]); + runtime_api.dmq_contents.insert( + para_b, + vec![InboundDownwardMessage { + sent_at: 228, + msg: b"Novus Ordo Seclorum".to_vec(), + }], + ); + + let subsystem = RuntimeApiSubsystem::new(runtime_api.clone(), Metrics(None)); + let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); + let test_task = async move { + let (tx, rx) = oneshot::channel(); + ctx_handle + .send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request(relay_parent, Request::DmqContents(para_a, tx)), + }) + .await; + assert_eq!(rx.await.unwrap().unwrap(), vec![]); + + let (tx, rx) = oneshot::channel(); + ctx_handle + .send(FromOverseer::Communication { + msg: RuntimeApiMessage::Request(relay_parent, Request::DmqContents(para_b, tx)), + }) + .await; + assert_eq!( + rx.await.unwrap().unwrap(), + vec![InboundDownwardMessage { + sent_at: 228, + msg: b"Novus Ordo Seclorum".to_vec(), + }] + ); + + ctx_handle + .send(FromOverseer::Signal(OverseerSignal::Conclude)) + .await; + }; + futures::executor::block_on(future::join(subsystem_task, test_task)); + } + } diff --git a/node/subsystem/src/messages.rs b/node/subsystem/src/messages.rs index c408dac3853e..e2268f7af010 100644 --- a/node/subsystem/src/messages.rs +++ b/node/subsystem/src/messages.rs @@ -37,7 +37,7 @@ use polkadot_primitives::v1::{ GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption, PersistedValidationData, PoV, SessionIndex, SignedAvailabilityBitfield, ValidationCode, ValidatorId, ValidationData, - ValidatorIndex, ValidatorSignature, + ValidatorIndex, ValidatorSignature, InboundDownwardMessage, }; use std::sync::Arc; @@ -430,6 +430,11 @@ pub enum RuntimeApiRequest { /// /// Returns `None` for validators not found in the current session. ValidatorDiscovery(Vec, RuntimeApiSender>>), + /// Get all the pending inbound messages in the downward message queue for a para. + DmqContents( + ParaId, + RuntimeApiSender>>, + ), } /// A message to the Runtime API subsystem. diff --git a/primitives/src/v1.rs b/primitives/src/v1.rs index d865e81b44d2..3fbb5957308a 100644 --- a/primitives/src/v1.rs +++ b/primitives/src/v1.rs @@ -723,6 +723,11 @@ sp_api::decl_runtime_apis! { /// We assume that every validator runs authority discovery, /// which would allow us to establish point-to-point connection to given validators. fn validator_discovery(validators: Vec) -> Vec>; + + /// Get all the pending inbound messages in the downward message queue for a para. + fn dmq_contents( + recipient: Id, + ) -> Vec>; } } diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index c17c6e8b17ea..128833103b36 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1134,6 +1134,12 @@ sp_api::impl_runtime_apis! { fn validator_discovery(_: Vec) -> Vec> { Vec::new() } + + fn dmq_contents( + _recipient: Id, + ) -> Vec> { + Vec::new() + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/runtime/parachains/src/inclusion.rs b/runtime/parachains/src/inclusion.rs index 2d11cbec588d..e5e831bcc38b 100644 --- a/runtime/parachains/src/inclusion.rs +++ b/runtime/parachains/src/inclusion.rs @@ -411,6 +411,7 @@ impl Module { para_id, &candidate.candidate.commitments.head_data, &candidate.candidate.commitments.new_validation_code, + candidate.candidate.commitments.processed_downward_messages, )?; for (i, assignment) in scheduled[skip..].iter().enumerate() { @@ -542,6 +543,7 @@ impl Module { para_id, &validation_outputs.head_data, &validation_outputs.new_validation_code, + validation_outputs.processed_downward_messages, ) } @@ -690,6 +692,7 @@ impl CandidateCheckContext { para_id: ParaId, head_data: &HeadData, new_validation_code: &Option, + processed_downward_messages: u32, ) -> Result<(), DispatchError> { ensure!( head_data.0.len() <= self.config.max_head_data_size as _, @@ -715,7 +718,7 @@ impl CandidateCheckContext { ensure!( >::check_processed_downward_messages( para_id, - candidate.candidate.commitments.processed_downward_messages, + processed_downward_messages, ), Error::::IncorrectDownwardMessageHandling, ); diff --git a/runtime/parachains/src/router/dmp.rs b/runtime/parachains/src/router/dmp.rs index 1cc1d172a375..2b7f165a2dbb 100644 --- a/runtime/parachains/src/router/dmp.rs +++ b/runtime/parachains/src/router/dmp.rs @@ -17,6 +17,7 @@ use super::{Trait, Module, Store}; use crate::configuration::HostConfiguration; use frame_support::{StorageMap, weights::Weight, traits::Get}; +use sp_std::prelude::*; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; use primitives::v1::{Id as ParaId, DownwardMessage, InboundDownwardMessage, Hash}; @@ -117,6 +118,13 @@ impl Module { .unwrap_or(0) .saturated_into::() } + + /// Returns the downward message queue contents for the given para. + /// + /// The most recent messages are the latest in the vector. + pub(crate) fn dmq_contents(recipient: ParaId) -> Vec> { + ::DownwardMessageQueues::get(&recipient) + } } #[cfg(test)] diff --git a/runtime/parachains/src/runtime_api_impl/v1.rs b/runtime/parachains/src/runtime_api_impl/v1.rs index e75e94e1e647..5eb6ac60261e 100644 --- a/runtime/parachains/src/runtime_api_impl/v1.rs +++ b/runtime/parachains/src/runtime_api_impl/v1.rs @@ -23,10 +23,11 @@ use primitives::v1::{ Id as ParaId, OccupiedCoreAssumption, SessionIndex, ValidationCode, CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex, GroupIndex, CandidateEvent, PersistedValidationData, AuthorityDiscoveryId, + InboundDownwardMessage, }; use sp_runtime::traits::Zero; use frame_support::debug; -use crate::{initializer, inclusion, scheduler, configuration, paras}; +use crate::{initializer, inclusion, scheduler, configuration, paras, router}; /// Implementation for the `validators` function of the runtime API. pub fn validators() -> Vec { @@ -299,3 +300,10 @@ where validator_index.and_then(|i| authorities.get(i).cloned()) }).collect() } + +/// Implementation for the `dmq_contents` function of the runtime API. +pub fn dmq_contents( + recipient: ParaId, +) -> Vec> { + >::dmq_contents(recipient) +} diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 4e1ad3aa62e4..5489b57b7d23 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1128,6 +1128,12 @@ sp_api::impl_runtime_apis! { fn validator_discovery(_: Vec) -> Vec> { Vec::new() } + + fn dmq_contents( + _recipient: Id, + ) -> Vec> { + Vec::new() + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/runtime/rococo-v1/src/lib.rs b/runtime/rococo-v1/src/lib.rs index 3c7f4fc32dc2..29af4d9479ab 100644 --- a/runtime/rococo-v1/src/lib.rs +++ b/runtime/rococo-v1/src/lib.rs @@ -224,6 +224,12 @@ sp_api::impl_runtime_apis! { fn validator_discovery(validators: Vec) -> Vec> { runtime_api_impl::validator_discovery::(validators) } + + fn dmq_contents( + recipient: Id, + ) -> Vec> { + runtime_api_impl::dmq_contents::(recipient) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 3656d3d728bd..ea73edcb6f24 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -656,6 +656,12 @@ sp_api::impl_runtime_apis! { fn validator_discovery(validators: Vec) -> Vec> { runtime_impl::validator_discovery::(validators) } + + fn dmq_contents( + recipient: ParaId, + ) -> Vec> { + runtime_impl::dmq_contents::(recipient) + } } impl fg_primitives::GrandpaApi for Runtime { diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 9646da4f8c31..42c744dce260 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -847,6 +847,12 @@ sp_api::impl_runtime_apis! { fn validator_discovery(_: Vec) -> Vec> { Vec::new() } + + fn dmq_contents( + _recipient: Id, + ) -> Vec> { + Vec::new() + } } impl fg_primitives::GrandpaApi for Runtime { From 44ef07eab64c0eecacf1b546572ee78d5d7ff273 Mon Sep 17 00:00:00 2001 From: Sergey Shulepov Date: Tue, 27 Oct 2020 15:10:23 +0100 Subject: [PATCH 5/7] OCD Small clean ups --- node/subsystem/src/messages.rs | 11 +++++++++-- runtime/kusama/src/lib.rs | 2 +- runtime/polkadot/src/lib.rs | 2 +- runtime/rococo-v1/src/lib.rs | 2 +- runtime/test-runtime/src/lib.rs | 2 +- runtime/westend/src/lib.rs | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/node/subsystem/src/messages.rs b/node/subsystem/src/messages.rs index e2268f7af010..425debfd122c 100644 --- a/node/subsystem/src/messages.rs +++ b/node/subsystem/src/messages.rs @@ -419,7 +419,11 @@ pub enum RuntimeApiRequest { /// Get the validation code for a para, taking the given `OccupiedCoreAssumption`, which /// will inform on how the validation data should be computed if the para currently /// occupies a core. - ValidationCode(ParaId, OccupiedCoreAssumption, RuntimeApiSender>), + ValidationCode( + ParaId, + OccupiedCoreAssumption, + RuntimeApiSender>, + ), /// Get a the candidate pending availability for a particular parachain by parachain / core index CandidatePendingAvailability(ParaId, RuntimeApiSender>), /// Get all events concerning candidates (backing, inclusion, time-out) in the parent of @@ -429,7 +433,10 @@ pub enum RuntimeApiRequest { /// Currently this request is limited to validators in the current session. /// /// Returns `None` for validators not found in the current session. - ValidatorDiscovery(Vec, RuntimeApiSender>>), + ValidatorDiscovery( + Vec, + RuntimeApiSender>>, + ), /// Get all the pending inbound messages in the downward message queue for a para. DmqContents( ParaId, diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 128833103b36..6d793161a8bb 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] +#![recursion_limit = "256"] use sp_std::prelude::*; use sp_core::u32_trait::{_1, _2, _3, _4, _5}; diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 5489b57b7d23..e2e0b1780ecc 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] +#![recursion_limit = "256"] use runtime_common::{ claims, SlowAdjustingFeeUpdate, CurrencyToVote, diff --git a/runtime/rococo-v1/src/lib.rs b/runtime/rococo-v1/src/lib.rs index 29af4d9479ab..4f6951ab8fb9 100644 --- a/runtime/rococo-v1/src/lib.rs +++ b/runtime/rococo-v1/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] +#![recursion_limit = "256"] use sp_std::prelude::*; use codec::Encode; diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index ea73edcb6f24..25e68f9e26ea 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] +#![recursion_limit = "256"] use rstd::prelude::*; use codec::Encode; diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 42c744dce260..c048d3d43c66 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] +#![recursion_limit = "256"] use sp_std::prelude::*; use codec::{Encode, Decode}; From fc87e24c471ee3593da770a161f75f9ab0c825de Mon Sep 17 00:00:00 2001 From: Sergey Shulepov Date: Tue, 27 Oct 2020 16:02:52 +0100 Subject: [PATCH 6/7] DMP: fix the naming of the error --- runtime/parachains/src/router/dmp.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/parachains/src/router/dmp.rs b/runtime/parachains/src/router/dmp.rs index 2b7f165a2dbb..1f735ed798e9 100644 --- a/runtime/parachains/src/router/dmp.rs +++ b/runtime/parachains/src/router/dmp.rs @@ -21,10 +21,11 @@ use sp_std::prelude::*; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; use primitives::v1::{Id as ParaId, DownwardMessage, InboundDownwardMessage, Hash}; +/// An error sending a downward message. #[cfg_attr(test, derive(Debug))] pub enum QueueDownwardMessageError { - /// The message being sent exceeds the configured critical message size. - ExceedsCriticalMessageSize, + /// The message being sent exceeds the configured max message size. + ExceedsMaxMessageSize, } /// Routines and getters related to downward message passing. @@ -45,7 +46,7 @@ impl Module { ) -> Result<(), QueueDownwardMessageError> { let serialized_len = msg.len() as u32; if serialized_len > config.max_downward_message_size { - return Err(QueueDownwardMessageError::ExceedsCriticalMessageSize); + return Err(QueueDownwardMessageError::ExceedsMaxMessageSize); } let inbound = InboundDownwardMessage { From f9a86f58bee36633520c65e91ab75a6323712129 Mon Sep 17 00:00:00 2001 From: Sergey Shulepov Date: Tue, 27 Oct 2020 16:07:46 +0100 Subject: [PATCH 7/7] DMP: add caution about a non-existent recipient --- runtime/parachains/src/router/dmp.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/parachains/src/router/dmp.rs b/runtime/parachains/src/router/dmp.rs index 1f735ed798e9..fa0d057c0165 100644 --- a/runtime/parachains/src/router/dmp.rs +++ b/runtime/parachains/src/router/dmp.rs @@ -39,6 +39,10 @@ impl Module { /// /// When encoded, the message should not exceed the `config.max_downward_message_size`. /// Otherwise, the message won't be sent and `Err` will be returned. + /// + /// It is possible to send a downward message to a non-existent para. That, however, would lead + /// to a dangling storage. If the caller cannot statically prove that the recipient exists + /// then the caller should perform a runtime check. pub fn queue_downward_message( config: &HostConfiguration, para: ParaId,