Inclusion Module (#1242)

* add availability bitfield types to primitives

* begin inclusion module

* use GitHub issue link for limitation

* fix some compiler errors

* integrate validators into initializer

* add generic signing context

* make signing-context more generic

* fix issues with inclusion module

* add TODO

* guide: add validators and session index to inclusion

* guide: add session index to change notification

* implement session change logic

* add BackedCandidate type

* guide: refine inclusion pipeline

* guide: rename group_on to group_validators

* guide: add check about collator for parathread

* guide: add last_code_upgrade to paras and use in inclusion

* implement Paras::last_code_upgrade

* implement most checks in process_candidates

* make candidate receipt structs more generic

* make BackedCandidate struct more generic

* use hash param, not block number

* check that candidate is in context of the parent block

* include inclusion module in initializer

* implement enact-candidate

* check that only occupied cores have bits set

* finish implementing bitfield processing

* restructure consistency checks on candidates

* make some more primitives generic

* signature checking logic for backed candidates

* finish implementing process_candidates

* implement collect_pending

* add some trait implementations to primitives

* implement InclusionInherent and squash warnings

* test bitfield signing checks

* rename parachain head to para_head

* fix note_new_head bug in paras

* test bitfield enactment in inclusion

* helpers for candidate checks

* add test for most candidate checks

* add test for backing setting storage

* test session change logic

* remove extraneous type parameter

* remove some allow(unused)s

* extract threshold computation to const fn

* remove some more allow(unused)s

* improve doc

* add debug assertion

* fix primitive test compilation

* tag unanimous variant as unused
This commit is contained in:
Robert Habermeier
2020-06-18 19:38:07 -04:00
committed by GitHub
parent 7accc6e499
commit 879892d3f9
12 changed files with 1969 additions and 74 deletions
+133 -40
View File
@@ -176,20 +176,20 @@ pub struct DutyRoster {
/// These are global parameters that apply to all parachain candidates in a block. /// These are global parameters that apply to all parachain candidates in a block.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct GlobalValidationSchedule { pub struct GlobalValidationSchedule<N = BlockNumber> {
/// The maximum code size permitted, in bytes. /// The maximum code size permitted, in bytes.
pub max_code_size: u32, pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes. /// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32, pub max_head_data_size: u32,
/// The relay-chain block number this is in the context of. /// The relay-chain block number this is in the context of.
pub block_number: BlockNumber, pub block_number: N,
} }
/// Extra data that is needed along with the other fields in a `CandidateReceipt` /// Extra data that is needed along with the other fields in a `CandidateReceipt`
/// to fully validate the candidate. These fields are parachain-specific. /// to fully validate the candidate. These fields are parachain-specific.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct LocalValidationData { pub struct LocalValidationData<N = BlockNumber> {
/// The parent head-data. /// The parent head-data.
pub parent_head: HeadData, pub parent_head: HeadData,
/// The balance of the parachain at the moment of validation. /// The balance of the parachain at the moment of validation.
@@ -205,28 +205,28 @@ pub struct LocalValidationData {
/// height. This may be equal to the current perceived relay-chain block height, in /// height. This may be equal to the current perceived relay-chain block height, in
/// which case the code upgrade should be applied at the end of the signaling /// which case the code upgrade should be applied at the end of the signaling
/// block. /// block.
pub code_upgrade_allowed: Option<BlockNumber>, pub code_upgrade_allowed: Option<N>,
} }
/// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation. /// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct CandidateCommitments { pub struct CandidateCommitments<H = Hash> {
/// Fees paid from the chain to the relay chain validators. /// Fees paid from the chain to the relay chain validators.
pub fees: Balance, pub fees: Balance,
/// Messages destined to be interpreted by the Relay chain itself. /// Messages destined to be interpreted by the Relay chain itself.
pub upward_messages: Vec<UpwardMessage>, pub upward_messages: Vec<UpwardMessage>,
/// The root of a block's erasure encoding Merkle tree. /// The root of a block's erasure encoding Merkle tree.
pub erasure_root: Hash, pub erasure_root: H,
/// New validation code. /// New validation code.
pub new_validation_code: Option<ValidationCode>, pub new_validation_code: Option<ValidationCode>,
} }
/// Get a collator signature payload on a relay-parent, block-data combo. /// Get a collator signature payload on a relay-parent, block-data combo.
pub fn collator_signature_payload( pub fn collator_signature_payload<H: AsRef<[u8]>>(
relay_parent: &Hash, relay_parent: &H,
parachain_index: &Id, parachain_index: &Id,
pov_block_hash: &Hash, pov_block_hash: &H,
) -> [u8; 68] { ) -> [u8; 68] {
// 32-byte hash length is protected in a test below. // 32-byte hash length is protected in a test below.
let mut payload = [0u8; 68]; let mut payload = [0u8; 68];
@@ -238,10 +238,10 @@ pub fn collator_signature_payload(
payload payload
} }
fn check_collator_signature( fn check_collator_signature<H: AsRef<[u8]>>(
relay_parent: &Hash, relay_parent: &H,
parachain_index: &Id, parachain_index: &Id,
pov_block_hash: &Hash, pov_block_hash: &H,
collator: &CollatorId, collator: &CollatorId,
signature: &CollatorSignature, signature: &CollatorSignature,
) -> Result<(),()> { ) -> Result<(),()> {
@@ -258,12 +258,12 @@ fn check_collator_signature(
/// All data pertaining to the execution of a parachain candidate. /// All data pertaining to the execution of a parachain candidate.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct CandidateReceipt { pub struct CandidateReceipt<H = Hash, N = BlockNumber> {
/// The ID of the parachain this is a candidate for. /// The ID of the parachain this is a candidate for.
pub parachain_index: Id, pub parachain_index: Id,
/// The hash of the relay-chain block this should be executed in /// The hash of the relay-chain block this should be executed in
/// the context of. /// the context of.
pub relay_parent: Hash, pub relay_parent: H,
/// The head-data /// The head-data
pub head_data: HeadData, pub head_data: HeadData,
/// The collator's relay-chain account ID /// The collator's relay-chain account ID
@@ -271,16 +271,16 @@ pub struct CandidateReceipt {
/// Signature on blake2-256 of the block data by collator. /// Signature on blake2-256 of the block data by collator.
pub signature: CollatorSignature, pub signature: CollatorSignature,
/// The hash of the PoV-block. /// The hash of the PoV-block.
pub pov_block_hash: Hash, pub pov_block_hash: H,
/// The global validation schedule. /// The global validation schedule.
pub global_validation: GlobalValidationSchedule, pub global_validation: GlobalValidationSchedule<N>,
/// The local validation data. /// The local validation data.
pub local_validation: LocalValidationData, pub local_validation: LocalValidationData<N>,
/// Commitments made as a result of validation. /// Commitments made as a result of validation.
pub commitments: CandidateCommitments, pub commitments: CandidateCommitments<H>,
} }
impl CandidateReceipt { impl<H: AsRef<[u8]>, N> CandidateReceipt<H, N> {
/// Check integrity vs. provided block data. /// Check integrity vs. provided block data.
pub fn check_signature(&self) -> Result<(), ()> { pub fn check_signature(&self) -> Result<(), ()> {
check_collator_signature( check_collator_signature(
@@ -294,7 +294,7 @@ impl CandidateReceipt {
/// Abridge this `CandidateReceipt`, splitting it into an `AbridgedCandidateReceipt` /// Abridge this `CandidateReceipt`, splitting it into an `AbridgedCandidateReceipt`
/// and its omitted component. /// and its omitted component.
pub fn abridge(self) -> (AbridgedCandidateReceipt, OmittedValidationData) { pub fn abridge(self) -> (AbridgedCandidateReceipt<H>, OmittedValidationData<N>) {
let CandidateReceipt { let CandidateReceipt {
parachain_index, parachain_index,
relay_parent, relay_parent,
@@ -345,11 +345,11 @@ impl Ord for CandidateReceipt {
/// is necessary for validation of the parachain candidate. /// is necessary for validation of the parachain candidate.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct OmittedValidationData { pub struct OmittedValidationData<N = BlockNumber> {
/// The global validation schedule. /// The global validation schedule.
pub global_validation: GlobalValidationSchedule, pub global_validation: GlobalValidationSchedule<N>,
/// The local validation data. /// The local validation data.
pub local_validation: LocalValidationData, pub local_validation: LocalValidationData<N>,
} }
/// An abridged candidate-receipt. /// An abridged candidate-receipt.
@@ -359,14 +359,14 @@ pub struct OmittedValidationData {
/// be re-generated from relay-chain state. /// be re-generated from relay-chain state.
#[derive(PartialEq, Eq, Clone, Encode, Decode)] #[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))] #[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct AbridgedCandidateReceipt { pub struct AbridgedCandidateReceipt<H = Hash> {
/// The ID of the parachain this is a candidate for. /// The ID of the parachain this is a candidate for.
pub parachain_index: Id, pub parachain_index: Id,
/// The hash of the relay-chain block this should be executed in /// The hash of the relay-chain block this should be executed in
/// the context of. /// the context of.
// NOTE: the fact that the hash includes this value means that code depends // NOTE: the fact that the hash includes this value means that code depends
// on this for deduplication. Removing this field is likely to break things. // on this for deduplication. Removing this field is likely to break things.
pub relay_parent: Hash, pub relay_parent: H,
/// The head-data /// The head-data
pub head_data: HeadData, pub head_data: HeadData,
/// The collator's relay-chain account ID /// The collator's relay-chain account ID
@@ -374,12 +374,23 @@ pub struct AbridgedCandidateReceipt {
/// Signature on blake2-256 of the block data by collator. /// Signature on blake2-256 of the block data by collator.
pub signature: CollatorSignature, pub signature: CollatorSignature,
/// The hash of the pov-block. /// The hash of the pov-block.
pub pov_block_hash: Hash, pub pov_block_hash: H,
/// Commitments made as a result of validation. /// Commitments made as a result of validation.
pub commitments: CandidateCommitments, pub commitments: CandidateCommitments<H>,
}
impl<H: AsRef<[u8]> + Encode> AbridgedCandidateReceipt<H> {
/// Check integrity vs. provided block data.
pub fn check_signature(&self) -> Result<(), ()> {
check_collator_signature(
&self.relay_parent,
&self.parachain_index,
&self.pov_block_hash,
&self.collator,
&self.signature,
)
} }
impl AbridgedCandidateReceipt {
/// Compute the hash of the abridged candidate receipt. /// Compute the hash of the abridged candidate receipt.
/// ///
/// This is often used as the canonical hash of the receipt, rather than /// This is often used as the canonical hash of the receipt, rather than
@@ -391,7 +402,9 @@ impl AbridgedCandidateReceipt {
use runtime_primitives::traits::{BlakeTwo256, Hash}; use runtime_primitives::traits::{BlakeTwo256, Hash};
BlakeTwo256::hash_of(self) BlakeTwo256::hash_of(self)
} }
}
impl AbridgedCandidateReceipt {
/// Combine the abridged candidate receipt with the omitted data, /// Combine the abridged candidate receipt with the omitted data,
/// forming a full `CandidateReceipt`. /// forming a full `CandidateReceipt`.
pub fn complete(self, omitted: OmittedValidationData) -> CandidateReceipt { pub fn complete(self, omitted: OmittedValidationData) -> CandidateReceipt {
@@ -616,13 +629,42 @@ pub enum ValidityAttestation {
Explicit(ValidatorSignature), Explicit(ValidatorSignature),
} }
impl ValidityAttestation {
/// Get a reference to the signature.
pub fn signature(&self) -> &ValidatorSignature {
match *self {
ValidityAttestation::Implicit(ref sig) => sig,
ValidityAttestation::Explicit(ref sig) => sig,
}
}
/// Produce the underlying signed payload of the attestation, given the hash of the candidate,
/// which should be known in context.
pub fn signed_payload<H: Encode>(
&self,
candidate_hash: Hash,
signing_context: &SigningContext<H>,
) -> Vec<u8> {
match *self {
ValidityAttestation::Implicit(_) => (
Statement::Candidate(candidate_hash),
signing_context,
).encode(),
ValidityAttestation::Explicit(_) => (
Statement::Valid(candidate_hash),
signing_context,
).encode(),
}
}
}
/// A type returned by runtime with current session index and a parent hash. /// A type returned by runtime with current session index and a parent hash.
#[derive(Clone, Eq, PartialEq, Default, Decode, Encode, RuntimeDebug)] #[derive(Clone, Eq, PartialEq, Default, Decode, Encode, RuntimeDebug)]
pub struct SigningContext { pub struct SigningContext<H = Hash> {
/// Current session index. /// Current session index.
pub session_index: sp_staking::SessionIndex, pub session_index: sp_staking::SessionIndex,
/// Hash of the parent. /// Hash of the parent.
pub parent_hash: Hash, pub parent_hash: H,
} }
/// An attested candidate. This is submitted to the relay chain by a block author. /// An attested candidate. This is submitted to the relay chain by a block author.
@@ -683,9 +725,9 @@ impl From<BitVec<bitvec::order::Lsb0, u8>> for AvailabilityBitfield {
impl AvailabilityBitfield { impl AvailabilityBitfield {
/// Encodes the signing payload into the given buffer. /// Encodes the signing payload into the given buffer.
pub fn encode_signing_payload_into( pub fn encode_signing_payload_into<H: Encode>(
&self, &self,
signing_context: &SigningContext, signing_context: &SigningContext<H>,
buf: &mut Vec<u8>, buf: &mut Vec<u8>,
) { ) {
self.0.encode_to(buf); self.0.encode_to(buf);
@@ -693,10 +735,9 @@ impl AvailabilityBitfield {
} }
/// Encodes the signing payload into a fresh byte-vector. /// Encodes the signing payload into a fresh byte-vector.
pub fn encode_signing_payload( pub fn encode_signing_payload<H: Encode>(
&self, &self,
signing_context: signing_context: &SigningContext<H>,
&SigningContext,
) -> Vec<u8> { ) -> Vec<u8> {
let mut v = Vec::new(); let mut v = Vec::new();
self.encode_signing_payload_into(signing_context, &mut v); self.encode_signing_payload_into(signing_context, &mut v);
@@ -724,7 +765,7 @@ pub fn check_availability_bitfield_signature<H: Encode>(
bitfield: &AvailabilityBitfield, bitfield: &AvailabilityBitfield,
validator: &ValidatorId, validator: &ValidatorId,
signature: &ValidatorSignature, signature: &ValidatorSignature,
signing_context: &SigningContext, signing_context: &SigningContext<H>,
payload_encode_buf: Option<&mut Vec<u8>>, payload_encode_buf: Option<&mut Vec<u8>>,
) -> Result<(),()> { ) -> Result<(),()> {
use runtime_primitives::traits::AppVerify; use runtime_primitives::traits::AppVerify;
@@ -750,15 +791,67 @@ pub struct SignedAvailabilityBitfields(pub Vec<SignedAvailabilityBitfield>);
// After https://github.com/paritytech/polkadot/issues/1250 // After https://github.com/paritytech/polkadot/issues/1250
// they should be unified to this type. // they should be unified to this type.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct BackedCandidate { pub struct BackedCandidate<H = Hash> {
/// The candidate referred to. /// The candidate referred to.
pub candidate: AbridgedCandidateReceipt, pub candidate: AbridgedCandidateReceipt<H>,
/// The validity votes themselves, expressed as signatures. /// The validity votes themselves, expressed as signatures.
pub validity_votes: Vec<ValidityAttestation>, pub validity_votes: Vec<ValidityAttestation>,
/// The indices of the validators within the group, expressed as a bitfield. /// The indices of the validators within the group, expressed as a bitfield.
pub validator_indices: BitVec<bitvec::order::Lsb0, u8>, pub validator_indices: BitVec<bitvec::order::Lsb0, u8>,
} }
/// Verify the backing of the given candidate.
///
/// Provide a lookup from the index of a validator within the group assigned to this para,
/// as opposed to the index of the validator within the overall validator set, as well as
/// the number of validators in the group.
///
/// Also provide the signing context.
///
/// Returns either an error, indicating that one of the signatures was invalid or that the index
/// was out-of-bounds, or the number of signatures checked.
pub fn check_candidate_backing<H: AsRef<[u8]> + Encode>(
backed: &BackedCandidate<H>,
signing_context: &SigningContext<H>,
group_len: usize,
validator_lookup: impl Fn(usize) -> Option<ValidatorId>,
) -> Result<usize, ()> {
use runtime_primitives::traits::AppVerify;
if backed.validator_indices.len() != group_len {
return Err(())
}
if backed.validity_votes.len() > group_len {
return Err(())
}
// this is known, even in runtime, to be blake2-256.
let hash: Hash = backed.candidate.hash();
let mut signed = 0;
for ((val_in_group_idx, _), attestation) in backed.validator_indices.iter().enumerate()
.filter(|(_, signed)| **signed)
.zip(backed.validity_votes.iter())
{
let validator_id = validator_lookup(val_in_group_idx).ok_or(())?;
let payload = attestation.signed_payload(hash.clone(), signing_context);
let sig = attestation.signature();
if sig.verify(&payload[..], &validator_id) {
signed += 1;
} else {
return Err(())
}
}
if signed != backed.validity_votes.len() {
return Err(())
}
Ok(signed)
}
sp_api::decl_runtime_apis! { sp_api::decl_runtime_apis! {
/// The API for querying the state of parachains on-chain. /// The API for querying the state of parachains on-chain.
#[api_version(3)] #[api_version(3)]
@@ -811,9 +904,9 @@ mod tests {
assert_eq!(h.as_ref().len(), 32); assert_eq!(h.as_ref().len(), 32);
let _payload = collator_signature_payload( let _payload = collator_signature_payload(
&[1; 32].into(), &Hash::from([1; 32]),
&5u32.into(), &5u32.into(),
&[2; 32].into(), &Hash::from([2; 32]),
); );
} }
} }
@@ -48,6 +48,8 @@ struct SessionChangeNotification {
new_config: HostConfiguration, new_config: HostConfiguration,
// A secure randomn seed for the session, gathered from BABE. // A secure randomn seed for the session, gathered from BABE.
random_seed: [u8; 32], random_seed: [u8; 32],
// The session index of the beginning session.
session_index: SessionIndex,
} }
``` ```
@@ -28,6 +28,12 @@ Storage Layout:
bitfields: map ValidatorIndex => AvailabilityBitfield; bitfields: map ValidatorIndex => AvailabilityBitfield;
/// Candidates pending availability. /// Candidates pending availability.
PendingAvailability: map ParaId => CandidatePendingAvailability; PendingAvailability: map ParaId => CandidatePendingAvailability;
/// The current validators, by their parachain session keys.
Validators: Vec<ValidatorId>;
/// The current session index.
CurrentSessionIndex: SessionIndex;
``` ```
> TODO: `CandidateReceipt` and `AbridgedCandidateReceipt` can contain code upgrades which make them very large. the code entries should be split into a different storage map with infrequent access patterns > TODO: `CandidateReceipt` and `AbridgedCandidateReceipt` can contain code upgrades which make them very large. the code entries should be split into a different storage map with infrequent access patterns
@@ -36,6 +42,8 @@ PendingAvailability: map ParaId => CandidatePendingAvailability;
1. Clear out all candidates pending availability. 1. Clear out all candidates pending availability.
1. Clear out all validator bitfields. 1. Clear out all validator bitfields.
1. Update `Validators` with the validators from the session change notification.
1. Update `CurrentSessionIndex` with the session index from the session change notification.
## Routines ## Routines
@@ -50,11 +58,15 @@ All failed checks should lead to an unrecoverable error making the block invalid
1. For all now-available candidates, invoke the `enact_candidate` routine with the candidate and relay-parent number. 1. For all now-available candidates, invoke the `enact_candidate` routine with the candidate and relay-parent number.
1. > TODO: pass it onwards to `Validity` module. 1. > TODO: pass it onwards to `Validity` module.
1. Return a list of freed cores consisting of the cores where candidates have become available. 1. Return a list of freed cores consisting of the cores where candidates have become available.
* `process_candidates(BackedCandidates, scheduled: Vec<CoreAssignment>)`: * `process_candidates(BackedCandidates, scheduled: Vec<CoreAssignment>, group_validators: Fn(GroupIndex) -> Option<Vec<ValidatorIndex>>)`:
1. check that each candidate corresponds to a scheduled core and that they are ordered in ascending order by `ParaId`. 1. check that each candidate corresponds to a scheduled core and that they are ordered in the same order the cores appear in assignments in `scheduled`.
1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_frequency` of the currently scheduled upgrade, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID. 1. check that `scheduled` is sorted ascending by `CoreIndex`, without duplicates.
1. check the backing of the candidate using the signatures and the bitfields. 1. check that there is no candidate pending availability for any scheduled `ParaId`.
1. check that the upward messages are not exceeding `config.max_upward_queue_count` and `config.watermark_upward_queue_size` parameters. 1. If the core assignment includes a specific collator, ensure the backed candidate is issued by that collator.
1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_frequency` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID.
1. Check the collator's signature on the pov block.
1. check the backing of the candidate using the signatures and the bitfields, comparing against the validators assigned to the groups, fetched with the `group_validators` lookup.
1. check that the upward messages, when combined with the existing queue size, are not exceeding `config.max_upward_queue_count` and `config.watermark_upward_queue_size` parameters.
1. create an entry in the `PendingAvailability` map for each backed candidate with a blank `availability_votes` bitfield. 1. create an entry in the `PendingAvailability` map for each backed candidate with a blank `availability_votes` bitfield.
1. Return a `Vec<CoreIndex>` of all scheduled cores of the list of passed assignments that a candidate was successfully backed for, sorted ascending by CoreIndex. 1. Return a `Vec<CoreIndex>` of all scheduled cores of the list of passed assignments that a candidate was successfully backed for, sorted ascending by CoreIndex.
* `enact_candidate(relay_parent_number: BlockNumber, AbridgedCandidateReceipt)`: * `enact_candidate(relay_parent_number: BlockNumber, AbridgedCandidateReceipt)`:
@@ -16,9 +16,10 @@ Included: Option<()>,
## Entry Points ## Entry Points
* `inclusion`: This entry-point accepts two parameters: [`Bitfields`](../types/availability.html#signed-availability-bitfield) and [`BackedCandidates`](../types/backing.html#backed-candidate). * `inclusion`: This entry-point accepts two parameters: [`Bitfields`](../types/availability.html#signed-availability-bitfield) and [`BackedCandidates`](../type-definitions.html#backed-candidate).
1. The `Bitfields` are first forwarded to the `process_bitfields` routine, returning a set of freed cores. Provide a `Scheduler::core_para` as a core-lookup to the `process_bitfields` routine. Annotate each of these freed cores with `FreedReason::Concluded`. 1. The `Bitfields` are first forwarded to the `Inclusion::process_bitfields` routine, returning a set of freed cores. Provide a `Scheduler::core_para` as a core-lookup to the `process_bitfields` routine. Annotate each of these freed cores with `FreedReason::Concluded`.
1. If `Scheduler::availability_timeout_predicate` is `Some`, invoke `Inclusion::collect_pending` using it, and add timed-out cores to the free cores, annotated with `FreedReason::TimedOut`. 1. If `Scheduler::availability_timeout_predicate` is `Some`, invoke `Inclusion::collect_pending` using it, and add timed-out cores to the free cores, annotated with `FreedReason::TimedOut`.
1. Invoke `Scheduler::schedule(freed)` 1. Invoke `Scheduler::schedule(freed)`
1. Call `Scheduler::occupied` for all scheduled cores where a backed candidate was submitted. 1. Invoke the `Inclusion::process_candidates` routine with the parameters `(backed_candidates, Scheduler::scheduled(), Scheduler::group_validators)`.
1. Call `Scheduler::occupied` using the return value of the `Inclusion::process_candidates` call above, first sorting the list of assigned core indices.
1. If all of the above succeeds, set `Included` to `Some(())`. 1. If all of the above succeeds, set `Included` to `Some(())`.
@@ -111,6 +111,8 @@ OutgoingParas: Vec<ParaId>;
* `validation_code_at(ParaId, at: BlockNumber, assume_intermediate: Option<BlockNumber>)`: Fetches the validation code to be used when validating a block in the context of the given relay-chain height. A second block number parameter may be used to tell the lookup to proceed as if an intermediate parablock has been included at the given relay-chain height. This may return past, current, or (with certain choices of `assume_intermediate`) future code. `assume_intermediate`, if provided, must be before `at`. If the validation code has been pruned, this will return `None`. * `validation_code_at(ParaId, at: BlockNumber, assume_intermediate: Option<BlockNumber>)`: Fetches the validation code to be used when validating a block in the context of the given relay-chain height. A second block number parameter may be used to tell the lookup to proceed as if an intermediate parablock has been included at the given relay-chain height. This may return past, current, or (with certain choices of `assume_intermediate`) future code. `assume_intermediate`, if provided, must be before `at`. If the validation code has been pruned, this will return `None`.
* `is_parathread(ParaId) -> bool`: Returns true if the para ID references any live parathread. * `is_parathread(ParaId) -> bool`: Returns true if the para ID references any live parathread.
* `last_code_upgrade(id: ParaId, include_future: bool) -> Option<BlockNumber>`: The block number of the last scheduled upgrade of the requested para. Includes future upgrades if the flag is set. This is the `expected_at` number, not the `activated_at` number.
## Finalization ## Finalization
No finalization routine runs for this module. No finalization routine runs for this module.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
// 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/>.
//! Provides glue code over the scheduler and inclusion modules, and accepting
//! one inherent per block that can include new para candidates and bitfields.
//!
//! Unlike other modules in this crate, it does not need to be initialized by the initializer,
//! as it has no initialization logic and its finalization logic depends only on the details of
//! this module.
use sp_std::prelude::*;
use primitives::{
parachain::{BackedCandidate, SignedAvailabilityBitfields},
};
use frame_support::{
decl_storage, decl_module, decl_error, ensure,
dispatch::DispatchResult,
weights::{DispatchClass, Weight},
traits::Get,
};
use system::ensure_none;
use crate::{inclusion, scheduler::{self, FreedReason}};
pub trait Trait: inclusion::Trait + scheduler::Trait { }
decl_storage! {
trait Store for Module<T: Trait> as ParaInclusionInherent {
/// Whether the inclusion inherent was included within this block.
///
/// The `Option<()>` is effectively a bool, but it never hits storage in the `None` variant
/// due to the guarantees of FRAME's storage APIs.
///
/// If this is `None` at the end of the block, we panic and render the block invalid.
Included: Option<()>;
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
/// Inclusion inherent called more than once per block.
TooManyInclusionInherents,
}
}
decl_module! {
/// The inclusion inherent module.
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
type Error = Error<T>;
fn on_initialize() -> Weight {
T::DbWeight::get().reads_writes(1, 1) // in on_finalize.
}
fn on_finalize() {
if Included::take().is_none() {
panic!("Bitfields and heads must be included every block");
}
}
/// Include backed candidates and bitfields.
#[weight = (1_000_000_000, DispatchClass::Mandatory)]
pub fn inclusion(
origin,
signed_bitfields: SignedAvailabilityBitfields,
backed_candidates: Vec<BackedCandidate<T::Hash>>,
) -> DispatchResult {
ensure_none(origin)?;
ensure!(!<Included>::exists(), Error::<T>::TooManyInclusionInherents);
// Process new availability bitfields, yielding any availability cores whose
// work has now concluded.
let freed_concluded = <inclusion::Module<T>>::process_bitfields(
signed_bitfields,
<scheduler::Module<T>>::core_para,
)?;
// Handle timeouts for any availability core work.
let availability_pred = <scheduler::Module<T>>::availability_timeout_predicate();
let freed_timeout = if let Some(pred) = availability_pred {
<inclusion::Module<T>>::collect_pending(pred)
} else {
Vec::new()
};
// Schedule paras again, given freed cores, and reasons for freeing.
let freed = freed_concluded.into_iter().map(|c| (c, FreedReason::Concluded))
.chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut)));
<scheduler::Module<T>>::schedule(freed.collect());
// Process backed candidates according to scheduled cores.
let occupied = <inclusion::Module<T>>::process_candidates(
backed_candidates,
<scheduler::Module<T>>::scheduled(),
<scheduler::Module<T>>::group_validators,
)?;
// Note which of the scheduled cores were actually occupied by a backed candidate.
<scheduler::Module<T>>::occupied(&occupied);
// And track that we've finished processing the inherent for this block.
Included::set(Some(()));
Ok(())
}
}
}
+32 -9
View File
@@ -27,7 +27,7 @@ use primitives::{
use frame_support::{ use frame_support::{
decl_storage, decl_module, decl_error, traits::Randomness, decl_storage, decl_module, decl_error, traits::Randomness,
}; };
use crate::{configuration::{self, HostConfiguration}, paras, scheduler}; use crate::{configuration::{self, HostConfiguration}, paras, scheduler, inclusion};
/// Information about a session change that has just occurred. /// Information about a session change that has just occurred.
#[derive(Default, Clone)] #[derive(Default, Clone)]
@@ -42,9 +42,13 @@ pub struct SessionChangeNotification<BlockNumber> {
pub new_config: HostConfiguration<BlockNumber>, pub new_config: HostConfiguration<BlockNumber>,
/// A secure random seed for the session, gathered from BABE. /// A secure random seed for the session, gathered from BABE.
pub random_seed: [u8; 32], pub random_seed: [u8; 32],
/// New session index.
pub session_index: sp_staking::SessionIndex,
} }
pub trait Trait: system::Trait + configuration::Trait + paras::Trait + scheduler::Trait { pub trait Trait:
system::Trait + configuration::Trait + paras::Trait + scheduler::Trait + inclusion::Trait
{
/// A randomness beacon. /// A randomness beacon.
type Randomness: Randomness<Self::Hash>; type Randomness: Randomness<Self::Hash>;
} }
@@ -81,7 +85,8 @@ decl_module! {
// - Validity // - Validity
let total_weight = configuration::Module::<T>::initializer_initialize(now) + let total_weight = configuration::Module::<T>::initializer_initialize(now) +
paras::Module::<T>::initializer_initialize(now) + paras::Module::<T>::initializer_initialize(now) +
scheduler::Module::<T>::initializer_initialize(now); scheduler::Module::<T>::initializer_initialize(now) +
inclusion::Module::<T>::initializer_initialize(now);
HasInitialized::set(Some(())); HasInitialized::set(Some(()));
@@ -91,6 +96,7 @@ decl_module! {
fn on_finalize() { fn on_finalize() {
// reverse initialization order. // reverse initialization order.
inclusion::Module::<T>::initializer_finalize();
scheduler::Module::<T>::initializer_finalize(); scheduler::Module::<T>::initializer_finalize();
paras::Module::<T>::initializer_finalize(); paras::Module::<T>::initializer_finalize();
configuration::Module::<T>::initializer_finalize(); configuration::Module::<T>::initializer_finalize();
@@ -101,16 +107,25 @@ decl_module! {
impl<T: Trait> Module<T> { impl<T: Trait> Module<T> {
/// Should be called when a new session occurs. Forwards the session notification to all /// Should be called when a new session occurs. Forwards the session notification to all
/// wrapped modules. /// wrapped modules. If `queued` is `None`, the `validators` are considered queued.
/// ///
/// Panics if the modules have already been initialized. /// Panics if the modules have already been initialized.
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued: I) fn on_new_session<'a, I: 'a>(
_changed: bool,
session_index: sp_staking::SessionIndex,
validators: I,
queued: Option<I>,
)
where I: Iterator<Item=(&'a T::AccountId, ValidatorId)> where I: Iterator<Item=(&'a T::AccountId, ValidatorId)>
{ {
assert!(HasInitialized::get().is_none()); assert!(HasInitialized::get().is_none());
let validators: Vec<_> = validators.map(|(_, v)| v).collect(); let validators: Vec<_> = validators.map(|(_, v)| v).collect();
let queued: Vec<_> = queued.map(|(_, v)| v).collect(); let queued: Vec<_> = if let Some(queued) = queued {
queued.map(|(_, v)| v).collect()
} else {
validators.clone()
};
let prev_config = <configuration::Module<T>>::config(); let prev_config = <configuration::Module<T>>::config();
@@ -134,10 +149,12 @@ impl<T: Trait> Module<T> {
prev_config, prev_config,
new_config, new_config,
random_seed, random_seed,
session_index,
}; };
paras::Module::<T>::initializer_on_new_session(&notification); paras::Module::<T>::initializer_on_new_session(&notification);
scheduler::Module::<T>::initializer_on_new_session(&notification); scheduler::Module::<T>::initializer_on_new_session(&notification);
inclusion::Module::<T>::initializer_on_new_session(&notification);
} }
} }
@@ -145,7 +162,7 @@ impl<T: Trait> sp_runtime::BoundToRuntimeAppPublic for Module<T> {
type Public = ValidatorId; type Public = ValidatorId;
} }
impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> { impl<T: session::Trait + Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
type Key = ValidatorId; type Key = ValidatorId;
fn on_genesis_session<'a, I: 'a>(_validators: I) fn on_genesis_session<'a, I: 'a>(_validators: I)
@@ -157,7 +174,8 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued: I) fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued: I)
where I: Iterator<Item=(&'a T::AccountId, Self::Key)> where I: Iterator<Item=(&'a T::AccountId, Self::Key)>
{ {
<Module<T>>::on_new_session(changed, validators, queued); let session_index = <session::Module<T>>::current_index();
<Module<T>>::on_new_session(changed, session_index, validators, Some(queued));
} }
fn on_disabled(_i: usize) { } fn on_disabled(_i: usize) { }
@@ -175,7 +193,12 @@ mod tests {
fn panics_if_session_changes_after_on_initialize() { fn panics_if_session_changes_after_on_initialize() {
new_test_ext(Default::default()).execute_with(|| { new_test_ext(Default::default()).execute_with(|| {
Initializer::on_initialize(1); Initializer::on_initialize(1);
Initializer::on_new_session(false, Vec::new().into_iter(), Vec::new().into_iter()); Initializer::on_new_session(
false,
1,
Vec::new().into_iter(),
Some(Vec::new().into_iter()),
);
}); });
} }
+1
View File
@@ -22,6 +22,7 @@
mod configuration; mod configuration;
mod inclusion; mod inclusion;
mod inclusion_inherent;
mod initializer; mod initializer;
mod paras; mod paras;
mod scheduler; mod scheduler;
+5
View File
@@ -99,6 +99,8 @@ impl crate::paras::Trait for Test { }
impl crate::scheduler::Trait for Test { } impl crate::scheduler::Trait for Test { }
impl crate::inclusion::Trait for Test { }
pub type System = system::Module<Test>; pub type System = system::Module<Test>;
/// Mocked initializer. /// Mocked initializer.
@@ -113,6 +115,9 @@ pub type Paras = crate::paras::Module<Test>;
/// Mocked scheduler. /// Mocked scheduler.
pub type Scheduler = crate::scheduler::Module<Test>; pub type Scheduler = crate::scheduler::Module<Test>;
/// Mocked inclusion module.
pub type Inclusion = crate::inclusion::Module<Test>;
/// Create a new set of test externalities. /// Create a new set of test externalities.
pub fn new_test_ext(state: GenesisConfig) -> TestExternalities { pub fn new_test_ext(state: GenesisConfig) -> TestExternalities {
let mut t = state.system.build_storage::<Test>().unwrap(); let mut t = state.system.build_storage::<Test>().unwrap();
+50 -6
View File
@@ -174,7 +174,7 @@ decl_storage! {
/// All parathreads. /// All parathreads.
Parathreads: map hasher(twox_64_concat) ParaId => Option<()>; Parathreads: map hasher(twox_64_concat) ParaId => Option<()>;
/// The head-data of every registered para. /// The head-data of every registered para.
Heads get(fn parachain_head): map hasher(twox_64_concat) ParaId => Option<HeadData>; Heads get(fn para_head): map hasher(twox_64_concat) ParaId => Option<HeadData>;
/// The validation code of every live para. /// The validation code of every live para.
CurrentCode get(fn current_code): map hasher(twox_64_concat) ParaId => Option<ValidationCode>; CurrentCode get(fn current_code): map hasher(twox_64_concat) ParaId => Option<ValidationCode>;
/// Actual past code, indicated by the para id as well as the block number at which it became outdated. /// Actual past code, indicated by the para id as well as the block number at which it became outdated.
@@ -368,7 +368,7 @@ impl<T: Trait> Module<T> {
<Self as Store>::PastCode::remove(&(para_id, pruned_repl_at)); <Self as Store>::PastCode::remove(&(para_id, pruned_repl_at));
} }
meta.most_recent_change().is_none() && Self::parachain_head(&para_id).is_none() meta.most_recent_change().is_none() && Self::para_head(&para_id).is_none()
}); });
// This parachain has been removed and now the vestigial code // This parachain has been removed and now the vestigial code
@@ -428,7 +428,6 @@ impl<T: Trait> Module<T> {
/// with number >= `expected_at` /// with number >= `expected_at`
/// ///
/// If there is already a scheduled code upgrade for the para, this is a no-op. /// If there is already a scheduled code upgrade for the para, this is a no-op.
#[allow(unused)]
pub(crate) fn schedule_code_upgrade( pub(crate) fn schedule_code_upgrade(
id: ParaId, id: ParaId,
new_code: ValidationCode, new_code: ValidationCode,
@@ -448,15 +447,14 @@ impl<T: Trait> Module<T> {
/// Note that a para has progressed to a new head, where the new head was executed in the context /// Note that a para has progressed to a new head, where the new head was executed in the context
/// of a relay-chain block with given number. This will apply pending code upgrades based /// of a relay-chain block with given number. This will apply pending code upgrades based
/// on the block number provided. /// on the block number provided.
#[allow(unused)]
pub(crate) fn note_new_head( pub(crate) fn note_new_head(
id: ParaId, id: ParaId,
new_head: HeadData, new_head: HeadData,
execution_context: T::BlockNumber, execution_context: T::BlockNumber,
) -> Weight { ) -> Weight {
if let Some(expected_at) = <Self as Store>::FutureCodeUpgrades::get(&id) {
Heads::insert(&id, new_head); Heads::insert(&id, new_head);
if let Some(expected_at) = <Self as Store>::FutureCodeUpgrades::get(&id) {
if expected_at <= execution_context { if expected_at <= execution_context {
<Self as Store>::FutureCodeUpgrades::remove(&id); <Self as Store>::FutureCodeUpgrades::remove(&id);
@@ -481,7 +479,7 @@ impl<T: Trait> Module<T> {
T::DbWeight::get().reads_writes(1, 1 + 0) T::DbWeight::get().reads_writes(1, 1 + 0)
} }
} else { } else {
T::DbWeight::get().reads_writes(1, 0) T::DbWeight::get().reads_writes(1, 1)
} }
} }
@@ -526,6 +524,18 @@ impl<T: Trait> Module<T> {
pub(crate) fn is_parathread(id: ParaId) -> bool { pub(crate) fn is_parathread(id: ParaId) -> bool {
Parathreads::get(&id).is_some() Parathreads::get(&id).is_some()
} }
/// The block number of the last scheduled upgrade of the requested para. Includes future upgrades
/// if the flag is set. This is the `expected_at` number, not the `activated_at` number.
pub(crate) fn last_code_upgrade(id: ParaId, include_future: bool) -> Option<T::BlockNumber> {
if include_future {
if let Some(at) = Self::future_code_upgrade_at(id) {
return Some(at);
}
}
Self::past_code_meta(&id).most_recent_change()
}
} }
#[cfg(test)] #[cfg(test)]
@@ -683,6 +693,40 @@ mod tests {
}); });
} }
#[test]
fn note_new_head_sets_head() {
let acceptance_period = 10;
let paras = vec![
(0u32.into(), ParaGenesisArgs {
parachain: true,
genesis_head: Default::default(),
validation_code: Default::default(),
}),
];
let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
acceptance_period,
..Default::default()
},
..Default::default()
},
..Default::default()
};
new_test_ext(genesis_config).execute_with(|| {
let id_a = ParaId::from(0u32);
assert_eq!(Paras::para_head(&id_a), Some(Default::default()));
Paras::note_new_head(id_a, vec![1, 2, 3].into(), 0);
assert_eq!(Paras::para_head(&id_a), Some(vec![1, 2, 3].into()));
});
}
#[test] #[test]
fn note_past_code_sets_up_pruning_correctly() { fn note_past_code_sets_up_pruning_correctly() {
let acceptance_period = 10; let acceptance_period = 10;
+15 -10
View File
@@ -57,11 +57,23 @@ use crate::{configuration, paras, initializer::SessionChangeNotification};
#[cfg_attr(test, derive(Debug))] #[cfg_attr(test, derive(Debug))]
pub struct CoreIndex(u32); pub struct CoreIndex(u32);
impl From<u32> for CoreIndex {
fn from(i: u32) -> CoreIndex {
CoreIndex(i)
}
}
/// The unique (during session) index of a validator group. /// The unique (during session) index of a validator group.
#[derive(Encode, Decode, Default, Clone, Copy)] #[derive(Encode, Decode, Default, Clone, Copy)]
#[cfg_attr(test, derive(PartialEq, Debug))] #[cfg_attr(test, derive(PartialEq, Debug))]
pub struct GroupIndex(u32); pub struct GroupIndex(u32);
impl From<u32> for GroupIndex {
fn from(i: u32) -> GroupIndex {
GroupIndex(i)
}
}
/// A claim on authoring the next block for a given parathread. /// A claim on authoring the next block for a given parathread.
#[derive(Clone, Encode, Decode, Default)] #[derive(Clone, Encode, Decode, Default)]
#[cfg_attr(test, derive(PartialEq, Debug))] #[cfg_attr(test, derive(PartialEq, Debug))]
@@ -130,7 +142,7 @@ pub enum AssignmentKind {
} }
/// How a free core is scheduled to be assigned. /// How a free core is scheduled to be assigned.
#[derive(Encode, Decode)] #[derive(Clone, Encode, Decode)]
#[cfg_attr(test, derive(PartialEq, Debug))] #[cfg_attr(test, derive(PartialEq, Debug))]
pub struct CoreAssignment { pub struct CoreAssignment {
/// The core that is assigned. /// The core that is assigned.
@@ -145,7 +157,6 @@ pub struct CoreAssignment {
impl CoreAssignment { impl CoreAssignment {
/// Get the ID of a collator who is required to collate this block. /// Get the ID of a collator who is required to collate this block.
#[allow(unused)]
pub(crate) fn required_collator(&self) -> Option<&CollatorId> { pub(crate) fn required_collator(&self) -> Option<&CollatorId> {
match self.kind { match self.kind {
AssignmentKind::Parachain => None, AssignmentKind::Parachain => None,
@@ -153,7 +164,6 @@ impl CoreAssignment {
} }
} }
#[allow(unused)]
fn to_core_occupied(&self) -> CoreOccupied { fn to_core_occupied(&self) -> CoreOccupied {
match self.kind { match self.kind {
AssignmentKind::Parachain => CoreOccupied::Parachain, AssignmentKind::Parachain => CoreOccupied::Parachain,
@@ -168,7 +178,6 @@ impl CoreAssignment {
} }
/// Reasons a core might be freed /// Reasons a core might be freed
#[allow(unused)]
pub enum FreedReason { pub enum FreedReason {
/// The core's work concluded and the parablock assigned to it is considered available. /// The core's work concluded and the parablock assigned to it is considered available.
Concluded, Concluded,
@@ -525,7 +534,6 @@ impl<T: Trait> Module<T> {
/// ///
/// Complexity: O(n) in the number of scheduled cores, which is capped at the number of total cores. /// Complexity: O(n) in the number of scheduled cores, which is capped at the number of total cores.
/// This is efficient in the case that most scheduled cores are occupied. /// This is efficient in the case that most scheduled cores are occupied.
#[allow(unused)]
pub(crate) fn occupied(now_occupied: &[CoreIndex]) { pub(crate) fn occupied(now_occupied: &[CoreIndex]) {
if now_occupied.is_empty() { return } if now_occupied.is_empty() { return }
@@ -557,7 +565,6 @@ impl<T: Trait> Module<T> {
/// Get the para (chain or thread) ID assigned to a particular core or index, if any. Core indices /// Get the para (chain or thread) ID assigned to a particular core or index, if any. Core indices
/// out of bounds will return `None`, as will indices of unassigned cores. /// out of bounds will return `None`, as will indices of unassigned cores.
#[allow(unused)]
pub(crate) fn core_para(core_index: CoreIndex) -> Option<ParaId> { pub(crate) fn core_para(core_index: CoreIndex) -> Option<ParaId> {
let cores = AvailabilityCores::get(); let cores = AvailabilityCores::get();
match cores.get(core_index.0 as usize).and_then(|c| c.as_ref()) { match cores.get(core_index.0 as usize).and_then(|c| c.as_ref()) {
@@ -571,7 +578,6 @@ impl<T: Trait> Module<T> {
} }
/// Get the validators in the given group, if the group index is valid for this session. /// Get the validators in the given group, if the group index is valid for this session.
#[allow(unused)]
pub(crate) fn group_validators(group_index: GroupIndex) -> Option<Vec<ValidatorIndex>> { pub(crate) fn group_validators(group_index: GroupIndex) -> Option<Vec<ValidatorIndex>> {
ValidatorGroups::get().get(group_index.0 as usize).map(|g| g.clone()) ValidatorGroups::get().get(group_index.0 as usize).map(|g| g.clone())
} }
@@ -615,10 +621,9 @@ impl<T: Trait> Module<T> {
/// timeouts, i.e. only within `max(config.chain_availability_period, config.thread_availability_period)` /// timeouts, i.e. only within `max(config.chain_availability_period, config.thread_availability_period)`
/// of the last rotation would this return `Some`. /// of the last rotation would this return `Some`.
/// ///
/// This really should not be a box, but is working around a compiler limitation described here: /// This really should not be a box, but is working around a compiler limitation filed here:
/// https://users.rust-lang.org/t/cannot-unify-associated-type-in-impl-fn-with-concrete-type/44129 /// https://github.com/rust-lang/rust/issues/73226
/// which prevents us from testing the code if using `impl Trait`. /// which prevents us from testing the code if using `impl Trait`.
#[allow(unused)]
pub(crate) fn availability_timeout_predicate() -> Option<Box<dyn Fn(CoreIndex, T::BlockNumber) -> bool>> { pub(crate) fn availability_timeout_predicate() -> Option<Box<dyn Fn(CoreIndex, T::BlockNumber) -> bool>> {
let now = <system::Module<T>>::block_number(); let now = <system::Module<T>>::block_number();
let config = <configuration::Module<T>>::config(); let config = <configuration::Module<T>>::config();