Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Inclusion Module #1242

Merged
merged 54 commits into from
Jun 18, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
aa7ae3f
add availability bitfield types to primitives
rphmeier Jun 11, 2020
2c62c23
begin inclusion module
rphmeier Jun 11, 2020
08f3bad
use GitHub issue link for limitation
rphmeier Jun 11, 2020
c817204
fix some compiler errors
rphmeier Jun 11, 2020
ee1c9c0
integrate validators into initializer
rphmeier Jun 11, 2020
7b7306a
add generic signing context
rphmeier Jun 11, 2020
6b3969e
make signing-context more generic
rphmeier Jun 11, 2020
8a01422
fix issues with inclusion module
rphmeier Jun 11, 2020
795bc54
add TODO
rphmeier Jun 11, 2020
221b99e
guide: add validators and session index to inclusion
rphmeier Jun 11, 2020
412d88c
guide: add session index to change notification
rphmeier Jun 11, 2020
c5131b8
implement session change logic
rphmeier Jun 11, 2020
7e702cd
add BackedCandidate type
rphmeier Jun 11, 2020
48c0467
guide: refine inclusion pipeline
rphmeier Jun 12, 2020
d1d7fa9
guide: rename group_on to group_validators
rphmeier Jun 12, 2020
80613a4
guide: add check about collator for parathread
rphmeier Jun 12, 2020
4a2ee63
guide: add last_code_upgrade to paras and use in inclusion
rphmeier Jun 12, 2020
c8be591
implement Paras::last_code_upgrade
rphmeier Jun 12, 2020
ab81eef
implement most checks in process_candidates
rphmeier Jun 12, 2020
d4585a1
make candidate receipt structs more generic
rphmeier Jun 12, 2020
5ce3860
make BackedCandidate struct more generic
rphmeier Jun 12, 2020
cf28820
use hash param, not block number
rphmeier Jun 12, 2020
b4bbf4c
check that candidate is in context of the parent block
rphmeier Jun 12, 2020
976c289
include inclusion module in initializer
rphmeier Jun 12, 2020
b37a08c
implement enact-candidate
rphmeier Jun 12, 2020
7ca3527
check that only occupied cores have bits set
rphmeier Jun 12, 2020
383db96
finish implementing bitfield processing
rphmeier Jun 12, 2020
f5372b7
restructure consistency checks on candidates
rphmeier Jun 13, 2020
3a19591
make some more primitives generic
rphmeier Jun 13, 2020
b2e2754
signature checking logic for backed candidates
rphmeier Jun 13, 2020
7a74656
finish implementing process_candidates
rphmeier Jun 13, 2020
8f1774a
implement collect_pending
rphmeier Jun 13, 2020
058e2f3
add some trait implementations to primitives
rphmeier Jun 13, 2020
6b000bc
implement InclusionInherent and squash warnings
rphmeier Jun 13, 2020
11cf8a0
test bitfield signing checks
rphmeier Jun 15, 2020
d4fac49
rename parachain head to para_head
rphmeier Jun 15, 2020
58184eb
fix note_new_head bug in paras
rphmeier Jun 15, 2020
9ed14c5
test bitfield enactment in inclusion
rphmeier Jun 15, 2020
4e5b648
helpers for candidate checks
rphmeier Jun 15, 2020
9340aba
add test for most candidate checks
rphmeier Jun 16, 2020
cf44264
add test for backing setting storage
rphmeier Jun 16, 2020
bd88a20
test session change logic
rphmeier Jun 16, 2020
17dd6b2
remove extraneous type parameter
rphmeier Jun 16, 2020
93cce07
Merge branch 'master' into rh-para-inclusion
rphmeier Jun 17, 2020
dc9bff0
remove some allow(unused)s
rphmeier Jun 17, 2020
4bf6519
extract threshold computation to const fn
rphmeier Jun 17, 2020
b6c55a4
remove some more allow(unused)s
rphmeier Jun 17, 2020
0dc9072
improve doc
rphmeier Jun 17, 2020
3c43c1a
add debug assertion
rphmeier Jun 17, 2020
c080c14
Merge branch 'master' into rh-para-inclusion
rphmeier Jun 17, 2020
2f7059b
fix primitive test compilation
rphmeier Jun 17, 2020
1587db7
tag unanimous variant as unused
rphmeier Jun 17, 2020
70f041f
Merge branch 'master' into rh-para-inclusion
rphmeier Jun 18, 2020
86280f2
Merge branch 'master' into rh-para-inclusion
rphmeier Jun 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 129 additions & 35 deletions primitives/src/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,20 +176,20 @@ pub struct DutyRoster {
/// These are global parameters that apply to all parachain candidates in a block.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct GlobalValidationSchedule {
pub struct GlobalValidationSchedule<N = BlockNumber> {
/// The maximum code size permitted, in bytes.
pub max_code_size: u32,
/// The maximum head-data size permitted, in bytes.
pub max_head_data_size: u32,
/// 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`
/// to fully validate the candidate. These fields are parachain-specific.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct LocalValidationData {
pub struct LocalValidationData<N = BlockNumber> {
/// The parent head-data.
pub parent_head: HeadData,
/// The balance of the parachain at the moment of validation.
Expand All @@ -205,28 +205,28 @@ pub struct LocalValidationData {
/// 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
/// 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.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[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.
pub fees: Balance,
/// Messages destined to be interpreted by the Relay chain itself.
pub upward_messages: Vec<UpwardMessage>,
/// The root of a block's erasure encoding Merkle tree.
pub erasure_root: Hash,
pub erasure_root: H,
/// New validation code.
pub new_validation_code: Option<ValidationCode>,
}

/// Get a collator signature payload on a relay-parent, block-data combo.
pub fn collator_signature_payload(
relay_parent: &Hash,
pub fn collator_signature_payload<H: AsRef<[u8]>>(
relay_parent: &H,
parachain_index: &Id,
pov_block_hash: &Hash,
pov_block_hash: &H,
) -> [u8; 68] {
// 32-byte hash length is protected in a test below.
let mut payload = [0u8; 68];
Expand All @@ -238,10 +238,10 @@ pub fn collator_signature_payload(
payload
}

fn check_collator_signature(
relay_parent: &Hash,
fn check_collator_signature<H: AsRef<[u8]>>(
relay_parent: &H,
parachain_index: &Id,
pov_block_hash: &Hash,
pov_block_hash: &H,
collator: &CollatorId,
signature: &CollatorSignature,
) -> Result<(),()> {
Expand All @@ -258,29 +258,29 @@ fn check_collator_signature(
/// All data pertaining to the execution of a parachain candidate.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[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.
pub parachain_index: Id,
/// The hash of the relay-chain block this should be executed in
/// the context of.
pub relay_parent: Hash,
pub relay_parent: H,
/// The head-data
pub head_data: HeadData,
/// The collator's relay-chain account ID
pub collator: CollatorId,
/// Signature on blake2-256 of the block data by collator.
pub signature: CollatorSignature,
/// The hash of the PoV-block.
pub pov_block_hash: Hash,
pub pov_block_hash: H,
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule,
pub global_validation: GlobalValidationSchedule<N>,
/// The local validation data.
pub local_validation: LocalValidationData,
pub local_validation: LocalValidationData<N>,
/// 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.
pub fn check_signature(&self) -> Result<(), ()> {
check_collator_signature(
Expand All @@ -294,7 +294,7 @@ impl CandidateReceipt {

/// Abridge this `CandidateReceipt`, splitting it into an `AbridgedCandidateReceipt`
/// and its omitted component.
pub fn abridge(self) -> (AbridgedCandidateReceipt, OmittedValidationData) {
pub fn abridge(self) -> (AbridgedCandidateReceipt<H>, OmittedValidationData<N>) {
let CandidateReceipt {
parachain_index,
relay_parent,
Expand Down Expand Up @@ -345,11 +345,11 @@ impl Ord for CandidateReceipt {
/// is necessary for validation of the parachain candidate.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
pub struct OmittedValidationData {
pub struct OmittedValidationData<N = BlockNumber> {
/// The global validation schedule.
pub global_validation: GlobalValidationSchedule,
pub global_validation: GlobalValidationSchedule<N>,
/// The local validation data.
pub local_validation: LocalValidationData,
pub local_validation: LocalValidationData<N>,
}

/// An abridged candidate-receipt.
Expand All @@ -359,27 +359,38 @@ pub struct OmittedValidationData {
/// be re-generated from relay-chain state.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[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.
pub parachain_index: Id,
/// The hash of the relay-chain block this should be executed in
/// the context of.
// 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.
pub relay_parent: Hash,
pub relay_parent: H,
/// The head-data
pub head_data: HeadData,
/// The collator's relay-chain account ID
pub collator: CollatorId,
/// Signature on blake2-256 of the block data by collator.
pub signature: CollatorSignature,
/// The hash of the pov-block.
pub pov_block_hash: Hash,
pub pov_block_hash: H,
/// Commitments made as a result of validation.
pub commitments: CandidateCommitments,
pub commitments: CandidateCommitments<H>,
}

impl AbridgedCandidateReceipt {
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,
)
}

/// Compute the hash of the abridged candidate receipt.
///
/// This is often used as the canonical hash of the receipt, rather than
Expand All @@ -391,10 +402,12 @@ impl AbridgedCandidateReceipt {
use runtime_primitives::traits::{BlakeTwo256, Hash};
BlakeTwo256::hash_of(self)
}
}

impl AbridgedCandidateReceipt {
/// Combine the abridged candidate receipt with the omitted data,
/// forming a full `CandidateReceipt`.
pub fn complete(self, omitted: OmittedValidationData) -> CandidateReceipt {
pub fn complete<N>(self, omitted: OmittedValidationData) -> CandidateReceipt {
let AbridgedCandidateReceipt {
parachain_index,
relay_parent,
Expand Down Expand Up @@ -606,6 +619,36 @@ pub enum ValidityAttestation {
#[codec(index = "2")]
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.
#[derive(Clone, Eq, PartialEq, Default, Decode, Encode, RuntimeDebug)]
pub struct SigningContext<H = Hash> {
Expand Down Expand Up @@ -695,8 +738,7 @@ impl AvailabilityBitfield {
}

/// A bitfield signed by a particular validator about the availability of pending candidates.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct SignedAvailabilityBitfield {
/// The index of the validator in the current set.
pub validator_index: ValidatorIndex,
Expand Down Expand Up @@ -733,23 +775,75 @@ pub fn check_availability_bitfield_signature<H: Encode>(
}

/// A set of signed availability bitfields. Should be sorted by validator index, ascending.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct SignedAvailabilityBitfields(pub Vec<SignedAvailabilityBitfield>);

/// A backed (or backable, depending on context) candidate.
// TODO: yes, this is roughly the same as AttestedCandidate.
// After https://github.com/paritytech/polkadot/issues/1250
// they should be unified to this type.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct BackedCandidate {
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct BackedCandidate<H = Hash> {
/// The candidate referred to.
pub candidate: AbridgedCandidateReceipt,
pub candidate: AbridgedCandidateReceipt<H>,
/// The validity votes themselves, expressed as signatures.
pub validity_votes: Vec<ValidityAttestation>,
/// The indices of the validators within the group, expressed as a bitfield.
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! {
/// The API for querying the state of parachains on-chain.
#[api_version(3)]
Expand Down
Loading