Sassafras Consensus Pallet (#1577)

This PR introduces the pallet for Sassafras consensus.

## Non Goals

The pallet delivers only the bare-bones and doesn't deliver support for
auxiliary functionalities such as equivocation report and support for
epoch change via session pallet.

These functionalities were drafted in the [main
PR](https://github.com/paritytech/polkadot-sdk/pull/1336), but IMO is
better to introduce this auxiliary stuff in a follow up PR and after
client code.

## Potential follow ups

https://github.com/paritytech/polkadot-sdk/issues/2364

---------

Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
Co-authored-by: Koute <koute@users.noreply.github.com>
Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
Davide Galassi
2023-12-01 16:39:07 +01:00
committed by GitHub
parent 7f0beaf2a5
commit 095f4bd9ae
21 changed files with 3763 additions and 127 deletions
@@ -48,11 +48,11 @@ pub struct SlotClaim {
/// This is mandatory in the first block of each epoch.
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
pub struct NextEpochDescriptor {
/// Randomness value.
pub randomness: Randomness,
/// Authorities list.
pub authorities: Vec<AuthorityId>,
/// Epoch randomness.
pub randomness: Randomness,
/// Epoch configurable parameters.
/// Epoch configuration.
///
/// If not present previous epoch parameters are used.
pub config: Option<EpochConfiguration>,
@@ -80,33 +80,43 @@ pub type EquivocationProof<H> = sp_consensus_slots::EquivocationProof<H, Authori
/// Randomness required by some protocol's operations.
pub type Randomness = [u8; RANDOMNESS_LENGTH];
/// Configuration data that can be modified on epoch change.
/// Protocol configuration that can be modified on epoch change.
///
/// Mostly tweaks to the ticketing system parameters.
#[derive(
Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo, Default,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EpochConfiguration {
/// Tickets threshold redundancy factor.
/// Tickets redundancy factor.
///
/// Expected ratio between epoch's slots and the cumulative number of tickets which can
/// be submitted by the set of epoch validators.
pub redundancy_factor: u32,
/// Tickets attempts for each validator.
/// Tickets max attempts for each validator.
///
/// Influences the anonymity of block producers. As all published tickets have a public
/// attempt number less than `attempts_number` if two tickets share an attempt number
/// then they must belong to two different validators, which reduces anonymity late as
/// we approach the epoch tail.
///
/// This anonymity loss already becomes small when `attempts_number = 64` or `128`.
pub attempts_number: u32,
}
/// Sassafras epoch information
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)]
pub struct Epoch {
/// The epoch index.
pub epoch_idx: u64,
/// The starting slot of the epoch.
pub start_slot: Slot,
/// Slot duration in milliseconds.
pub slot_duration: SlotDuration,
/// Duration of epoch in slots.
pub epoch_duration: u64,
/// Authorities for the epoch.
pub authorities: Vec<AuthorityId>,
/// Randomness for the epoch.
/// Epoch index.
pub index: u64,
/// Starting slot of the epoch.
pub start: Slot,
/// Number of slots in the epoch.
pub length: u32,
/// Randomness value.
pub randomness: Randomness,
/// Authorities list.
pub authorities: Vec<AuthorityId>,
/// Epoch configuration.
pub config: EpochConfiguration,
}
@@ -62,10 +62,10 @@ pub struct TicketClaim {
pub erased_signature: EphemeralSignature,
}
/// Computes ticket-id maximum allowed value for a given epoch.
/// Computes a boundary for [`TicketId`] maximum allowed value for a given epoch.
///
/// Only ticket identifiers below this threshold should be considered for slot
/// assignment.
/// Only ticket identifiers below this threshold should be considered as candidates
/// for slot assignment.
///
/// The value is computed as `TicketId::MAX*(redundancy*slots)/(attempts*validators)`
///
@@ -76,16 +76,51 @@ pub struct TicketClaim {
/// - `validators`: number of validators in epoch.
///
/// If `attempts * validators = 0` then we return 0.
///
/// For details about the formula and implications refer to
/// [*probabilities an parameters*](https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS#probabilities-and-parameters)
/// paragraph of the w3f introduction to the protocol.
// TODO: replace with [RFC-26](https://github.com/polkadot-fellows/RFCs/pull/26)
// "Tickets Threshold" paragraph once is merged
pub fn ticket_id_threshold(
redundancy: u32,
slots: u32,
attempts: u32,
validators: u32,
) -> TicketId {
let den = attempts as u64 * validators as u64;
let num = redundancy as u64 * slots as u64;
let den = attempts as u64 * validators as u64;
TicketId::max_value()
.checked_div(den.into())
.unwrap_or_default()
.saturating_mul(num.into())
}
#[cfg(test)]
mod tests {
use super::*;
// This is a trivial example/check which just better explain explains the rationale
// behind the threshold.
//
// After this reading the formula should become obvious.
#[test]
fn ticket_id_threshold_trivial_check() {
// For an epoch with `s` slots we want to accept a number of tickets equal to ~s·r
let redundancy = 2;
let slots = 1000;
let attempts = 100;
let validators = 500;
let threshold = ticket_id_threshold(redundancy, slots, attempts, validators);
let threshold = threshold as f64 / TicketId::MAX as f64;
// We expect that the total number of tickets allowed to be submited
// is slots*redundancy
let avt = ((attempts * validators) as f64 * threshold) as u32;
assert_eq!(avt, slots * redundancy);
println!("threshold: {}", threshold);
println!("avt = {}", avt);
}
}
@@ -23,7 +23,7 @@ use sp_consensus_slots::Slot;
use sp_std::vec::Vec;
pub use sp_core::bandersnatch::{
ring_vrf::{RingContext, RingProver, RingVerifier, RingVrfSignature},
ring_vrf::{RingContext, RingProver, RingVerifier, RingVerifierData, RingVrfSignature},
vrf::{VrfInput, VrfOutput, VrfSignData, VrfSignature},
};