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

Commit

Permalink
Inclusion Module (#1242)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rphmeier authored Jun 18, 2020
1 parent f92be01 commit 9d874fa
Show file tree
Hide file tree
Showing 12 changed files with 1,969 additions and 74 deletions.
173 changes: 133 additions & 40 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,7 +402,9 @@ 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 {
Expand Down Expand Up @@ -616,13 +629,42 @@ pub enum ValidityAttestation {
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 {
pub struct SigningContext<H = Hash> {
/// Current session index.
pub session_index: sp_staking::SessionIndex,
/// 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.
Expand Down Expand Up @@ -683,20 +725,19 @@ impl From<BitVec<bitvec::order::Lsb0, u8>> for AvailabilityBitfield {

impl AvailabilityBitfield {
/// Encodes the signing payload into the given buffer.
pub fn encode_signing_payload_into(
pub fn encode_signing_payload_into<H: Encode>(
&self,
signing_context: &SigningContext,
signing_context: &SigningContext<H>,
buf: &mut Vec<u8>,
) {
self.0.encode_to(buf);
signing_context.encode_to(buf);
}

/// Encodes the signing payload into a fresh byte-vector.
pub fn encode_signing_payload(
pub fn encode_signing_payload<H: Encode>(
&self,
signing_context:
&SigningContext,
signing_context: &SigningContext<H>,
) -> Vec<u8> {
let mut v = Vec::new();
self.encode_signing_payload_into(signing_context, &mut v);
Expand Down Expand Up @@ -724,7 +765,7 @@ pub fn check_availability_bitfield_signature<H: Encode>(
bitfield: &AvailabilityBitfield,
validator: &ValidatorId,
signature: &ValidatorSignature,
signing_context: &SigningContext,
signing_context: &SigningContext<H>,
payload_encode_buf: Option<&mut Vec<u8>>,
) -> Result<(),()> {
use runtime_primitives::traits::AppVerify;
Expand All @@ -750,15 +791,67 @@ pub struct SignedAvailabilityBitfields(pub Vec<SignedAvailabilityBitfield>);
// After https://github.com/paritytech/polkadot/issues/1250
// they should be unified to this type.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct BackedCandidate {
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 Expand Up @@ -811,9 +904,9 @@ mod tests {
assert_eq!(h.as_ref().len(), 32);

let _payload = collator_signature_payload(
&[1; 32].into(),
&Hash::from([1; 32]),
&5u32.into(),
&[2; 32].into(),
&Hash::from([2; 32]),
);
}
}
2 changes: 2 additions & 0 deletions roadmap/implementors-guide/src/runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct SessionChangeNotification {
new_config: HostConfiguration,
// A secure randomn seed for the session, gathered from BABE.
random_seed: [u8; 32],
// The session index of the beginning session.
session_index: SessionIndex,
}
```

Expand Down
Loading

0 comments on commit 9d874fa

Please sign in to comment.