Unverified Commit 4f79b770 authored by Peter Goodspeed-Niklaus's avatar Peter Goodspeed-Niklaus Committed by GitHub
Browse files

signed wrapper (#1283)



* add signed wrapper, typedef SignedStatement

* typedef SignedAvailabilityBitfield

* implement Signed wrapper

This is strictly an addition as of this commit; nothing is yet
changed in existing behavior.

* inline getters, remove review comment

* move EncodeAs, Signed from node::primitives to primitives::parachain

* Refactor SignedAvailabilityBitfield to use Signed

* don't double-encode real payload

This isn't an ideal solution, because it depends on the
implementation details of how SCALE encodes tuples, but OTOH
that behavior seems unlikely to change anytime soon.

* fix build errors

* cause the runtime to build properly with the new changes

Not sure why cargo check didn't catch this earlier; oh well.

* fix runtime tests and separate SignedStatement from SignedFullStatement

* better explain why CompactStatement exists
Co-authored-by: asynchronous rob's avatarRobert Habermeier <rphmeier@gmail.com>
Co-authored-by: asynchronous rob's avatarRobert Habermeier <rphmeier@gmail.com>
parent ff708f3a
Pipeline #97758 passed with stages
in 22 minutes and 43 seconds
......@@ -31,7 +31,7 @@ use polkadot_primitives::parachain::{
SignedAvailabilityBitfield, SigningContext, ValidatorId, ValidationCode, ValidatorIndex,
};
use polkadot_node_primitives::{
MisbehaviorReport, SignedStatement,
MisbehaviorReport, SignedFullStatement,
};
/// Signals sent by an overseer to a subsystem.
......@@ -68,7 +68,7 @@ pub enum CandidateBackingMessage {
Second(Hash, AbridgedCandidateReceipt),
/// Note a validator's statement about a particular candidate. Disagreements about validity must be escalated
/// to a broader check by Misbehavior Arbitration. Agreements are simply tallied until a quorum is reached.
Statement(Hash, SignedStatement),
Statement(Hash, SignedFullStatement),
}
/// Blanket error for validation failing.
......@@ -180,7 +180,7 @@ pub enum RuntimeApiMessage {
pub enum StatementDistributionMessage {
/// We have originated a signed statement in the context of
/// given relay-parent hash and it should be distributed to other validators.
Share(Hash, SignedStatement),
Share(Hash, SignedFullStatement),
}
/// This data becomes intrinsics or extrinsics which should be included in a future relay chain block.
......
......@@ -20,13 +20,13 @@
//! not shared between the node and the runtime. This crate builds on top of the primitives defined
//! there.
use runtime_primitives::traits::AppVerify;
use polkadot_primitives::Hash;
use polkadot_primitives::parachain::{
AbridgedCandidateReceipt, CandidateReceipt, SigningContext, ValidatorSignature,
ValidatorIndex, ValidatorId,
use parity_scale_codec::{Decode, Encode};
use polkadot_primitives::{Hash,
parachain::{
AbridgedCandidateReceipt, CandidateReceipt, CompactStatement,
EncodeAs, Signed,
}
};
use parity_scale_codec::{Encode, Decode};
/// A statement, where the candidate receipt is included in the `Seconded` variant.
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
......@@ -42,54 +42,26 @@ pub enum Statement {
Invalid(Hash),
}
impl Statement {
/// Get the signing payload of the statement.
pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
// convert to fully hash-based payload.
impl EncodeAs<CompactStatement> for Statement {
fn encode_as(&self) -> Vec<u8> {
let statement = match *self {
Statement::Seconded(ref c) => polkadot_primitives::parachain::Statement::Candidate(c.hash()),
Statement::Valid(hash) => polkadot_primitives::parachain::Statement::Valid(hash),
Statement::Invalid(hash) => polkadot_primitives::parachain::Statement::Invalid(hash),
Statement::Seconded(ref c) => {
polkadot_primitives::parachain::CompactStatement::Candidate(c.hash())
}
Statement::Valid(hash) => polkadot_primitives::parachain::CompactStatement::Valid(hash),
Statement::Invalid(hash) => polkadot_primitives::parachain::CompactStatement::Invalid(hash),
};
statement.signing_payload(context)
statement.encode()
}
}
/// A statement, the corresponding signature, and the index of the sender.
///
/// Signing context and validator set should be apparent from context.
#[derive(Debug, Clone, PartialEq, Encode, Decode)]
pub struct SignedStatement {
/// The statement signed.
pub statement: Statement,
/// The signature of the validator.
pub signature: ValidatorSignature,
/// The index in the validator set of the signing validator. Which validator set should
/// be apparent from context.
pub sender: ValidatorIndex,
}
impl SignedStatement {
/// Check the signature on a statement. Provide a list of validators to index into
/// and the context in which the statement is presumably signed.
///
/// Returns an error if out of bounds or the signature is invalid. Otherwise, returns Ok.
pub fn check_signature(
&self,
validators: &[ValidatorId],
signing_context: &SigningContext,
) -> Result<(), ()> {
let validator = validators.get(self.sender as usize).ok_or(())?;
let payload = self.statement.signing_payload(signing_context);
if self.signature.verify(&payload[..], validator) {
Ok(())
} else {
Err(())
}
}
}
///
/// This statement is "full" in the sense that the `Seconded` variant includes the candidate receipt.
/// Only the compact `SignedStatement` is suitable for submission to the chain.
pub type SignedFullStatement = Signed<Statement, CompactStatement>;
/// A misbehaviour report.
pub enum MisbehaviorReport {
......@@ -101,9 +73,9 @@ pub enum MisbehaviorReport {
/// this message should be dispatched with all of them, in arbitrary order.
///
/// This variant is also used when our own validity checks disagree with others'.
CandidateValidityDisagreement(CandidateReceipt, Vec<SignedStatement>),
CandidateValidityDisagreement(CandidateReceipt, Vec<SignedFullStatement>),
/// I've noticed a peer contradicting itself about a particular candidate
SelfContradiction(CandidateReceipt, SignedStatement, SignedStatement),
SelfContradiction(CandidateReceipt, SignedFullStatement, SignedFullStatement),
/// This peer has seconded more than one parachain candidate for this relay parent head
DoubleVote(CandidateReceipt, SignedStatement, SignedStatement),
DoubleVote(CandidateReceipt, SignedFullStatement, SignedFullStatement),
}
......@@ -27,9 +27,9 @@ use super::{Hash, Balance, BlockNumber};
use serde::{Serialize, Deserialize};
#[cfg(feature = "std")]
use primitives::bytes;
use primitives::{bytes, crypto::Pair};
use primitives::RuntimeDebug;
use runtime_primitives::traits::Block as BlockT;
use runtime_primitives::traits::{AppVerify, Block as BlockT};
use inherents::InherentIdentifier;
use application_crypto::KeyTypeId;
......@@ -245,8 +245,6 @@ fn check_collator_signature<H: AsRef<[u8]>>(
collator: &CollatorId,
signature: &CollatorSignature,
) -> Result<(),()> {
use runtime_primitives::traits::AppVerify;
let payload = collator_signature_payload(relay_parent, parachain_index, pov_block_hash);
if signature.verify(&payload[..], collator) {
Ok(())
......@@ -594,7 +592,7 @@ pub struct Activity(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8
/// actual values that are signed.
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Statement {
pub enum CompactStatement {
/// Proposal of a parachain candidate.
#[codec(index = "1")]
Candidate(Hash),
......@@ -606,14 +604,8 @@ pub enum Statement {
Invalid(Hash),
}
impl Statement {
/// Produce a payload on this statement that is used for signing.
///
/// It includes the context provided.
pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
(self, context).encode()
}
}
/// A signed compact statement, suitable to be sent to the chain.
pub type SignedStatement = Signed<CompactStatement>;
/// An either implicit or explicit attestation to the validity of a parachain
/// candidate.
......@@ -647,11 +639,11 @@ impl ValidityAttestation {
) -> Vec<u8> {
match *self {
ValidityAttestation::Implicit(_) => (
Statement::Candidate(candidate_hash),
CompactStatement::Candidate(candidate_hash),
signing_context,
).encode(),
ValidityAttestation::Explicit(_) => (
Statement::Valid(candidate_hash),
CompactStatement::Valid(candidate_hash),
signing_context,
).encode(),
}
......@@ -723,64 +715,8 @@ 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<H: Encode>(
&self,
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<H: Encode>(
&self,
signing_context: &SigningContext<H>,
) -> Vec<u8> {
let mut v = Vec::new();
self.encode_signing_payload_into(signing_context, &mut v);
v
}
}
/// A bitfield signed by a particular validator about the availability of pending candidates.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct SignedAvailabilityBitfield {
/// The index of the validator in the current set.
pub validator_index: ValidatorIndex,
/// The bitfield itself, with one bit per core. Only occupied cores may have the `1` bit set.
pub bitfield: AvailabilityBitfield,
/// The signature by the validator on the bitfield's signing payload. The context of the signature
/// should be apparent when checking the signature.
pub signature: ValidatorSignature,
}
/// Check a signature on an availability bitfield. Provide the bitfield, the validator who signed it,
/// the signature, the signing context, and an optional buffer in which to encode.
///
/// If the buffer is provided, it is assumed to be empty.
pub fn check_availability_bitfield_signature<H: Encode>(
bitfield: &AvailabilityBitfield,
validator: &ValidatorId,
signature: &ValidatorSignature,
signing_context: &SigningContext<H>,
payload_encode_buf: Option<&mut Vec<u8>>,
) -> Result<(),()> {
use runtime_primitives::traits::AppVerify;
let mut v = Vec::new();
let payload_encode_buf = payload_encode_buf.unwrap_or(&mut v);
bitfield.encode_signing_payload_into(signing_context, payload_encode_buf);
if signature.verify(&payload_encode_buf[..], validator) {
Ok(())
} else {
Err(())
}
}
pub type SignedAvailabilityBitfield = Signed<AvailabilityBitfield>;
/// A set of signed availability bitfields. Should be sorted by validator index, ascending.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
......@@ -816,8 +752,6 @@ pub fn check_candidate_backing<H: AsRef<[u8]> + Encode>(
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(())
}
......@@ -885,6 +819,113 @@ pub mod id {
pub const PARACHAIN_HOST: ApiId = *b"parahost";
}
/// This helper trait ensures that we can encode Statement as CompactStatement,
/// and anything as itself.
///
/// This resembles `parity_scale_codec::EncodeLike`, but it's distinct:
/// EncodeLike is a marker trait which asserts at the typesystem level that
/// one type's encoding is a valid encoding for another type. It doesn't
/// perform any type conversion when encoding.
///
/// This trait, on the other hand, provides a method which can be used to
/// simultaneously convert and encode one type as another.
pub trait EncodeAs<T> {
/// Convert Self into T, then encode T.
///
/// This is useful when T is a subset of Self, reducing encoding costs;
/// its signature also means that we do not need to clone Self in order
/// to retain ownership, as we would if we were to do
/// `self.clone().into().encode()`.
fn encode_as(&self) -> Vec<u8>;
}
impl<T: Encode> EncodeAs<T> for T {
fn encode_as(&self) -> Vec<u8> {
self.encode()
}
}
/// A signed type which encapsulates the common desire to sign some data and validate a signature.
///
/// Note that the internal fields are not public; they are all accessable by immutable getters.
/// This reduces the chance that they are accidentally mutated, invalidating the signature.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct Signed<Payload, RealPayload = Payload> {
/// The payload is part of the signed data. The rest is the signing context,
/// which is known both at signing and at validation.
payload: Payload,
/// The index of the validator signing this statement.
validator_index: ValidatorIndex,
/// The signature by the validator of the signed payload.
signature: ValidatorSignature,
/// This ensures the real payload is tracked at the typesystem level.
real_payload: sp_std::marker::PhantomData<RealPayload>,
}
// We can't bound this on `Payload: Into<RealPayload>` beacuse that conversion consumes
// the payload, and we don't want that. We can't bound it on `Payload: AsRef<RealPayload>`
// because there's no blanket impl of `AsRef<T> for T`. In the end, we just invent our
// own trait which does what we need: EncodeAs.
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
fn payload_data<H: Encode>(payload: &Payload, context: &SigningContext<H>) -> Vec<u8> {
// equivalent to (real_payload, context).encode()
let mut out = payload.encode_as();
out.extend(context.encode());
out
}
/// Sign this payload with the given context and key, storing the validator index.
#[cfg(feature = "std")]
pub fn sign<H: Encode>(
payload: Payload,
context: &SigningContext<H>,
validator_index: ValidatorIndex,
key: &ValidatorPair,
) -> Self {
let data = Self::payload_data(&payload, context);
let signature = key.sign(&data);
Self {
payload,
validator_index,
signature,
real_payload: std::marker::PhantomData,
}
}
/// Validate the payload given the context and public key.
pub fn check_signature<H: Encode>(&self, context: &SigningContext<H>, key: &ValidatorId) -> Result<(), ()> {
let data = Self::payload_data(&self.payload, context);
if self.signature.verify(data.as_slice(), key) { Ok(()) } else { Err(()) }
}
/// Immutably access the payload.
#[inline]
pub fn payload(&self) -> &Payload {
&self.payload
}
/// Immutably access the validator index.
#[inline]
pub fn validator_index(&self) -> ValidatorIndex {
self.validator_index
}
/// Immutably access the signature.
#[inline]
pub fn signature(&self) -> &ValidatorSignature {
&self.signature
}
/// Discard signing data, get the payload
// Note: can't `impl<P, R> From<Signed<P, R>> for P` because the orphan rule exception doesn't
// handle this case yet. Likewise can't `impl<P, R> Into<P> for Signed<P, R>` because it might
// potentially conflict with the global blanket impl, even though it currently doesn't.
#[inline]
pub fn into_payload(self) -> Payload {
self.payload
}
}
#[cfg(test)]
mod tests {
use super::*;
......
......@@ -35,11 +35,11 @@ The Statement Distribution subsystem sends statements to peer nodes and detects
## Peer Receipt State Machine
There is a very simple state machine which governs which messages we are willing to receive from peers. Not depicted in the state machine: on initial receipt of any [`SignedStatement`](../../types/backing.md#signed-statement-type), validate that the provided signature does in fact sign the included data. Note that each individual parablock candidate gets its own instance of this state machine; it is perfectly legal to receive a `Valid(X)` before a `Seconded(Y)`, as long as a `Seconded(X)` has been received.
There is a very simple state machine which governs which messages we are willing to receive from peers. Not depicted in the state machine: on initial receipt of any [`SignedFullStatement`](../../types/backing.md#signed-statement-type), validate that the provided signature does in fact sign the included data. Note that each individual parablock candidate gets its own instance of this state machine; it is perfectly legal to receive a `Valid(X)` before a `Seconded(Y)`, as long as a `Seconded(X)` has been received.
A: Initial State. Receive `SignedStatement(Statement::Second)`: extract `Statement`, forward to Candidate Backing, proceed to B. Receive any other `SignedStatement` variant: drop it.
A: Initial State. Receive `SignedFullStatement(Statement::Second)`: extract `Statement`, forward to Candidate Backing, proceed to B. Receive any other `SignedFullStatement` variant: drop it.
B: Receive any `SignedStatement`: check signature, forward to Candidate Backing. Receive `OverseerMessage::StopWork`: proceed to C.
B: Receive any `SignedFullStatement`: check signature, forward to Candidate Backing. Receive `OverseerMessage::StopWork`: proceed to C.
C: Receive any message for this block: drop it.
......
......@@ -5,21 +5,15 @@ candidates for the duration of a challenge period. This is done via an erasure-c
## Signed Availability Bitfield
A bitfield signed by a particular validator about the availability of pending candidates.
A bitfield [signed](backing.html#signed-wrapper) by a particular validator about the availability of pending candidates.
```rust
struct SignedAvailabilityBitfield {
validator_index: ValidatorIndex,
bitfield: Bitvec,
signature: ValidatorSignature,
}
pub type SignedAvailabilityBitfield = Signed<Bitvec>;
struct Bitfields(Vec<(SignedAvailabilityBitfield)>), // bitfields sorted by validator index, ascending
```
The signed payload is the SCALE encoding of the tuple `(bitfield, signing_context)` where `signing_context` is a [`SigningContext`](../types/candidate.md#signing-context).
## Proof-of-Validity
Often referred to as PoV, this is a type-safe wrapper around bytes (`Vec<u8>`) when referring to data that acts as a stateless-client proof of validity of a candidate, when used as input to the validation function of the para.
......
......@@ -19,6 +19,35 @@ enum ValidityAttestation {
}
```
## Signed Wrapper
There are a few distinct types which we desire to sign, and validate the signatures of. Instead of duplicating this work, we extract a signed wrapper.
```rust,ignore
/// A signed type which encapsulates the common desire to sign some data and validate a signature.
///
/// Note that the internal fields are not public; they are all accessable by immutable getters.
/// This reduces the chance that they are accidentally mutated, invalidating the signature.
struct Signed<Payload, RealPayload=Payload> {
/// The payload is part of the signed data. The rest is the signing context,
/// which is known both at signing and at validation.
payload: Payload,
/// The index of the validator signing this statement.
validator_index: ValidatorIndex,
/// The signature by the validator of the signed payload.
signature: ValidatorSignature,
}
impl<Payload: EncodeAs<RealPayload>, RealPayload: Encode> Signed<Payload, RealPayload> {
fn sign(payload: Payload, context: SigningContext, index: ValidatorIndex, key: ValidatorPair) -> Signed<Payload, RealPayload> { ... }
fn validate(&self, context: SigningContext, key: ValidatorId) -> bool { ... }
}
```
Note the presence of the [`SigningContext`](../types/candidate.html#signing-context) in the signatures of the `sign` and `validate` methods. To ensure cryptographic security, the actual signed payload is always the SCALE encoding of `(payload.into(), signing_context)`. Including the signing context prevents replay attacks.
`EncodeAs` is a helper trait with a blanket impl which ensures that any `T` can `EncodeAs<T>`. Therefore, for the generic case where `RealPayload = Payload`, it changes nothing. However, we `impl EncodeAs<CompactStatement> for Statement`, which helps efficiency.
## Statement Type
The [Candidate Backing subsystem](../node/backing/candidate-backing.md) issues and signs these after candidate validation.
......@@ -38,28 +67,38 @@ enum Statement {
/// A statement about the invalidity of a candidate.
Invalid(Hash),
}
/// A statement about the validity of a parachain candidate.
///
/// This variant should only be used in the production of `SignedStatement`s. The only difference between
/// this enum and `Statement` is that the `Seconded` variant contains a `Hash` instead of a `CandidateReceipt`.
/// The rationale behind the difference is that the signature should always be on the hash instead of the
/// full data, as this lowers the requirement for checking while retaining necessary cryptographic properties
enum CompactStatement {
/// A statement about a new candidate being seconded by a validator. This is an implicit validity vote.
Seconded(Hash),
/// A statement about the validity of a candidate, based on candidate's hash.
Valid(Hash),
/// A statement about the invalidity of a candidate.
Invalid(Hash),
}
```
`CompactStatement` exists because a `CandidateReceipt` includes `HeadData`, which does not have a bounded size.
## Signed Statement Type
A statement, the identifier of a validator, and a signature.
A statement which has been [cryptographically signed](#signed-wrapper) by a validator.
```rust
/// A signed statement.
struct SignedStatement {
/// The index of the validator signing this statement.
validator_index: ValidatorIndex,
/// The statement itself.
statement: Statement,
/// The signature by the validator on the signing payload.
signature: ValidatorSignature
}
```
/// A signed statement, containing the abridged candidate receipt in the `Seconded` variant.
pub type SignedFullStatement = Signed<Statement, CompactStatement>;
The actual signed payload will be the SCALE encoding of `(compact_statement, signing_context)` where
`compact_statement` is a tweak of the [`Statement`](#statement) enum where all variants, including `Seconded`, contain only the hash of the candidate, and the `signing_context` is a [`SigningContext`](../types/candidate.md#signing-context).
/// A signed statement, containing only the hash.
pub type SignedStatement = Signed<CompactStatement>;
```
This prevents against replay attacks and allows the candidate receipt itself to be omitted when checking a signature on a `Seconded` statement in situations where the hash is known.
Munging the signed `Statement` into a `CompactStatement` before signing allows the candidate receipt itself to be omitted when checking a signature on a `Seconded` statement.
## Backed Candidate
......
......@@ -158,11 +158,11 @@ enum MisbehaviorReport {
/// this message should be dispatched with all of them, in arbitrary order.
///
/// This variant is also used when our own validity checks disagree with others'.
CandidateValidityDisagreement(CandidateReceipt, Vec<SignedStatement>),
CandidateValidityDisagreement(CandidateReceipt, Vec<SignedFullStatement>),
/// I've noticed a peer contradicting itself about a particular candidate
SelfContradiction(CandidateReceipt, SignedStatement, SignedStatement),
SelfContradiction(CandidateReceipt, SignedFullStatement, SignedFullStatement),
/// This peer has seconded more than one parachain candidate for this relay parent head
DoubleVote(CandidateReceipt, SignedStatement, SignedStatement),
DoubleVote(CandidateReceipt, SignedFullStatement, SignedFullStatement),
}
```
......@@ -227,7 +227,7 @@ enum RuntimeApiMessage {
## Statement Distribution Message
The Statement Distribution subsystem distributes signed statements from validators to other validators.
The Statement Distribution subsystem distributes signed statements and candidates from validators to other validators. It does this by distributing full statements, which embed the candidate receipt, as opposed to compact statements which don't.
It receives updates from the network bridge and signed statements to share with other validators.
```rust
......@@ -239,7 +239,7 @@ enum StatementDistributionMessage {
///
/// The statement distribution subsystem assumes that the statement should be correctly
/// signed.
Share(Hash, SignedStatement),
Share(Hash, SignedFullStatement),
}
```
......
......@@ -41,7 +41,7 @@ use primitives::{
Balance,
BlockNumber,
parachain::{
Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, ParachainDispatchOrigin,
Id as ParaId, Chain, DutyRoster, AttestedCandidate, CompactStatement as Statement, ParachainDispatchOrigin,
UpwardMessage, ValidatorId, ActiveParas, CollatorId, Retriable, OmittedValidationData,
CandidateReceipt, GlobalValidationSchedule, AbridgedCandidateReceipt,
LocalValidationData, Scheduling, ValidityAttestation, NEW_HEADS_IDENTIFIER, PARACHAIN_KEY_TYPE_ID,
......
......@@ -673,7 +673,7 @@ mod tests {
use primitives::{
parachain::{
ValidatorId, Info as ParaInfo, Scheduling, LOWEST_USER_ID, AttestedCandidate,
CandidateReceipt, HeadData, ValidityAttestation, Statement, Chain,
CandidateReceipt, HeadData, ValidityAttestation, CompactStatement as Statement, Chain,
CollatorPair, CandidateCommitments,
},
Balance, BlockNumber, Header, Signature,
......
......@@ -184,7 +184,6 @@ impl<T: Trait> Module<T> {
.collect();
let mut last_index = None;
let mut payload_encode_buf = Vec::new();
let signing_context = SigningContext {
parent_hash: <system::Module<T>>::parent_hash(),
......@@ -193,46 +192,37 @@ impl<T: Trait> Module<T> {
for signed_bitfield in &signed_bitfields.0 {
ensure!(
signed_bitfield.bitfield.0.len() == n_bits,
signed_bitfield.payload().0.len() == n_bits,
Error::<T>::WrongBitfieldSize,
);
ensure!(
last_index.map_or(true, |last| last < signed_bitfield.validator_index),
last_index.map_or(true, |last| last < signed_bitfield.validator_index()),
Error::<T>::BitfieldDuplicateOrUnordered,
);
ensure!(
signed_bitfield.validator_index < validators.len() as ValidatorIndex,
signed_bitfield.validator_index() < validators.len() as ValidatorIndex,
Error::<T>::ValidatorIndexOutOfBounds,
);
ensure!(
occupied_bitmask.clone() & signed_bitfield.bitfield.0.clone() == signed_bitfield.bitfield.0,
occupied_bitmask.clone() & signed_bitfield.payload().0.clone() == signed_bitfield.payload().0,
Error::<T>::UnoccupiedBitInBitfield,
);
let validator_public = &validators[signed_bitfield.validator_index as usize];
let validator_public = &validators[signed_bitfield.validator_index() as usize];
if let Err(()) = primitives::parachain::check_availability_bitfield_signature(
&signed_bitfield.bitfield,
validator_public,
&signed_bitfield.signature,
&signing_context,
Some(&mut payload_encode_buf),
) {
Err(Error::<T>::InvalidBitfieldSignature)?;
}
signed_bitfield.check_signature(&signing_context, validator_public).map_err(|_| Error::<T>::InvalidBitfieldSignature)?;
last_index = Some(signed_bitfield.validator_index);