From 0c939003d6ed7349a88768999833ea75d8b77e25 Mon Sep 17 00:00:00 2001 From: bkchr Date: Sun, 9 Jun 2024 00:59:50 +0000 Subject: [PATCH] deploy: 4d0a3443263e3b78d3eba9c29093bb1a358ba869 --- 404.html | 2 +- approved/0001-agile-coretime.html | 2 +- approved/0005-coretime-interface.html | 2 +- approved/0007-system-collator-selection.html | 2 +- approved/0008-parachain-bootnodes-dht.html | 2 +- approved/0010-burn-coretime-revenue.html | 2 +- ...12-process-for-adding-new-collectives.html | 2 +- ...uilder-and-core-runtime-apis-for-mbms.html | 2 +- ...rove-locking-mechanism-for-parachains.html | 2 +- approved/0022-adopt-encointer-runtime.html | 2 +- approved/0032-minimal-relay.html | 2 +- approved/0042-extrinsics-state-version.html | 2 +- .../0043-storage-proof-size-hostfunction.html | 2 +- approved/0045-nft-deposits-asset-hub.html | 2 +- ...047-assignment-of-availability-chunks.html | 2 +- approved/0048-session-keys-runtime-api.html | 2 +- approved/0050-fellowship-salaries.html | 2 +- ...0056-one-transaction-per-notification.html | 2 +- .../0059-nodes-capabilities-discovery.html | 2 +- approved/0078-merkleized-metadata.html | 2 +- ...-general-transaction-extrinsic-format.html | 2 +- index.html | 2 +- introduction.html | 2 +- print.html | 1708 ++++++++--------- proposed/0015-market-design-revisit.html | 6 +- proposed/0089-flexible-inflation.html | 6 +- proposed/0091-dht-record-creation-time.html | 2 +- searchindex.js | 2 +- searchindex.json | 2 +- ...04-remove-unnecessary-allocator-usage.html | 2 +- ...namic-pricing-for-bulk-coretime-sales.html | 2 +- ...09-improved-net-light-client-requests.html | 2 +- ...irmation-period-duration-modification.html | 6 +- .../0026-sassafras-consensus.html | 10 +- ...-absolute-location-account-derivation.html | 6 +- ...ction-voting-delegation-modifications.html | 2 +- stale/0044-rent-based-registration.html | 2 +- stale/0054-remove-heap-pages.html | 2 +- stale/0070-x-track-kusamanetwork.html | 2 +- stale/0073-referedum-deposit-track.html | 2 +- stale/0074-stateful-multisig-pallet.html | 2 +- ...gth-of-identity-pgp-fingerprint-value.html | 2 +- ...t-purchaser-reputation-reserved-cores.html | 2 +- 43 files changed, 908 insertions(+), 908 deletions(-) rename {proposed => stale}/0026-sassafras-consensus.html (89%) diff --git a/404.html b/404.html index 90e9c35..589aea0 100644 --- a/404.html +++ b/404.html @@ -91,7 +91,7 @@ diff --git a/approved/0001-agile-coretime.html b/approved/0001-agile-coretime.html index bd3a8c4..10a60bc 100644 --- a/approved/0001-agile-coretime.html +++ b/approved/0001-agile-coretime.html @@ -90,7 +90,7 @@ diff --git a/approved/0005-coretime-interface.html b/approved/0005-coretime-interface.html index 6a4c6fa..189225b 100644 --- a/approved/0005-coretime-interface.html +++ b/approved/0005-coretime-interface.html @@ -90,7 +90,7 @@ diff --git a/approved/0007-system-collator-selection.html b/approved/0007-system-collator-selection.html index 534c323..331f5a4 100644 --- a/approved/0007-system-collator-selection.html +++ b/approved/0007-system-collator-selection.html @@ -90,7 +90,7 @@ diff --git a/approved/0008-parachain-bootnodes-dht.html b/approved/0008-parachain-bootnodes-dht.html index eb1ce26..3ba5b1d 100644 --- a/approved/0008-parachain-bootnodes-dht.html +++ b/approved/0008-parachain-bootnodes-dht.html @@ -90,7 +90,7 @@ diff --git a/approved/0010-burn-coretime-revenue.html b/approved/0010-burn-coretime-revenue.html index 945a1a5..8c3cd20 100644 --- a/approved/0010-burn-coretime-revenue.html +++ b/approved/0010-burn-coretime-revenue.html @@ -90,7 +90,7 @@ diff --git a/approved/0012-process-for-adding-new-collectives.html b/approved/0012-process-for-adding-new-collectives.html index 884b99d..f9b31ab 100644 --- a/approved/0012-process-for-adding-new-collectives.html +++ b/approved/0012-process-for-adding-new-collectives.html @@ -90,7 +90,7 @@ diff --git a/approved/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html b/approved/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html index 36333ce..b624627 100644 --- a/approved/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html +++ b/approved/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html @@ -90,7 +90,7 @@ diff --git a/approved/0014-improve-locking-mechanism-for-parachains.html b/approved/0014-improve-locking-mechanism-for-parachains.html index db2dc7f..17f4fd8 100644 --- a/approved/0014-improve-locking-mechanism-for-parachains.html +++ b/approved/0014-improve-locking-mechanism-for-parachains.html @@ -90,7 +90,7 @@ diff --git a/approved/0022-adopt-encointer-runtime.html b/approved/0022-adopt-encointer-runtime.html index 1bbbfa0..0f7a4c5 100644 --- a/approved/0022-adopt-encointer-runtime.html +++ b/approved/0022-adopt-encointer-runtime.html @@ -90,7 +90,7 @@ diff --git a/approved/0032-minimal-relay.html b/approved/0032-minimal-relay.html index 64c72d2..5a6b098 100644 --- a/approved/0032-minimal-relay.html +++ b/approved/0032-minimal-relay.html @@ -90,7 +90,7 @@ diff --git a/approved/0042-extrinsics-state-version.html b/approved/0042-extrinsics-state-version.html index 3c82b17..303eac1 100644 --- a/approved/0042-extrinsics-state-version.html +++ b/approved/0042-extrinsics-state-version.html @@ -90,7 +90,7 @@ diff --git a/approved/0043-storage-proof-size-hostfunction.html b/approved/0043-storage-proof-size-hostfunction.html index dc47386..fc622ed 100644 --- a/approved/0043-storage-proof-size-hostfunction.html +++ b/approved/0043-storage-proof-size-hostfunction.html @@ -90,7 +90,7 @@ diff --git a/approved/0045-nft-deposits-asset-hub.html b/approved/0045-nft-deposits-asset-hub.html index 6ab34ee..4aba170 100644 --- a/approved/0045-nft-deposits-asset-hub.html +++ b/approved/0045-nft-deposits-asset-hub.html @@ -90,7 +90,7 @@ diff --git a/approved/0047-assignment-of-availability-chunks.html b/approved/0047-assignment-of-availability-chunks.html index 9c9d5bd..f70dbc1 100644 --- a/approved/0047-assignment-of-availability-chunks.html +++ b/approved/0047-assignment-of-availability-chunks.html @@ -90,7 +90,7 @@ diff --git a/approved/0048-session-keys-runtime-api.html b/approved/0048-session-keys-runtime-api.html index 6ac855d..d8d6440 100644 --- a/approved/0048-session-keys-runtime-api.html +++ b/approved/0048-session-keys-runtime-api.html @@ -90,7 +90,7 @@ diff --git a/approved/0050-fellowship-salaries.html b/approved/0050-fellowship-salaries.html index 95c76c9..de06428 100644 --- a/approved/0050-fellowship-salaries.html +++ b/approved/0050-fellowship-salaries.html @@ -90,7 +90,7 @@ diff --git a/approved/0056-one-transaction-per-notification.html b/approved/0056-one-transaction-per-notification.html index 6f7ea91..1c8a717 100644 --- a/approved/0056-one-transaction-per-notification.html +++ b/approved/0056-one-transaction-per-notification.html @@ -90,7 +90,7 @@ diff --git a/approved/0059-nodes-capabilities-discovery.html b/approved/0059-nodes-capabilities-discovery.html index ec4ae28..0dca852 100644 --- a/approved/0059-nodes-capabilities-discovery.html +++ b/approved/0059-nodes-capabilities-discovery.html @@ -90,7 +90,7 @@ diff --git a/approved/0078-merkleized-metadata.html b/approved/0078-merkleized-metadata.html index 37c08d0..bbf1487 100644 --- a/approved/0078-merkleized-metadata.html +++ b/approved/0078-merkleized-metadata.html @@ -90,7 +90,7 @@ diff --git a/approved/0084-general-transaction-extrinsic-format.html b/approved/0084-general-transaction-extrinsic-format.html index c631560..1d36cda 100644 --- a/approved/0084-general-transaction-extrinsic-format.html +++ b/approved/0084-general-transaction-extrinsic-format.html @@ -90,7 +90,7 @@ diff --git a/index.html b/index.html index 40da9a4..2dd5d2e 100644 --- a/index.html +++ b/index.html @@ -90,7 +90,7 @@ diff --git a/introduction.html b/introduction.html index 40da9a4..2dd5d2e 100644 --- a/introduction.html +++ b/introduction.html @@ -90,7 +90,7 @@ diff --git a/print.html b/print.html index bbeccfa..06bda06 100644 --- a/print.html +++ b/print.html @@ -91,7 +91,7 @@ @@ -291,859 +291,6 @@ detailing proposed changes to the technical implementation of the Polkadot netwo

Additionally, I want to express a special thanks to Samuel Haefner and Shahar Dobzinski for fruitful discussions and helping me structure my thoughts.

Unresolved Questions

The technical feasability needs to be assessed.

-

(source)

-

Table of Contents

- -

RFC-0026: Sassafras Consensus Protocol

-
- - - -
Start DateSeptember 06, 2023
DescriptionSassafras consensus protocol specification
AuthorsDavide Galassi
-
-

Abstract

-

Sassafras is a novel consensus protocol designed to address the recurring -fork-related challenges encountered in other lottery-based protocols.

-

The protocol aims to create a mapping between each epoch's slots and the -validators set while ensuring that the identity of validators assigned to -the slots remains undisclosed until the slot is actively claimed during block -production.

-

1. Motivation

-

Sassafras Protocol has been rigorously detailed in a comprehensive -research paper authored by the -Web3 foundation research team.

-

This RFC is primarily intended to detail the critical implementation aspects -vital for ensuring interoperability and to clarify certain aspects that are -left open by the research paper and thus subject to interpretation during -implementation.

-

1.1. Relevance to Implementors

-

This RFC focuses on providing implementors with the necessary insights into the -protocol's operation.

-

In instances of inconsistency between this document and the research paper, -this RFC should be considered authoritative to eliminate ambiguities and ensure -interoperability.

-

1.2. Supporting Sassafras for Polkadot

-

Beyond promoting interoperability, this RFC also aims to facilitate the -implementation of Sassafras within the Polkadot ecosystem.

-

Although the specifics of deployment strategies are beyond the scope of this -document, it lays the groundwork for the integration of Sassafras into the -Polkadot network.

-

2. Stakeholders

-

2.1. Blockchain Developers

-

Developers responsible for creating blockchains who intend to leverage the -benefits offered by the Sassafras Protocol.

-

2.2. Polkadot Ecosystem Contributors

-

Developers contributing to the Polkadot ecosystem, both relay-chain and -para-chains.

-

The protocol will have a central role in the next generation block authoring -consensus systems.

-

3. Notation

-

This section outlines the notation and conventions adopted throughout this -document to ensure clarity and consistency.

-

3.1. Data Structures Definitions

-

Data structures are primarily defined using standard ASN.1, -syntax with few exceptions

-

To ensure interoperability of serialized structures, the order of the fields -must match the structures definitions found within this document.

-

3.2. Types Alias

-

We define some type alias to make ASN.1 syntax more intuitive.

- -

3.2. Pseudo-Code

-

It is advantageous to make use of code snippets as part of the protocol -description. As a convention, the code is formatted in a style similar to -Rust, and can make use of the following set of predefined functions:

-

Syntax:

- -

3.3. Incremental Introduction of Types and Functions

-

More types and helper functions are introduced incrementally as they become -relevant within the document's context.

-

4. Protocol Introduction

-

The timeline is segmented into a sequentially ordered sequence of slots. -This entire sequence of slots is then further partitioned into distinct segments -known as epochs.

-

The Sassafras protocol aims to map each slot within an epoch to the designated -validators for that epoch, utilizing a ticketing system.

-

The protocol operation can be roughly divided into five phases:

-

4.1. Submission of Candidate Tickets

-

Each of the validators associated to the target epoch generates and submits -a set of candidate tickets to the blockchain. Every ticket is bundled with an -anonymous proof of validity.

-

4.2. Validation of Candidate Tickets

-

Each candidate ticket undergoes a validation process for the associated validity -proof and compliance with other protocol-specific constraints.

-

4.3. Tickets and Slots Binding

-

After collecting all valid candidate tickets, a deterministic method is used to -uniquely associate a subset of these tickets with the slots of the target epoch.

-

4.4. Claim of Ticket Ownership

-

During the block production phase of the target epoch, validators are required -to demonstrate their ownership of tickets. This step discloses the identity of -the ticket owners.

-

5. Bandersnatch VRFs Cryptographic Primitives

-

It's important to note that this section is not intended to serve as an -exhaustive exploration of the mathematically intensive foundations of the -cryptographic primitive. Rather, its primary aim is to offer a concise and -accessible explanation of the primitive's role and usage which is relevant -within the scope of this RFC.

-

For an in-depth explanation, refer to the Bandersnatch VRF -spec

-

Bandersnatch VRF can be used in two flavors:

- -

Together with the input, which determines the signed VRF output, both the -flavors offer the capability to sign some arbitrary additional data (extra) -which doesn't contribute to the VRF output.

-

5.1 Plain VRF Interface

-

Function to construct a VrfSignature.

-
#![allow(unused)]
-fn main() {
-    fn vrf_sign(
-        secret: BandernatchSecretKey,
-        input: OctetString,
-        extra: OctetString,
-    ) -> VrfSignature
-}
-

Function for signature verification returning a Boolean value indicating the -validity of the signature (1 on success):

-
#![allow(unused)]
-fn main() {
-    fn vrf_verify(
-        public: PublicKey,
-        input: OctetString,
-        extra: OctetString,
-        signature: VrfSignature
-    ) -> Unsigned<1>;
-}
-

Function to derive the VRF output from input and secret:

-
#![allow(unused)]
-fn main() {
-    fn vrf_output(
-        secret: BandernatchSecretKey,
-        input: OctetString,
-    ) -> OctetString<32>;
-}
-

Function to derive the VRF output from a signature:

-
#![allow(unused)]
-fn main() {
-    fn vrf_signed_output(
-        signature: VrfSignature,
-    ) -> OctetString<32>;
-}
-

Note that the following condition is always satisfied:

-
#![allow(unused)]
-fn main() {
-    let signature = vrf_sign(secret, input, extra);
-    vrf_output(secret, input) == vrf_signed_output(signature)
-}
-

In this document, the types SecretKey, PublicKey and VrfSignature are -intentionally left undefined. Their definitions can be found in the Bandersnatch -VRF specification and related documents.

-

5.4.2. Ring VRF Interface

-

Function to construct RingVrfSignature.

-
#![allow(unused)]
-fn main() {
-    fn ring_vrf_sign(
-        secret: SecretKey,
-        prover: RingProverKey,
-        input: OctetString,
-        extra: OctetString,
-    ) -> RingVrfSignature;
-}
-

Function for signature verification returning a Boolean value -indicating the validity of the signature (1 on success). -Note that this function doesn't require the signer's public key.

-
#![allow(unused)]
-fn main() {
-    fn ring_vrf_verify(
-        verifier: RingVerifierKey,
-        input: OctetString,
-        extra: OctetString,
-        signature: RingVrfSignature,
-    ) -> Unsigned<1>;
-}
-

Function to derive the VRF output from a ring signature:

-
#![allow(unused)]
-fn main() {
-    fn ring_vrf_signed_output(
-        signature: RingVrfSignature,
-    ) -> OctetString<32>;
-}
-

Note that the following condition is always satisfied:

-
#![allow(unused)]
-fn main() {
-    let signature = vrf_sign(secret, input, extra);
-    let ring_signature = ring_vrf_sign(secret, prover, input, extra);
-    vrf_signed_output(plain_signature) == ring_vrf_signed_output(ring_signature);
-}
-

In this document, the types RingProverKey, RingVerifierKey, and -RingSignature are intentionally left undefined. Their definitions can be found -in the Bandersnatch VRF specification and related documents.

-

6. Sassafras Protocol

-

6.1. Protocol Configuration

-

The ProtocolConfiguration is constant and primarily influences certain checks -carried out during tickets validation. It is defined as:

-
#![allow(unused)]
-fn main() {
-    ProtocolConfiguration ::= Sequence {
-        epoch_length: Unsigned32,
-        attempts_number: Unsigned8,
-        redundancy_factor: Unsigned8,
-    }
-}
-

Where:

- -

The attempts_number influences the anonymity of block producers. As all -published tickets have a public attempt number less than attempts_number, -all the tickets which share the attempt number value must belong to different -block producers, which reduces anonymity late as we approach the epoch tail. -Bigger values guarantee more anonymity but also more computation.

-

Details about how exactly these parameters drives the ticket validity -probability can be found in section 6.2.2.

-

6.2. Header Digest Log

-

Each block's header contains a Digest, which is a sequence of DigestItems -where the protocol is allowed to append any information required for correct -progress.

-

The structures are defined to be quite generic and usable by other subsystems:

-
#![allow(unused)]
-fn main() {
-    DigestItem ::= Sequence {
-        id: OctetString<4>,
-        data: OctetString
-    }
-
-    Digest ::= Sequence<DigestItem>
-}
-

For Sassafras related DiegestItems the id is set to the constant ASCII string "SASS".

-

6.3. On-Chain Randomness

-

On-Chain, we maintain a sequence with four randomness entries.

-
#![allow(unused)]
-fn main() {
-    RandomnessBuffer ::= Sequence<OctetString<32>, 4>
-}
-

During epoch N

- -

The buffer is entries are updated after block execution.

-

6.4. Epoch's First Block

-

The first block produced during an epoch N must include a descriptor for some -of the subsequent epoch (N+1) parameters. This descriptor is defined as:

-
#![allow(unused)]
-fn main() {
-    NextEpochDescriptor ::= Sequence {
-        randomness: OctetString<32>,
-        authorities: Sequence<PublicKey>,
-    }
-}
-

Where:

- -

This descriptor is SCALE encoded and embedded in the block header's digest -log.

-

A special case arises for the first block of epoch 0, which each node produces -independently during the genesis phase. In this case, the NextEpochDescriptor -relative to epoch 1 is shared within the second block, as outlined in section -6.4.1.

-

6.4.1. Startup Parameters

-

Some of the initial parameters for the first epoch, Epoch #0, are set through -the genesis configuration, which is defined as:

-
#![allow(unused)]
-fn main() {
-    GenesisConfig ::= Sequence {
-        authorities: Sequence<PublicKey>,
-    }
-}
-

The on-chain randomness accumulator is initialized only after the genesis -block is produced, and its value is set to the hash of the genesis block.

-

Since block #0 is generated locally by each node as part of the genesis -process, the first block that a validator explicitly produces for Epoch -#0 is block #1. Therefore, block #1 is required to contain the -NextEpochDescriptor for the following epoch, Epoch #1.

-

The NextEpochDescriptor for Epoch #1:

- -

6.5. Offchain Tickets Creation and Submission

-

During epoch N, each validator associated to epoch N+2 constructs a set of -tickets which may be eligible (6.5.2) to be delivered -to on-chain proxies, which are the validators scheduled for epoch N+1.

-

These tickets are constructed using the on-chain randomness snapshot taken -after the execution of the last block of epoch N-1 together with other -parameters and aims to secure ownership of one or more slots of epoch N+2.

-

Each validator is allowed to submit a maximum number of tickets, constrained by -attempts_number field of the ProtocolConfiguration.

-

The ideal timing for the candidate validator to start constructing the tickets -is subject to strategy. A recommended approach is to initiate tickets creation -once the last block of epoch N-1 is either probabilistically or, even better, -deterministically finalized. This delay is suggested to prevent wasting -resources creating tickets that might become unusable if a different chain -branch is chosen as the canonical one.

-

As said, proxies collect tickets during epoch N and when epoch N+1 begins -the collected tickets are submitted on-chain. -TODO (inherents/ unsigned ext?).

-

6.5.1. Ticket Identifier

-

Each ticket has an associated identifier defined as:

-
#![allow(unused)]
-fn main() {
-    TicketId ::= OctetString<32>;
-}
-

The value of the TicketId is completely determined by the output of the -Bandersnatch VRF with the following unbiasable input:

-
#![allow(unused)]
-fn main() {
-    let ticket_vrf_input = CONCAT(
-        BYTES("sassafras_ticket"),
-        GET(randomness_buffer, 1),
-        BYTES(attempt_index)
-    );
-
-    let ticket_id = vrf_output(AUTHORITY_SECRET_KEY, ticket_vrf_input);
-}
-

Where:

- -

6.5.2. Tickets Threshold

-

A TicketId value is valid for on-chain submission if its value, when interpreted -as a big-endian 256-bit integer normalized as a float within the range [0..1], -is less than the ticket threshold computed as:

-
T = (r·s)/(a·v)
-
-

Where:

- -

In an epoch with s slots, the goal is to achieve an expected number of tickets -for block production equal to r·s.

-

It's crucial to ensure that the probability of having fewer than s winning -tickets is very low, even in scenarios where up to 1/3 of the authorities -might be offline.

-

To accomplish this, we first define the winning probability of a single ticket -as T = (r·s)/(a·v).

-

Let n be the actual number of participating validators, where v·2/3 ≤ n ≤ v.

-

These n validators each make a attempts, for a total of a·n attempts.

-

Let X be the random variable associated to the number of winning tickets, then -its expected value is:

-
E[X] = T·a·n = (r·s·n)/v
-
-

By setting r = 2, we get

-
s·4/3 ≤ E[X] ≤ s·2
-
-

Using Bernestein's inequality we get Pr[X < s] ≤ e^(-s/21).

-

For instance, with s = 600 this results in Pr[X < s] < 4·10⁻¹³. -Consequently, this approach offers considerable tolerance for offline nodes and -ensures that all slots are likely to be filled with tickets.

-

For more details about threshold formula please refer to the -probabilities and parameters -paragraph in the Web3 foundation description of the protocol.

-

6.5.3. Ticket Body

-

Every ticket candidate has an associated body, defined as:

-
#![allow(unused)]
-fn main() {
-    TicketBody ::= Sequence {
-        attempt_index: Unsigned8,
-        opaque: OctetString,
-    }
-}
-

Where:

- -

6.5.4. Ticket Signature

-

TicketBody must be signed using the Bandersnatch Ring VRF flavor (5.4.2).

-
#![allow(unused)]
-fn main() {
-    let signature = ring_vrf_sign(
-        secret_key,
-        ring_prover_key
-        ticket_vrf_input,
-        ENCODE(ticket_body),
-    );
-}
-

ring_prover_key object is constructed using the set of public keys which -belong to the target epoch's validators and the zk-SNARK context parameters -(for more details refer to the Bandersnatch VRFs specification).

-

Finally, the body and the ring signature are combined within the TicketEnvelope:

-
#![allow(unused)]
-fn main() {
-    TicketEnvelope ::= Sequence {
-        ticket_body: TicketBody,
-        ring_signature: RingVrfSignature
-    }   
-}
-

6.6. Onchain Tickets Validation

-

All the actions in the steps described by this paragraph are executed by -on-chain code.

-

Validation rules:

-
    -
  1. -

    Ring signature is verified using the on-chain ring_verifier_key derived by the -static ring context parameters and the next epoch validators public keys.

    -
  2. -
  3. -

    Ticket identifier is locally recomputed from the RingVrfSignature and its value -is checked to be less than the tickets' threshold.

    -
  4. -
  5. -

    Tickets submissions can't occur within a block part of the epoch's tail, which -are a given number of the slots at the end of the epoch. The tail length is a -configuration value (e.g. 1/6 of epoch length) part of the configuration. -This constraint is to give time to the on-chain tickets to be probabilistically -(or even better deterministically) finalized and thus further reduce the fork chances.

    -
  6. -
  7. -

    All tickets which are proposed within a block must be valid and all of them -must end up in the on-chain queue. That is, no submitted ticket should be -discarded.

    -
  8. -
  9. -

    No duplicates are allowed.

    -
  10. -
-

If at least one of the checks fails then the block must be discarded.

-

Valid tickets bodies, together with the ticket identifiers, are all persisted on-chain -and kept incrementally sorted according to the TicketId interpreted as a 256-bit -big-endian unsigned integer.

-

Pseudo-code for ticket validation for steps 1 and 2:

-
#![allow(unused)]
-fn main() {
-    let ticket_vrf_input = CONCAT(
-        BYTES("sassafras_ticket"),
-        GET(randomness_buffer, 2),
-        BYTES(envelope.body.attempt_index)
-    );
-
-    let result = ring_vrf_verify(
-        verifier,
-        ticket_vrf_input,
-        ENCODE(ticket_body),
-        envelope.ring_signature
-    );
-    assert(result == 1);
-
-    let ticket_id = ring_vrf_signed_output(envelope.ring_signature);
-    assert(ticket_id < ticket_threshold);
-}
-

6.7. Ticket-Slot Binding

-

Before the beginning of the claiming phase (i.e. what we've called the target -epoch), the on-chain list of tickets must be associated with the next epoch's -slots such that there must be at most one ticket per slot.

-

Given an ordered sequence of tickets [t₀, t₁, ..., tₙ] to be assigned to -n slots, the tickets are allocated according to the following outside-in -strategy:

-
    slot_index  : [  0,  1,  2,  3 ,  ... ]
-    tickets     : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
-
-

Here slot-index is a relative value computed as:

-
slot_index = slot - epoch_start_slot
-
-

The association between each ticket and a slot is recorded on-chain and thus -is public. What remains confidential is the identity of the ticket's author, and -consequently, who possesses the validator to claim the corresponding slot. This -information is known only to the author of the ticket.

-

If the number of published tickets is less than the number of epoch slots, -some orphan slots in the end of the epoch will remain unbounded to any ticket. -For claiming strategy refer to 6.8.2. -Note that this situation always apply to the first epochs after genesis.

-

6.8. Slot Claim

-

With tickets bound to epoch slots, every validator acquires information about -the slots for which they are supposed to produce a block.

-

The procedure for slot claiming depends on whether a given slot has an -associated ticket according to the on-chain state.

-

If a slot is associated with a ticket, the primary authoring method is used. -Conversely, the protocol resorts to the secondary method as a fallback.

-

6.8.1. Primary Method

-

We can proceed to claim a slot using the primary method if we are the -legit owner of the ticket associated to the given slot.

-

Let randomness_buffer be the instance of RandomnessBuffer stored in the -chain state and ticket_body be the TicketBody that is associated to the -slot to claim, the VRF input for slot claiming is constructed as:

-
#![allow(unused)]
-fn main() {
-    let seal_vrf_input = CONCAT(
-        BYTES("sassafras_ticket"),
-        GET(randomness_buffer, 3),
-        BYTES(ticket_body.attempt_index)
-    );
-}
-

This seal_vrf_input, when signed with the correct validator secret key must -generate the same TicketId associated on-chain to the target slot.

-

6.8.2. Secondary Method

-

Given that the authorities registered on-chain are kept in an ordered list, -the index of the validator which has the privilege to claim an orphan slot -is given by the following procedure:

-
#![allow(unused)]
-fn main() {
-    let hash_input = CONCAT(
-        GET(randomness_buffer, 2),
-        relative_slot_index,
-    );
-    let hash = BLAKE2(hash_input);
-    let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
-    let index = DECODE<Unsigned32>(index_bytes) % LENGTH(authorities);
-}
-

With relative_slot_index the slot offset relative to the epoch's start and authorities -the Sequence of current epoch validators.

-

Let randomness_buffer be the instance of RandomnessBuffer stored in on-chain state -then the VRF input for slot claiming is constructed as:

-
#![allow(unused)]
-fn main() {
-    let seal_vrf_input = CONCAT(
-        BYTES("sassafras_fallback"),
-        GET(randomness_buffer, 3),
-    );
-}
-

6.8.3. Claim Data

-

The slot claim data is a digest entry which contains additional information -which is required by the protocol in order to verify the block:

-
#![allow(unused)]
-fn main() {
-    ClaimData ::= Sequence {
-        slot: Unsigned32,
-        validator_index: Unsigned32,
-        randomness_source: VrfSignature,
-    }
-}
- -

Given the seal_vrf_input constructed using the primary or secondary method, -the claim is derived as follows:

-
#![allow(unused)]
-fn main() {
-    let randomness_vrf_input = CONCAT(
-        BYTES("sassafras_randomness"),
-        vrf_output(AUTHORITY_SECRET_KEY, seal_vrf_input)
-    );
-
-    let randomness_source = vrf_sign(
-        AUTHORITY_SECRET_KEY,
-        randomness_vrf_input,
-        []
-    );
-
-    let claim = ClaimData {
-        slot,
-        validator_index,
-        randomness_source,
-    }
-}
-

The claim object is SCALE encoded and pushed into the header digest log.

-

6.8.4. Block Seal

-

A block is sealed as follows:

-
#![allow(unused)]
-fn main() {
-    let unsealed_header_bytes = ENCODE(header);
-
-    let seal = vrf_sign(
-        AUTHORITY_SECRET_KEY,
-        seal_vrf_input,
-        unsealed_header_bytes
-    );
-
-    PUSH(header.digest, ENCODE(seal));
-}
-

With header the block's header without the seal digest log entry.

-

The seal object is a VrfSignature instance, which is SCALE encoded and -pushed as the last entry of the block's header digest log.

-

6.9. Slot Claim Verification

-

The last entry is extracted from the header digest log, and is interpreted as -the seal VrfSignature. The unsealed header is then SCALE encoded in order to -be verified.

-

The next entry is extracted from the header digest log, and is interpreted as a -ClaimData instance.

-

The validity of the signatures is then verified using as the public key the -validator key corresponding to the validator_index found in the ClaimData, -together with the VRF input (which depends on primary/secondary method) and -additional data expected to have been used by the block author.

-
#![allow(unused)]
-fn main() {
-    let seal_signature = DECODE<VrfSignature>(POP(header.digest));
-    let unsealed_header_bytes = ENCODE(header);
-    let claim_data = DECODE<ClaimData>(POP(header.digest));
-
-    let public_key = GET(authorities, claim_data.validator_index);
-
-    let result = vrf_verify(
-        public_key,
-        seal_vrf_input,
-        unsealed_header_bytes,
-        seal_signature
-    );
-    assert(result == 1);
-
-    let randomness_vrf_input = vrf_signed_output(seal_signature);
-
-    let result = vrf_verify(
-        public_key,
-        randomness_vrf_input,
-        [],
-        claim_data.randomness_source
-    );
-    assert(result == 1);
-}
-

With:

- -

If signatures verification is successful, then the verification process diverges -based on whether the slot is associated with a ticket according to the on-chain -state.

-

6.9.1. Primary Method

-

For slots tied to a ticket, the primary verification method is employed. -This method verifies ticket ownership using the TicketId associated to the slot.

-
#![allow(unused)]
-fn main() {
-    let ticket_id = vrf_signed_output(seal_signature);
-    assert(ticket_id == expected_ticket_id);
-}
-

With expected_ticket_id the ticket identifier committed on-chain together -with the associated ticket_body.

-

6.9.2. Secondary Method

-

If the slot doesn't have any associated ticket then the validator index contained in -the claim data must match the one given by the procedure outlined in section -6.8.2.

-

6.10. Randomness Accumulator

-

The randomness accumulator is updated using the randomness_source signature found -within the ClaimData object.

-

In particular, fresh randomness is derived and accumulated after block -execution as follows:

-
#![allow(unused)]
-fn main() {
-    let fresh_randomness = vrf_signed_output(claim.randomness_source);  
-
-    let prev_accumulator = POP(randomness_buffer);
-    let curr_accumulator = BLAKE2(CONCAT(randomness_accumulator, fresh_randomness));
-    PUSH(randomness_buffer, curr_accumulator);
-}
-

7. Drawbacks

-

None

-

8. Testing, Security, and Privacy

-

It is critical that implementations of this RFC undergo thorough testing on -test networks.

-

A security audit may be desirable to ensure the implementation does not -introduce unwanted side effects.

-

9. Performance, Ergonomics, and Compatibility

-

9.1. Performance

-

Adopting Sassafras consensus marks a significant improvement in reducing the -frequency of short-lived forks.

-

Forks are eliminated by design. Forks may only result from network disruptions -or protocol attacks. In such cases, the choice of which fork to follow upon -recovery is clear-cut, with only one valid option.

-

9.2. Ergonomics

-

No specific considerations.

-

9.3. Compatibility

-

The adoption of Sassafras affects the native client and thus can't be introduced -just via a runtime upgrade.

-

A deployment strategy should be carefully engineered for live networks.

-

This subject is left open for a dedicated RFC.

-

10. Prior Art and References

- -

11. Unresolved Questions

-

None

- -

While this RFC lays the groundwork and outlines the core aspects of the -protocol, several crucial topics remain to be addressed in future RFCs.

-

12.1. Interactions with On-Chain Code

- -

12.2. Deployment Strategies

- -

12.3. ZK-SNARK URS Initialization

- -

12.4. Anonymous Submission of Tickets.

-

(source)

Table of Contents