#[allow(unused_imports)]
use super::Authorizer;
use crate::{FixedVec, chain_params, opaque};
use bounded_collections::{BoundedVec, Get};
use codec::{Decode, Encode, MaxEncodedLen};
use core::sync::atomic::Ordering::Relaxed;
pub const GP_VERSION: &str = "0.8.0";
pub const JAM_COMMON_ERA: u64 = 1_735_732_800;
pub const MEMO_LEN: usize = 128;
#[doc(hidden)]
pub const MAX_PREIMAGE_LEN: usize = 32 * 1024 * 1024;
#[doc(hidden)]
pub const MAX_PREIMAGE_BLOB_LEN: usize = MAX_PREIMAGE_LEN - 8;
pub const PAGE_SIZE: u32 = 4096;
pub const SUFFIX_SKIP_LEN: usize = 6;
pub const PROVEN_PER_SEGMENT: usize = 1 << SUFFIX_SKIP_LEN;
pub const FOOTPRINT_STORAGE_OVERHEAD: u64 = 34;
pub const FOOTPRINT_PREIMAGE_OVERHEAD: u64 = 81;
pub type TicketAttempt = u8;
const DEFAULT_PARAMS: ProtocolParameters = ProtocolParameters::full();
chain_params! {
pub fn max_val_count() -> ValIndex {
(core_count() as usize * VALS_PER_CORE) as ValIndex
}
pub struct MaxValCount; impl Get<_> for _ {}
static AUTH_QUEUE_LEN: _ = _(DEFAULT_PARAMS.auth_queue_len as _);
pub fn auth_queue_len() -> usize;
pub struct AuthQueueLen; impl Get<_> for _ {}
static MIN_TURNAROUND_PERIOD: _ = _(DEFAULT_PARAMS.min_turnaround_period);
pub fn min_turnaround_period() -> Slot;
static MAX_WORK_ITEMS: _ = _(DEFAULT_PARAMS.max_work_items as _);
pub fn max_work_items() -> usize;
pub struct MaxWorkItems; impl Get<_> for _ {}
static MAX_IMPORTS: _ = _(DEFAULT_PARAMS.max_imports);
pub fn max_imports() -> u32;
pub struct MaxImports; impl Get<_> for _ {}
static MAX_EXPORTS: _ = _(DEFAULT_PARAMS.max_exports);
pub fn max_exports() -> u32;
static MAX_EXTRINSICS: _ = _(DEFAULT_PARAMS.max_extrinsics as _);
pub fn max_extrinsics() -> u32;
pub struct MaxExtrinsics; impl Get<_> for _ {}
static MAX_DEPENDENCIES: _ = _(DEFAULT_PARAMS.max_dependencies as _);
pub fn max_dependencies() -> usize;
pub struct MaxDependencies; impl Get<_> for _ {}
static MAX_INPUT: _ = _(DEFAULT_PARAMS.max_input);
pub fn max_input() -> u32;
pub struct MaxInput; impl Get<_> for _ {}
pub const SEGMENT_LEN: _ = 4104;
pub fn segment_len() -> usize;
pub struct SegmentLen; impl Get<_> for _ {}
static SLOT_PERIOD_SEC : _ = _(DEFAULT_PARAMS.slot_period_sec as _);
pub fn slot_period_sec() -> u64;
static ROTATION_PERIOD: _ = _(DEFAULT_PARAMS.rotation_period as _);
pub fn rotation_period() -> Slot;
static EPOCH_PERIOD: _ = _(DEFAULT_PARAMS.epoch_period);
pub fn epoch_period() -> Slot;
pub struct EpochPeriod; impl Get<_> for _ {}
static EPOCH_TAIL_START: _ = _(DEFAULT_PARAMS.epoch_tail_start);
pub fn epoch_tail_start() -> Slot;
static MAX_TICKETS_PER_BLOCK: _ = _(DEFAULT_PARAMS.max_tickets_per_block as _);
pub fn max_tickets_per_block() -> usize;
pub struct MaxTicketsPerBlock; impl Get<_> for _ {}
static DEPOSIT_PER_ACCOUNT: _ = _(DEFAULT_PARAMS.deposit_per_account);
pub fn deposit_per_account() -> Balance;
static DEPOSIT_PER_ITEM: _ = _(DEFAULT_PARAMS.deposit_per_item);
pub fn deposit_per_item() -> Balance;
static DEPOSIT_PER_BYTE: _ = _(DEFAULT_PARAMS.deposit_per_byte);
pub fn deposit_per_byte() -> Balance;
static BLOCK_GAS_LIMIT: _ = _(DEFAULT_PARAMS.block_gas_limit);
pub fn block_gas_limit() -> UnsignedGas;
static MAX_IS_AUTHORIZED_GAS: _ = _(DEFAULT_PARAMS.max_is_authorized_gas);
pub fn max_is_authorized_gas() -> UnsignedGas;
static MAX_REFINE_GAS: _ = _(DEFAULT_PARAMS.max_refine_gas);
pub fn max_refine_gas() -> UnsignedGas;
static MAX_ACCUMULATE_GAS: _ = _(DEFAULT_PARAMS.max_accumulate_gas);
pub fn max_accumulate_gas() -> UnsignedGas;
static MAX_SERVICE_CODE_SIZE: _ = _(DEFAULT_PARAMS.max_service_code_size as _);
pub fn max_service_code_size() -> usize;
static MAX_AUTHORIZER_CODE_SIZE: _ = _(DEFAULT_PARAMS.max_authorizer_code_size as _);
pub fn max_authorizer_code_size() -> usize;
static RECENT_BLOCK_COUNT: _ = _(DEFAULT_PARAMS.recent_block_count as _);
pub fn recent_block_count() -> Slot;
pub struct RecentBlockCount; impl Get<_> for _ {}
pub const VALS_PER_CORE: _ = 3;
pub fn _vals_per_core() -> usize;
pub struct _ValsPerCore; impl Get<_> for _ {}
static CORE_COUNT: _ = _(DEFAULT_PARAMS.core_count);
pub fn core_count() -> CoreIndex;
pub struct CoreCount; impl Get<_> for _ {}
pub fn max_export_segments() -> u32 {
max_exports() + max_exports().div_ceil(PROVEN_PER_SEGMENT as u32)
}
pub struct MaxExportSegments; impl Get<_> for _ {}
pub fn max_import_segments() -> u32 { max_imports() * 2 }
pub struct MaxImportSegments; impl Get<_> for _ {}
static AVAILABILITY_TIMEOUT: _ = _(DEFAULT_PARAMS.availability_timeout as _);
pub fn availability_timeout() -> Slot;
static AUTH_WINDOW: _ = _(DEFAULT_PARAMS.auth_window as _);
pub fn auth_window() -> usize;
pub struct AuthWindow; impl Get<_> for _ {}
static MAX_LOOKUP_ANCHOR_AGE: _ = _(DEFAULT_PARAMS.max_lookup_anchor_age);
pub fn max_lookup_anchor_age() -> Slot;
static MAX_REPORT_ELECTIVE_DATA: _ = _(DEFAULT_PARAMS.max_report_elective_data);
pub fn max_report_elective_data() -> u32;
pub struct MaxReportElectiveData; impl Get<_> for _ {}
}
pub fn is_valid_val_count(count: ValIndex) -> bool {
count >= MIN_VAL_COUNT &&
count <= max_val_count() &&
(count as usize).is_multiple_of(VALS_PER_CORE)
}
pub const MIN_VAL_COUNT: ValIndex = 2 * VALS_PER_CORE as ValIndex;
pub const MAX_CORE_COUNT: CoreIndex = 341;
pub const MAX_EPOCH_PERIOD: Slot = TicketAttempt::MAX as Slot * MIN_VAL_COUNT as Slot / 2;
pub fn tickets_per_validator(val_count: ValIndex) -> TicketAttempt {
((2 * epoch_period()).div_ceil(val_count as Slot)) as TicketAttempt
}
pub const fn erasure_recovery_count(val_count: usize) -> usize {
let mut recovery_count = val_count / VALS_PER_CORE + 1;
while !SEGMENT_LEN.is_multiple_of(POINT_LEN * recovery_count) {
recovery_count -= 1;
}
recovery_count
}
pub fn basic_piece_len(val_count: usize) -> usize {
erasure_recovery_count(val_count) * POINT_LEN
}
pub fn pieces_per_segment(val_count: usize) -> usize {
SEGMENT_LEN / basic_piece_len(val_count)
}
pub fn segment_slice_len(val_count: usize) -> usize {
SEGMENT_LEN / erasure_recovery_count(val_count)
}
pub const MAX_SEGMENT_SLICE_LEN: usize =
SEGMENT_LEN / erasure_recovery_count(MIN_VAL_COUNT as usize);
pub struct MaxSegmentSliceLen;
impl Get<u32> for MaxSegmentSliceLen {
fn get() -> u32 {
MAX_SEGMENT_SLICE_LEN as u32
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Encode, Decode, MaxEncodedLen)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ProtocolParameters {
pub deposit_per_item: Balance,
pub deposit_per_byte: Balance,
pub deposit_per_account: Balance,
pub core_count: CoreIndex,
pub min_turnaround_period: Slot,
pub epoch_period: Slot,
pub max_accumulate_gas: UnsignedGas,
pub max_is_authorized_gas: UnsignedGas,
pub max_refine_gas: UnsignedGas,
pub block_gas_limit: UnsignedGas,
pub recent_block_count: u16,
pub max_work_items: u16,
pub max_dependencies: u16,
pub max_tickets_per_block: u16,
pub max_lookup_anchor_age: Slot,
pub auth_window: u16,
pub slot_period_sec: u16,
pub auth_queue_len: u16,
pub rotation_period: u16,
pub max_extrinsics: u16,
pub availability_timeout: u16,
pub max_authorizer_code_size: u32,
pub max_input: u32,
pub max_service_code_size: u32,
pub max_imports: u32,
pub max_report_elective_data: u32,
pub transfer_memo_size: u32,
pub max_exports: u32,
pub epoch_tail_start: Slot,
}
impl ProtocolParameters {
pub const fn full() -> Self {
Self {
core_count: 341,
auth_queue_len: 80,
min_turnaround_period: 19_200,
max_work_items: 16,
max_imports: 3072,
max_exports: 3072,
max_extrinsics: 128,
max_dependencies: 8,
max_input: 13_791_360,
slot_period_sec: 6,
epoch_period: 600,
epoch_tail_start: 500,
rotation_period: 10,
block_gas_limit: 3_500_000_000,
recent_block_count: 8,
max_tickets_per_block: 16,
deposit_per_account: 100,
deposit_per_item: 10,
deposit_per_byte: 1,
max_is_authorized_gas: 50_000_000,
max_refine_gas: 5_000_000_000,
max_accumulate_gas: 10_000_000,
max_service_code_size: 4_000_000,
max_authorizer_code_size: 64_000,
availability_timeout: 5,
auth_window: 8,
max_lookup_anchor_age: 24 * 600,
max_report_elective_data: 48 * 1024,
transfer_memo_size: 128,
}
}
pub const fn large() -> Self {
let mut params = Self::full();
params.core_count = 85;
params.epoch_period = 150;
params.epoch_tail_start = 125;
params.rotation_period = 5;
params.block_gas_limit = 875_000_000;
params.max_tickets_per_block = 4;
params.max_refine_gas = 1_250_000_000;
params.min_turnaround_period = 4_800;
params.max_lookup_anchor_age = 3_600;
params
}
pub const fn mini() -> Self {
let mut params = Self::full();
params.core_count = 26;
params
}
pub const fn tiny() -> Self {
let mut params = Self::full();
params.core_count = 2;
params.epoch_period = 12;
params.epoch_tail_start = 10;
params.rotation_period = 4;
params.block_gas_limit = 20_000_000;
params.max_tickets_per_block = 3;
params.max_refine_gas = 1_000_000_000;
params.min_turnaround_period = 32;
params.max_lookup_anchor_age = 24;
params
}
pub const fn max_val_count(&self) -> ValIndex {
(self.core_count as usize * VALS_PER_CORE) as ValIndex
}
pub fn get() -> Self {
Self {
deposit_per_item: deposit_per_item(),
deposit_per_byte: deposit_per_byte(),
deposit_per_account: deposit_per_account(),
core_count: core_count(),
min_turnaround_period: min_turnaround_period(),
epoch_period: epoch_period(),
max_accumulate_gas: max_accumulate_gas(),
max_is_authorized_gas: max_is_authorized_gas(),
max_refine_gas: max_refine_gas(),
block_gas_limit: block_gas_limit(),
recent_block_count: recent_block_count() as _,
max_work_items: max_work_items() as _,
max_dependencies: max_dependencies() as _,
max_tickets_per_block: max_tickets_per_block() as _,
max_lookup_anchor_age: max_lookup_anchor_age(),
auth_window: auth_window() as _,
slot_period_sec: slot_period_sec() as _,
auth_queue_len: auth_queue_len() as _,
rotation_period: rotation_period() as _,
max_extrinsics: max_extrinsics() as _,
availability_timeout: availability_timeout() as _,
max_authorizer_code_size: max_authorizer_code_size() as _,
max_input: max_input(),
max_service_code_size: max_service_code_size() as _,
max_imports: max_imports() as _,
max_report_elective_data: max_report_elective_data(),
transfer_memo_size: MEMO_LEN as _,
max_exports: max_exports(),
epoch_tail_start: epoch_tail_start(),
}
}
pub fn validate(&self) -> Result<(), &'static str> {
if !self.epoch_period.is_multiple_of(self.rotation_period as Slot) {
return Err("`rotation_period` does not divide into `epoch_period`")
}
if self.epoch_tail_start >= self.epoch_period {
return Err("`epoch_tail_start` must be less than `epoch_period`")
}
if self.epoch_period > MAX_EPOCH_PERIOD {
return Err("`epoch_period` may be no larger than `MAX_EPOCH_PERIOD` (765)")
}
if self.core_count < 2 {
return Err("`core_count` must be at least 2, so the minimum validator set fits")
}
if self.core_count > MAX_CORE_COUNT {
return Err("`core_count` may be no larger than `MAX_CORE_COUNT` (341)")
}
if self.transfer_memo_size != MEMO_LEN as u32 {
return Err("`transfer_memo_size` must be equal tor `MEMO_LEN` (128)")
}
if recent_block_count() > 255 {
return Err("`recent_block_count` may be no larger than `BlockIndex::MAX` (255)")
}
Ok(())
}
pub fn apply(self) -> Result<(), &'static str> {
self.validate()?;
DEPOSIT_PER_ITEM.store(self.deposit_per_item, Relaxed);
DEPOSIT_PER_BYTE.store(self.deposit_per_byte, Relaxed);
DEPOSIT_PER_ACCOUNT.store(self.deposit_per_account, Relaxed);
MIN_TURNAROUND_PERIOD.store(self.min_turnaround_period, Relaxed);
EPOCH_PERIOD.store(self.epoch_period, Relaxed);
MAX_ACCUMULATE_GAS.store(self.max_accumulate_gas, Relaxed);
MAX_IS_AUTHORIZED_GAS.store(self.max_is_authorized_gas, Relaxed);
MAX_REFINE_GAS.store(self.max_refine_gas, Relaxed);
BLOCK_GAS_LIMIT.store(self.block_gas_limit, Relaxed);
RECENT_BLOCK_COUNT.store(self.recent_block_count as _, Relaxed);
MAX_WORK_ITEMS.store(self.max_work_items as _, Relaxed);
MAX_DEPENDENCIES.store(self.max_dependencies as _, Relaxed);
MAX_TICKETS_PER_BLOCK.store(self.max_tickets_per_block as _, Relaxed);
MAX_LOOKUP_ANCHOR_AGE.store(self.max_lookup_anchor_age as _, Relaxed);
AUTH_WINDOW.store(self.auth_window as _, Relaxed);
SLOT_PERIOD_SEC.store(self.slot_period_sec as _, Relaxed);
AUTH_QUEUE_LEN.store(self.auth_queue_len as _, Relaxed);
ROTATION_PERIOD.store(self.rotation_period as _, Relaxed);
MAX_EXTRINSICS.store(self.max_extrinsics as _, Relaxed);
AVAILABILITY_TIMEOUT.store(self.availability_timeout as _, Relaxed);
CORE_COUNT.store(self.core_count, Relaxed);
MAX_AUTHORIZER_CODE_SIZE.store(self.max_authorizer_code_size as _, Relaxed);
MAX_INPUT.store(self.max_input, Relaxed);
MAX_SERVICE_CODE_SIZE.store(self.max_service_code_size as _, Relaxed);
MAX_IMPORTS.store(self.max_imports, Relaxed);
MAX_REPORT_ELECTIVE_DATA.store(self.max_report_elective_data, Relaxed);
MAX_EXPORTS.store(self.max_exports, Relaxed);
EPOCH_TAIL_START.store(self.epoch_tail_start, Relaxed);
Ok(())
}
}
pub const POINT_LEN: usize = 2;
pub const fn byzantine_threshold(n: ValIndex) -> ValIndex {
n.saturating_sub(1) / 3
}
pub const fn supermajority_threshold(n: ValIndex) -> ValIndex {
n - byzantine_threshold(n)
}
#[doc(hidden)]
#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
pub struct MaxValSuperMajority;
impl Get<u32> for MaxValSuperMajority {
fn get() -> u32 {
supermajority_threshold(max_val_count()) as u32
}
}
pub type Slot = u32;
pub type ValIndex = u16;
pub type CoreIndex = u16;
pub type ServiceId = u32;
pub type Balance = u64;
pub type DoubleBalance = u128;
pub type SignedGas = i64;
pub type UnsignedGas = u64;
pub type DoubleGas = u128;
pub type Hash = [u8; 32];
opaque! {
pub struct HeaderHash(pub [u8; 32]);
pub struct CodeHash(pub [u8; 32]);
pub struct WorkPackageHash(pub [u8; 32]);
pub struct WorkReportHash(pub [u8; 32]);
pub struct PayloadHash(pub [u8; 32]);
pub struct StateRootHash(pub [u8; 32]);
pub struct MmrPeakHash(pub [u8; 32]);
pub struct AccumulateRootHash(pub [u8; 32]);
pub struct ExtrinsicHash(pub [u8; 32]);
pub struct AuthorizerHash(pub [u8; 32]);
pub struct SegmentTreeRoot(pub [u8; 32]);
pub struct SegmentHash(pub [u8; 32]);
pub struct MerkleNodeHash(pub [u8; 32]);
pub struct AnyHash(pub [u8; 32]);
pub struct Memo(pub [u8; MEMO_LEN]);
pub struct Authorization(pub Vec<u8>);
pub struct Code(pub Vec<u8>);
pub struct WorkPayload(pub Vec<u8>);
pub struct AuthConfig(pub Vec<u8>);
pub struct AnyVec(pub Vec<u8>);
pub struct WorkOutput(pub Vec<u8>);
pub struct AuthTrace(pub Vec<u8>);
pub struct OpaqueEd25519Public(pub [u8; 32]);
pub struct OpaqueBandersnatchPublic(pub [u8; 32]);
pub struct OpaqueBlsPublic(pub [u8; 144]);
pub struct OpaqueValidatorMetadata(pub [u8; 128]);
}
#[cfg(feature = "bytes")]
opaque! {
pub struct AnyBytes(pub Bytes);
}
pub type AuthQueue = FixedVec<AuthorizerHash, AuthQueueLen>;
pub type Segment = FixedVec<u8, SegmentLen>;
impl Segment {
pub fn as_array(&self) -> &[u8; SEGMENT_LEN] {
self.as_slice().try_into().expect("Segment length is fixed")
}
}
impl AsRef<[u8; SEGMENT_LEN]> for Segment {
fn as_ref(&self) -> &[u8; SEGMENT_LEN] {
self.as_array()
}
}
pub trait ToAny {
type Any;
fn any(&self) -> Self::Any;
fn into_any(self) -> Self::Any;
}
impl ToAny for [u8; 32] {
type Any = AnyHash;
fn any(&self) -> Self::Any {
AnyHash(*self)
}
fn into_any(self) -> Self::Any {
AnyHash(self)
}
}
impl ToAny for alloc::vec::Vec<u8> {
type Any = AnyVec;
fn any(&self) -> Self::Any {
AnyVec(self.clone())
}
fn into_any(self) -> Self::Any {
AnyVec(self)
}
}
impl ToAny for &[u8] {
type Any = AnyVec;
fn any(&self) -> Self::Any {
AnyVec(self.to_vec())
}
fn into_any(self) -> Self::Any {
AnyVec(self.to_vec())
}
}
impl MaxEncodedLen for Authorization {
fn max_encoded_len() -> usize {
BoundedVec::<u8, MaxInput>::max_encoded_len()
}
}
impl MaxEncodedLen for WorkPayload {
fn max_encoded_len() -> usize {
BoundedVec::<u8, MaxInput>::max_encoded_len()
}
}
impl MaxEncodedLen for AuthConfig {
fn max_encoded_len() -> usize {
BoundedVec::<u8, MaxInput>::max_encoded_len()
}
}
impl MaxEncodedLen for AuthTrace {
fn max_encoded_len() -> usize {
BoundedVec::<u8, MaxReportElectiveData>::max_encoded_len()
}
}
impl AuthTrace {
pub fn leak(self) -> &'static [u8] {
self.0.leak()
}
}
impl MaxEncodedLen for WorkOutput {
fn max_encoded_len() -> usize {
BoundedVec::<u8, MaxReportElectiveData>::max_encoded_len()
}
}
impl WorkOutput {
pub fn leak(self) -> &'static [u8] {
self.0.leak()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn protocol_parameters_fetch_encoding() {
for params in [
ProtocolParameters::full(),
ProtocolParameters::large(),
ProtocolParameters::mini(),
ProtocolParameters::tiny(),
] {
assert!(params.validate().is_ok(), "{params:?}");
let encoded = params.encode();
assert_eq!(encoded.len(), 7 * 8 + 11 * 4 + 11 * 2);
let decoded = ProtocolParameters::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded, params);
}
}
#[test]
fn erasure_recovery_count_matches_gp() {
assert_eq!(erasure_recovery_count(6), 3);
assert_eq!(erasure_recovery_count(78), 27);
assert_eq!(erasure_recovery_count(1023), 342);
for count in (6..=1023).step_by(3) {
let d = erasure_recovery_count(count);
assert!(d <= count / VALS_PER_CORE + 1);
assert!(SEGMENT_LEN.is_multiple_of(POINT_LEN * d), "v={count}");
}
}
#[test]
fn core_count_bounds_enforced() {
let mut params = ProtocolParameters::full();
assert!(params.validate().is_ok());
params.core_count = 1;
assert!(params.validate().is_err());
params.core_count = MAX_CORE_COUNT;
assert!(params.validate().is_ok());
params.core_count = MAX_CORE_COUNT + 1;
assert!(params.validate().is_err());
}
#[test]
fn ticket_attempts_matches_gp() {
assert_eq!(tickets_per_validator(max_val_count()), 2);
assert_eq!(tickets_per_validator(MIN_VAL_COUNT), 200);
for count in (MIN_VAL_COUNT as usize..=max_val_count() as usize).step_by(VALS_PER_CORE) {
let attempts = tickets_per_validator(count as ValIndex) as usize;
assert!(attempts * count >= 2 * epoch_period() as usize, "v={count}");
assert!((attempts - 1) * count < 2 * epoch_period() as usize, "v={count}");
}
}
}