past-session validator discovery APIs (#2009)

* guide: fix formatting for SessionInfo module

* primitives: SessionInfo type

* punt on approval keys

* ah, revert the type alias

* session info runtime module skeleton

* update the guide

* runtime/configuration: sync with the guide

* runtime/configuration: setters for newly added fields

* runtime/configuration: set codec indexes

* runtime/configuration: update test

* primitives: fix SessionInfo definition

* runtime/session_info: initial impl

* runtime/session_info: use initializer for session handling (wip)

* runtime/session_info: mock authority discovery trait

* guide: update the initializer's order

* runtime/session_info: tests skeleton

* runtime/session_info: store n_delay_tranches in Configuration

* runtime/session_info: punt on approval keys

* runtime/session_info: add some basic tests

* Update primitives/src/v1.rs

* small fixes

* remove codec index annotation on structs

* fix off-by-one error

* validator_discovery: accept a session index

* runtime: replace validator_discovery api with session_info

* Update runtime/parachains/src/session_info.rs

Co-authored-by: Sergei Shulepov <sergei@parity.io>

* runtime/session_info: add a comment about missing entries

* runtime/session_info: define the keys

* util: expose connect_to_past_session_validators

* util: allow session_info requests for jobs

* runtime-api: add mock test for session_info

* collator-protocol: add session_index to test state

* util: fix error message for runtime error

* fix compilation

* fix tests after merge with master

Co-authored-by: Sergei Shulepov <sergei@parity.io>
This commit is contained in:
Andronik Ordian
2020-11-26 12:02:50 +01:00
committed by GitHub
parent 4ce744818c
commit 39a12b68f6
25 changed files with 696 additions and 213 deletions
+35 -7
View File
@@ -134,7 +134,7 @@ fn make_runtime_api_request<Client>(
Request::CandidatePendingAvailability(para, sender) =>
query!(candidate_pending_availability(para), sender),
Request::CandidateEvents(sender) => query!(candidate_events(), sender),
Request::ValidatorDiscovery(ids, sender) => query!(validator_discovery(ids), sender),
Request::SessionInfo(index, sender) => query!(session_info(index), sender),
Request::DmqContents(id, sender) => query!(dmq_contents(id), sender),
Request::InboundHrmpChannelsContents(id, sender) => query!(inbound_hrmp_channels_contents(id), sender),
}
@@ -201,8 +201,8 @@ mod tests {
use polkadot_primitives::v1::{
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, PersistedValidationData,
Id as ParaId, OccupiedCoreAssumption, ValidationData, SessionIndex, ValidationCode,
CommittedCandidateReceipt, CandidateEvent, AuthorityDiscoveryId, InboundDownwardMessage,
BlockNumber, InboundHrmpMessage,
CommittedCandidateReceipt, CandidateEvent, InboundDownwardMessage,
BlockNumber, InboundHrmpMessage, SessionInfo,
};
use polkadot_node_subsystem_test_helpers as test_helpers;
use sp_core::testing::TaskExecutor;
@@ -216,6 +216,7 @@ mod tests {
availability_cores: Vec<CoreState>,
validation_data: HashMap<ParaId, ValidationData>,
session_index_for_child: SessionIndex,
session_info: HashMap<SessionIndex, SessionInfo>,
validation_code: HashMap<ParaId, ValidationCode>,
historical_validation_code: HashMap<ParaId, Vec<(BlockNumber, ValidationCode)>>,
validation_outputs_results: HashMap<ParaId, bool>,
@@ -289,6 +290,10 @@ mod tests {
self.session_index_for_child.clone()
}
fn session_info(&self, index: SessionIndex) -> Option<SessionInfo> {
self.session_info.get(&index).cloned()
}
fn validation_code(
&self,
para: ParaId,
@@ -321,10 +326,6 @@ mod tests {
self.candidate_events.clone()
}
fn validator_discovery(ids: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
vec![None; ids.len()]
}
fn dmq_contents(
&self,
recipient: ParaId,
@@ -569,6 +570,33 @@ mod tests {
futures::executor::block_on(future::join(subsystem_task, test_task));
}
#[test]
fn requests_session_info() {
let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new());
let mut runtime_api = MockRuntimeApi::default();
let session_index = 1;
runtime_api.session_info.insert(session_index, Default::default());
let runtime_api = Arc::new(runtime_api);
let relay_parent = [1; 32].into();
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::SessionInfo(session_index, tx))
}).await;
assert_eq!(rx.await.unwrap().unwrap(), Some(Default::default()));
ctx_handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
};
futures::executor::block_on(future::join(subsystem_task, test_task));
}
#[test]
fn requests_validation_code() {
let (ctx, mut ctx_handle) = test_helpers::make_subsystem_context(TaskExecutor::new());
@@ -738,6 +738,7 @@ mod tests {
use polkadot_primitives::v1::{
BlockData, CandidateDescriptor, CollatorPair, ScheduledCore,
ValidatorIndex, GroupRotationInfo, AuthorityDiscoveryId,
SessionIndex, SessionInfo,
};
use polkadot_subsystem::{ActiveLeavesUpdate, messages::{RuntimeApiMessage, RuntimeApiRequest}};
use polkadot_node_subsystem_util::TimeoutExt;
@@ -776,6 +777,7 @@ mod tests {
relay_parent: Hash,
availability_core: CoreState,
our_collator_pair: CollatorPair,
session_index: SessionIndex,
}
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
@@ -832,6 +834,7 @@ mod tests {
relay_parent,
availability_core,
our_collator_pair,
session_index: 1,
}
}
}
@@ -841,6 +844,10 @@ mod tests {
&self.validator_groups.0[0]
}
fn current_session_index(&self) -> SessionIndex {
self.session_index
}
fn current_group_validator_peer_ids(&self) -> Vec<PeerId> {
self.current_group_validator_indices().iter().map(|i| self.validator_peer_id[*i as usize].clone()).collect()
}
@@ -870,20 +877,6 @@ mod tests {
.collect()
}
fn next_group_validator_ids(&self) -> Vec<ValidatorId> {
self.next_group_validator_indices()
.iter()
.map(|i| self.validator_public[*i as usize].clone())
.collect()
}
/// Returns the unique count of validators in the current and next group.
fn current_and_next_group_unique_validator_count(&self) -> usize {
let mut indices = self.next_group_validator_indices().iter().collect::<HashSet<_>>();
indices.extend(self.current_group_validator_indices());
indices.len()
}
/// Generate a new relay parent and inform the subsystem about the new view.
///
/// If `merge_views == true` it means the subsystem will be informed that we working on the old `relay_parent`
@@ -1090,20 +1083,33 @@ mod tests {
overseer_recv(virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::ValidatorDiscovery(validators, tx),
RuntimeApiRequest::SessionIndexForChild(tx),
)) => {
assert_eq!(relay_parent, test_state.relay_parent);
assert_eq!(validators.len(), test_state.current_and_next_group_unique_validator_count());
tx.send(Ok(test_state.current_session_index())).unwrap();
}
);
let current_validators = test_state.current_group_validator_ids();
let next_validators = test_state.next_group_validator_ids();
assert_matches!(
overseer_recv(virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::SessionInfo(index, tx),
)) => {
assert_eq!(relay_parent, test_state.relay_parent);
assert_eq!(index, test_state.current_session_index());
assert!(validators.iter().all(|v| current_validators.contains(&v) || next_validators.contains(&v)));
let validators = test_state.current_group_validator_ids();
let current_discovery_keys = test_state.current_group_validator_authority_ids();
let next_discovery_keys = test_state.next_group_validator_authority_ids();
let current_validators = test_state.current_group_validator_authority_ids();
let next_validators = test_state.next_group_validator_authority_ids();
let discovery_keys = [&current_discovery_keys[..], &next_discovery_keys[..]].concat();
tx.send(Ok(current_validators.into_iter().chain(next_validators).map(Some).collect())).unwrap();
tx.send(Ok(Some(SessionInfo {
validators,
discovery_keys,
..Default::default()
}))).unwrap();
}
);
@@ -60,16 +60,6 @@ enum Error {
Prometheus(#[from] prometheus::PrometheusError),
}
impl From<util::validator_discovery::Error> for Error {
fn from(me: util::validator_discovery::Error) -> Self {
match me {
util::validator_discovery::Error::Subsystem(s) => Error::Subsystem(s),
util::validator_discovery::Error::RuntimeApi(ra) => Error::RuntimeApi(ra),
util::validator_discovery::Error::Oneshot(c) => Error::Oneshot(c),
}
}
}
type Result<T> = std::result::Result<T, Error>;
/// What side of the collator protocol is being engaged
@@ -27,8 +27,6 @@ pub enum Error {
#[error(transparent)]
Runtime(#[from] polkadot_subsystem::errors::RuntimeApiError),
#[error(transparent)]
ValidatorDiscovery(#[from] polkadot_node_subsystem_util::validator_discovery::Error),
#[error(transparent)]
Util(#[from] polkadot_node_subsystem_util::Error),
}
@@ -11,7 +11,7 @@ use sp_keyring::Sr25519Keyring;
use polkadot_primitives::v1::{
AuthorityDiscoveryId, BlockData, CoreState, GroupRotationInfo, Id as ParaId,
ScheduledCore, ValidatorIndex,
ScheduledCore, ValidatorIndex, SessionIndex, SessionInfo,
};
use polkadot_subsystem::messages::{RuntimeApiMessage, RuntimeApiRequest};
use polkadot_node_subsystem_test_helpers as test_helpers;
@@ -37,8 +37,10 @@ fn validator_authority_id(val_ids: &[Sr25519Keyring]) -> Vec<AuthorityDiscoveryI
val_ids.iter().map(|v| v.public().into()).collect()
}
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>;
struct TestHarness {
virtual_overseer: test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>,
virtual_overseer: VirtualOverseer,
}
fn test_harness<T: Future<Output = ()>>(
@@ -75,7 +77,7 @@ fn test_harness<T: Future<Output = ()>>(
const TIMEOUT: Duration = Duration::from_millis(100);
async fn overseer_send(
overseer: &mut test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>,
overseer: &mut VirtualOverseer,
msg: PoVDistributionMessage,
) {
trace!("Sending message:\n{:?}", &msg);
@@ -87,7 +89,7 @@ async fn overseer_send(
}
async fn overseer_recv(
overseer: &mut test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>,
overseer: &mut VirtualOverseer,
) -> AllMessages {
let msg = overseer_recv_with_timeout(overseer, TIMEOUT)
.await
@@ -99,7 +101,7 @@ async fn overseer_recv(
}
async fn overseer_recv_with_timeout(
overseer: &mut test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>,
overseer: &mut VirtualOverseer,
timeout: Duration,
) -> Option<AllMessages> {
trace!("Waiting for message...");
@@ -110,7 +112,7 @@ async fn overseer_recv_with_timeout(
}
async fn overseer_signal(
overseer: &mut test_helpers::TestSubsystemContextHandle<PoVDistributionMessage>,
overseer: &mut VirtualOverseer,
signal: OverseerSignal,
) {
overseer
@@ -130,6 +132,7 @@ struct TestState {
validator_groups: (Vec<Vec<ValidatorIndex>>, GroupRotationInfo),
relay_parent: Hash,
availability_cores: Vec<CoreState>,
session_index: SessionIndex,
}
impl Default for TestState {
@@ -184,10 +187,56 @@ impl Default for TestState {
validator_groups,
relay_parent,
availability_cores,
session_index: 1,
}
}
}
async fn test_validator_discovery(
virtual_overseer: &mut VirtualOverseer,
expected_relay_parent: Hash,
session_index: SessionIndex,
validator_ids: &[ValidatorId],
discovery_ids: &[AuthorityDiscoveryId],
validator_group: &[ValidatorIndex],
) {
assert_matches!(
overseer_recv(virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::SessionIndexForChild(tx),
)) => {
assert_eq!(relay_parent, expected_relay_parent);
tx.send(Ok(session_index)).unwrap();
}
);
assert_matches!(
overseer_recv(virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::SessionInfo(index, tx),
)) => {
assert_eq!(relay_parent, expected_relay_parent);
assert_eq!(index, session_index);
let validators = validator_group.iter()
.map(|idx| validator_ids[*idx as usize].clone())
.collect();
let discovery_keys = validator_group.iter()
.map(|idx| discovery_ids[*idx as usize].clone())
.collect();
tx.send(Ok(Some(SessionInfo {
validators,
discovery_keys,
..Default::default()
}))).unwrap();
}
);
}
#[test]
fn ask_validators_for_povs() {
let test_state = TestState::default();
@@ -271,25 +320,14 @@ fn ask_validators_for_povs() {
}
);
// obtain the validator_id to authority_id mapping
assert_matches!(
overseer_recv(&mut virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::ValidatorDiscovery(validators, tx),
)) => {
assert_eq!(relay_parent, current);
assert_eq!(validators.len(), 3);
assert!(validators.iter().all(|v| test_state.validator_public.contains(&v)));
let result = vec![
Some(test_state.validator_authority_id[2].clone()),
Some(test_state.validator_authority_id[0].clone()),
Some(test_state.validator_authority_id[4].clone()),
];
tx.send(Ok(result)).unwrap();
}
);
test_validator_discovery(
&mut virtual_overseer,
current,
test_state.session_index,
&test_state.validator_public,
&test_state.validator_authority_id,
&test_state.validator_groups.0[0],
).await;
// We now should connect to our validator group.
assert_matches!(
@@ -448,24 +486,14 @@ fn ask_validators_for_povs() {
);
// obtain the validator_id to authority_id mapping
assert_matches!(
overseer_recv(&mut virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::ValidatorDiscovery(validators, tx),
)) => {
assert_eq!(relay_parent, next_leaf);
assert_eq!(validators.len(), 3);
assert!(validators.iter().all(|v| test_state.validator_public.contains(&v)));
let result = vec![
Some(test_state.validator_authority_id[2].clone()),
Some(test_state.validator_authority_id[0].clone()),
Some(test_state.validator_authority_id[4].clone()),
];
tx.send(Ok(result)).unwrap();
}
);
test_validator_discovery(
&mut virtual_overseer,
next_leaf,
test_state.session_index,
&test_state.validator_public,
&test_state.validator_authority_id,
&test_state.validator_groups.0[0],
).await;
// We now should connect to our validator group.
assert_matches!(
@@ -716,7 +744,7 @@ fn we_inform_peers_with_same_view_we_are_awaiting() {
RuntimeApiRequest::ValidatorGroups(tx)
)) => {
assert_eq!(relay_parent, hash_a);
tx.send(Ok(validator_groups)).unwrap();
tx.send(Ok(validator_groups.clone())).unwrap();
}
);
@@ -731,25 +759,14 @@ fn we_inform_peers_with_same_view_we_are_awaiting() {
}
);
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::ValidatorDiscovery(validators_res, tx),
)) => {
assert_eq!(relay_parent, hash_a);
assert_eq!(validators_res.len(), 3);
assert!(validators_res.iter().all(|v| validators.contains(&v)));
let result = vec![
Some(validator_authority_id[2].clone()),
Some(validator_authority_id[0].clone()),
Some(validator_authority_id[4].clone()),
];
tx.send(Ok(result)).unwrap();
}
);
test_validator_discovery(
&mut handle,
hash_a,
1,
&validators,
&validator_authority_id,
&validator_groups.0[0],
).await;
assert_matches!(
handle.recv().await,
+3
View File
@@ -37,6 +37,7 @@ use polkadot_primitives::v1::{
CandidateEvent, CommittedCandidateReceipt, CoreState, EncodeAs, PersistedValidationData,
GroupRotationInfo, Hash, Id as ParaId, ValidationData, OccupiedCoreAssumption,
SessionIndex, Signed, SigningContext, ValidationCode, ValidatorId, ValidatorIndex,
SessionInfo,
};
use sp_core::{
traits::SpawnNamed,
@@ -193,6 +194,7 @@ specialize_requests! {
fn request_validation_code(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode;
fn request_candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability;
fn request_candidate_events() -> Vec<CandidateEvent>; CandidateEvents;
fn request_session_info(index: SessionIndex) -> Option<SessionInfo>; SessionInfo;
}
/// Request some data from the `RuntimeApi` via a SubsystemContext.
@@ -274,6 +276,7 @@ specialize_requests_ctx! {
fn request_validation_code_ctx(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode;
fn request_candidate_pending_availability_ctx(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability;
fn request_candidate_events_ctx() -> Vec<CandidateEvent>; CandidateEvents;
fn request_session_info_ctx(index: SessionIndex) -> Option<SessionInfo>; SessionInfo;
}
/// From the given set of validators, find the first key we can sign with, if any.
@@ -20,34 +20,20 @@ use std::collections::HashMap;
use std::pin::Pin;
use futures::{
channel::{mpsc, oneshot},
channel::mpsc,
task::{Poll, self},
stream,
};
use streamunordered::{StreamUnordered, StreamYield};
use thiserror::Error;
use polkadot_node_subsystem::{
errors::RuntimeApiError, SubsystemError,
messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest, NetworkBridgeMessage},
errors::RuntimeApiError,
messages::{AllMessages, NetworkBridgeMessage},
SubsystemContext,
};
use polkadot_primitives::v1::{Hash, ValidatorId, AuthorityDiscoveryId};
use polkadot_primitives::v1::{Hash, ValidatorId, AuthorityDiscoveryId, SessionIndex};
use sc_network::PeerId;
/// Error when making a request to connect to validators.
#[derive(Debug, Error)]
pub enum Error {
/// Attempted to send or receive on a oneshot channel which had been canceled
#[error(transparent)]
Oneshot(#[from] oneshot::Canceled),
/// A subsystem error.
#[error(transparent)]
Subsystem(#[from] SubsystemError),
/// An error in the Runtime API.
#[error(transparent)]
RuntimeApi(#[from] RuntimeApiError),
}
use crate::Error;
/// Utility function to make it easier to connect to validators.
pub async fn connect_to_validators<Context: SubsystemContext>(
@@ -55,17 +41,42 @@ pub async fn connect_to_validators<Context: SubsystemContext>(
relay_parent: Hash,
validators: Vec<ValidatorId>,
) -> Result<ConnectionRequest, Error> {
// ValidatorId -> AuthorityDiscoveryId
let (tx, rx) = oneshot::channel();
let current_index = crate::request_session_index_for_child_ctx(relay_parent, ctx).await?.await??;
connect_to_past_session_validators(ctx, relay_parent, validators, current_index).await
}
ctx.send_message(AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::ValidatorDiscovery(validators.clone(), tx),
)
)).await;
/// Utility function to make it easier to connect to validators in the past sessions.
pub async fn connect_to_past_session_validators<Context: SubsystemContext>(
ctx: &mut Context,
relay_parent: Hash,
validators: Vec<ValidatorId>,
session_index: SessionIndex,
) -> Result<ConnectionRequest, Error> {
let session_info = crate::request_session_info_ctx(
relay_parent,
session_index,
ctx,
).await?.await??;
let (session_validators, discovery_keys) = match session_info {
Some(info) => (info.validators, info.discovery_keys),
None => return Err(RuntimeApiError::from(
format!("No SessionInfo found for the index {}", session_index)
).into()),
};
let id_to_index = session_validators.iter()
.zip(0usize..)
.collect::<HashMap<_, _>>();
// We assume the same ordering in authorities as in validators so we can do an index search
let maybe_authorities: Vec<_> = validators.iter()
.map(|id| {
let validator_index = id_to_index.get(&id);
validator_index.and_then(|i| discovery_keys.get(*i).cloned())
})
.collect();
let maybe_authorities = rx.await??;
let authorities: Vec<_> = maybe_authorities.iter()
.cloned()
.filter_map(|id| id)
+3 -9
View File
@@ -31,7 +31,7 @@ use polkadot_node_primitives::{
CollationGenerationConfig, MisbehaviorReport, SignedFullStatement, ValidationResult,
};
use polkadot_primitives::v1::{
AuthorityDiscoveryId, AvailableData, BackedCandidate, BlockNumber,
AuthorityDiscoveryId, AvailableData, BackedCandidate, BlockNumber, SessionInfo,
Header as BlockHeader, CandidateDescriptor, CandidateEvent, CandidateReceipt,
CollatorId, CommittedCandidateReceipt, CoreState, ErasureChunk,
GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption,
@@ -434,14 +434,8 @@ pub enum RuntimeApiRequest {
/// Get all events concerning candidates (backing, inclusion, time-out) in the parent of
/// the block in whose state this request is executed.
CandidateEvents(RuntimeApiSender<Vec<CandidateEvent>>),
/// Get the `AuthorityDiscoveryId`s corresponding to the given `ValidatorId`s.
/// Currently this request is limited to validators in the current session.
///
/// Returns `None` for validators not found in the current session.
ValidatorDiscovery(
Vec<ValidatorId>,
RuntimeApiSender<Vec<Option<AuthorityDiscoveryId>>>,
),
/// Get the session info for the given session, if stored.
SessionInfo(SessionIndex, RuntimeApiSender<Option<SessionInfo>>),
/// Get all the pending inbound messages in the downward message queue for a para.
DmqContents(
ParaId,
+61 -7
View File
@@ -25,6 +25,7 @@ use primitives::RuntimeDebug;
use runtime_primitives::traits::AppVerify;
use inherents::InherentIdentifier;
use sp_arithmetic::traits::{BaseArithmetic, Saturating, Zero};
use application_crypto::KeyTypeId;
pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT};
@@ -57,6 +58,34 @@ pub use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
/// Unique identifier for the Inclusion Inherent
pub const INCLUSION_INHERENT_IDENTIFIER: InherentIdentifier = *b"inclusn0";
/// The key type ID for a parachain approval voting key.
pub const APPROVAL_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"aprv");
mod approval_app {
use application_crypto::{app_crypto, sr25519};
app_crypto!(sr25519, super::APPROVAL_KEY_TYPE_ID);
}
/// The public key of a keypair used by a validator for approval voting
/// on included parachain candidates.
pub type ApprovalId = approval_app::Public;
/// The key type ID for parachain assignment key.
pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn");
// The public key of a keypair used by a validator for determining assignments
/// to approve included parachain candidates.
mod assigment_app {
use application_crypto::{app_crypto, sr25519};
app_crypto!(sr25519, super::ASSIGNMENT_KEY_TYPE_ID);
}
/// The public key of a keypair used by a validator for determining assignments
/// to approve included parachain candidates.
pub type AssignmentId = assigment_app::Public;
/// Get a collator signature payload on a relay-parent, block-data combo.
pub fn collator_signature_payload<H: AsRef<[u8]>>(
relay_parent: &H,
@@ -671,6 +700,35 @@ pub enum CandidateEvent<H = Hash> {
CandidateTimedOut(CandidateReceipt<H>, HeadData),
}
/// Information about validator sets of a session.
#[derive(Clone, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(PartialEq, Default))]
pub struct SessionInfo {
/// Validators in canonical ordering.
pub validators: Vec<ValidatorId>,
/// Validators' authority discovery keys for the session in canonical ordering.
pub discovery_keys: Vec<AuthorityDiscoveryId>,
/// The assignment and approval keys for validators.
pub approval_keys: Vec<(ApprovalId, AssignmentId)>,
/// Validators in shuffled ordering - these are the validator groups as produced
/// by the `Scheduler` module for the session and are typically referred to by
/// `GroupIndex`.
pub validator_groups: Vec<Vec<ValidatorIndex>>,
/// The number of availability cores used by the protocol during this session.
pub n_cores: u32,
/// The zeroth delay tranche width.
pub zeroth_delay_tranche_width: u32,
/// The number of samples we do of relay_vrf_modulo.
pub relay_vrf_modulo_samples: u32,
/// The number of delay tranches in total.
pub n_delay_tranches: u32,
/// How many slots (BABE / SASSAFRAS) must pass before an assignment is considered a
/// no-show.
pub no_show_slots: u32,
/// The number of validators needed to approve a block.
pub needed_approvals: u32,
}
sp_api::decl_runtime_apis! {
/// The API for querying the state of parachains on-chain.
pub trait ParachainHost<H: Decode = Hash, N: Encode + Decode = BlockNumber> {
@@ -710,6 +768,9 @@ sp_api::decl_runtime_apis! {
/// This can be used to instantiate a `SigningContext`.
fn session_index_for_child() -> SessionIndex;
/// Get the session info for the given session, if stored.
fn session_info(index: SessionIndex) -> Option<SessionInfo>;
/// Fetch the validation code used by a para, making the given `OccupiedCoreAssumption`.
///
/// Returns `None` if either the para is not registered or the assumption is `Freed`
@@ -735,13 +796,6 @@ sp_api::decl_runtime_apis! {
#[skip_initialize_block]
fn candidate_events() -> Vec<CandidateEvent<H>>;
/// Get the `AuthorityDiscoveryId`s corresponding to the given `ValidatorId`s.
/// Currently this request is limited to validators in the current session.
///
/// 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<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>>;
/// Get all the pending inbound messages in the downward message queue for a para.
fn dmq_contents(
recipient: Id,
@@ -23,7 +23,7 @@ The other parachains modules are initialized in this order:
1. Paras
1. Scheduler
1. Inclusion
1. Validity
1. SessionInfo
1. DMP
1. UMP
1. HRMP
@@ -30,11 +30,11 @@ struct SessionInfo {
// no-show.
no_show_slots: u32,
/// The number of validators needed to approve a block.
needed_approvals: u32,
needed_approvals: u32,
}
```
Storage Layout:
Storage Layout:
```rust
/// The earliest session for which previous session info is stored.
@@ -45,11 +45,10 @@ Sessions: map SessionIndex => Option<SessionInfo>,
## Session Change
1. Update the `CurrentSessionIndex`.
1. Update `EarliestStoredSession` based on `config.dispute_period` and remove all entries from `Sessions` from the previous value up to the new value.
1. Create a new entry in `Sessions` with information about the current session.
## Routines
* `earliest_stored_session() -> SessionIndex`: Yields the earliest session for which we have information stored.
* `session_info(session: SessionIndex) -> Option<SessionInfo>`: Yields the session info for the given session, if stored.
* `session_info(session: SessionIndex) -> Option<SessionInfo>`: Yields the session info for the given session, if stored.
@@ -424,14 +424,8 @@ enum RuntimeApiRequest {
Validators(ResponseChannel<Vec<ValidatorId>>),
/// Get the validator groups and rotation info.
ValidatorGroups(ResponseChannel<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo)>),
/// Get the session index for children of the block. This can be used to construct a signing
/// context.
SessionIndex(ResponseChannel<SessionIndex>),
/// Get the validation code for a specific para, using the given occupied core assumption.
ValidationCode(ParaId, OccupiedCoreAssumption, ResponseChannel<Option<ValidationCode>>),
/// Fetch the historical validation code used by a para for candidates executed in
/// the context of a given block height in the current chain.
HistoricalValidationCode(ParaId, BlockNumber, ResponseChannel<Option<ValidationCode>>),
/// Get information about all availability cores.
AvailabilityCores(ResponseChannel<Vec<CoreState>>),
/// with the given occupied core assumption.
PersistedValidationData(
ParaId,
@@ -450,12 +444,25 @@ enum RuntimeApiRequest {
CandidateCommitments,
RuntimeApiSender<bool>,
),
/// Get information about all availability cores.
AvailabilityCores(ResponseChannel<Vec<CoreState>>),
/// Get the session index for children of the block. This can be used to construct a signing
/// context.
SessionIndexForChild(ResponseChannel<SessionIndex>),
/// Get the validation code for a specific para, using the given occupied core assumption.
ValidationCode(ParaId, OccupiedCoreAssumption, ResponseChannel<Option<ValidationCode>>),
/// Fetch the historical validation code used by a para for candidates executed in
/// the context of a given block height in the current chain.
HistoricalValidationCode(ParaId, BlockNumber, ResponseChannel<Option<ValidationCode>>),
/// Get a committed candidate receipt for all candidates pending availability.
CandidatePendingAvailability(ParaId, ResponseChannel<Option<CommittedCandidateReceipt>>),
/// Get all events concerning candidates in the last block.
CandidateEvents(ResponseChannel<Vec<CandidateEvent>>),
/// Get the session info for the given session, if stored.
SessionInfo(SessionIndex, ResponseChannel<Option<SessionInfo>>),
/// Get all the pending inbound messages in the downward message queue for a para.
DmqContents(ParaId, ResponseChannel<Vec<InboundDownwardMessage<BlockNumber>>>),
/// Get the contents of all channels addressed to the given recipient. Channels that have no
/// messages in them are also included.
InboundHrmpChannelsContents(ParaId, ResponseChannel<BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumber>>>>),
}
enum RuntimeApiMessage {
@@ -12,6 +12,9 @@ struct HostConfiguration {
pub validation_upgrade_frequency: BlockNumber,
/// The delay, in blocks, before a validation upgrade is applied.
pub validation_upgrade_delay: BlockNumber,
/// The acceptance period, in blocks. This is the amount of blocks after availability that validators
/// and fishermen have to perform secondary checks or issue reports.
pub acceptance_period: BlockNumber,
/// The maximum validation code size, in bytes.
pub max_code_size: u32,
/// The maximum head-data size, in bytes.
@@ -37,6 +40,8 @@ struct HostConfiguration {
/// submitting an approval vote before a validator is considered a no-show.
/// Must be at least 1.
pub no_show_slots: u32,
/// The number of delay tranches in total.
pub n_delay_tranches: u32,
/// The width of the zeroth delay tranche for approval assignments. This many delay tranches
/// beyond 0 are all consolidated to form a wide 0 tranche.
pub zeroth_delay_tranche_width: u32,
+10 -2
View File
@@ -262,14 +262,14 @@ mod tests {
}, testing::{UintAuthorityId, TestXt}, Perbill, curve::PiecewiseLinear,
};
use primitives::v1::{
Balance, BlockNumber, Header, Signature,
Balance, BlockNumber, Header, Signature, AuthorityDiscoveryId,
};
use frame_support::{
traits::{Randomness, OnInitialize, OnFinalize},
impl_outer_origin, impl_outer_dispatch, assert_ok, parameter_types,
};
use keyring::Sr25519Keyring;
use runtime_parachains::{initializer, configuration, inclusion, scheduler, dmp, ump, hrmp};
use runtime_parachains::{initializer, configuration, inclusion, session_info, scheduler, dmp, ump, hrmp};
use pallet_session::OneSessionHandler;
impl_outer_origin! {
@@ -477,6 +477,14 @@ mod tests {
type Event = ();
}
impl session_info::AuthorityDiscoveryTrait for Test {
fn authorities() -> Vec<AuthorityDiscoveryId> {
Vec::new()
}
}
impl session_info::Trait for Test { }
pub struct TestRandomness;
impl Randomness<H256> for TestRandomness {
+5 -5
View File
@@ -29,7 +29,7 @@ use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CommittedCandidateReceipt,
CoreState, GroupRotationInfo, Hash, Id, Moment, Nonce, OccupiedCoreAssumption,
PersistedValidationData, Signature, ValidationCode, ValidationData, ValidatorId, ValidatorIndex,
InboundDownwardMessage, InboundHrmpMessage,
InboundDownwardMessage, InboundHrmpMessage, SessionInfo,
};
use runtime_common::{
claims, SlowAdjustingFeeUpdate, CurrencyToVote,
@@ -1092,6 +1092,10 @@ sp_api::impl_runtime_apis! {
0
}
fn session_info(_: SessionIndex) -> Option<SessionInfo> {
None
}
fn validation_code(_: Id, _: OccupiedCoreAssumption) -> Option<ValidationCode> {
None
}
@@ -1108,10 +1112,6 @@ sp_api::impl_runtime_apis! {
Vec::new()
}
fn validator_discovery(_: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
Vec::new()
}
fn dmq_contents(
_recipient: Id,
) -> Vec<InboundDownwardMessage<BlockNumber>> {
@@ -19,9 +19,10 @@
//! Configuration can change only at session boundaries and is buffered until then.
use sp_std::prelude::*;
use primitives::v1::{Balance, ValidatorId};
use primitives::v1::{Balance, ValidatorId, SessionIndex};
use frame_support::{
decl_storage, decl_module, decl_error,
ensure,
dispatch::DispatchResult,
weights::{DispatchClass, Weight},
};
@@ -60,6 +61,21 @@ pub struct HostConfiguration<BlockNumber> {
pub thread_availability_period: BlockNumber,
/// The amount of blocks ahead to schedule parachains and parathreads.
pub scheduling_lookahead: u32,
/// The amount of sessions to keep for disputes.
pub dispute_period: SessionIndex,
/// The amount of consensus slots that must pass between submitting an assignment and
/// submitting an approval vote before a validator is considered a no-show.
/// Must be at least 1.
pub no_show_slots: u32,
/// The number of delay tranches in total.
pub n_delay_tranches: u32,
/// The width of the zeroth delay tranche for approval assignments. This many delay tranches
/// beyond 0 are all consolidated to form a wide 0 tranche.
pub zeroth_delay_tranche_width: u32,
/// The number of validators needed to approve a block.
pub needed_approvals: u32,
/// The number of samples to do of the RelayVRFModulo approval assignment criterion.
pub relay_vrf_modulo_samples: u32,
/// Total number of individual messages allowed in the parachain -> relay-chain message queue.
pub max_upward_queue_count: u32,
/// Total size of messages allowed in the parachain -> relay-chain message queue before which
@@ -255,6 +271,68 @@ decl_module! {
Ok(())
}
/// Set the dispute period, in number of sessions to keep for disputes.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_dispute_period(origin, new: SessionIndex) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.dispute_period, new) != new
});
Ok(())
}
/// Set the no show slots, in number of number of consensus slots.
/// Must be at least 1.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_no_show_slots(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
ensure!(new >= 1, "no_show_slots must be at least 1");
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.no_show_slots, new) != new
});
Ok(())
}
/// Set the total number of delay tranches.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_n_delay_tranches(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.n_delay_tranches, new) != new
});
Ok(())
}
/// Set the zeroth delay tranche width.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_zeroth_delay_tranche_width(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.zeroth_delay_tranche_width, new) != new
});
Ok(())
}
/// Set the number of validators needed to approve a block.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_needed_approvals(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.needed_approvals, new) != new
});
Ok(())
}
/// Set the number of samples to do of the RelayVRFModulo approval assignment criterion.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_relay_vrf_modulo_samples(origin, new: u32) -> DispatchResult {
ensure_root(origin)?;
Self::update_config_member(|config| {
sp_std::mem::replace(&mut config.relay_vrf_modulo_samples, new) != new
});
Ok(())
}
/// Sets the maximum items that can present in a upward dispatch queue at once.
#[weight = (1_000, DispatchClass::Operational)]
pub fn set_max_upward_queue_count(origin, new: u32) -> DispatchResult {
@@ -504,6 +582,12 @@ mod tests {
chain_availability_period: 10,
thread_availability_period: 8,
scheduling_lookahead: 3,
dispute_period: 239,
no_show_slots: 240,
n_delay_tranches: 241,
zeroth_delay_tranche_width: 242,
needed_approvals: 242,
relay_vrf_modulo_samples: 243,
max_upward_queue_count: 1337,
max_upward_queue_size: 228,
max_downward_message_size: 2048,
@@ -561,6 +645,24 @@ mod tests {
Configuration::set_scheduling_lookahead(
Origin::root(), new_config.scheduling_lookahead,
).unwrap();
Configuration::set_dispute_period(
Origin::root(), new_config.dispute_period,
).unwrap();
Configuration::set_no_show_slots(
Origin::root(), new_config.no_show_slots,
).unwrap();
Configuration::set_n_delay_tranches(
Origin::root(), new_config.n_delay_tranches,
).unwrap();
Configuration::set_zeroth_delay_tranche_width(
Origin::root(), new_config.zeroth_delay_tranche_width,
).unwrap();
Configuration::set_needed_approvals(
Origin::root(), new_config.needed_approvals,
).unwrap();
Configuration::set_relay_vrf_modulo_samples(
Origin::root(), new_config.relay_vrf_modulo_samples,
).unwrap();
Configuration::set_max_upward_queue_count(
Origin::root(), new_config.max_upward_queue_count,
).unwrap();
@@ -29,7 +29,7 @@ use sp_runtime::traits::One;
use parity_scale_codec::{Encode, Decode};
use crate::{
configuration::{self, HostConfiguration},
paras, scheduler, inclusion, dmp, ump, hrmp,
paras, scheduler, inclusion, session_info, dmp, ump, hrmp,
};
/// Information about a session change that has just occurred.
@@ -63,6 +63,7 @@ pub trait Trait:
+ paras::Trait
+ scheduler::Trait
+ inclusion::Trait
+ session_info::Trait
+ dmp::Trait
+ ump::Trait
+ hrmp::Trait
@@ -123,6 +124,7 @@ decl_module! {
// - Paras
// - Scheduler
// - Inclusion
// - SessionInfo
// - Validity
// - DMP
// - UMP
@@ -131,6 +133,7 @@ decl_module! {
paras::Module::<T>::initializer_initialize(now) +
scheduler::Module::<T>::initializer_initialize(now) +
inclusion::Module::<T>::initializer_initialize(now) +
session_info::Module::<T>::initializer_initialize(now) +
dmp::Module::<T>::initializer_initialize(now) +
ump::Module::<T>::initializer_initialize(now) +
hrmp::Module::<T>::initializer_initialize(now);
@@ -146,6 +149,7 @@ decl_module! {
hrmp::Module::<T>::initializer_finalize();
ump::Module::<T>::initializer_finalize();
dmp::Module::<T>::initializer_finalize();
session_info::Module::<T>::initializer_finalize();
inclusion::Module::<T>::initializer_finalize();
scheduler::Module::<T>::initializer_finalize();
paras::Module::<T>::initializer_finalize();
@@ -189,6 +193,7 @@ impl<T: Trait> Module<T> {
paras::Module::<T>::initializer_on_new_session(&notification);
scheduler::Module::<T>::initializer_on_new_session(&notification);
inclusion::Module::<T>::initializer_on_new_session(&notification);
session_info::Module::<T>::initializer_on_new_session(&notification);
dmp::Module::<T>::initializer_on_new_session(&notification);
ump::Module::<T>::initializer_on_new_session(&notification);
hrmp::Module::<T>::initializer_on_new_session(&notification);
+1
View File
@@ -28,6 +28,7 @@ pub mod inclusion_inherent;
pub mod initializer;
pub mod paras;
pub mod scheduler;
pub mod session_info;
pub mod validity;
pub mod origin;
pub mod dmp;
+12 -1
View File
@@ -24,7 +24,7 @@ use sp_runtime::{
BlakeTwo256, IdentityLookup,
},
};
use primitives::v1::{BlockNumber, Header};
use primitives::v1::{AuthorityDiscoveryId, BlockNumber, Header};
use frame_support::{
impl_outer_origin, impl_outer_dispatch, impl_outer_event, parameter_types,
weights::Weight, traits::Randomness as RandomnessT,
@@ -124,6 +124,14 @@ impl crate::inclusion::Trait for Test {
type Event = TestEvent;
}
impl crate::session_info::Trait for Test { }
impl crate::session_info::AuthorityDiscoveryTrait for Test {
fn authorities() -> Vec<AuthorityDiscoveryId> {
Vec::new()
}
}
pub type System = frame_system::Module<Test>;
/// Mocked initializer.
@@ -150,6 +158,9 @@ pub type Scheduler = crate::scheduler::Module<Test>;
/// Mocked inclusion module.
pub type Inclusion = crate::inclusion::Module<Test>;
/// Mocked session info module.
pub type SessionInfo = crate::session_info::Module<Test>;
/// Create a new set of test externalities.
pub fn new_test_ext(state: GenesisConfig) -> TestExternalities {
let mut t = state.system.build_storage::<Test>().unwrap();
@@ -23,12 +23,12 @@ use primitives::v1::{
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, ValidationData,
Id as ParaId, OccupiedCoreAssumption, SessionIndex, ValidationCode,
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
GroupIndex, CandidateEvent, PersistedValidationData, AuthorityDiscoveryId,
GroupIndex, CandidateEvent, PersistedValidationData, SessionInfo,
InboundDownwardMessage, InboundHrmpMessage,
};
use sp_runtime::traits::Zero;
use frame_support::debug;
use crate::{initializer, inclusion, scheduler, configuration, paras, dmp, hrmp};
use crate::{initializer, inclusion, scheduler, configuration, paras, session_info, dmp, hrmp};
/// Implementation for the `validators` function of the runtime API.
pub fn validators<T: initializer::Trait>() -> Vec<ValidatorId> {
@@ -285,28 +285,9 @@ where
.collect()
}
/// Get the `AuthorityDiscoveryId`s corresponding to the given `ValidatorId`s.
/// Currently this request is limited to validators in the current session.
///
/// We assume that every validator runs authority discovery,
/// which would allow us to establish point-to-point connection to given validators.
// FIXME: handle previous sessions:
// https://github.com/paritytech/polkadot/issues/1461
pub fn validator_discovery<T>(validators: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>>
where
T: initializer::Trait + pallet_authority_discovery::Trait,
{
// FIXME: the mapping might be invalid if a session change happens in between the calls
// use SessionInfo from https://github.com/paritytech/polkadot/pull/1691
let current_validators = <inclusion::Module<T>>::validators();
let authorities = <pallet_authority_discovery::Module<T>>::authorities();
// We assume the same ordering in authorities as in validators so we can do an index search
validators.iter().map(|id| {
// FIXME: linear search is slow O(n^2)
// use SessionInfo from https://github.com/paritytech/polkadot/pull/1691
let validator_index = current_validators.iter().position(|v| v == id);
validator_index.and_then(|i| authorities.get(i).cloned())
}).collect()
/// Get the session info for the given session, if stored.
pub fn session_info<T: session_info::Trait>(index: SessionIndex) -> Option<SessionInfo> {
<session_info::Module<T>>::session_info(index)
}
/// Implementation for the `dmq_contents` function of the runtime API.
@@ -0,0 +1,256 @@
// 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/>.
//! The session info module provides information about validator sets
//! from prior sessions needed for approvals and disputes.
//!
//! See https://w3f.github.io/parachain-implementers-guide/runtime/session_info.html.
use primitives::v1::{AuthorityDiscoveryId, SessionIndex, SessionInfo};
use frame_support::{
decl_storage, decl_module, decl_error,
weights::Weight,
};
use crate::{configuration, paras, scheduler};
use sp_std::{cmp, vec::Vec};
pub trait Trait:
frame_system::Trait
+ configuration::Trait
+ paras::Trait
+ scheduler::Trait
+ AuthorityDiscoveryTrait
{
}
decl_storage! {
trait Store for Module<T: Trait> as ParaSessionInfo {
/// The earliest session for which previous session info is stored.
EarliestStoredSession get(fn earliest_stored_session): SessionIndex;
/// Session information in a rolling window.
/// Should have an entry in range `EarliestStoredSession..=CurrentSessionIndex`.
/// Does not have any entries before the session index in the first session change notification.
Sessions get(fn session_info): map hasher(identity) SessionIndex => Option<SessionInfo>;
}
}
decl_error! {
pub enum Error for Module<T: Trait> { }
}
decl_module! {
/// The session info module.
pub struct Module<T: Trait> for enum Call where origin: <T as frame_system::Trait>::Origin {
type Error = Error<T>;
}
}
/// An abstraction for the authority discovery pallet
/// to help with mock testing.
pub trait AuthorityDiscoveryTrait {
/// Retrieve authority identifiers of the current and next authority set.
fn authorities() -> Vec<AuthorityDiscoveryId>;
}
impl<T: pallet_authority_discovery::Trait> AuthorityDiscoveryTrait for T {
fn authorities() -> Vec<AuthorityDiscoveryId> {
<pallet_authority_discovery::Module<T>>::authorities()
}
}
impl<T: Trait> Module<T> {
/// Handle an incoming session change.
pub(crate) fn initializer_on_new_session(
notification: &crate::initializer::SessionChangeNotification<T::BlockNumber>
) {
let config = <configuration::Module<T>>::config();
let dispute_period = config.dispute_period;
let n_parachains = <paras::Module<T>>::parachains().len() as u32;
let validators = notification.validators.clone();
let discovery_keys = <T as AuthorityDiscoveryTrait>::authorities();
// FIXME: once we store these keys: https://github.com/paritytech/polkadot/issues/1975
let approval_keys = Default::default();
let validator_groups = <scheduler::Module<T>>::validator_groups();
let n_cores = n_parachains + config.parathread_cores;
let zeroth_delay_tranche_width = config.zeroth_delay_tranche_width;
let relay_vrf_modulo_samples = config.relay_vrf_modulo_samples;
let n_delay_tranches = config.n_delay_tranches;
let no_show_slots = config.no_show_slots;
let needed_approvals = config.needed_approvals;
let new_session_index = notification.session_index;
let old_earliest_stored_session = EarliestStoredSession::get();
let dispute_period = cmp::max(1, dispute_period);
let new_earliest_stored_session = new_session_index.checked_sub(dispute_period - 1).unwrap_or(0);
let new_earliest_stored_session = cmp::max(new_earliest_stored_session, old_earliest_stored_session);
// update `EarliestStoredSession` based on `config.dispute_period`
EarliestStoredSession::set(new_earliest_stored_session);
// remove all entries from `Sessions` from the previous value up to the new value
for idx in old_earliest_stored_session..new_earliest_stored_session {
Sessions::remove(&idx);
}
// create a new entry in `Sessions` with information about the current session
let new_session_info = SessionInfo {
validators,
discovery_keys,
approval_keys,
validator_groups,
n_cores,
zeroth_delay_tranche_width,
relay_vrf_modulo_samples,
n_delay_tranches,
no_show_slots,
needed_approvals,
};
Sessions::insert(&new_session_index, &new_session_info);
}
/// Called by the initializer to initialize the session info module.
pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight {
0
}
/// Called by the initializer to finalize the session info module.
pub(crate) fn initializer_finalize() {}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{
new_test_ext, Configuration, SessionInfo, System, GenesisConfig as MockGenesisConfig,
Origin,
};
use crate::initializer::SessionChangeNotification;
use crate::configuration::HostConfiguration;
use frame_support::traits::{OnFinalize, OnInitialize};
use primitives::v1::BlockNumber;
fn run_to_block(
to: BlockNumber,
new_session: impl Fn(BlockNumber) -> Option<SessionChangeNotification<BlockNumber>>,
) {
while System::block_number() < to {
let b = System::block_number();
SessionInfo::initializer_finalize();
Configuration::initializer_finalize();
System::on_finalize(b);
System::on_initialize(b + 1);
System::set_block_number(b + 1);
if let Some(notification) = new_session(b + 1) {
Configuration::initializer_on_new_session(&notification.validators, &notification.queued);
SessionInfo::initializer_on_new_session(&notification);
}
Configuration::initializer_initialize(b + 1);
SessionInfo::initializer_initialize(b + 1);
}
}
fn default_config() -> HostConfiguration<BlockNumber> {
HostConfiguration {
parathread_cores: 1,
dispute_period: 2,
needed_approvals: 3,
..Default::default()
}
}
fn genesis_config() -> MockGenesisConfig {
MockGenesisConfig {
configuration: configuration::GenesisConfig {
config: default_config(),
..Default::default()
},
..Default::default()
}
}
fn session_changes(n: BlockNumber) -> Option<SessionChangeNotification<BlockNumber>> {
match n {
100 => Some(SessionChangeNotification {
session_index: 10,
..Default::default()
}),
200 => Some(SessionChangeNotification {
session_index: 20,
..Default::default()
}),
300 => Some(SessionChangeNotification {
session_index: 30,
..Default::default()
}),
400 => Some(SessionChangeNotification {
session_index: 40,
..Default::default()
}),
_ => None,
}
}
fn new_session_every_block(n: BlockNumber) -> Option<SessionChangeNotification<BlockNumber>> {
Some(SessionChangeNotification{
session_index: n,
..Default::default()
})
}
#[test]
fn session_pruning_is_based_on_dispute_deriod() {
new_test_ext(genesis_config()).execute_with(|| {
run_to_block(100, session_changes);
assert_eq!(EarliestStoredSession::get(), 9);
// changing dispute_period works
let dispute_period = 5;
Configuration::set_dispute_period(Origin::root(), dispute_period).unwrap();
run_to_block(200, session_changes);
assert_eq!(EarliestStoredSession::get(), 20 - dispute_period + 1);
// we don't have that many sessions stored
let new_dispute_period = 16;
Configuration::set_dispute_period(Origin::root(), new_dispute_period).unwrap();
run_to_block(300, session_changes);
assert_eq!(EarliestStoredSession::get(), 20 - dispute_period + 1);
// now we do
run_to_block(400, session_changes);
assert_eq!(EarliestStoredSession::get(), 40 - new_dispute_period + 1);
})
}
#[test]
fn session_info_is_based_on_config() {
new_test_ext(genesis_config()).execute_with(|| {
run_to_block(1, new_session_every_block);
let session = Sessions::get(&1).unwrap();
assert_eq!(session.needed_approvals, 3);
// change some param
Configuration::set_needed_approvals(Origin::root(), 42).unwrap();
run_to_block(2, new_session_every_block);
let session = Sessions::get(&2).unwrap();
assert_eq!(session.needed_approvals, 42);
})
}
}
+5 -5
View File
@@ -37,7 +37,7 @@ use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CommittedCandidateReceipt,
CoreState, GroupRotationInfo, Hash, Id, Moment, Nonce, OccupiedCoreAssumption,
PersistedValidationData, Signature, ValidationCode, ValidationData, ValidatorId, ValidatorIndex,
InboundDownwardMessage, InboundHrmpMessage,
InboundDownwardMessage, InboundHrmpMessage, SessionInfo,
};
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys, ModuleId, ApplyExtrinsicResult,
@@ -1086,6 +1086,10 @@ sp_api::impl_runtime_apis! {
0
}
fn session_info(_: SessionIndex) -> Option<SessionInfo> {
None
}
fn validation_code(_: Id, _: OccupiedCoreAssumption) -> Option<ValidationCode> {
None
}
@@ -1102,10 +1106,6 @@ sp_api::impl_runtime_apis! {
Vec::new()
}
fn validator_discovery(_: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
Vec::new()
}
fn dmq_contents(
_recipient: Id,
) -> Vec<InboundDownwardMessage<BlockNumber>> {
+7 -3
View File
@@ -28,7 +28,7 @@ use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, Hash, Nonce, Signature, Moment,
GroupRotationInfo, CoreState, Id, ValidationData, ValidationCode, CandidateEvent,
ValidatorId, ValidatorIndex, CommittedCandidateReceipt, OccupiedCoreAssumption,
PersistedValidationData, InboundDownwardMessage, InboundHrmpMessage,
PersistedValidationData, InboundDownwardMessage, InboundHrmpMessage, SessionInfo,
};
use runtime_common::{
SlowAdjustingFeeUpdate,
@@ -72,6 +72,7 @@ use runtime_parachains::configuration as parachains_configuration;
use runtime_parachains::inclusion as parachains_inclusion;
use runtime_parachains::inclusion_inherent as parachains_inclusion_inherent;
use runtime_parachains::initializer as parachains_initializer;
use runtime_parachains::session_info as parachains_session_info;
use runtime_parachains::paras as parachains_paras;
use runtime_parachains::dmp as parachains_dmp;
use runtime_parachains::ump as parachains_ump;
@@ -536,6 +537,8 @@ impl parachains_paras::Trait for Runtime {
type Origin = Origin;
}
impl parachains_session_info::Trait for Runtime {}
impl parachains_ump::Trait for Runtime {
type UmpSink = (); // TODO: #1873 To be handled by the XCM receiver.
}
@@ -688,8 +691,9 @@ sp_api::impl_runtime_apis! {
}
})
}
fn validator_discovery(validators: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
runtime_api_impl::validator_discovery::<Runtime>(validators)
fn session_info(index: SessionIndex) -> Option<SessionInfo> {
runtime_api_impl::session_info::<Runtime>(index)
}
fn dmq_contents(recipient: Id) -> Vec<InboundDownwardMessage<BlockNumber>> {
+6 -3
View File
@@ -29,6 +29,7 @@ use polkadot_runtime_parachains::configuration as parachains_configuration;
use polkadot_runtime_parachains::inclusion as parachains_inclusion;
use polkadot_runtime_parachains::inclusion_inherent as parachains_inclusion_inherent;
use polkadot_runtime_parachains::initializer as parachains_initializer;
use polkadot_runtime_parachains::session_info as parachains_session_info;
use polkadot_runtime_parachains::paras as parachains_paras;
use polkadot_runtime_parachains::dmp as parachains_dmp;
use polkadot_runtime_parachains::ump as parachains_ump;
@@ -40,7 +41,7 @@ use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CommittedCandidateReceipt,
CoreState, GroupRotationInfo, Hash as HashT, Id as ParaId, Moment, Nonce, OccupiedCoreAssumption,
PersistedValidationData, Signature, ValidationCode, ValidationData, ValidatorId, ValidatorIndex,
InboundDownwardMessage, InboundHrmpMessage,
InboundDownwardMessage, InboundHrmpMessage, SessionInfo,
};
use runtime_common::{
claims, SlowAdjustingFeeUpdate, paras_sudo_wrapper,
@@ -457,6 +458,8 @@ impl parachains_initializer::Trait for Runtime {
type Randomness = RandomnessCollectiveFlip;
}
impl parachains_session_info::Trait for Runtime {}
impl parachains_paras::Trait for Runtime {
type Origin = Origin;
}
@@ -678,8 +681,8 @@ sp_api::impl_runtime_apis! {
runtime_impl::candidate_events::<Runtime, _>(|trait_event| trait_event.try_into().ok())
}
fn validator_discovery(validators: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
runtime_impl::validator_discovery::<Runtime>(validators)
fn session_info(index: SessionIndex) -> Option<SessionInfo> {
runtime_impl::session_info::<Runtime>(index)
}
fn dmq_contents(
+5 -5
View File
@@ -28,7 +28,7 @@ use primitives::v1::{
AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CommittedCandidateReceipt,
CoreState, GroupRotationInfo, Hash, Id, Moment, Nonce, OccupiedCoreAssumption,
PersistedValidationData, Signature, ValidationCode, ValidationData, ValidatorId, ValidatorIndex,
InboundDownwardMessage, InboundHrmpMessage,
InboundDownwardMessage, InboundHrmpMessage, SessionInfo,
};
use runtime_common::{
SlowAdjustingFeeUpdate, CurrencyToVote,
@@ -840,6 +840,10 @@ sp_api::impl_runtime_apis! {
0
}
fn session_info(_: SessionIndex) -> Option<SessionInfo> {
None
}
fn validation_code(_: Id, _: OccupiedCoreAssumption) -> Option<ValidationCode> {
None
}
@@ -852,10 +856,6 @@ sp_api::impl_runtime_apis! {
Vec::new()
}
fn validator_discovery(_: Vec<ValidatorId>) -> Vec<Option<AuthorityDiscoveryId>> {
Vec::new()
}
fn dmq_contents(
_recipient: Id,
) -> Vec<InboundDownwardMessage<BlockNumber>> {