diff --git a/404.html b/404.html index 5402574..a60c357 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 11b7e4b..0435c3a 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 e1da946..c002f83 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 0ceda64..ba5cc67 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 9821fb9..bce336e 100644 --- a/approved/0008-parachain-bootnodes-dht.html +++ b/approved/0008-parachain-bootnodes-dht.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 82ffe1e..8b07149 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/0014-improve-locking-mechanism-for-parachains.html b/approved/0014-improve-locking-mechanism-for-parachains.html index 7ee1595..fe0bb44 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 ba3ba8a..63d7687 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 3f7ec9c..7d8f456 100644 --- a/approved/0032-minimal-relay.html +++ b/approved/0032-minimal-relay.html @@ -90,7 +90,7 @@ diff --git a/approved/0050-fellowship-salaries.html b/approved/0050-fellowship-salaries.html index fe4d850..893f112 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 2417244..f25bdaa 100644 --- a/approved/0056-one-transaction-per-notification.html +++ b/approved/0056-one-transaction-per-notification.html @@ -90,7 +90,7 @@ @@ -271,7 +271,7 @@ This is equivalent to forcing the Vec<Transaction> to always - @@ -285,7 +285,7 @@ This is equivalent to forcing the Vec<Transaction> to always - diff --git a/index.html b/index.html index bdeb790..14f0dae 100644 --- a/index.html +++ b/index.html @@ -90,7 +90,7 @@ diff --git a/introduction.html b/introduction.html index bdeb790..14f0dae 100644 --- a/introduction.html +++ b/introduction.html @@ -90,7 +90,7 @@ diff --git a/print.html b/print.html index e0109df..7ddff74 100644 --- a/print.html +++ b/print.html @@ -91,7 +91,7 @@ @@ -1902,101 +1902,6 @@ This is equivalent to forcing the Vec<Transaction> to always

None.

None. This is a simple isolated change.

-

(source)

-

Table of Contents

- -

RFC-0062: Lowering Existential Deposit on Asset Hub for Polkadot

-
- - - -
Start Date28 December 2023
DescriptionA proposal to reduce the existential deposit required for Asset Hub for Polkadot, making (a) asset minting to all DOT token holders more affordable for Asset Minters and (b) asset conversion on Asset Hub for Polkadot more accessible for all DOT Token holders.
AuthorsSourabh Niyogi
-
-

Summary

-

This RFC proposes lowering the existential deposit requirements on Asset Hub for Polkadot by a factor of 25, from 0.1 DOT to .004 DOT. The objective is to lower the barrier to entry for asset minters to mint a new asset to the entire DOT token holder base, and make Asset Hub on Polkadot a place where everyone can do small asset conversions.

-

Motivation

-

The current Existential deposit is 0.1 DOT on Asset Hub for Polkadot. While this is not does not appear to be a significant financial barrier for most people (only $0.80), this value makes Asset Hub impractical for Asset Hub Minters, specifically for the case where the Asset Hub Minters wishes to mint a new asset for the entire community of DOT holders (e.g. 1.25MM DOT holders would cost 125K DOT @ $8 = $1MM).

-

By lowering the existential deposit requirements from 0.1 DOT to 0.004 DOT, the cost of minting to the entire community of DOT holders goes from an unmanagable number [125K DOT, the value of several houses circa December 2023] down to a manageable number [5K DOT, the value of a car circa December 2023].

-

Stakeholders

- -

Explanation

-

The exact amount of the existential deposit (ED) is proposed to be 0.004 DOT based on

- -

Empirically, asset.transferKeepAlive is the lowest valued extrinsic at this time, so there is no value to lowering the ED below 0.001 DOT. Lowering further would be unnecessary invite account spam attacks common to EVM chains, which have no ED.

-

By RFC #32 Minimal Relay Chain, believed to be implemented within the next couple of years, Asset Hub should be able to support the entire DOT existing token holder base. If there is any doubt that Substrate chains can store 10x-100x as many elements, then this change should test Asset Hub for Polkadot's capabilities.

-

The implementation is believed to be trivial:

-

https://github.com/polkadot-fellows/runtimes/blob/30e0dbfdcb78722ed61325c0ebf1efdcdb6033ba/system-parachains/asset-hubs/asset-hub-polkadot/src/constants.rs#L21

-

from

-
pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 10;
-
-

to

-
pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 250;
-
-

Given this change, once Asset Hub Minter 1 spends approximately 5K DOT to cover the ED for the entire DOT Token Holder base, then Asset Hub Minter 2 who subsequently wishes to mint to the same DOT Tokenholder will not pay anything (assuming no new DOT Tokenholders); however, both the first and second holder will need to spend 2,485 DOT to conduct their asset.mint operations (0.001988 DOT per asset.mint) on the entire 1.25MM DOT Token holders. If Minter 3 does the same thing when there are 1.26MM DOT Token holders (10K new DOT holders), then Minter 3 will bear the cost of 40 DOT. This is summarized here:

-
- - - -
MinterCost to fund ED for 1.25MM usersCost to call asset.mint for 1.25MM users
Minter 15K DOT (instead of 125K DOT)2,485 DOT
Minter 20 DOT2,485 DOT
Minter 340 DOT2,485 DOT
-
-

As new DOT Token Holders always enter the system, this lower ED will reduce costs for all new minters, not just Minter 1. Given this reduced cost for Asset Hub Minters (Minter 2, 3, ...), this will enable a greater number of DOT Token Holders to use the assetconversion pallet for newly minted assets.

-

It is believed that having a greater number of assetconversion end-users will be massively beneficial for DOT ecosystem growth, especially for key asset pools of DOT/USDC and DOT/USDT, which can be reliably predicted to be the most widely used pools on the Asset Hub for Polkadot.

-

It is assumed that the estimated cost to store a single account is less than 0.004 DOT. If this assumption is challenged by Polkadot Fellows, we request the Fellows provided a empirical determination of what the actual cost of storing a single account is, at present day numbers of DOT Token Holders (approximately 1-2MM) and then to support a factor or 10-1000x growth over the next 5 years. This assumption has been discussed on the forum: Polkadot AssetHub - high NFT collection deposit

-

First, the cost has to be mapped from DOT into real world USD storage costs of running an Asset Hub on Polkadot node, and the DOT / USD ratio itself has varied widely in the past and will continue to do so in the future. Second, according to this analysis, at present the pragmatic cost of estimating storage is approximated by what it costs to store accounts for 1 or 2 years at most. Underestimates on this cost is believed to be an economic subsidy while overestimates on this cost is believe to be an economic depressant on activity.

-

Given the relatively underused AssetHub for Polkadot, we believe the correct thing to do is to aim to subsidize Asset Hub activity with a lower ED.

-

Drawbacks

-

The primary drawback for subsidize Asset Hub activity with a 25x lower ED is borne by Asset Hub users in the distant future who will pay for the subsidized activity by lowering the ED.

-

Testing, Security, and Privacy

-

Lowering the ED from 0.004 DOT to 0 DOT would clearly unnecessarily invite account spam attacks common to EVM chains, which have no ED.

-

Lowering ED from 0.004 DOT to 0.002 DOT or 0.001 DOT would threaten user experience wherein just 1 or 2 asset pallet operation would reap the account.

-

Performance, Ergonomics, and Compatibility

-

Performance

-

This change is not expected to have a significant impact on the overall performance of the Asset Hub for Polkadot.

-

Ergonomics

-

The proposed change aims to enhance the user experience for:

- -

Compatibility

-

It is believed that Asset Hub for Kusama can undergo the same logic change without issue.

-

For Asset Hub for Polkadot, it is extremely desirable that this change be approved in early 2024 with some urgency.

-

Unresolved Questions

-

It is desirable to know the cost to store an account on Asset Hub for Polkadot when the number of accounts is 10MM, 100MM, 1B to better the cost of the subsidy. We do not believe a precise answer to this merits delaying a subsidy at present. However, if approved, we believe once the number of accounts reaches 10MM-25MM or exponential growth is observed, this ED be reevaluated.

- -

If accepted, this RFC could pave the way for other accessibility improvements:

-

(source)

Table of Contents

-

Drawbacks

+

Drawbacks

This RFC might be difficult to implement in Substrate due to the internal code design. It is not clear to the author of this RFC how difficult it would be.

Prior Art

The API of these new functions was heavily inspired by API used by the C programming language.

-

Unresolved Questions

+

Unresolved Questions

The changes in this RFC would need to be benchmarked. This involves implementing the RFC and measuring the speed difference.

It is expected that most host functions are faster or equal speed to their deprecated counterparts, with the following exceptions:

@@ -2328,9 +2233,9 @@ This would remove the possibility to synchronize older blocks, which is probably
  • 11. Unresolved Questions
  • 12. Future Directions and Related Material
  • @@ -2347,188 +2252,191 @@ This would remove the possibility to synchronize older blocks, which is probably

    Abstract

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

    -

    Sassafras aims to establish a unique association between each epoch's slots and -the validators, ensuring that there is one and only one validator per slot.

    -

    The protocol ensures the anonymity of the validator associated to a slot until -the slot is not claimed at block production time.

    +

    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 extensively documented in a comprehensive research -paper from the Web3 foundation -research team.

    -

    This RFC serves the purpose of conveying most of the essential implementation -details that are crucial for interoperability and clarifying aspects left open -for implementation discretion.

    +

    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.

    -

    To avoid ambiguities and interoperability issues, this document takes precedence -over the research paper in cases where discrepancies arise between the two.

    +

    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

    -

    In addition to fostering interoperability, another objective of this RFC is to -facilitate the implementation of Sassafras within the Polkadot ecosystem. While -the specifics of deployment mechanics are beyond the scope of this document, it -paves the way for integrating Sassafras into the Polkadot network.

    +

    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. Developers of Blockchains

    +

    2.1. Blockchain Developers

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

    -

    2.2. Contributors to the Polkadot Ecosystem

    -

    Developers contributing to the Polkadot ecosystem, both relay-chain and para-chains. -The protocol will have a central role in the next generation Polkadot relay chain -block authoring system.

    +

    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 and Convention

    -

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

    +

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

    3.1. Data Structures Definitions and Encoding

    -

    Data structures are primarily defined using ASN.1, -with a few exceptions:

    +

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

    -

    If no context-specific instructions are given, all types must be serialized -using SCALE codec.

    -

    To ensure interoperability of serialized structures, the order of the single -fields is required to match the structures definitions found in this document.

    +

    Unless explicitly noted, all types must be serialized using +SCALE codec.

    +

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

    3.2. Pseudo-Code

    -

    Through this document it is advantageous to make use of code snippets as part -of the comprehensive description. These snippets shall adhere to the subsequent -conventions:

    +

    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:

    3.3. Incremental Introduction of Types and Functions

    -

    Types and helper functions will be introduced incrementally as they become +

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

    We find this approach more agile, especially given that the set of types used is -not extensive or overly complex.

    -

    This incremental presentation enhances readability and comprehension.

    +not overly complex.

    4. Protocol Introduction

    -

    Timeline is partitioned in epochs, epochs are partitioned in slots.

    -

    The Sassafras protocol employs a binding mechanism between validators and slots -through the use of a ticketing system.

    -

    The protocol can be divided into five discrete and asynchronous phases:

    +

    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

    -

    Validators generate and submit their candidate tickets to the blockchain. Each -ticket comes with an anonymous validity proof.

    +

    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 tickets undergo a validation process for the associated validity +

    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 candidate tickets, a deterministic method is employed to -uniquely associate a subset of these tickets to the next epoch slots.

    +

    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

    -

    Validators prove ownership of tickets during the block production phase. This -step establishes a secure binding between validators and their respective slots.

    +

    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.

    4.5. Validation of Ticket Ownership

    -

    During block verification, the claims of ticket ownership are validated to -uphold the protocol's integrity.

    +

    During block verification, the claim of ticket ownership is validated.

    5. Bandersnatch VRFs Cryptographic Primitives

    This chapter provides a high-level overview of the Bandersnatch VRF primitive as it relates to the Sassafras protocol.

    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. Instead, its primary purpose is to offer a concise and -comprehensible interpretation of the primitive within the context of this RFC.

    -

    For a more detailed understanding we recommend referring to the Ring-VRF -research paper from W3F.

    +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 Ring-VRF +paper authored by the Web3 foundation +research team.

    5.1. VRF Input

    -

    The VRF Input, denoted as VrfInput, is constructed by combining a domain identifier -with arbitrary data using the vrf_input function:

    +

    The VRF Input, denoted as VrfInput, is constructed by combining a domain +identifier with arbitrary data through the vrf_input function:

    #![allow(unused)]
     fn main() {
    -    fn vrf_input(domain: OCTET_STRING, buf: OCTET_STRING) -> VrfInput;
    +    fn vrf_input(domain: OCTET_STRING, data: OCTET_STRING) -> VrfInput;
     }
    -

    The specific implementation details of this function are intentionally omitted -here, you can find a complete reference implementation in the -bandersnatch_vrfs +

    The specific implementation details of this function are intentionally omitted. +A reference implementation is provided by the +bandersnatch_vrfs project.

    + +The above link points to some temporary code (Transcript label set to "TemporaryDoNotDeploy"). +Also replace with docs.rs link once published to crates.io. +

    Helper function to construct a VrfInput from a sequence of data items:

    #![allow(unused)]
     fn main() {
    -    fn vrf_input_from_items(domain: OCTET_STRING, data: SEQUENCE_OF OCTET_STRING) -> VrfInput {
    -        buf = OCTET_STRING(SIZE(0));
    -        for item in data {
    -            buf.append(item);
    -            buf.append(LENGTH(item) as U8);
    +    fn vrf_input_from_items(domain: OCTET_STRING, items: SEQUENCE_OF OCTET_STRING) -> VrfInput {
    +        let data = OCTET_STRING(SIZE = 0); // empty octet string
    +        for item in items {
    +            data.append(item);
    +            data.append(LENGTH(item) as U8);
             }
    -        return vrf_input(domain, buf);
    +        return vrf_input(domain, data);
         }
     }
    -

    Note that we cast the length of each item to a U8. In the context of the -protocol we never have to append strings longer than 255. The function is -internal and not designed to be generic.

    - -Or we should provide a generic one in bandersnatch primitive wrapper to be -used in other contexts? - -

    5.2. VRF Output

    -

    A VrfOutput in this context is computed in function of a VrfInput and a -BandersnatchSecretKey.

    -

    A VrfOutput can be created in two ways: as a standalone object or as part of a -VRF signature. In both scenarios, the resulting VrfOutput remains the same, but -the primary difference lies in the inclusion of a signature in the latter, which -serves to confirm its validity.

    -

    In practice, the VrfOutput is a verifiable seed to produce a variable number -of pseudo-random bytes. These bytes are considered valid when VrfOutput is -accompanied by a valid signature.

    -

    When constructed as a standalone object, VrfOutput is primarily employed -in situations where the secret key owner needs to check if the generated -pseudo-random bytes fulfill some criteria before applying the signature.

    -

    To facilitate the construction of VrfOutput from a secret key and VrfInput, -the following helper function is provided:

    +

    Note that each item length is safely casted to an U8 as:

    +
      +
    1. In the context of this protocol all items lengths are less than 256.
    2. +
    3. The function is internal and not designed for generic use.
    4. +
    +

    5.2. VRF PreOutput

    +

    Functionally, the VrfPreOutput can be considered as a seed for a PRNG to +produce an arbitrary number of output bytes.

    +

    It is computed as function of a VrfInput and a BandersnatchSecretKey.

    +

    Two different approaches can be used to generate it: as a standalone object +or as part of a signature. While the resulting VrfPreOutput is identical +in both cases, the legitimacy of the latter can be confirmed by verifying the +signature using the BandersnatchPublicKey of the expected signer.

    +

    When constructed as a standalone object, VrfPreOutput is primarily employed +in situations where the secret key owner needs to check if the generated output +bytes fulfill some context specific criteria before applying the signature.

    +

    To facilitate the construction, the following helper function is provided:

    #![allow(unused)]
     fn main() {
    -    fn vrf_output(secret: BandernatchSecretKey, input: VrfInput) -> VrfOutput;
    +    fn vrf_pre_output(secret: BandernatchSecretKey, input: VrfInput) -> VrfPreOutput;
     }
    -

    Additionally, a helper function is provided for producing len bytes from -VrfInput and VrfOutput:

    +

    An additional helper function is provided for producing an arbitrary number of +output bytes from VrfInput and VrfPreOutput:

    #![allow(unused)]
     fn main() {
    -    fn vrf_bytes(len: U32, input: VrfInput, output: VrfOuput) -> OCTET_STRING;
    +    fn vrf_bytes(len: U32, input: VrfInput, pre_output: VrfPreOuput) -> OCTET_STRING;
     }
    -

    Just like the VrfInput support function, we have intentionally excluded the -detailed implementation of this function in this document. A reference implementation -is provided in the dleq_vrfs library:

    +

    Similar to the vrf_input function, the details about the implementation +of these functions is omitted. Reference implementations are provided by the +dleq_vrfs project

    5.3. VRF Signature Data

    -

    This section defines the data to be signed using the VRF primitive:

    +

    This section outlines the data to be signed utilizing the VRF primitive:

    #![allow(unused)]
     fn main() {
         VrfSignatureData ::= SEQUENCE {
    @@ -2536,13 +2444,14 @@ is provided in the dleq_vrfs library:

    inputs: SEQUENCE_OF VrfInput } }
    +

    Where:

    -

    To simplify the construction of a VrfSignatureData object, a helper function is provided:

    +

    To simplify the construction of VrfSignatureData objects, a helper function is defined:

    #![allow(unused)]
     fn main() {
         fn vrf_signature_data(
    @@ -2560,27 +2469,29 @@ to sign which should not influence the VrfOutput.
     

    5.4. VRF Signature

    Bandersnatch VRF offers two signature flavors:

      -
    • plain signature, which is much like a traditional Schnorr signature,
    • -
    • ring signature which leverages a zk-SNARK to allows for anonymous signatures +
    • plain signature: much like a traditional Schnorr signature,
    • +
    • ring signature: leverages a zk-SNARK to allows for anonymous signatures using a key from a predefined set of enabled keys, known as the ring.

    5.4.1. Plain VRF Signature

    This section describes the signature process for VrfSignatureData using the -plain Bandersnatch signature flavor.

    +plain signature flavor.

    #![allow(unused)]
     fn main() {
         PlainSignature ::= OCTET_STRING;
     
         VrfSignature ::= SEQUENCE {
             signature: PlainSignature,
    -        outputs: SEQUENCE-OF VrfOutput
    +        pre_outputs: SEQUENCE-OF VrfPreOutput
         }
     }
    +

    Where:

      -
    • signature: the actual signature.
    • -
    • outputs: a sequence of VrfOutputs corresponding to the VrfInputs values.
    • +
    • signature: the actual plain signature.
    • +
    • pre_outputs: sequence of VrfPreOutputs corresponding to the VrfInputs +found within the VrfSignatureData.
    -

    Helper function to create a VrfPlainSignature from VrfSignatureData:

    +

    Helper function to construct VrfPlainSignature from VrfSignatureData:

    #![allow(unused)]
     fn main() {
         BandersnatchSecretKey ::= OCTET_STRING;
    @@ -2590,8 +2501,8 @@ plain Bandersnatch signature flavor.

    signature_data: VrfSignatureData ) -> VrfSignature }
    -

    Helper function for validating the signature and returning a BOOLEAN value -indicating the validity of the signature.

    +

    Helper function for signature verification returning a BOOLEAN value +indicating the validity of the signature (true on success):

    #![allow(unused)]
     fn main() {
         BandersnatchPublicKey ::= OCTET_STRING;
    @@ -2605,22 +2516,23 @@ indicating the validity of the signature.

    and PlainSignature are intentionally left undefined. Their definitions can be found in the bandersnatch_vrfs reference implementation.

    5.4.2. Ring VRF Signature

    -

    This section deals with the signature process for VrfSignatureData using the -Bandersnatch ring signature flavor.

    +

    This section describes the signature process for VrfSignatureData using the +ring signature flavor.

    #![allow(unused)]
     fn main() {
         RingSignature ::= OCTET_STRING;
     
         RingVrfSignature ::= SEQUENCE {
             signature: RingSignature,
    -        outputs: SEQUENCE_OF VrfOutput
    +        pre_outputs: SEQUENCE_OF VrfPreOutput
         }
     }
      -
    • signature: the actual signature.
    • -
    • outputs: sequence of VrfOutput objects corresponding to the VrfInput values.
    • +
    • signature: the actual ring signature.
    • +
    • pre_outputs: sequence of VrfPreOutputs corresponding to the VrfInputs +found within the VrfSignatureData.
    -

    Helper function to create a RingVrfSignature from VrfSignatureData:

    +

    Helper function to construct RingVrfSignature from VrfSignatureData:

    #![allow(unused)]
     fn main() {
         BandersnatchRingProverKey ::= OCTET_STRING;
    @@ -2630,9 +2542,8 @@ Bandersnatch ring signature flavor.

    signature_data: VrfSignatureData, ) -> RingVrfSignature; }
    -

    Helper function for validating the signature and returning a BOOLEAN -indicating the validity of the signature (True if it's valid). It's important -to note that this function does not require the signer's public key.

    +

    Helper function for signature verification returning a BOOLEAN value +indicating the validity of the signature (true on success).

    #![allow(unused)]
     fn main() {
         BandersnatchRingVerifierKey ::= OCTET_STRING;
    @@ -2642,54 +2553,49 @@ to note that this function does not require the signer's public key.

    signature: RingVrfSignature, ) -> BOOLEAN; }
    +

    Note that this function doesn't require the signer's public key.

    In this document, the types BandersnatchRingProverKey, BandersnatchRingVerifierKey, and RingSignature are intentionally left undefined. Their definitions can be found in the bandersnatch_vrfs reference implementation.

    6. Sassafras Protocol

    6.1. Epoch's First Block

    -

    The first block produced for epoch N is required to include the descriptor for -the next epoch N+1.

    -

    The descriptor for next epoch is NextEpochDescriptor.

    +

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

    #![allow(unused)]
     fn main() {
    -    AuthorityId ::= BandersnatchPublicKey;
    -
    -    Randomness ::= OCTET_STRING(SIZE(32));
    -
    -    NextEpochDescriptor ::= SEQUENCE {
    -        randomness: Randomness,
    -        authorities: SEQUENCE_OF AuthorityId,
    +    NextEpochDescriptor ::= SEQUENCE {
    +        randomness: OCTET_STRING(SIZE(32)),
    +        authorities: SEQUENCE_OF BandersnatchPublicKey,
             configuration: ProtocolConfiguration OPTIONAL
         }
     }
    +

    Where:

      -
    • randomness: randomness value.
    • +
    • randomness: 32-bytes pseudo random value.
    • authorities: list of authorities.
    • configuration: optional protocol configuration.
    -

    The NextEpochDescriptor must be SCALE encoded and embedded in the block -header digest log.

    -

    The identifier for the digest element is BYTES("SASS").

    -

    Security Consideration: Instances of NextEpochDescriptor are generated -through on-chain code whenever a block is identified as the first of an epoch. -Consequently, every node executing the block should verify that the descriptor -locally generated during block execution matches the one produced by the block -author, which is found in the digest data before block import.

    +

    This descriptor must be encoded using the SCALE encoding system and embedded +in the block header's digest log. The identifier for the digest element is +BYTES("SASS").

    +

    A special case arises for the first block for 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.1.3.

    6.1.1. Epoch Randomness

    -

    Each block ships with some entropy source in the form of bandersnatch -VrfOutput. Per block randomness is accumulated in the protocol's on-chain -randomness accumulator after block import.

    -

    The exact procedure to accumulate per-block randomness is described in detail -later, in the randomness accumulator paragraph (6.7).

    -

    Next epoch randomness is computed as:

    +

    The randomness in the NextEpochDescriptor randomness is computed as:

    #![allow(unused)]
     fn main() {
    -    next_epoch_randomness = BLAKE2(32, CONCAT(randomness_accumulator, BYTES(next_epoch_index)));
    +    randomness = BLAKE2(32, CONCAT(randomness_accumulator, BYTES(next_epoch.index)));
     }
    +

    Here, randomness_accumulator refers to a 32-byte OCTET_STRING stored +on-chain and computed through a process that incorporates verifiable random +elements from all previously imported blocks. The exact procedure is described +in section 6.7.

    6.1.2. Protocol Configuration

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

    +during tickets validation. It is defined as:

    #![allow(unused)]
     fn main() {
         ProtocolConfiguration ::= SEQUENCE {
    @@ -2697,101 +2603,120 @@ during tickets validation. It is defined as follows:

    redundancy_factor: U32 } }
    +

    Where:

      -
    • attempts_number: max number of tickets that can be submitted by each -next epoch authority.
    • -
    • redundancy_factor: controls the expected number of extra tickets produced -beyond epoch_length.
    • +
    • attempts_number: maximum number of tickets that each authority for the next +epoch is allowed to submit.
    • +
    • redundancy_factor: expected ratio between epoch's slots and the cumulative +number of tickets which can be submitted by the set of epoch validators.
    -

    The attempts number influences the anonymity of block producers. As all +

    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 in the epoch.

    -

    We do not mind max_attempts < epoch_length though because this loss of -anonymity already becomes small when attempts_number = 64 or 128 and larger -values requires more computation.

    +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 the section dedicated to candidate ticket validation -(6.2.2).

    -

    ProtocolConfiguration values can be adjusted via a dedicated extrinsic which -should have origin set to Root. A valid configuration proposal submitted on -epoch K will be propagated in the NextEpochDescriptor at the beginning of -epoch K+1 and will be effectively enacted on epoch K+2.

    +probability can be found in section 6.2.2.

    +

    ProtocolConfiguration values can be adjusted via a dedicated on-chain call +which should have origin set to Root. Any proposed changes to +ProtocolConfiguration that are submitted in epoch K will be included in the +NextEpochDescriptor at the start of epoch K+1 and will come into effect in +epoch K+2.

    6.1.3. Startup Parameters

    -

    Some parameters for first epoch (index = 0) are configurable via genesis configuration.

    +

    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_OF AuthorityId,
    -        configuration: ProtocolConfiguration OPTIONAL
    +        authorities: SEQUENCE_OF BandersnatchPublicKey,
    +        configuration: ProtocolConfiguration,
         }
     }
    -

    Randomness for first epoch is set to all zeros.

    -

    As block #0 is locally produced by every node by processing the genesis configuration, -the first block explicitly produced by a validator for the first epoch is block #1.

    -

    Block #1 must embed the NextEpochDescriptor for next epoch. This is -constructed re-using the same values used for the first epoch.

    -

    6.2. Creation and Submission of Candidate Tickets

    -

    As a shorthand notation, in this section we refer to one of the next epoch -validators as 'the validator'.

    -

    Upon the beginning of a new epoch N, the validator will construct a set of -'tickets' to be submitted on-chain. These tickets aim to secure ownership of one -or more slots in the upcoming epoch N+1.

    -

    Each validator is allowed to submit a maximum number of tickets whose value is -found in the next epoch ProtocolConfiguration attempts_number field.

    -

    The expected ratio between the attempts and the number of tickets which are -assigned to the next epoch slots is driven by the ticket threshold -(6.2.2).

    -

    Each ticket has an associated unique identifier, denoted as TicketId.

    +

    The on-chain randomness accumulator is initialized only after the genesis +block is produced. It starts with the hash of the genesis block:

    #![allow(unused)]
     fn main() {
    -    TicketId ::= U128
    +    randomness_accumulator = genesis_hash
     }
    -

    6.2.1. Ticket Identifier Value

    -

    The value of the TicketId is determined by the output of the Bandersnatch VRF -when using the following inputs:

    +

    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:

      -
    • Next epoch randomness: Randomness obtained from the NextEpochDescriptor.
    • -
    • Next epoch index: U64 computed as epoch start slot divided epoch duration.
    • -
    • Attempt index: U32 value going from 0 to attempts_number.
    • +
    • randomness: computed using the randomness_accumulator established +post-genesis, as mentioned above.
    • +
    • authorities: the same as those specified in the genesis configuration.
    • +
    • configuration: not set (i.e., None), implying the reuse of the +one found in the genesis configuration.
    -

    Let next_epoch be an object with the information associated to the next epoch.

    +

    6.2. Creation and Submission of Candidate Tickets

    +

    After the beginning of a new epoch N, each validator associated to the next +epoch (N+1) constructs a set of tickets which may be eligible (6.2.2) +to be submitted on-chain. These tickets aim to secure ownership of one or more +slots in the upcoming epoch N+1.

    +

    Each validator is allowed to submit a maximum number of tickets, as specified by +the attempts_number field in the ProtocolConfiguration for the next epoch.

    +

    The ideal timing for a validator to start creating the tickets is subject to +strategy. A recommended approach is to initiate tickets creation once the block +containing the NextEpochDescriptor is either probabilistically or, preferably, +deterministically finalized. This timing is suggested to prevent to waste +resources on tickets that might become obsolete if a different chain branch +is finally chosen as the best one by the distributed system.

    +

    However, validators are also advised to avoid submitting tickets too late, +as tickets submitted during the second half of the epoch must be discarded.

    +

    6.2.1. Ticket Identifier Value

    +

    Each ticket has an associated 128-bit unique identifier defined as:

    +
    #![allow(unused)]
    +fn main() {
    +    TicketId ::= U128;
    +}
    +

    The value of the TicketId is determined by the output of the Bandersnatch VRF +with the following input:

    #![allow(unused)]
     fn main() {
         ticket_id_vrf_input = vrf_input_from_items(
             BYTES("sassafras-ticket-v1.0"),
             [ 
                 next_epoch.randomness,
    -            BYTES(next_epoch.epoch_index),
    +            BYTES(next_epoch.index),
                 BYTES(attempt_index)
             ]
         );
     
    -    ticket_id_vrf_output = vrf_output(AUTHORITY_SECRET_KEY, ticket_id_vrf_input);
    +    ticket_id_vrf_pre_output = vrf_pre_output(AUTHORITY_SECRET_KEY, ticket_id_vrf_input);
     
    -    ticket_bytes = vrf_bytes(16, ticket_id_vrf_input, ticket_id_vrf_output);
    +    ticket_bytes = vrf_bytes(16, ticket_id_vrf_input, ticket_id_vrf_pre_output);
         ticket_id = U128(ticket_bytes);
     }
    +

    Where:

    +
      +
    • next_epoch.randomness: randomness associated to the target epoch.
    • +
    • next_epoch.index: index of the target epoch as a U64.
    • +
    • attempt_index: value going from 0 to attempts_number as a U32.
    • +

    6.2.2. Tickets Threshold

    -

    A TicketId value is valid if its value is less than the ticket threshold.

    +

    A TicketId value is valid if its value is less than the ticket threshold:

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

    Where:

      -
    • v: the number of authorities (aka validators) in the epoch
    • -
    • s: number of slots in the epoch
    • -
    • r: the redundancy factor
    • -
    • a: number of attempts
    • +
    • v: epoch's authorities (aka validators) number
    • +
    • s: epoch's slots number
    • +
    • r: redundancy factor
    • +
    • a: attempts number
    • T: ticket threshold value (0 ≤ T ≤ 1)
    6.2.2.1 Formula Derivation
    -

    For an epoch of s slots we want to have a number of tickets in expectation for -block production equal to the r·s.

    -

    We need that there is a very small probability of their being less than s -winning tickets, even if up to 1/3 of authorities are offline.

    -

    First we set the probability of a ticket winning as T = (r·s)/(a·v).

    -

    Let n be the number of validators who actually participate and so v·2/3 ≤ n ≤ v.

    -

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

    +

    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
    @@ -2799,15 +2724,15 @@ its expected value is:

    By setting r = 2, we get

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

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

    -

    For s = 600 this gives Pr[X < s] < 4·10⁻¹³, and thus we end up with a great -tolerance over offline nodes and we end-up filling all the slots with tickets -with high probability.

    +

    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 of the w3f description of the protocol.

    +paragraph in the Web3 foundation description of the protocol.

    6.2.3. Ticket Body

    -

    Every candidate ticket identifier has an associated body.

    +

    Every candidate ticket identifier has an associated body, defined as:

    #![allow(unused)]
     fn main() {
         TicketBody ::= SEQUENCE {
    @@ -2816,37 +2741,43 @@ paragraph of the w3f description of the protocol.

    revealed_pub: Ed25519PublicKey } }
    +

    Where:

    • attempt_index: attempt index used to generate the associated TicketId.
    • erased_pub: Ed25519 ephemeral public key which gets erased as soon as the -ticket is claimed.
    • +ticket is claimed. This key can be used to encrypt data for the validator.
    • revealed_pub: Ed25519 ephemeral public key which gets exposed as soon as the ticket is claimed.

    The process of generating an erased key pair is intentionally left undefined, allowing the implementor the freedom to choose the most suitable strategy.

    -

    Revealed key pair is generated using bytes produced by the VRF with input +

    Revealed key pair is generated using the bytes produced by the VRF with input parameters equal to those employed in TicketId generation, only the label is different.

    -

    Let next_epoch be an object with the information associated to the next epoch:

    #![allow(unused)]
     fn main() {
         revealed_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [ 
                 next_epoch.randomness,
    -            BYTES(next_epoch.epoch_index),
    +            BYTES(next_epoch.index),
                 BYTES(attempt_index)
             ]
         );
     
    -    revealed_vrf_output = vrf_output(AUTHORITY_SECRET_KEY, revealed_vrf_input);
    +    revealed_vrf_pre_output = vrf_pre_output(AUTHORITY_SECRET_KEY, revealed_vrf_input);
     
    -    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_output);
    +    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_pre_output);
         revealed_pub = ed25519_secret_from_seed(revealed_seed).public();
     }
    -

    The usage of the ephemeral public keys will be clarified in the ticket claiming -section (6.5).

    +

    Where:

    +
      +
    • next_epoch.randomness: randomness associated to the target epoch.
    • +
    • next_epoch.index: index of the target epoch as a U64.
    • +
    • attempt_index: value going from 0 to attempts_number as a U32.
    • +
    +

    The ephemeral public keys are also used for claiming the tickets on block production. +Refer to section 6.5 for details.

    6.2.4. Ring Signature Production

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

    #![allow(unused)]
    @@ -2861,14 +2792,14 @@ section (}
    -

    RING_PROVER object is constructed using the authority secret key, the set -public keys which belong to the next epoch authorities and the zk-SNARK -context parameters (more details in the +

    RING_PROVER_KEY object is constructed using the set of public keys which +belong to the target epoch's authorities and the zk-SNARK context parameters +(for more details refer to the bandersnatch_vrfs reference implementation).

    -

    The body and the ring signature are combined in the TicketEnvelope:

    +

    The body and the ring signature are combined in the TicketEnvelope structure:

    #![allow(unused)]
     fn main() {
         TicketEnvelope ::= SEQUENCE {
    @@ -2876,36 +2807,32 @@ reference implementation).

    ring_signature: RingVrfSignature } }
    -

    All the ticket envelopes corresponding to valid tickets are submitted on-chain -via a dedicated unsigned extrinsic.

    +

    All the envelopes corresponding to valid tickets can be submitted on-chain via a +dedicated on-chain call (extrinsic).

    6.3. Validation of candidate tickets

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

    -

    The tickets are received via a dedicated extrinsic call.

    -

    Generic validation rules:

    +

    Validation rules:

      -
    • Tickets submissions must occur within the first half of the epoch.
    • -
    • For unsigned extrinsics, it must be submitted by one of the current session -validators.
    • +
    • Tickets submissions must occur within a block part of the first half of the epoch.
    • +
    • Ring signature is verified using the on-chain RING_VERIFIER_KEY.
    • +
    • Ticket identifier is locally (re)computed from the VrfPreOutput contained in the +RingVrfSignature and its value is checked to be less than the tickets' threshold.
    -

    Ticket specific validation rules:

    -
      -
    • Ring signature is verified using the on-chain BandersnatchRingVerifierKey.
    • -
    • Ticket identifier is locally computed from the VrfOutput contained in the -RingVrfSignature and its value is checked to be less than the ticket-threshold.
    • -
    -

    Valid tickets bodies are persisted on-chain.

    -

    6.4. Ticket-Slot assignment

    +

    Valid tickets bodies are all persisted on-chain.

    +

    6.4. Ticket-Slot Binding

    Before the beginning of the next epoch, the on-chain list of tickets must be -associated with the next epoch's slots.

    -

    The assignment process happens in the second half of the submission epoch.

    -

    In the end, there must be at most one ticket per slot.

    +associated with the next epoch's slots such that there must be at most one +ticket per slot.

    +

    The assignment process happens in the second half of the submission epoch and +follows these steps:

      -
    • Initially, the complete list of tickets is sorted based on their ticket-id, -with smaller values coming first.
    • -
    • In cases where there are more tickets than available slots, the list is pruned -by removing the larger value.
    • -
    • Tickets are then assigned to the slots using an outside-in assignment strategy.
    • +
    • Sorting: The complete list of tickets is sorted based on their TicketId +value, with smaller values coming first.
    • +
    • Trimming: In scenarios where there are more tickets than available slots, the +list is trimmed to fit the epoch's slots by removing the larger value.
    • +
    • Assignment: Tickets are assigned to the epoch's slots following an +outside-in strategy.

    6.4.1. Outside-In Assignment

    Given an ordered sequence of tickets [t0, t1, t2, ..., tk] to be assigned to @@ -2915,38 +2842,26 @@ strategy:

    tickets : [ t1, t3, t5, ... , t4, t2, t0 ]

    Here slot-index is a relative value computed as:

    -
    slot-index = absolute_slot_index - epoch_start_slot
    +
    slot-index = absolute_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 owner, and +is public. What remains confidential is the identity of the ticket's author, and consequently, who possesses the authority to claim the corresponding slot. This information is known only to the author of the ticket.

    -

    6.4.2. Fallback Assignment

    In case the number of available tickets is less than the number of epoch slots, -some (orphan) slots in the middle of the epoch will remain unbounded to any -ticket.

    -

    In such situation, these unassigned slots are allocated using a fallback -assignment strategy.

    -

    The authorities registered on-chain are kept in a sorted buffer.

    -

    The index of the authority which has the privilege to claim an unbounded slot is -calculated as follows:

    -
    #![allow(unused)]
    -fn main() {
    -    index_bytes = BLAKE2(4, CONCAT(epoch_randomness, BYTES(slot)));
    -    index = U32(index_bytes) mod authorities_number;
    -}
    -

    6.5. Claim of ticket ownership during block production

    +some orphan slots in the middle of the epoch will remain unbounded to any +ticket. For claiming strategy refer to 6.5.2.

    +

    6.5. Slot Claim Production

    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 block authoring varies based on whether a given slot has an +

    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, we will employ the primary authoring -method. Conversely, if the slot lacks an associated ticket, we will resort to -the secondary authoring method as a fallback.

    -

    6.5.1. Primary Claim Method

    -

    Let ticket_body represent the TicketBody that has been committed to the on- -chain state, curr_epoch denote an object containing information about the -current epoch, and slot represent the absolute monotonic slot number.

    +

    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.5.1. Primary Method

    +

    Let ticket_body be the TicketBody that has been committed to the on-chain +state, curr_epoch denote an object containing information about the current +epoch, and slot represent the slot number (absolute).

    Follows the construction of VrfSignatureData:

    #![allow(unused)]
     fn main() {
    @@ -2954,7 +2869,7 @@ current epoch, and slot represent the absolute monotonic slot numbe
             domain: BYTES("sassafras-randomness-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(slot)
             ]
         );
    @@ -2963,7 +2878,7 @@ current epoch, and slot represent the absolute monotonic slot numbe
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(ticket_body.attempt_index)
             ]
         );
    @@ -2979,46 +2894,52 @@ current epoch, and slot represent the absolute monotonic slot numbe
             ]
         );
     }
    -

    The inclusion of revealed_vrf_input will generate a VrfSignature with a -VrfOutput allowing the verifier to reconstruct a revealed_pub key -which is expected to be equal to the one committed into the TicketBody.

    -
    6.5.1.1. (Optional) Ed25519 Erased Ephemeral Key Claim
    -

    As the ticket ownership can be claimed by reconstructing the revealed_pub -entry of the ticket, this step is purely optional and serves only to enforce -the claim.

    - -Is this step really necessary? -- Isn't better to keep it simple if this step doesn't offer any extra security? -- We already have a strong method to claim ticket ownership. - -

    The Fiat-Shamir transform is used to obtain a 32-byte challenge associated -with the VrfSignData transcript.

    +
    6.5.1.1. Ephemeral Key Claim
    +

    Fiat-Shamir transform is used to obtain a 32-byte challenge associated with +the VrfSignData transcript.

    Validators employ the secret key associated with erased_pub, which has been -committed in the TicketBody, to sign this challenge.

    +committed in the TicketBody, to sign the challenge.

    #![allow(unused)]
     fn main() {
         challenge = sign_data.transcript.challenge();
    -    erased_signature = ed25519_sign(ERASED_SECRET_KEY, challenge)
    +    erased_signature = ed25519_sign(ERASED_SECRET_KEY, challenge);
     }
    -

    6.5.2. Secondary Claim Method

    -

    If the slot doesn't have any associated ticket then the validator is the one -with index equal to the rule exposed in the fallback assignment section -(6.4.2).

    -

    Given randomness_vrf_input constructed as shown for the primary method, the -VrfSignatureData is constructed as:

    +

    As ticket's ownership can be claimed by reconstructing the revealed_pub entry +of the committed TicketBody, this step is considered optional.

    + +Is this step really necessary? +- Isn't better to keep it simple if this step doesn't offer any extra security? +- We already have a strong method to claim ticket ownership using the vrf output +- What if a validator provides both the proofs? + More weight for the branch (i.e. used to decide what is the best branch by validators)? + E.g. + - primary method + ed25519 erased signature => score 2 + - primary method => score 1 + - fallback method => score 0 + +

    6.5.2. Secondary Method

    +

    By noting that the authorities registered on-chain are kept in an ordered list, +the index of the authority which has the privilege to claim an orphan slot is:

    +
    #![allow(unused)]
    +fn main() {
    +    index_bytes = BLAKE2(4, CONCAT(epoch_randomness, BYTES(slot)));
    +    index = U32(index_bytes) mod authorities_number;
    +}
    +

    Given randomness_vrf_input constructed as shown for the primary method (6.5.1), +the VrfSignatureData is constructed as:

    #![allow(unused)]
     fn main() {
         sign_data = vrf_signature_data(
    -        transcript_label: BYTES("sassafras-slot-claim-transcript-v1.0"),
    +        transcript_label: BYTES("sassafras-claim-v1.0"),
             transcript_data: [ ],
             inputs: [
                 randomness_vrf_input
             ]
         )
     }
    -

    6.5.3. Slot Claim object

    -

    To establish ownership of a slot, the block author must construct a SlotClaim object -which contains all the necessary information to assert ownership of the slot.

    +

    6.5.3. Slot Claim Object

    +

    The SlotClaim structure is used to contain all the necessary information to +assess ownership of a slot.

    #![allow(unused)]
     fn main() {
         SlotClaim ::= SEQUENCE {
    @@ -3028,27 +2949,7 @@ which contains all the necessary information to assert ownership of the slot.

    }
    -
      -
    • -

      authority_index: index of the block author in the on-chain authorities list.

      -
    • -
    • -

      slot: absolute slot number (not relative with respect to the epoch start)

      -
    • -
    • -

      signature: signature that includes one or two VrfOutputs.

      -
        -
      • The first VrfOutput is always present and is used to generate per-block -randomness. This is used to claim ticket ownership.
      • -
      • The second VrfOutput is included if the slot is associated with a ticket. -This is relevant to claim ticket ownership.
      • -
      -
    • -
    • -

      erased_signature: optional signature providing an additional proof of ticket -ownership (see 6.5.1.1).

      -
    • -
    +

    The claim is constructed as follows:

    #![allow(unused)]
     fn main() {
         signature = vrf_sign(AUTHORITY_SECRET_KEY, sign_data);
    @@ -3060,170 +2961,184 @@ ownership (see 6.5.1.1).

    erased_signature } }
    +

    Where:

    +
      +
    • authority_index: index of the block author in the on-chain authorities list.
    • +
    • slot: slot number (absolute, not relative to the epoch start)
    • +
    • signature: signature relative to the sign_data constructed via the +primary 6.5.1 or secondary (6.5.2) method.
    • +
    • erased_signature: optional signature providing an additional proof of ticket +ownership (6.5.1.1).
    • +
    +

    The signature includes one or two VrfPreOutputs.

    +
      +
    • The first is always present and is used to generate per-block randomness +to feed the randomness accumulator (6.7).
    • +
    • The second is included if the slot is bound to a ticket. This is relevant to +claim ticket ownership (6.6.1).
    • +

    The claim object is SCALE encoded and sent in the block's header digest log.

    -

    6.6. Validation of the claim during block verification

    -

    Validation of SlotClaim object found in the block's header.

    -

    The procedure depends on whether the slot has an associated ticket or not -according to the on-chain state.

    -

    If there is a ticket linked to the slot, the primary verification method will be -used; otherwise, the protocol resorts to the secondary one.

    -

    In both scenarios, the signature within the SlotClaim is verified using -a VrfSignData constructed as specified by paragraph 6.5.

    -

    Given claim an instance of SlotClaim:

    +

    6.6. Slot Claim Verification

    +

    The signature within the SlotClaim is verified using a VrfSignData +constructed as specified in 6.5.

    #![allow(unused)]
     fn main() {
    -    public_key = AUTHORITIES[claim.authority_index];
    +    public_key = authorities[claim.authority_index];
     
    -    vrf_verify(public_key, sign_data, claim.signature);
    +    result = vrf_verify(public_key, sign_data, claim.signature);
    +    assert(result == true);
     }
    -

    If signature verification fails then the claim is not legit.

    -

    6.6.1. Primary Claim Method Verification

    -

    This verification is performed to confirm ticket ownership and is performed -utilizing the second VrfOutput contained within the SlotClaim signature.

    -

    By using the VrfOutput object together with the associated expected VrfInput -the verifier should be able to reconstruct the revealed_pub key committed in -the TicketBody. If there is a mismatch, the claim is not legit.

    +

    With:

    +
      +
    • authorities: list of authorities for the epoch, as recorded on-chain.
    • +
    • sign_data: data that has been signed, constructed as specified in 6.5.
    • +
    +

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

    +

    For slots tied to a ticket, the primary verification method is employed. Otherwise, +the secondary method is utilized.

    +

    6.6.1. Primary Method

    +

    This method verifies ticket ownership using the second VrfPreOutput from the +SlotClaim signature

    +

    The process involves comparing the revealed_pub key from the committed +TicketBody with a reconstructed key using the VrfPreOutput and the expected +VrfInput. A mismatch indicates an illegitimate claim.

    #![allow(unused)]
     fn main() {
         revealed_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(ticket_body.attempt_index)
             ]
         );
     
    -    reveled_vrf_output = claim.signature.outputs[1];
    +    reveled_vrf_pre_output = claim.signature.pre_outputs[1];
     
    -    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_output);
    +    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_pre_output);
         revealed_pub = ed25519_secret_from_seed(revealed_seed).public();
    -
         assert(revealed_pub == ticket_body.revealed_pub);
     }
    -
    6.6.1.1. (Optional) Ephemeral Key Signature Check
    -

    If the erased_signature element within the SlotClaim is present the -erased_pub key is used to verify it.

    -

    The signed challenge is generated with identical steps as outlined in section -6.5.1.1.

    +
    6.6.1.1. Ephemeral Key Signature Check
    +

    If the erased_signature is present in SlotClaim, the erased_pub within the +committed TicketBody key is used to verify it.

    +

    The signed challenge is generated as outlined in section 6.5.1.1.

    #![allow(unused)]
     fn main() {
         challenge = sign_data.transcript.challenge();
         result = ed25519_verify(ticket_body.erased_pub, challenge, claim.erased_signature);
    -
         assert(result == true);
     }
    -

    6.6.2. Secondary Claim Method Verification

    +

    6.6.2. Secondary Method

    If the slot doesn't have any associated ticket then the validator index contained in -the claim should match the one given by the rule outlined in the fallback assignment -section (6.4.2)

    +the claim should match the one given by the rule outlined in section 6.5.2.

    6.7. Randomness Accumulator

    -

    The first VrfOutput which ships with the block's SlotClaim signature -is mandatory and must be used as the entropy source for the randomness which -gets accumulated on-chain after block processing.

    -

    Given claim the instance of SlotClaim within the block header, and -accumulator the current value for the current epoch randomness accumulator, -the accumulator value is updated as follows:

    +

    The first VrfPreOutput which ships within the block's SlotClaim signature +is mandatory and must be used as entropy source for the randomness which gets +accumulated on-chain after block transactions execution.

    +

    Given claim the instance of SlotClaim found within the block header, and +randomness_accumulator the current value for the randomness accumulator, the +randomness_accumulator value is updated as follows:

    #![allow(unused)]
     fn main() {
         randomness_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-randomness-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(slot)
             ]
         );
     
    -    randomness_vrf_output = claim.signature.outputs[0];
    -
    -    randomness = vrf_bytes(32, randomness_vrf_input, randomness_vrf_output);
    +    randomness_vrf_pre_output = claim.signature.pre_outputs[0];
    +    randomness = vrf_bytes(32, randomness_vrf_input, randomness_vrf_pre_output);
     
         randomness_accumulator = BLAKE2(32, CONCAT(randomness_accumulator, randomness));
     }
    -

    The updated accumulator value is stored on-chain.

    -

    The randomness accumulated during epoch N will be used, at the start of the -next epoch (N+1), as an input to compute the NextEpochDescriptor -randomness element (see section 6.1).

    -

    As outlined throughout the document, epoch randomness value secures various -protocol-specific functions, including ticket generation and assignment of -fallback slots (refer to section 6.4.2). Additionally, users may utilize this -value for other purposes as needed.

    +

    The randomness_accumulator never resets and is a continuously evolving value. +It primarily serves as a basis for calculating the randomness associated to the +epochs as outlined on section 6.1, but custom usages +from the user are not excluded.

    7. Drawbacks

    None

    8. Testing, Security, and Privacy

    -

    The reference implementation for this RFC will be tested on testnets first.

    -

    An audit may be required to ensure the implementation does not introduce unwanted side effects

    +

    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

    -

    The utilization of Sassafras consensus represents a significant advancement in -the mitigation of short-lived fork occurrences.

    -

    Generation of forks are not possible when following the protocol and the only source -of forks is network partitioning. In this case, on recovery, the decision of -which fork to follow is not opinionated and there is only one choice.

    +

    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 impacts native client code and thus can't be -introduced via a simple runtime upgrade.

    +

    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 to ensure -the protocol's completeness and security.

    -

    These topics include:

    -

    12.1. Interactions with the Runtime

    +protocol, several crucial topics remain to be addressed in future RFCs. +These include:

    +

    12.1. Interactions with On-Chain Code

    • -

      Outbound Interface. Interfaces exposed by the host which are required by the runtime. -These are commonly dubbed Host Functions.

      +

      Outbound Interfaces: Interfaces that the host environment provides to the +on-chain code, typically known as Host Functions.

    • -

      Unrecorded Inbound Interfaces. Interfaces exposed by the runtime which are required by the host. -These are commonly dubbed Runtime APIs.

      +

      Unrecorded Inbound Interfaces. Interfaces that the on-chain code provides +to the host code, typically known as Runtime APIs.

    • -

      Transactional Inbound Interfaces. Interfaces exposed by the runtime which alter the state. -These are commonly dubbed Extrinsics and Inherents.

      +

      Transactional Inbound Interfaces. Interfaces that the on-chain code provides +to the world to alter the chain state, typically known as Transactions +(or extrinsics in the Polkadot ecosystem)

    12.2. Deployment Strategies

      -
    • Protocol Migration. Exploring how this protocol can seamlessly replace -an already operational instance of another protocol is essential. Future RFCs -should delve into the deployment strategy, including considerations for a smooth -transition process.
    • +
    • Protocol Migration. Exploring how this protocol can seamlessly replace an +already operational instance of another protocol. Future RFCs should focus on +deployment strategies to facilitate a smooth transition.
    -

    12.3. ZK-SNARK SRS Initialization Ceremony.

    +

    12.3. ZK-SNARK SRS Initialization

    • -

      Timing and Procedure: Determining the timing and procedure for the ZK-SNARK -SRS (Structured Reference String) initialization ceremony. Future RFCs should -provide insights into whether this process should be performed before the -deployment of Sassafras and the steps involved.

      +

      Procedure: Determining the procedure for the zk-SNARK SRS (Structured +Reference String) initialization. Future RFCs should provide insights into +whether this process should include an ad-hoc initialization ceremony or if +we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).

    • -

      Sharing with Para-chains: Considering the complexity of the ceremony, we -must understand whether the SRS is shared with para-chains or maintained -independently.

      +

      Sharing with Para-chains: Considering the complexity of the process, we +must understand whether the SRS is shared with system para-chains or +maintained independently.

    12.4. Anonymous Submission of Tickets.

    • Mixnet Integration: Submitting tickets directly can pose a risk of potential deanonymization through traffic analysis. Subsequent RFCs should -investigate the potential for incorporating Mixnet technology or other +investigate the potential for incorporating Mixnet protocol or other privacy-enhancing mechanisms to address this concern.

    (source)

    @@ -3256,16 +3171,16 @@ privacy-enhancing mechanisms to address this concern. AuthorsGabriel Facco de Arruda -

    Summary

    +

    Summary

    This RFC proposes changes that enable the use of absolute locations in AccountId derivations, which allows protocols built using XCM to have static account derivations in any runtime, regardless of its position in the family hierarchy.

    -

    Motivation

    +

    Motivation

    These changes would allow protocol builders to leverage absolute locations to maintain the exact same derived account address across all networks in the ecosystem, thus enhancing user experience.

    One such protocol, that is the original motivation for this proposal, is InvArch's Saturn Multisig, which gives users a unifying multisig and DAO experience across all XCM connected chains.

    -

    Stakeholders

    +

    Stakeholders

    • Ecosystem developers
    -

    Explanation

    +

    Explanation

    This proposal aims to make it possible to derive accounts for absolute locations, enabling protocols that require the ability to maintain the same derived account in any runtime. This is done by deriving accounts from the hash of described absolute locations, which are static across different destinations.

    The same location can be represented in relative form and absolute form like so:

    #![allow(unused)]
    @@ -3322,25 +3237,25 @@ privacy-enhancing mechanisms to address this concern.
     

    DescribeFamily

    The DescribeFamily location descriptor is part of the HashedDescription MultiLocation hashing system and exists to describe locations in an easy format for encoding and hashing, so that an AccountId can be derived from this MultiLocation.

    This implementation contains a match statement that does not match against absolute locations, so changes to it involve matching against absolute locations and providing appropriate descriptions for hashing.

    -

    Drawbacks

    +

    Drawbacks

    No drawbacks have been identified with this proposal.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    Tests can be done using simple unit tests, as this is not a change to XCM itself but rather to types defined in xcm-builder.

    Security considerations should be taken with the implementation to make sure no unwanted behavior is introduced.

    This proposal does not introduce any privacy considerations.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    Depending on the final implementation, this proposal should not introduce much overhead to performance.

    -

    Ergonomics

    +

    Ergonomics

    The ergonomics of this proposal depend on the final implementation details.

    -

    Compatibility

    +

    Compatibility

    Backwards compatibility should remain unchanged, although that depend on the final implementation.

    Prior Art and References

    • DescirbeFamily type: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/location_conversion.rs#L122
    • WithComputedOrigin type: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/barriers.rs#L153
    -

    Unresolved Questions

    +

    Unresolved Questions

    Implementation details and overall code is still up to discussion.

    (source)

    Table of Contents

    @@ -3373,13 +3288,13 @@ privacy-enhancing mechanisms to address this concern. AuthorsVedhavyas Singareddi -

    Summary

    +

    Summary

    At the moment, we have system_version field on RuntimeVersion that derives which state version is used for the Storage. We have a use case where we want extrinsics root is derived using StateVersion::V1. Without defining a new field under RuntimeVersion, we would like to propose adding system_version that can be used to derive both storage and extrinsic state version.

    -

    Motivation

    +

    Motivation

    Since the extrinsic state version is always StateVersion::V0, deriving extrinsic root requires full extrinsic data. This would be problematic when we need to verify the extrinsics root if the extrinsic sizes are bigger. This problem is further explored in https://github.com/polkadot-fellows/RFCs/issues/19

    @@ -3391,11 +3306,11 @@ One of the main challenge here is some extrinsics could be big enough that this included in the Consensus block due to Block's weight restriction. If the extrinsic root is derived using StateVersion::V1, then we do not need to pass the full extrinsic data but rather at maximum, 32 byte of extrinsic data.

    -

    Stakeholders

    +

    Stakeholders

    • Technical Fellowship, in its role of maintaining system runtimes.
    -

    Explanation

    +

    Explanation

    In order to use project specific StateVersion for extrinsic roots, we proposed an implementation that introduced parameter to frame_system::Config but that unfortunately did not feel correct. @@ -3421,26 +3336,26 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { system_version: 1, }; }

    -

    Drawbacks

    +

    Drawbacks

    There should be no drawbacks as it would replace state_version with same behavior but documentation should be updated so that chains know which system_version to use.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    AFAIK, should not have any impact on the security or privacy.

    -

    Performance, Ergonomics, and Compatibility

    +

    Performance, Ergonomics, and Compatibility

    These changes should be compatible for existing chains if they use state_version value for system_verision.

    -

    Performance

    +

    Performance

    I do not believe there is any performance hit with this change.

    -

    Ergonomics

    +

    Ergonomics

    This does not break any exposed Apis.

    -

    Compatibility

    +

    Compatibility

    This change should not break any compatibility.

    Prior Art and References

    We proposed introducing a similar change by introducing a parameter to frame_system::Config but did not feel that is the correct way of introducing this change.

    -

    Unresolved Questions

    +

    Unresolved Questions

    I do not have any specific questions about this change at the moment.

    - +

    IMO, this change is pretty self-contained and there won't be any future work necessary.

    (source)

    Table of Contents

    @@ -3469,9 +3384,9 @@ is the correct way of introducing this change.

    AuthorsSebastian Kunert -

    Summary

    +

    Summary

    This RFC proposes a new host function for parachains, storage_proof_size. It shall provide the size of the currently recorded storage proof to the runtime. Runtime authors can use the proof size to improve block utilization by retroactively reclaiming unused storage weight.

    -

    Motivation

    +

    Motivation

    The number of extrinsics that are included in a parachain block is limited by two constraints: execution time and proof size. FRAME weights cover both concepts, and block-builders use them to decide how many extrinsics to include in a block. However, these weights are calculated ahead of time by benchmarking on a machine with reference hardware. The execution-time properties of the state-trie and its storage items are unknown at benchmarking time. Therefore, we make some assumptions about the state-trie:

    • Trie Depth: We assume a trie depth to account for intermediary nodes.
    • @@ -3480,12 +3395,12 @@ is the correct way of introducing this change.

      These pessimistic assumptions lead to an overestimation of storage weight, negatively impacting block utilization on parachains.

      In addition, the current model does not account for multiple accesses to the same storage items. While these repetitive accesses will not increase storage-proof size, the runtime-side weight monitoring will account for them multiple times. Since the proof size is completely opaque to the runtime, we can not implement retroactive storage weight correction.

      A solution must provide a way for the runtime to track the exact storage-proof size consumed on a per-extrinsic basis.

      -

      Stakeholders

      +

      Stakeholders

      • Parachain Teams: They MUST include this host function in their runtime and node.
      • Light-client Implementors: They SHOULD include this host function in their runtime and node.
      -

      Explanation

      +

      Explanation

      This RFC proposes a new host function that exposes the storage-proof size to the runtime. As a result, runtimes can implement storage weight reclaiming mechanisms that improve block utilization.

      This RFC proposes the following host function signature:

      #![allow(unused)]
      @@ -3493,12 +3408,12 @@ is the correct way of introducing this change.

      fn ext_storage_proof_size_version_1() -> u64; }

      The host function MUST return an unsigned 64-bit integer value representing the current proof size. In block-execution and block-import contexts, this function MUST return the current size of the proof. To achieve this, parachain node implementors need to enable proof recording for block imports. In other contexts, this function MUST return 18446744073709551615 (u64::MAX), which represents disabled proof recording.

      -

      Performance, Ergonomics, and Compatibility

      -

      Performance

      +

      Performance, Ergonomics, and Compatibility

      +

      Performance

      Parachain nodes need to enable proof recording during block import to correctly implement the proposed host function. Benchmarking conducted with balance transfers has shown a performance reduction of around 0.6% when proof recording is enabled.

      -

      Ergonomics

      +

      Ergonomics

      The host function proposed in this RFC allows parachain runtime developers to keep track of the proof size. Typical usage patterns would be to keep track of the overall proof size or the difference between subsequent calls to the host function.

      -

      Compatibility

      +

      Compatibility

      Parachain teams will need to include this host function to upgrade.

      Prior Art and References

        @@ -3544,9 +3459,9 @@ is the correct way of introducing this change.

        AuthorsSergej Sakac -

        Summary

        +

        Summary

        This RFC proposes a new model for a sustainable on-demand parachain registration, involving a smaller initial deposit and periodic rent payments. The new model considers that on-demand chains may be unregistered and later re-registered. The proposed solution also ensures a quick startup for on-demand chains on Polkadot in such cases.

        -

        Motivation

        +

        Motivation

        With the support of on-demand parachains on Polkadot, there is a need to explore a new, more cost-effective model for registering validation code. In the current model, the parachain manager is responsible for reserving a unique ParaId and covering the cost of storing the validation code of the parachain. These costs can escalate, particularly if the validation code is large. We need a better, sustainable model for registering on-demand parachains on Polkadot to help smaller teams deploy more easily.

        This RFC suggests a new payment model to create a more financially viable approach to on-demand parachain registration. In this model, a lower initial deposit is required, followed by recurring payments upon parachain registration.

        This new model will coexist with the existing one-time deposit payment model, offering teams seeking to deploy on-demand parachains on Polkadot a more cost-effective alternative.

        @@ -3560,11 +3475,11 @@ is the correct way of introducing this change.

      • The solution MUST allow anyone to pay the rent.
      • The solution MUST prevent the removal of validation code if it could still be required for disputes or approval checking.
      • -

        Stakeholders

        +

        Stakeholders

        • Future Polkadot on-demand Parachains
        -

        Explanation

        +

        Explanation

        This RFC proposes a set of changes that will enable the new rent based approach to registering and storing validation code on-chain. The new model, compared to the current one, will require periodic rent payments. The parachain won't be pruned automatically if the rent is not paid, but by permitting anyone to prune the parachain and rewarding the caller, there will be an incentive for the removal of the validation code.

        On-demand parachains should still be able to utilize the current one-time payment model. However, given the size of the deposit required, it's highly likely that most on-demand parachains will opt for the new rent-based model.

        @@ -3671,27 +3586,27 @@ pub(super) type CheckedCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>; }

    To enable parachain re-registration, we should introduce a new extrinsic in the paras-registrar pallet that allows this. The logic of this extrinsic will be same as regular registration, with the distinction that it can be called by anyone, and the required deposit will be smaller since it only has to cover for the storage of the validation code.

    -

    Drawbacks

    +

    Drawbacks

    This RFC does not alter the process of reserving a ParaId, and therefore, it does not propose reducing it, even though such a reduction could be beneficial.

    Even though this RFC doesn't delve into the specifics of the configuration values for parachain registration but rather focuses on the mechanism, configuring it carelessly could lead to potential problems.

    Since the validation code hash and head data are not removed when the parachain is pruned but only when the deregister extrinsic is called, the T::DataDepositPerByte must be set to a higher value to create a strong enough incentive for removing it from the state.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    The implementation of this RFC will be tested on Rococo first.

    Proper research should be conducted on setting the configuration values of the new system since these values can have great impact on the network.

    An audit is required to ensure the implementation's correctness.

    The proposal introduces no new privacy concerns.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    This RFC should not introduce any performance impact.

    -

    Ergonomics

    +

    Ergonomics

    This RFC does not affect the current parachains, nor the parachains that intend to use the one-time payment model for parachain registration.

    -

    Compatibility

    +

    Compatibility

    This RFC does not break compatibility.

    Prior Art and References

    Prior discussion on this topic: https://github.com/paritytech/polkadot-sdk/issues/1796

    -

    Unresolved Questions

    +

    Unresolved Questions

    None at this time.

    - +

    As noted in this GitHub issue, we want to raise the per-byte cost of on-chain data storage. However, a substantial increase in this cost would make it highly impractical for on-demand parachains to register on Polkadot. This RFC offers an alternative solution for on-demand parachains, ensuring that the per-byte cost increase doesn't overly burden the registration process.

    (source)

    @@ -3748,9 +3663,9 @@ This RFC offers an alternative solution for on-demand parachains, ensuring that AuthorsAlzymologist Oy, Zondax LLC, Parity GmbH -

    Summary

    +

    Summary

    Add a metadata digest value (33-byte constant within fixed spec_version) to Signed Extensions to supplement signer party with proof of correct extrinsic interpretation. The digest value is generated once before release and is well-known and deterministic. The digest mechanism is designed to be modular and flexible. It also supports partial metadata transfer as needed by the signing party's extrinsic decoding mechanism. This considers signing devices potentially limited communication bandwidth and/or memory capacity.

    -

    Motivation

    +

    Motivation

    Background

    While all blockchain systems support (at least in some sense) offline signing used in air-gapped wallets and lightweight embedded devices, only few allow simultaneously complex upgradeable logic and full message decoding on the cold off-line signer side; Substrate is one of these heartening few, and therefore - we should build on this feature to greatly improve transaction security, and thus in general, network resilience.

    As a starting point, it is important to recognise that prudence and due care are naturally required. As we build further reliance on this feature we should be very careful to make sure it works correctly every time so as not to create false sense of security.

    @@ -3780,11 +3695,11 @@ This RFC offers an alternative solution for on-demand parachains, ensuring that
  • Chunks handling mechanism SHOULD support chunks being sent in any order without memory utilization overhead;
  • Unused enum variants MUST be stripped (this has great impact on transmitted metadata size; examples: era enum, enum with all calls for call batching).
  • -

    Stakeholders

    +

    Stakeholders

    All chain teams are stakeholders, as implementing this feature would require timely effort on their side and would impact compatibility with older tools.

    This feature is essential for all offline signer tools; many regular signing tools might make use of it. In general, this RFC greatly improves security of any network implementing it, as many governing keys are used with offline signers.

    Implementing this RFC would remove requirement to maintain metadata portals manually, as task of metadata verification would be effectively moved to consensus mechanism of the chain.

    -

    Explanation

    +

    Explanation

    Detailed description of metadata shortening and digest process is provided in metadata-shortener crate (see cargo doc --open and examples). Below are presented algorithms of the process.

    Definitions

    Metadata structure

    @@ -3921,29 +3836,29 @@ modularized_registry.sort(|a, b| { 0x02 - 0xFFreservedreserved for future use -

    Drawbacks

    +

    Drawbacks

    Increased transaction size

    A 1-byte increase in transaction size due to signed extension value. Digest is not included in transferred transaction, only in signing process.

    Transition overhead

    Some slightly out of spec systems might experience breaking changes as new content of signed extensions is added. It is important to note, that there is no real overhead in processing time nor complexity, as the metadata checking mechanism is voluntary. The only drawbacks are expected for tools that do not implement MetadataV14 self-descripting features.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    The metadata shortening protocol should be extensively tested on all available examples of metadata before releasing changes to either metadata or shortener. Careful code review should be performed on shortener implementation code to ensure security. The main metadata tree would inevitably be constructed on runtime build which would also ensure correctness.

    To be able to recall shortener protocol in case of vulnerability issues, a version byte is included.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    This is negligibly short pessimization during build time on the chain side. Cold wallets performance would improve mostly as metadata validity mechanism that was taking most of effort in cold wallet support would become trivial.

    -

    Ergonomics

    +

    Ergonomics

    The proposal was optimized for cold storage wallets usage with minimal impact on all other parts of the ecosystem

    -

    Compatibility

    +

    Compatibility

    Proposal in this form is not compatible with older tools that do not implement proper MetadataV14 self-descriptive features; those would have to be upgraded to include a new signed extensions field.

    Prior Art and References

    This project was developed upon a Polkadot Treasury grant; relevant development links are located in metadata-offline-project repository.

    -

    Unresolved Questions

    +

    Unresolved Questions

    1. How would polkadot-js handle the transition?
    2. Where would non-rust tools like Ledger apps get shortened metadata content?
    - +

    Changes to code of all cold signers to implement this mechanism SHOULD be done when this is enabled; non-cold signers may perform extra metadata check for better security. Ultimately, signing anything without decoding it with verifiable metadata should become discouraged in all situations where a decision-making mechanism is involved (that is, outside of fully automated blind signers like trade bots or staking rewards payout tools).

    (source)

    Table of Contents

    @@ -3986,12 +3901,12 @@ modularized_registry.sort(|a, b| { AuthorsAlin Dima -

    Summary

    +

    Summary

    Propose a way of permuting the availability chunk indices assigned to validators for a given core and relay chain block, in the context of recovering available data from systematic chunks, with the purpose of fairly distributing network bandwidth usage.

    -

    Motivation

    +

    Motivation

    Currently, the ValidatorIndex is always identical to the ChunkIndex. Since the validator array is only shuffled once per session, naively using the ValidatorIndex as the ChunkIndex would pose an unreasonable stress on the first N/3 validators during an entire session, when favouring availability recovery from systematic chunks.

    @@ -3999,9 +3914,9 @@ validators during an entire session, when favouring availability recovery from s systematic availability chunks to different validators, based on the relay chain block and core. The main purpose is to ensure fair distribution of network bandwidth usage for availability recovery in general and in particular for systematic chunk holders.

    -

    Stakeholders

    +

    Stakeholders

    Relay chain node core developers.

    -

    Explanation

    +

    Explanation

    Systematic erasure codes

    An erasure coding algorithm is considered systematic if it preserves the original unencoded data as part of the resulting code. @@ -4187,7 +4102,7 @@ struct (added in https://github.com/paritytech/polkadot-sdk/pull/2177Configuration::set_node_feature extrinsic. Once the feature is enabled and new configuration is live, the validator->chunk mapping ceases to be a 1:1 mapping and systematic recovery may begin.

    -

    Drawbacks

    +

    Drawbacks

    • Getting access to the core_index that used to be occupied by a candidate in some parts of the dispute protocol is very complicated (See appendix A). This RFC assumes that availability-recovery processes initiated during @@ -4197,30 +4112,30 @@ mitigate this problem and will likely be needed in the future for CoreJam. Related discussion about CandidateReceipt
    • It's a breaking change that requires all validators and collators to upgrade their node version.
    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    Extensive testing will be conducted - both automated and manual. This proposal doesn't affect security or privacy.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    This is a necessary data availability optimisation, as reed-solomon erasure coding has proven to be a top consumer of CPU time in polkadot as we scale up the parachain block size and number of availability cores.

    With this optimisation, preliminary performance results show that CPU time used for reed-solomon coding/decoding can be halved and total POV recovery time decrease by 80% for large POVs. See more here.

    -

    Ergonomics

    +

    Ergonomics

    Not applicable.

    -

    Compatibility

    +

    Compatibility

    This is a breaking change. See upgrade path section above. All validators need to have upgraded their node versions before the feature will be enabled via a runtime upgrade governance call.

    Prior Art and References

    See comments on the tracking issue and the in-progress PR

    -

    Unresolved Questions

    +

    Unresolved Questions

    • Is there a better upgrade path that would preserve backwards compatibility?
    - +

    This enables future optimisations for the performance of availability recovery, such as retrieving batched systematic chunks from backers/approval-checkers.

    Appendix A

    @@ -4300,20 +4215,20 @@ occupying. However, considering it's part of an unimported fork, the validator c AuthorsPierre Krieger -

    Summary

    +

    Summary

    This RFC proposes to make the mechanism of RFC #8 more generic by introducing the concept of "capabilities".

    Implementations can implement certain "capabilities", such as serving old block headers or being a parachain bootnode.

    The discovery mechanism of RFC #8 is extended to be able to discover nodes of specific capabilities.

    -

    Motivation

    +

    Motivation

    The Polkadot peer-to-peer network is made of nodes. Not all these nodes are equal. Some nodes store only the headers of recently blocks, some nodes store all the block headers and bodies since the genesis, some nodes store the storage of all blocks since the genesis, and so on.

    It is currently not possible to know ahead of time (without connecting to it and asking) which nodes have which data available, and it is not easily possible to build a list of nodes that have a specific piece of data available.

    If you want to download for example the header of block 500, you have to connect to a randomly-chosen node, ask it for block 500, and if it says that it doesn't have the block, disconnect and try another randomly-chosen node. In certain situations such as downloading the storage of old blocks, nodes that have the information are relatively rare, and finding through trial and error a node that has the data can take a long time.

    This RFC attempts to solve this problem by giving the possibility to build a list of nodes that are capable of serving specific data.

    -

    Stakeholders

    +

    Stakeholders

    Low-level client developers. People interested in accessing the archive of the chain.

    -

    Explanation

    +

    Explanation

    Reading RFC #8 first might help with comprehension, as this RFC is very similar.

    Please keep in mind while reading that everything below applies for both relay chains and parachains, except mentioned otherwise.

    Capabilities

    @@ -4348,30 +4263,30 @@ If blocks pruning is enabled and the chain is a relay chain, then Substrate unfo

    Implementations that have the head of the chain provider capability do not register themselves as providers, but instead are the nodes that participate in the main DHT. In other words, they are the nodes that serve requests of the /<genesis_hash>/kad protocol.

    Any implementation that isn't a head of the chain provider (read: light clients) must not participate in the main DHT. This is already presently the case.

    Implementations must not participate in the main DHT if they don't fulfill the capability yet. For example, a node that is still in the process of warp syncing must not participate in the main DHT. However, assuming that warp syncing doesn't last more than a few seconds, it is acceptable to ignore this requirement in order to avoid complicating implementations too much.

    -

    Drawbacks

    +

    Drawbacks

    None that I can see.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    The content of this section is basically the same as the one in RFC 8.

    This mechanism doesn't add or remove any security by itself, as it relies on existing mechanisms.

    Due to the way Kademlia works, it would become the responsibility of the 20 Polkadot nodes whose sha256(peer_id) is closest to the key (described in the explanations section) to store the list of nodes that have specific capabilities. Furthermore, when a large number of providers are registered, only the providers closest to the key are kept, up to a certain implementation-defined limit.

    For this reason, an attacker can abuse this mechanism by randomly generating libp2p PeerIds until they find the 20 entries closest to the key representing the target capability. They are then in control of the list of nodes with that capability. While doing this can in no way be actually harmful, it could lead to eclipse attacks.

    Because the key changes periodically and isn't predictable, and assuming that the Polkadot DHT is sufficiently large, it is not realistic for an attack like this to be maintained in the long term.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    The DHT mechanism generally has a low overhead, especially given that publishing providers is done only every 24 hours.

    Doing a Kademlia iterative query then sending a provider record shouldn't take more than around 50 kiB in total of bandwidth for the parachain bootnode.

    Assuming 1000 nodes with a specific capability, the 20 Polkadot full nodes corresponding to that capability will each receive a sudden spike of a few megabytes of networking traffic when the key rotates. Again, this is relatively negligible. If this becomes a problem, one can add a random delay before a node registers itself to be the provider of the key corresponding to BabeApi_next_epoch.

    Maybe the biggest uncertainty is the traffic that the 20 Polkadot full nodes will receive from light clients that desire knowing the nodes with a capability. If this every becomes a problem, this value of 20 is an arbitrary constant that can be increased for more redundancy.

    -

    Ergonomics

    +

    Ergonomics

    Irrelevant.

    -

    Compatibility

    +

    Compatibility

    Irrelevant.

    Prior Art and References

    Unknown.

    -

    Unresolved Questions

    +

    Unresolved Questions

    While it fundamentally doesn't change much to this RFC, using BabeApi_currentEpoch and BabeApi_nextEpoch might be inappropriate. I'm not familiar enough with good practices within the runtime to have an opinion here. Should it be an entirely new pallet?

    - +

    This RFC would make it possible to reliably discover archive nodes, which would make it possible to reliably send archive node requests, something that isn't currently possible. This could solve the problem of finding archive RPC node providers by migrating archive-related request to using the native peer-to-peer protocol rather than JSON-RPC.

    If we ever decide to break backwards compatibility, we could divide the "history" and "archive" capabilities in two, between nodes capable of serving older blocks and nodes capable of serving newer blocks. We could even add to the peer-to-peer network nodes that are only capable of serving older blocks (by reading from a database) but do not participate in the head of the chain, and that just exist for historical purposes.

    @@ -4411,19 +4326,19 @@ We could even add to the peer-to-peer network nodes that are only capable of ser AuthorsJiahao Ye -

    Summary

    +

    Summary

    Currently, substrate runtime use an simple allocator defined by host side. Every runtime MUST import these allocator functions for normal execution. This situation make runtime code not versatile enough.

    So this RFC proposes to define a new spec for allocator part to make substrate runtime more generic.

    -

    Motivation

    +

    Motivation

    Since this RFC define a new way for allocator, we now regard the old one as legacy allocator. As we all know, since the allocator implementation details are defined by the substrate client, parachain/parathread cannot customize memory allocator algorithm, so the new specification allows the runtime to customize memory allocation, and then export the allocator function according to the specification for the client side to use. Another benefit is that some new host functions can be designed without allocating memory on the client, which may have potential performance improvements. Also it will help provide a unified and clean specification if substrate runtime support multi-targets(e.g. RISC-V). There is also a potential benefit. Many programming languages that support compilation to wasm may not be friendly to supporting external allocator. This is beneficial for other programming languages ​​to enter the substrate runtime ecosystem. The last and most important benefit is that for offchain context execution, the runtime can fully support pure wasm. What this means here is that all imported host functions could not actually be called (as stub functions), then the various verification logic of the runtime can be converted into pure wasm, which provides the possibility for the substrate runtime to run block verification in other environments (such as in browsers and other non-substrate environments).

    -

    Stakeholders

    +

    Stakeholders

    No attempt was made at convincing stakeholders.

    -

    Explanation

    +

    Explanation

    Runtime side spec

    This section contains a list of functions should be exported by substrate runtime.

    We define the spec as version 1, so the following dummy function v1 MUST be exported to hint @@ -4462,20 +4377,20 @@ Check if parsed wasm module contains a exported v1 function:

    allocator.

    Detail-heavy explanation of the RFC, suitable for explanation to an implementer of the changeset. This should address corner cases in detail and provide justification behind decisions, and provide rationale for how the design meets the solution requirements.

    -

    Drawbacks

    +

    Drawbacks

    The allocator inside of the runtime will make code size bigger, but it's not obvious. The allocator inside of the runtime maybe slow down(or speed up) the runtime, still not obvious.

    We could ignore these drawbacks since they are not prominent. And the execution efficiency is highly decided by runtime developer. We could not prevent a poor efficiency if developer want to do it.

    -

    Testing, Security, and Privacy

    +

    Testing, Security, and Privacy

    Keep the legacy allocator runtime test cases, and add new feature to compile test cases for v1 allocator spec. And then update the test asserts.

    Update template runtime to enable v1 spec. Once the dev network runs well, it seems that the spec is implmented correctly.

    -

    Performance, Ergonomics, and Compatibility

    -

    Performance

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    As the above says, not obvious impact about performance. And polkadot-sdk could offer the best practice allocator for all chains. Third party also could customized by theirself. So the performance could be improved over time.

    -

    Ergonomics

    +

    Ergonomics

    Only for runtime developer, Just need to import a new crate and enable a new feature. Maybe it's convienient for other wasm-target language to implment.

    -

    Compatibility

    +

    Compatibility

    It's 100% compatible. Only Some runtime configs and executor configs need to be depreacted.

    For support new runtime spec, we MUST upgrade the client binary to support new spec of client part firstly.

    We SHALL add an optional primtive crate to enable the version 1 spec and disable the legacy allocator by cargo feature. @@ -4485,11 +4400,106 @@ For the first year, we SHALL disable the v1 by default, and enable it by default

  • Move the allocator inside of the runtime
  • Add new allocator design
  • -

    Unresolved Questions

    +

    Unresolved Questions

    None at this time.

    - +

    The content discussed with RFC-0004 is basically orthogonal, but it could still be considered together, and it is preferred that this rfc be implmentented first.

    This feature could make substrate runtime be easier supported by other languages and integreted into other ecosystem.

    +

    (source)

    +

    Table of Contents

    + +

    RFC-0062: Lowering Existential Deposit on Asset Hub for Polkadot

    +
    + + + +
    Start Date28 December 2023
    DescriptionA proposal to reduce the existential deposit required for Asset Hub for Polkadot, making (a) asset minting to all DOT token holders more affordable for Asset Minters and (b) asset conversion on Asset Hub for Polkadot more accessible for all DOT Token holders.
    AuthorsSourabh Niyogi
    +
    +

    Summary

    +

    This RFC proposes lowering the existential deposit requirements on Asset Hub for Polkadot by a factor of 25, from 0.1 DOT to .004 DOT. The objective is to lower the barrier to entry for asset minters to mint a new asset to the entire DOT token holder base, and make Asset Hub on Polkadot a place where everyone can do small asset conversions.

    +

    Motivation

    +

    The current Existential deposit is 0.1 DOT on Asset Hub for Polkadot. While this is not does not appear to be a significant financial barrier for most people (only $0.80), this value makes Asset Hub impractical for Asset Hub Minters, specifically for the case where the Asset Hub Minters wishes to mint a new asset for the entire community of DOT holders (e.g. 1.25MM DOT holders would cost 125K DOT @ $8 = $1MM).

    +

    By lowering the existential deposit requirements from 0.1 DOT to 0.004 DOT, the cost of minting to the entire community of DOT holders goes from an unmanagable number [125K DOT, the value of several houses circa December 2023] down to a manageable number [5K DOT, the value of a car circa December 2023].

    +

    Stakeholders

    +
      +
    • Asset Hub Minters: Those who call asset.mint.
    • +
    • DOT Token Holders: Those who hold DOT on the Polkadot Relay Chain, Asset Hub for Polkadot or other chains.
    • +
    +

    Explanation

    +

    The exact amount of the existential deposit (ED) is proposed to be 0.004 DOT based on

    +
      +
    • asset.transfer costing 0.00124 DOT
    • +
    • asset.transferKeepAlive costing 0.00092 DOT +This implies that the new ED can support 3 asset.transfer or 4 asset.transferKeepAlive operations.
    • +
    +

    Empirically, asset.transferKeepAlive is the lowest valued extrinsic at this time, so there is no value to lowering the ED below 0.001 DOT. Lowering further would be unnecessary invite account spam attacks common to EVM chains, which have no ED.

    +

    By RFC #32 Minimal Relay Chain, believed to be implemented within the next couple of years, Asset Hub should be able to support the entire DOT existing token holder base. If there is any doubt that Substrate chains can store 10x-100x as many elements, then this change should test Asset Hub for Polkadot's capabilities.

    +

    The implementation is believed to be trivial:

    +

    https://github.com/polkadot-fellows/runtimes/blob/30e0dbfdcb78722ed61325c0ebf1efdcdb6033ba/system-parachains/asset-hubs/asset-hub-polkadot/src/constants.rs#L21

    +

    from

    +
    pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 10;
    +
    +

    to

    +
    pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 250;
    +
    +

    Given this change, once Asset Hub Minter 1 spends approximately 5K DOT to cover the ED for the entire DOT Token Holder base, then Asset Hub Minter 2 who subsequently wishes to mint to the same DOT Tokenholder will not pay anything (assuming no new DOT Tokenholders); however, both the first and second holder will need to spend 2,485 DOT to conduct their asset.mint operations (0.001988 DOT per asset.mint) on the entire 1.25MM DOT Token holders. If Minter 3 does the same thing when there are 1.26MM DOT Token holders (10K new DOT holders), then Minter 3 will bear the cost of 40 DOT. This is summarized here:

    +
    + + + +
    MinterCost to fund ED for 1.25MM usersCost to call asset.mint for 1.25MM users
    Minter 15K DOT (instead of 125K DOT)2,485 DOT
    Minter 20 DOT2,485 DOT
    Minter 340 DOT2,485 DOT
    +
    +

    As new DOT Token Holders always enter the system, this lower ED will reduce costs for all new minters, not just Minter 1. Given this reduced cost for Asset Hub Minters (Minter 2, 3, ...), this will enable a greater number of DOT Token Holders to use the assetconversion pallet for newly minted assets.

    +

    It is believed that having a greater number of assetconversion end-users will be massively beneficial for DOT ecosystem growth, especially for key asset pools of DOT/USDC and DOT/USDT, which can be reliably predicted to be the most widely used pools on the Asset Hub for Polkadot.

    +

    It is assumed that the estimated cost to store a single account is less than 0.004 DOT. If this assumption is challenged by Polkadot Fellows, we request the Fellows provided a empirical determination of what the actual cost of storing a single account is, at present day numbers of DOT Token Holders (approximately 1-2MM) and then to support a factor or 10-1000x growth over the next 5 years. This assumption has been discussed on the forum: Polkadot AssetHub - high NFT collection deposit

    +

    First, the cost has to be mapped from DOT into real world USD storage costs of running an Asset Hub on Polkadot node, and the DOT / USD ratio itself has varied widely in the past and will continue to do so in the future. Second, according to this analysis, at present the pragmatic cost of estimating storage is approximated by what it costs to store accounts for 1 or 2 years at most. Underestimates on this cost is believed to be an economic subsidy while overestimates on this cost is believe to be an economic depressant on activity.

    +

    Given the relatively underused AssetHub for Polkadot, we believe the correct thing to do is to aim to subsidize Asset Hub activity with a lower ED.

    +

    Drawbacks

    +

    The primary drawback for subsidize Asset Hub activity with a 25x lower ED is borne by Asset Hub users in the distant future who will pay for the subsidized activity by lowering the ED.

    +

    Testing, Security, and Privacy

    +

    Lowering the ED from 0.004 DOT to 0 DOT would clearly unnecessarily invite account spam attacks common to EVM chains, which have no ED.

    +

    Lowering ED from 0.004 DOT to 0.002 DOT or 0.001 DOT would threaten user experience wherein just 1 or 2 asset pallet operation would reap the account.

    +

    Performance, Ergonomics, and Compatibility

    +

    Performance

    +

    This change is not expected to have a significant impact on the overall performance of the Asset Hub for Polkadot.

    +

    Ergonomics

    +

    The proposed change aims to enhance the user experience for:

    +
      +
    • Asset Creators/Minters, making the cost to mint an asset for all DOT Token holders around 5K DOT.
    • +
    • DOT Token Holders, who will enjoy many new assets on Asset Hub created by the above minters
    • +
    +

    Compatibility

    +

    It is believed that Asset Hub for Kusama can undergo the same logic change without issue.

    +

    For Asset Hub for Polkadot, it is extremely desirable that this change be approved in early 2024 with some urgency.

    +

    Unresolved Questions

    +

    It is desirable to know the cost to store an account on Asset Hub for Polkadot when the number of accounts is 10MM, 100MM, 1B to better the cost of the subsidy. We do not believe a precise answer to this merits delaying a subsidy at present. However, if approved, we believe once the number of accounts reaches 10MM-25MM or exponential growth is observed, this ED be reevaluated.

    + +

    If accepted, this RFC could pave the way for other accessibility improvements:

    +
      +
    • EVM Contracts on Asset Hub for Polkadot/Kusama
    • +
    • ink! Contracts on Asset Hub for Polkadot/Kusama
    • +
    • CorePlay activity on Asset Hub for Polkadot/Kusama
    • +

    (source)

    Table of Contents

    @@ -241,9 +241,9 @@
  • 11. Unresolved Questions
  • 12. Future Directions and Related Material
  • @@ -260,188 +260,191 @@

    Abstract

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

    -

    Sassafras aims to establish a unique association between each epoch's slots and -the validators, ensuring that there is one and only one validator per slot.

    -

    The protocol ensures the anonymity of the validator associated to a slot until -the slot is not claimed at block production time.

    +

    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 extensively documented in a comprehensive research -paper from the Web3 foundation -research team.

    -

    This RFC serves the purpose of conveying most of the essential implementation -details that are crucial for interoperability and clarifying aspects left open -for implementation discretion.

    +

    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.

    -

    To avoid ambiguities and interoperability issues, this document takes precedence -over the research paper in cases where discrepancies arise between the two.

    +

    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

    -

    In addition to fostering interoperability, another objective of this RFC is to -facilitate the implementation of Sassafras within the Polkadot ecosystem. While -the specifics of deployment mechanics are beyond the scope of this document, it -paves the way for integrating Sassafras into the Polkadot network.

    +

    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. Developers of Blockchains

    +

    2.1. Blockchain Developers

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

    -

    2.2. Contributors to the Polkadot Ecosystem

    -

    Developers contributing to the Polkadot ecosystem, both relay-chain and para-chains. -The protocol will have a central role in the next generation Polkadot relay chain -block authoring system.

    +

    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 and Convention

    -

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

    +

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

    3.1. Data Structures Definitions and Encoding

    -

    Data structures are primarily defined using ASN.1, -with a few exceptions:

    +

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

      -
    • Integer types are not explicitly defined in ASN.1 and in the context of -this document U<n> should be interpreted as a n-bit unsigned integers
    • +
    • Fixed width integer types are not explicitly defined by ASN.1 standard. +Within this document, U<n> denotes a n-bit unsigned integer.
    -

    If no context-specific instructions are given, all types must be serialized -using SCALE codec.

    -

    To ensure interoperability of serialized structures, the order of the single -fields is required to match the structures definitions found in this document.

    +

    Unless explicitly noted, all types must be serialized using +SCALE codec.

    +

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

    3.2. Pseudo-Code

    -

    Through this document it is advantageous to make use of code snippets as part -of the comprehensive description. These snippets shall adhere to the subsequent -conventions:

    +

    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:

    • -

      For simplicity, code snippets are presented in a Rust-like pseudocode format.

      -
    • -
    • -

      The function BYTES(x: T) returns an OCTET_STRING representing the raw -byte array representation of the object x with type T.

      +

      BYTES(x: T): returns an OCTET_STRING that represents the raw byte array of +the object x with type T.

        -
      • If T is VisibleString (i.e. an ASCII string): it returns the sequence +
      • If T is a VisibleString (ASCII string), it returns the sequence of octets of its ASCII representation.
      • -
      • If T is U<n>: it returns the little-endian encoding of the integer +
      • If T is U<n>, it returns the little-endian encoding of the integer U<n> as n/8 octets.
    • -

      The function U<n>(x: OCTET_STRING) returns a U<n> interpreting x as -the little-endian encoding of a n bits unsigned integer.

      +

      U<n>(x: OCTET_STRING): returns a U<n> interpreting x as the +little-endian encoding of a n bits unsigned integer.

    • -

      The function SCALE(x: T) returns an OCTET_STRING representing the -SCALE encoding of +

      SCALE(x: T): returns an OCTET_STRING representing the SCALE encoding of x with type T.

    • -

      The function BLAKE2(n: U32, x: OCTET_STRING) returns n bytes of the -standard blake2b hash of x as an OCTET_STRING.

      +

      BLAKE2(n: U32, x: OCTET_STRING): returns the standard Blake2b n +bytes hash of x as an OCTET_STRING (note this is not equivalent to the +truncation of the full 64 bytes Blake2b hash).

    • -

      The function CONCAT(x₀: OCTET_STRING, ..., xₖ: OCTET_STRING) returns the -concatenation of the inputs as an OCTET_STRING.

      +

      CONCAT(x₀: OCTET_STRING, ..., xₖ: OCTET_STRING): returns the concatenation +of the inputs as an OCTET_STRING.

    • -

      The function LENGTH(x: OCTET_STRING) returns a U32 representing the -number of octets in x.

      +

      LENGTH(x: OCTET_STRING): returns the number of octets in x as an U32.

    3.3. Incremental Introduction of Types and Functions

    -

    Types and helper functions will be introduced incrementally as they become +

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

    We find this approach more agile, especially given that the set of types used is -not extensive or overly complex.

    -

    This incremental presentation enhances readability and comprehension.

    +not overly complex.

    4. Protocol Introduction

    -

    Timeline is partitioned in epochs, epochs are partitioned in slots.

    -

    The Sassafras protocol employs a binding mechanism between validators and slots -through the use of a ticketing system.

    -

    The protocol can be divided into five discrete and asynchronous phases:

    +

    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

    -

    Validators generate and submit their candidate tickets to the blockchain. Each -ticket comes with an anonymous validity proof.

    +

    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 tickets undergo a validation process for the associated validity +

    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 candidate tickets, a deterministic method is employed to -uniquely associate a subset of these tickets to the next epoch slots.

    +

    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

    -

    Validators prove ownership of tickets during the block production phase. This -step establishes a secure binding between validators and their respective slots.

    +

    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.

    4.5. Validation of Ticket Ownership

    -

    During block verification, the claims of ticket ownership are validated to -uphold the protocol's integrity.

    +

    During block verification, the claim of ticket ownership is validated.

    5. Bandersnatch VRFs Cryptographic Primitives

    This chapter provides a high-level overview of the Bandersnatch VRF primitive as it relates to the Sassafras protocol.

    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. Instead, its primary purpose is to offer a concise and -comprehensible interpretation of the primitive within the context of this RFC.

    -

    For a more detailed understanding we recommend referring to the Ring-VRF -research paper from W3F.

    +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 Ring-VRF +paper authored by the Web3 foundation +research team.

    5.1. VRF Input

    -

    The VRF Input, denoted as VrfInput, is constructed by combining a domain identifier -with arbitrary data using the vrf_input function:

    +

    The VRF Input, denoted as VrfInput, is constructed by combining a domain +identifier with arbitrary data through the vrf_input function:

    #![allow(unused)]
     fn main() {
    -    fn vrf_input(domain: OCTET_STRING, buf: OCTET_STRING) -> VrfInput;
    +    fn vrf_input(domain: OCTET_STRING, data: OCTET_STRING) -> VrfInput;
     }
    -

    The specific implementation details of this function are intentionally omitted -here, you can find a complete reference implementation in the -bandersnatch_vrfs +

    The specific implementation details of this function are intentionally omitted. +A reference implementation is provided by the +bandersnatch_vrfs project.

    + +The above link points to some temporary code (Transcript label set to "TemporaryDoNotDeploy"). +Also replace with docs.rs link once published to crates.io. +

    Helper function to construct a VrfInput from a sequence of data items:

    #![allow(unused)]
     fn main() {
    -    fn vrf_input_from_items(domain: OCTET_STRING, data: SEQUENCE_OF OCTET_STRING) -> VrfInput {
    -        buf = OCTET_STRING(SIZE(0));
    -        for item in data {
    -            buf.append(item);
    -            buf.append(LENGTH(item) as U8);
    +    fn vrf_input_from_items(domain: OCTET_STRING, items: SEQUENCE_OF OCTET_STRING) -> VrfInput {
    +        let data = OCTET_STRING(SIZE = 0); // empty octet string
    +        for item in items {
    +            data.append(item);
    +            data.append(LENGTH(item) as U8);
             }
    -        return vrf_input(domain, buf);
    +        return vrf_input(domain, data);
         }
     }
    -

    Note that we cast the length of each item to a U8. In the context of the -protocol we never have to append strings longer than 255. The function is -internal and not designed to be generic.

    - -Or we should provide a generic one in bandersnatch primitive wrapper to be -used in other contexts? - -

    5.2. VRF Output

    -

    A VrfOutput in this context is computed in function of a VrfInput and a -BandersnatchSecretKey.

    -

    A VrfOutput can be created in two ways: as a standalone object or as part of a -VRF signature. In both scenarios, the resulting VrfOutput remains the same, but -the primary difference lies in the inclusion of a signature in the latter, which -serves to confirm its validity.

    -

    In practice, the VrfOutput is a verifiable seed to produce a variable number -of pseudo-random bytes. These bytes are considered valid when VrfOutput is -accompanied by a valid signature.

    -

    When constructed as a standalone object, VrfOutput is primarily employed -in situations where the secret key owner needs to check if the generated -pseudo-random bytes fulfill some criteria before applying the signature.

    -

    To facilitate the construction of VrfOutput from a secret key and VrfInput, -the following helper function is provided:

    +

    Note that each item length is safely casted to an U8 as:

    +
      +
    1. In the context of this protocol all items lengths are less than 256.
    2. +
    3. The function is internal and not designed for generic use.
    4. +
    +

    5.2. VRF PreOutput

    +

    Functionally, the VrfPreOutput can be considered as a seed for a PRNG to +produce an arbitrary number of output bytes.

    +

    It is computed as function of a VrfInput and a BandersnatchSecretKey.

    +

    Two different approaches can be used to generate it: as a standalone object +or as part of a signature. While the resulting VrfPreOutput is identical +in both cases, the legitimacy of the latter can be confirmed by verifying the +signature using the BandersnatchPublicKey of the expected signer.

    +

    When constructed as a standalone object, VrfPreOutput is primarily employed +in situations where the secret key owner needs to check if the generated output +bytes fulfill some context specific criteria before applying the signature.

    +

    To facilitate the construction, the following helper function is provided:

    #![allow(unused)]
     fn main() {
    -    fn vrf_output(secret: BandernatchSecretKey, input: VrfInput) -> VrfOutput;
    +    fn vrf_pre_output(secret: BandernatchSecretKey, input: VrfInput) -> VrfPreOutput;
     }
    -

    Additionally, a helper function is provided for producing len bytes from -VrfInput and VrfOutput:

    +

    An additional helper function is provided for producing an arbitrary number of +output bytes from VrfInput and VrfPreOutput:

    #![allow(unused)]
     fn main() {
    -    fn vrf_bytes(len: U32, input: VrfInput, output: VrfOuput) -> OCTET_STRING;
    +    fn vrf_bytes(len: U32, input: VrfInput, pre_output: VrfPreOuput) -> OCTET_STRING;
     }
    -

    Just like the VrfInput support function, we have intentionally excluded the -detailed implementation of this function in this document. A reference implementation -is provided in the dleq_vrfs library:

    +

    Similar to the vrf_input function, the details about the implementation +of these functions is omitted. Reference implementations are provided by the +dleq_vrfs project

    5.3. VRF Signature Data

    -

    This section defines the data to be signed using the VRF primitive:

    +

    This section outlines the data to be signed utilizing the VRF primitive:

    #![allow(unused)]
     fn main() {
         VrfSignatureData ::= SEQUENCE {
    @@ -449,13 +452,14 @@ is provided in the dleq_vrfs library:

    inputs: SEQUENCE_OF VrfInput } }
    +

    Where:

      -
    • transcript: an ark-transcript -object. In practice, this is a special hash of some protocol-specific data -to sign which should not influence the VrfOutput.
    • +
    • transcript: a Transcript +instance. In practice, this is a special hash of some protocol-specific data +to sign which doesn't influence the VrfPreOutput.
    • inputs: sequence of VrfInputs to be signed.
    -

    To simplify the construction of a VrfSignatureData object, a helper function is provided:

    +

    To simplify the construction of VrfSignatureData objects, a helper function is defined:

    #![allow(unused)]
     fn main() {
         fn vrf_signature_data(
    @@ -473,27 +477,29 @@ to sign which should not influence the VrfOutput.
     

    5.4. VRF Signature

    Bandersnatch VRF offers two signature flavors:

      -
    • plain signature, which is much like a traditional Schnorr signature,
    • -
    • ring signature which leverages a zk-SNARK to allows for anonymous signatures +
    • plain signature: much like a traditional Schnorr signature,
    • +
    • ring signature: leverages a zk-SNARK to allows for anonymous signatures using a key from a predefined set of enabled keys, known as the ring.

    5.4.1. Plain VRF Signature

    This section describes the signature process for VrfSignatureData using the -plain Bandersnatch signature flavor.

    +plain signature flavor.

    #![allow(unused)]
     fn main() {
         PlainSignature ::= OCTET_STRING;
     
         VrfSignature ::= SEQUENCE {
             signature: PlainSignature,
    -        outputs: SEQUENCE-OF VrfOutput
    +        pre_outputs: SEQUENCE-OF VrfPreOutput
         }
     }
    +

    Where:

      -
    • signature: the actual signature.
    • -
    • outputs: a sequence of VrfOutputs corresponding to the VrfInputs values.
    • +
    • signature: the actual plain signature.
    • +
    • pre_outputs: sequence of VrfPreOutputs corresponding to the VrfInputs +found within the VrfSignatureData.
    -

    Helper function to create a VrfPlainSignature from VrfSignatureData:

    +

    Helper function to construct VrfPlainSignature from VrfSignatureData:

    #![allow(unused)]
     fn main() {
         BandersnatchSecretKey ::= OCTET_STRING;
    @@ -503,8 +509,8 @@ plain Bandersnatch signature flavor.

    signature_data: VrfSignatureData ) -> VrfSignature }
    -

    Helper function for validating the signature and returning a BOOLEAN value -indicating the validity of the signature.

    +

    Helper function for signature verification returning a BOOLEAN value +indicating the validity of the signature (true on success):

    #![allow(unused)]
     fn main() {
         BandersnatchPublicKey ::= OCTET_STRING;
    @@ -518,22 +524,23 @@ indicating the validity of the signature.

    and PlainSignature are intentionally left undefined. Their definitions can be found in the bandersnatch_vrfs reference implementation.

    5.4.2. Ring VRF Signature

    -

    This section deals with the signature process for VrfSignatureData using the -Bandersnatch ring signature flavor.

    +

    This section describes the signature process for VrfSignatureData using the +ring signature flavor.

    #![allow(unused)]
     fn main() {
         RingSignature ::= OCTET_STRING;
     
         RingVrfSignature ::= SEQUENCE {
             signature: RingSignature,
    -        outputs: SEQUENCE_OF VrfOutput
    +        pre_outputs: SEQUENCE_OF VrfPreOutput
         }
     }
      -
    • signature: the actual signature.
    • -
    • outputs: sequence of VrfOutput objects corresponding to the VrfInput values.
    • +
    • signature: the actual ring signature.
    • +
    • pre_outputs: sequence of VrfPreOutputs corresponding to the VrfInputs +found within the VrfSignatureData.
    -

    Helper function to create a RingVrfSignature from VrfSignatureData:

    +

    Helper function to construct RingVrfSignature from VrfSignatureData:

    #![allow(unused)]
     fn main() {
         BandersnatchRingProverKey ::= OCTET_STRING;
    @@ -543,9 +550,8 @@ Bandersnatch ring signature flavor.

    signature_data: VrfSignatureData, ) -> RingVrfSignature; }
    -

    Helper function for validating the signature and returning a BOOLEAN -indicating the validity of the signature (True if it's valid). It's important -to note that this function does not require the signer's public key.

    +

    Helper function for signature verification returning a BOOLEAN value +indicating the validity of the signature (true on success).

    #![allow(unused)]
     fn main() {
         BandersnatchRingVerifierKey ::= OCTET_STRING;
    @@ -555,54 +561,49 @@ to note that this function does not require the signer's public key.

    signature: RingVrfSignature, ) -> BOOLEAN; }
    +

    Note that this function doesn't require the signer's public key.

    In this document, the types BandersnatchRingProverKey, BandersnatchRingVerifierKey, and RingSignature are intentionally left undefined. Their definitions can be found in the bandersnatch_vrfs reference implementation.

    6. Sassafras Protocol

    6.1. Epoch's First Block

    -

    The first block produced for epoch N is required to include the descriptor for -the next epoch N+1.

    -

    The descriptor for next epoch is NextEpochDescriptor.

    +

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

    #![allow(unused)]
     fn main() {
    -    AuthorityId ::= BandersnatchPublicKey;
    -
    -    Randomness ::= OCTET_STRING(SIZE(32));
    -
    -    NextEpochDescriptor ::= SEQUENCE {
    -        randomness: Randomness,
    -        authorities: SEQUENCE_OF AuthorityId,
    +    NextEpochDescriptor ::= SEQUENCE {
    +        randomness: OCTET_STRING(SIZE(32)),
    +        authorities: SEQUENCE_OF BandersnatchPublicKey,
             configuration: ProtocolConfiguration OPTIONAL
         }
     }
    +

    Where:

      -
    • randomness: randomness value.
    • +
    • randomness: 32-bytes pseudo random value.
    • authorities: list of authorities.
    • configuration: optional protocol configuration.
    -

    The NextEpochDescriptor must be SCALE encoded and embedded in the block -header digest log.

    -

    The identifier for the digest element is BYTES("SASS").

    -

    Security Consideration: Instances of NextEpochDescriptor are generated -through on-chain code whenever a block is identified as the first of an epoch. -Consequently, every node executing the block should verify that the descriptor -locally generated during block execution matches the one produced by the block -author, which is found in the digest data before block import.

    +

    This descriptor must be encoded using the SCALE encoding system and embedded +in the block header's digest log. The identifier for the digest element is +BYTES("SASS").

    +

    A special case arises for the first block for 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.1.3.

    6.1.1. Epoch Randomness

    -

    Each block ships with some entropy source in the form of bandersnatch -VrfOutput. Per block randomness is accumulated in the protocol's on-chain -randomness accumulator after block import.

    -

    The exact procedure to accumulate per-block randomness is described in detail -later, in the randomness accumulator paragraph (6.7).

    -

    Next epoch randomness is computed as:

    +

    The randomness in the NextEpochDescriptor randomness is computed as:

    #![allow(unused)]
     fn main() {
    -    next_epoch_randomness = BLAKE2(32, CONCAT(randomness_accumulator, BYTES(next_epoch_index)));
    +    randomness = BLAKE2(32, CONCAT(randomness_accumulator, BYTES(next_epoch.index)));
     }
    +

    Here, randomness_accumulator refers to a 32-byte OCTET_STRING stored +on-chain and computed through a process that incorporates verifiable random +elements from all previously imported blocks. The exact procedure is described +in section 6.7.

    6.1.2. Protocol Configuration

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

    +during tickets validation. It is defined as:

    #![allow(unused)]
     fn main() {
         ProtocolConfiguration ::= SEQUENCE {
    @@ -610,101 +611,120 @@ during tickets validation. It is defined as follows:

    redundancy_factor: U32 } }
    +

    Where:

      -
    • attempts_number: max number of tickets that can be submitted by each -next epoch authority.
    • -
    • redundancy_factor: controls the expected number of extra tickets produced -beyond epoch_length.
    • +
    • attempts_number: maximum number of tickets that each authority for the next +epoch is allowed to submit.
    • +
    • redundancy_factor: expected ratio between epoch's slots and the cumulative +number of tickets which can be submitted by the set of epoch validators.
    -

    The attempts number influences the anonymity of block producers. As all +

    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 in the epoch.

    -

    We do not mind max_attempts < epoch_length though because this loss of -anonymity already becomes small when attempts_number = 64 or 128 and larger -values requires more computation.

    +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 the section dedicated to candidate ticket validation -(6.2.2).

    -

    ProtocolConfiguration values can be adjusted via a dedicated extrinsic which -should have origin set to Root. A valid configuration proposal submitted on -epoch K will be propagated in the NextEpochDescriptor at the beginning of -epoch K+1 and will be effectively enacted on epoch K+2.

    +probability can be found in section 6.2.2.

    +

    ProtocolConfiguration values can be adjusted via a dedicated on-chain call +which should have origin set to Root. Any proposed changes to +ProtocolConfiguration that are submitted in epoch K will be included in the +NextEpochDescriptor at the start of epoch K+1 and will come into effect in +epoch K+2.

    6.1.3. Startup Parameters

    -

    Some parameters for first epoch (index = 0) are configurable via genesis configuration.

    +

    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_OF AuthorityId,
    -        configuration: ProtocolConfiguration OPTIONAL
    +        authorities: SEQUENCE_OF BandersnatchPublicKey,
    +        configuration: ProtocolConfiguration,
         }
     }
    -

    Randomness for first epoch is set to all zeros.

    -

    As block #0 is locally produced by every node by processing the genesis configuration, -the first block explicitly produced by a validator for the first epoch is block #1.

    -

    Block #1 must embed the NextEpochDescriptor for next epoch. This is -constructed re-using the same values used for the first epoch.

    -

    6.2. Creation and Submission of Candidate Tickets

    -

    As a shorthand notation, in this section we refer to one of the next epoch -validators as 'the validator'.

    -

    Upon the beginning of a new epoch N, the validator will construct a set of -'tickets' to be submitted on-chain. These tickets aim to secure ownership of one -or more slots in the upcoming epoch N+1.

    -

    Each validator is allowed to submit a maximum number of tickets whose value is -found in the next epoch ProtocolConfiguration attempts_number field.

    -

    The expected ratio between the attempts and the number of tickets which are -assigned to the next epoch slots is driven by the ticket threshold -(6.2.2).

    -

    Each ticket has an associated unique identifier, denoted as TicketId.

    +

    The on-chain randomness accumulator is initialized only after the genesis +block is produced. It starts with the hash of the genesis block:

    #![allow(unused)]
     fn main() {
    -    TicketId ::= U128
    +    randomness_accumulator = genesis_hash
     }
    -

    6.2.1. Ticket Identifier Value

    -

    The value of the TicketId is determined by the output of the Bandersnatch VRF -when using the following inputs:

    +

    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:

      -
    • Next epoch randomness: Randomness obtained from the NextEpochDescriptor.
    • -
    • Next epoch index: U64 computed as epoch start slot divided epoch duration.
    • -
    • Attempt index: U32 value going from 0 to attempts_number.
    • +
    • randomness: computed using the randomness_accumulator established +post-genesis, as mentioned above.
    • +
    • authorities: the same as those specified in the genesis configuration.
    • +
    • configuration: not set (i.e., None), implying the reuse of the +one found in the genesis configuration.
    -

    Let next_epoch be an object with the information associated to the next epoch.

    +

    6.2. Creation and Submission of Candidate Tickets

    +

    After the beginning of a new epoch N, each validator associated to the next +epoch (N+1) constructs a set of tickets which may be eligible (6.2.2) +to be submitted on-chain. These tickets aim to secure ownership of one or more +slots in the upcoming epoch N+1.

    +

    Each validator is allowed to submit a maximum number of tickets, as specified by +the attempts_number field in the ProtocolConfiguration for the next epoch.

    +

    The ideal timing for a validator to start creating the tickets is subject to +strategy. A recommended approach is to initiate tickets creation once the block +containing the NextEpochDescriptor is either probabilistically or, preferably, +deterministically finalized. This timing is suggested to prevent to waste +resources on tickets that might become obsolete if a different chain branch +is finally chosen as the best one by the distributed system.

    +

    However, validators are also advised to avoid submitting tickets too late, +as tickets submitted during the second half of the epoch must be discarded.

    +

    6.2.1. Ticket Identifier Value

    +

    Each ticket has an associated 128-bit unique identifier defined as:

    +
    #![allow(unused)]
    +fn main() {
    +    TicketId ::= U128;
    +}
    +

    The value of the TicketId is determined by the output of the Bandersnatch VRF +with the following input:

    #![allow(unused)]
     fn main() {
         ticket_id_vrf_input = vrf_input_from_items(
             BYTES("sassafras-ticket-v1.0"),
             [ 
                 next_epoch.randomness,
    -            BYTES(next_epoch.epoch_index),
    +            BYTES(next_epoch.index),
                 BYTES(attempt_index)
             ]
         );
     
    -    ticket_id_vrf_output = vrf_output(AUTHORITY_SECRET_KEY, ticket_id_vrf_input);
    +    ticket_id_vrf_pre_output = vrf_pre_output(AUTHORITY_SECRET_KEY, ticket_id_vrf_input);
     
    -    ticket_bytes = vrf_bytes(16, ticket_id_vrf_input, ticket_id_vrf_output);
    +    ticket_bytes = vrf_bytes(16, ticket_id_vrf_input, ticket_id_vrf_pre_output);
         ticket_id = U128(ticket_bytes);
     }
    +

    Where:

    +
      +
    • next_epoch.randomness: randomness associated to the target epoch.
    • +
    • next_epoch.index: index of the target epoch as a U64.
    • +
    • attempt_index: value going from 0 to attempts_number as a U32.
    • +

    6.2.2. Tickets Threshold

    -

    A TicketId value is valid if its value is less than the ticket threshold.

    +

    A TicketId value is valid if its value is less than the ticket threshold:

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

    Where:

      -
    • v: the number of authorities (aka validators) in the epoch
    • -
    • s: number of slots in the epoch
    • -
    • r: the redundancy factor
    • -
    • a: number of attempts
    • +
    • v: epoch's authorities (aka validators) number
    • +
    • s: epoch's slots number
    • +
    • r: redundancy factor
    • +
    • a: attempts number
    • T: ticket threshold value (0 ≤ T ≤ 1)
    6.2.2.1 Formula Derivation
    -

    For an epoch of s slots we want to have a number of tickets in expectation for -block production equal to the r·s.

    -

    We need that there is a very small probability of their being less than s -winning tickets, even if up to 1/3 of authorities are offline.

    -

    First we set the probability of a ticket winning as T = (r·s)/(a·v).

    -

    Let n be the number of validators who actually participate and so v·2/3 ≤ n ≤ v.

    -

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

    +

    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
    @@ -712,15 +732,15 @@ its expected value is:

    By setting r = 2, we get

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

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

    -

    For s = 600 this gives Pr[X < s] < 4·10⁻¹³, and thus we end up with a great -tolerance over offline nodes and we end-up filling all the slots with tickets -with high probability.

    +

    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 of the w3f description of the protocol.

    +paragraph in the Web3 foundation description of the protocol.

    6.2.3. Ticket Body

    -

    Every candidate ticket identifier has an associated body.

    +

    Every candidate ticket identifier has an associated body, defined as:

    #![allow(unused)]
     fn main() {
         TicketBody ::= SEQUENCE {
    @@ -729,37 +749,43 @@ paragraph of the w3f description of the protocol.

    revealed_pub: Ed25519PublicKey } }
    +

    Where:

    • attempt_index: attempt index used to generate the associated TicketId.
    • erased_pub: Ed25519 ephemeral public key which gets erased as soon as the -ticket is claimed.
    • +ticket is claimed. This key can be used to encrypt data for the validator.
    • revealed_pub: Ed25519 ephemeral public key which gets exposed as soon as the ticket is claimed.

    The process of generating an erased key pair is intentionally left undefined, allowing the implementor the freedom to choose the most suitable strategy.

    -

    Revealed key pair is generated using bytes produced by the VRF with input +

    Revealed key pair is generated using the bytes produced by the VRF with input parameters equal to those employed in TicketId generation, only the label is different.

    -

    Let next_epoch be an object with the information associated to the next epoch:

    #![allow(unused)]
     fn main() {
         revealed_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [ 
                 next_epoch.randomness,
    -            BYTES(next_epoch.epoch_index),
    +            BYTES(next_epoch.index),
                 BYTES(attempt_index)
             ]
         );
     
    -    revealed_vrf_output = vrf_output(AUTHORITY_SECRET_KEY, revealed_vrf_input);
    +    revealed_vrf_pre_output = vrf_pre_output(AUTHORITY_SECRET_KEY, revealed_vrf_input);
     
    -    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_output);
    +    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_pre_output);
         revealed_pub = ed25519_secret_from_seed(revealed_seed).public();
     }
    -

    The usage of the ephemeral public keys will be clarified in the ticket claiming -section (6.5).

    +

    Where:

    +
      +
    • next_epoch.randomness: randomness associated to the target epoch.
    • +
    • next_epoch.index: index of the target epoch as a U64.
    • +
    • attempt_index: value going from 0 to attempts_number as a U32.
    • +
    +

    The ephemeral public keys are also used for claiming the tickets on block production. +Refer to section 6.5 for details.

    6.2.4. Ring Signature Production

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

    #![allow(unused)]
    @@ -774,14 +800,14 @@ section (6.5
             ]
         )
       
    -    ring_signature = ring_vrf_sign(RING_PROVER_KEY, sign_data)
    +    ring_signature = ring_vrf_sign(AUTHORITY_SECRET_KEY, RING_PROVER_KEY, sign_data)
     }
    -

    RING_PROVER object is constructed using the authority secret key, the set -public keys which belong to the next epoch authorities and the zk-SNARK -context parameters (more details in the +

    RING_PROVER_KEY object is constructed using the set of public keys which +belong to the target epoch's authorities and the zk-SNARK context parameters +(for more details refer to the bandersnatch_vrfs reference implementation).

    -

    The body and the ring signature are combined in the TicketEnvelope:

    +

    The body and the ring signature are combined in the TicketEnvelope structure:

    #![allow(unused)]
     fn main() {
         TicketEnvelope ::= SEQUENCE {
    @@ -789,36 +815,32 @@ reference implementation).

    ring_signature: RingVrfSignature } }
    -

    All the ticket envelopes corresponding to valid tickets are submitted on-chain -via a dedicated unsigned extrinsic.

    +

    All the envelopes corresponding to valid tickets can be submitted on-chain via a +dedicated on-chain call (extrinsic).

    6.3. Validation of candidate tickets

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

    -

    The tickets are received via a dedicated extrinsic call.

    -

    Generic validation rules:

    +

    Validation rules:

      -
    • Tickets submissions must occur within the first half of the epoch.
    • -
    • For unsigned extrinsics, it must be submitted by one of the current session -validators.
    • +
    • Tickets submissions must occur within a block part of the first half of the epoch.
    • +
    • Ring signature is verified using the on-chain RING_VERIFIER_KEY.
    • +
    • Ticket identifier is locally (re)computed from the VrfPreOutput contained in the +RingVrfSignature and its value is checked to be less than the tickets' threshold.
    -

    Ticket specific validation rules:

    -
      -
    • Ring signature is verified using the on-chain BandersnatchRingVerifierKey.
    • -
    • Ticket identifier is locally computed from the VrfOutput contained in the -RingVrfSignature and its value is checked to be less than the ticket-threshold.
    • -
    -

    Valid tickets bodies are persisted on-chain.

    -

    6.4. Ticket-Slot assignment

    +

    Valid tickets bodies are all persisted on-chain.

    +

    6.4. Ticket-Slot Binding

    Before the beginning of the next epoch, the on-chain list of tickets must be -associated with the next epoch's slots.

    -

    The assignment process happens in the second half of the submission epoch.

    -

    In the end, there must be at most one ticket per slot.

    +associated with the next epoch's slots such that there must be at most one +ticket per slot.

    +

    The assignment process happens in the second half of the submission epoch and +follows these steps:

      -
    • Initially, the complete list of tickets is sorted based on their ticket-id, -with smaller values coming first.
    • -
    • In cases where there are more tickets than available slots, the list is pruned -by removing the larger value.
    • -
    • Tickets are then assigned to the slots using an outside-in assignment strategy.
    • +
    • Sorting: The complete list of tickets is sorted based on their TicketId +value, with smaller values coming first.
    • +
    • Trimming: In scenarios where there are more tickets than available slots, the +list is trimmed to fit the epoch's slots by removing the larger value.
    • +
    • Assignment: Tickets are assigned to the epoch's slots following an +outside-in strategy.

    6.4.1. Outside-In Assignment

    Given an ordered sequence of tickets [t0, t1, t2, ..., tk] to be assigned to @@ -828,38 +850,26 @@ strategy:

    tickets : [ t1, t3, t5, ... , t4, t2, t0 ]

    Here slot-index is a relative value computed as:

    -
    slot-index = absolute_slot_index - epoch_start_slot
    +
    slot-index = absolute_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 owner, and +is public. What remains confidential is the identity of the ticket's author, and consequently, who possesses the authority to claim the corresponding slot. This information is known only to the author of the ticket.

    -

    6.4.2. Fallback Assignment

    In case the number of available tickets is less than the number of epoch slots, -some (orphan) slots in the middle of the epoch will remain unbounded to any -ticket.

    -

    In such situation, these unassigned slots are allocated using a fallback -assignment strategy.

    -

    The authorities registered on-chain are kept in a sorted buffer.

    -

    The index of the authority which has the privilege to claim an unbounded slot is -calculated as follows:

    -
    #![allow(unused)]
    -fn main() {
    -    index_bytes = BLAKE2(4, CONCAT(epoch_randomness, BYTES(slot)));
    -    index = U32(index_bytes) mod authorities_number;
    -}
    -

    6.5. Claim of ticket ownership during block production

    +some orphan slots in the middle of the epoch will remain unbounded to any +ticket. For claiming strategy refer to 6.5.2.

    +

    6.5. Slot Claim Production

    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 block authoring varies based on whether a given slot has an +

    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, we will employ the primary authoring -method. Conversely, if the slot lacks an associated ticket, we will resort to -the secondary authoring method as a fallback.

    -

    6.5.1. Primary Claim Method

    -

    Let ticket_body represent the TicketBody that has been committed to the on- -chain state, curr_epoch denote an object containing information about the -current epoch, and slot represent the absolute monotonic slot number.

    +

    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.5.1. Primary Method

    +

    Let ticket_body be the TicketBody that has been committed to the on-chain +state, curr_epoch denote an object containing information about the current +epoch, and slot represent the slot number (absolute).

    Follows the construction of VrfSignatureData:

    #![allow(unused)]
     fn main() {
    @@ -867,7 +877,7 @@ current epoch, and slot represent the absolute monotonic slot numbe
             domain: BYTES("sassafras-randomness-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(slot)
             ]
         );
    @@ -876,7 +886,7 @@ current epoch, and slot represent the absolute monotonic slot numbe
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(ticket_body.attempt_index)
             ]
         );
    @@ -892,46 +902,52 @@ current epoch, and slot represent the absolute monotonic slot numbe
             ]
         );
     }
    -

    The inclusion of revealed_vrf_input will generate a VrfSignature with a -VrfOutput allowing the verifier to reconstruct a revealed_pub key -which is expected to be equal to the one committed into the TicketBody.

    -
    6.5.1.1. (Optional) Ed25519 Erased Ephemeral Key Claim
    -

    As the ticket ownership can be claimed by reconstructing the revealed_pub -entry of the ticket, this step is purely optional and serves only to enforce -the claim.

    - -Is this step really necessary? -- Isn't better to keep it simple if this step doesn't offer any extra security? -- We already have a strong method to claim ticket ownership. - -

    The Fiat-Shamir transform is used to obtain a 32-byte challenge associated -with the VrfSignData transcript.

    +
    6.5.1.1. Ephemeral Key Claim
    +

    Fiat-Shamir transform is used to obtain a 32-byte challenge associated with +the VrfSignData transcript.

    Validators employ the secret key associated with erased_pub, which has been -committed in the TicketBody, to sign this challenge.

    +committed in the TicketBody, to sign the challenge.

    #![allow(unused)]
     fn main() {
         challenge = sign_data.transcript.challenge();
    -    erased_signature = ed25519_sign(ERASED_SECRET_KEY, challenge)
    +    erased_signature = ed25519_sign(ERASED_SECRET_KEY, challenge);
     }
    -

    6.5.2. Secondary Claim Method

    -

    If the slot doesn't have any associated ticket then the validator is the one -with index equal to the rule exposed in the fallback assignment section -(6.4.2).

    -

    Given randomness_vrf_input constructed as shown for the primary method, the -VrfSignatureData is constructed as:

    +

    As ticket's ownership can be claimed by reconstructing the revealed_pub entry +of the committed TicketBody, this step is considered optional.

    + +Is this step really necessary? +- Isn't better to keep it simple if this step doesn't offer any extra security? +- We already have a strong method to claim ticket ownership using the vrf output +- What if a validator provides both the proofs? + More weight for the branch (i.e. used to decide what is the best branch by validators)? + E.g. + - primary method + ed25519 erased signature => score 2 + - primary method => score 1 + - fallback method => score 0 + +

    6.5.2. Secondary Method

    +

    By noting that the authorities registered on-chain are kept in an ordered list, +the index of the authority which has the privilege to claim an orphan slot is:

    +
    #![allow(unused)]
    +fn main() {
    +    index_bytes = BLAKE2(4, CONCAT(epoch_randomness, BYTES(slot)));
    +    index = U32(index_bytes) mod authorities_number;
    +}
    +

    Given randomness_vrf_input constructed as shown for the primary method (6.5.1), +the VrfSignatureData is constructed as:

    #![allow(unused)]
     fn main() {
         sign_data = vrf_signature_data(
    -        transcript_label: BYTES("sassafras-slot-claim-transcript-v1.0"),
    +        transcript_label: BYTES("sassafras-claim-v1.0"),
             transcript_data: [ ],
             inputs: [
                 randomness_vrf_input
             ]
         )
     }
    -

    6.5.3. Slot Claim object

    -

    To establish ownership of a slot, the block author must construct a SlotClaim object -which contains all the necessary information to assert ownership of the slot.

    +

    6.5.3. Slot Claim Object

    +

    The SlotClaim structure is used to contain all the necessary information to +assess ownership of a slot.

    #![allow(unused)]
     fn main() {
         SlotClaim ::= SEQUENCE {
    @@ -941,27 +957,7 @@ which contains all the necessary information to assert ownership of the slot.

    }
    -
      -
    • -

      authority_index: index of the block author in the on-chain authorities list.

      -
    • -
    • -

      slot: absolute slot number (not relative with respect to the epoch start)

      -
    • -
    • -

      signature: signature that includes one or two VrfOutputs.

      -
        -
      • The first VrfOutput is always present and is used to generate per-block -randomness. This is used to claim ticket ownership.
      • -
      • The second VrfOutput is included if the slot is associated with a ticket. -This is relevant to claim ticket ownership.
      • -
      -
    • -
    • -

      erased_signature: optional signature providing an additional proof of ticket -ownership (see 6.5.1.1).

      -
    • -
    +

    The claim is constructed as follows:

    #![allow(unused)]
     fn main() {
         signature = vrf_sign(AUTHORITY_SECRET_KEY, sign_data);
    @@ -973,170 +969,184 @@ ownership (see 6.5.1.1).

    erased_signature } }
    +

    Where:

    +
      +
    • authority_index: index of the block author in the on-chain authorities list.
    • +
    • slot: slot number (absolute, not relative to the epoch start)
    • +
    • signature: signature relative to the sign_data constructed via the +primary 6.5.1 or secondary (6.5.2) method.
    • +
    • erased_signature: optional signature providing an additional proof of ticket +ownership (6.5.1.1).
    • +
    +

    The signature includes one or two VrfPreOutputs.

    +
      +
    • The first is always present and is used to generate per-block randomness +to feed the randomness accumulator (6.7).
    • +
    • The second is included if the slot is bound to a ticket. This is relevant to +claim ticket ownership (6.6.1).
    • +

    The claim object is SCALE encoded and sent in the block's header digest log.

    -

    6.6. Validation of the claim during block verification

    -

    Validation of SlotClaim object found in the block's header.

    -

    The procedure depends on whether the slot has an associated ticket or not -according to the on-chain state.

    -

    If there is a ticket linked to the slot, the primary verification method will be -used; otherwise, the protocol resorts to the secondary one.

    -

    In both scenarios, the signature within the SlotClaim is verified using -a VrfSignData constructed as specified by paragraph 6.5.

    -

    Given claim an instance of SlotClaim:

    +

    6.6. Slot Claim Verification

    +

    The signature within the SlotClaim is verified using a VrfSignData +constructed as specified in 6.5.

    #![allow(unused)]
     fn main() {
    -    public_key = AUTHORITIES[claim.authority_index];
    +    public_key = authorities[claim.authority_index];
     
    -    vrf_verify(public_key, sign_data, claim.signature);
    +    result = vrf_verify(public_key, sign_data, claim.signature);
    +    assert(result == true);
     }
    -

    If signature verification fails then the claim is not legit.

    -

    6.6.1. Primary Claim Method Verification

    -

    This verification is performed to confirm ticket ownership and is performed -utilizing the second VrfOutput contained within the SlotClaim signature.

    -

    By using the VrfOutput object together with the associated expected VrfInput -the verifier should be able to reconstruct the revealed_pub key committed in -the TicketBody. If there is a mismatch, the claim is not legit.

    +

    With:

    +
      +
    • authorities: list of authorities for the epoch, as recorded on-chain.
    • +
    • sign_data: data that has been signed, constructed as specified in 6.5.
    • +
    +

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

    +

    For slots tied to a ticket, the primary verification method is employed. Otherwise, +the secondary method is utilized.

    +

    6.6.1. Primary Method

    +

    This method verifies ticket ownership using the second VrfPreOutput from the +SlotClaim signature

    +

    The process involves comparing the revealed_pub key from the committed +TicketBody with a reconstructed key using the VrfPreOutput and the expected +VrfInput. A mismatch indicates an illegitimate claim.

    #![allow(unused)]
     fn main() {
         revealed_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-revealed-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(ticket_body.attempt_index)
             ]
         );
     
    -    reveled_vrf_output = claim.signature.outputs[1];
    +    reveled_vrf_pre_output = claim.signature.pre_outputs[1];
     
    -    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_output);
    +    revealed_seed = vrf_bytes(32, revealed_vrf_input, revealed_vrf_pre_output);
         revealed_pub = ed25519_secret_from_seed(revealed_seed).public();
    -
         assert(revealed_pub == ticket_body.revealed_pub);
     }
    -
    6.6.1.1. (Optional) Ephemeral Key Signature Check
    -

    If the erased_signature element within the SlotClaim is present the -erased_pub key is used to verify it.

    -

    The signed challenge is generated with identical steps as outlined in section -6.5.1.1.

    +
    6.6.1.1. Ephemeral Key Signature Check
    +

    If the erased_signature is present in SlotClaim, the erased_pub within the +committed TicketBody key is used to verify it.

    +

    The signed challenge is generated as outlined in section 6.5.1.1.

    #![allow(unused)]
     fn main() {
         challenge = sign_data.transcript.challenge();
         result = ed25519_verify(ticket_body.erased_pub, challenge, claim.erased_signature);
    -
         assert(result == true);
     }
    -

    6.6.2. Secondary Claim Method Verification

    +

    6.6.2. Secondary Method

    If the slot doesn't have any associated ticket then the validator index contained in -the claim should match the one given by the rule outlined in the fallback assignment -section (6.4.2)

    +the claim should match the one given by the rule outlined in section 6.5.2.

    6.7. Randomness Accumulator

    -

    The first VrfOutput which ships with the block's SlotClaim signature -is mandatory and must be used as the entropy source for the randomness which -gets accumulated on-chain after block processing.

    -

    Given claim the instance of SlotClaim within the block header, and -accumulator the current value for the current epoch randomness accumulator, -the accumulator value is updated as follows:

    +

    The first VrfPreOutput which ships within the block's SlotClaim signature +is mandatory and must be used as entropy source for the randomness which gets +accumulated on-chain after block transactions execution.

    +

    Given claim the instance of SlotClaim found within the block header, and +randomness_accumulator the current value for the randomness accumulator, the +randomness_accumulator value is updated as follows:

    #![allow(unused)]
     fn main() {
         randomness_vrf_input = vrf_input_from_items(
             domain: BYTES("sassafras-randomness-v1.0"),
             data: [
                 curr_epoch.randomness,
    -            BYTES(curr_epoch.epoch_index),
    +            BYTES(curr_epoch.index),
                 BYTES(slot)
             ]
         );
     
    -    randomness_vrf_output = claim.signature.outputs[0];
    -
    -    randomness = vrf_bytes(32, randomness_vrf_input, randomness_vrf_output);
    +    randomness_vrf_pre_output = claim.signature.pre_outputs[0];
    +    randomness = vrf_bytes(32, randomness_vrf_input, randomness_vrf_pre_output);
     
         randomness_accumulator = BLAKE2(32, CONCAT(randomness_accumulator, randomness));
     }
    -

    The updated accumulator value is stored on-chain.

    -

    The randomness accumulated during epoch N will be used, at the start of the -next epoch (N+1), as an input to compute the NextEpochDescriptor -randomness element (see section 6.1).

    -

    As outlined throughout the document, epoch randomness value secures various -protocol-specific functions, including ticket generation and assignment of -fallback slots (refer to section 6.4.2). Additionally, users may utilize this -value for other purposes as needed.

    +

    The randomness_accumulator never resets and is a continuously evolving value. +It primarily serves as a basis for calculating the randomness associated to the +epochs as outlined on section 6.1, but custom usages +from the user are not excluded.

    7. Drawbacks

    None

    8. Testing, Security, and Privacy

    -

    The reference implementation for this RFC will be tested on testnets first.

    -

    An audit may be required to ensure the implementation does not introduce unwanted side effects

    +

    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

    -

    The utilization of Sassafras consensus represents a significant advancement in -the mitigation of short-lived fork occurrences.

    -

    Generation of forks are not possible when following the protocol and the only source -of forks is network partitioning. In this case, on recovery, the decision of -which fork to follow is not opinionated and there is only one choice.

    +

    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 impacts native client code and thus can't be -introduced via a simple runtime upgrade.

    +

    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 to ensure -the protocol's completeness and security.

    -

    These topics include:

    -

    12.1. Interactions with the Runtime

    +protocol, several crucial topics remain to be addressed in future RFCs. +These include:

    +

    12.1. Interactions with On-Chain Code

    • -

      Outbound Interface. Interfaces exposed by the host which are required by the runtime. -These are commonly dubbed Host Functions.

      +

      Outbound Interfaces: Interfaces that the host environment provides to the +on-chain code, typically known as Host Functions.

    • -

      Unrecorded Inbound Interfaces. Interfaces exposed by the runtime which are required by the host. -These are commonly dubbed Runtime APIs.

      +

      Unrecorded Inbound Interfaces. Interfaces that the on-chain code provides +to the host code, typically known as Runtime APIs.

    • -

      Transactional Inbound Interfaces. Interfaces exposed by the runtime which alter the state. -These are commonly dubbed Extrinsics and Inherents.

      +

      Transactional Inbound Interfaces. Interfaces that the on-chain code provides +to the world to alter the chain state, typically known as Transactions +(or extrinsics in the Polkadot ecosystem)

    12.2. Deployment Strategies

      -
    • Protocol Migration. Exploring how this protocol can seamlessly replace -an already operational instance of another protocol is essential. Future RFCs -should delve into the deployment strategy, including considerations for a smooth -transition process.
    • +
    • Protocol Migration. Exploring how this protocol can seamlessly replace an +already operational instance of another protocol. Future RFCs should focus on +deployment strategies to facilitate a smooth transition.
    -

    12.3. ZK-SNARK SRS Initialization Ceremony.

    +

    12.3. ZK-SNARK SRS Initialization

    • -

      Timing and Procedure: Determining the timing and procedure for the ZK-SNARK -SRS (Structured Reference String) initialization ceremony. Future RFCs should -provide insights into whether this process should be performed before the -deployment of Sassafras and the steps involved.

      +

      Procedure: Determining the procedure for the zk-SNARK SRS (Structured +Reference String) initialization. Future RFCs should provide insights into +whether this process should include an ad-hoc initialization ceremony or if +we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).

    • -

      Sharing with Para-chains: Considering the complexity of the ceremony, we -must understand whether the SRS is shared with para-chains or maintained -independently.

      +

      Sharing with Para-chains: Considering the complexity of the process, we +must understand whether the SRS is shared with system para-chains or +maintained independently.

    12.4. Anonymous Submission of Tickets.

    • Mixnet Integration: Submitting tickets directly can pose a risk of potential deanonymization through traffic analysis. Subsequent RFCs should -investigate the potential for incorporating Mixnet technology or other +investigate the potential for incorporating Mixnet protocol or other privacy-enhancing mechanisms to address this concern.
    diff --git a/proposed/0034-xcm-absolute-location-account-derivation.html b/proposed/0034-xcm-absolute-location-account-derivation.html index 9118c50..5d660b3 100644 --- a/proposed/0034-xcm-absolute-location-account-derivation.html +++ b/proposed/0034-xcm-absolute-location-account-derivation.html @@ -90,7 +90,7 @@ diff --git a/proposed/0042-extrinsics-state-version.html b/proposed/0042-extrinsics-state-version.html index 3d23ab7..4de856a 100644 --- a/proposed/0042-extrinsics-state-version.html +++ b/proposed/0042-extrinsics-state-version.html @@ -90,7 +90,7 @@ diff --git a/proposed/0043-storage-proof-size-hostfunction.html b/proposed/0043-storage-proof-size-hostfunction.html index 5115b3f..abe4032 100644 --- a/proposed/0043-storage-proof-size-hostfunction.html +++ b/proposed/0043-storage-proof-size-hostfunction.html @@ -90,7 +90,7 @@ diff --git a/proposed/0044-rent-based-registration.html b/proposed/0044-rent-based-registration.html index fb1099c..3ea94a9 100644 --- a/proposed/0044-rent-based-registration.html +++ b/proposed/0044-rent-based-registration.html @@ -90,7 +90,7 @@ diff --git a/proposed/0046-metadata-for-offline-signers.html b/proposed/0046-metadata-for-offline-signers.html index 58957aa..641bdd8 100644 --- a/proposed/0046-metadata-for-offline-signers.html +++ b/proposed/0046-metadata-for-offline-signers.html @@ -90,7 +90,7 @@ diff --git a/proposed/0047-assignment-of-availability-chunks.html b/proposed/0047-assignment-of-availability-chunks.html index 9c75901..c1ea4cb 100644 --- a/proposed/0047-assignment-of-availability-chunks.html +++ b/proposed/0047-assignment-of-availability-chunks.html @@ -90,7 +90,7 @@ diff --git a/proposed/0059-nodes-capabilities-discovery.html b/proposed/0059-nodes-capabilities-discovery.html index 15fc8bf..801440b 100644 --- a/proposed/0059-nodes-capabilities-discovery.html +++ b/proposed/0059-nodes-capabilities-discovery.html @@ -90,7 +90,7 @@ diff --git a/proposed/0061-allocator-inside-of-runtime.html b/proposed/0061-allocator-inside-of-runtime.html index 4e09696..76917b9 100644 --- a/proposed/0061-allocator-inside-of-runtime.html +++ b/proposed/0061-allocator-inside-of-runtime.html @@ -90,7 +90,7 @@ @@ -298,7 +298,7 @@ For the first year, we SHALL disable the v1 by default, and enable it by default - @@ -312,7 +312,7 @@ For the first year, we SHALL disable the v1 by default, and enable it by default - diff --git a/new/0062-lowering-existential-deposit-on-assethub.html b/proposed/0062-lowering-existential-deposit-on-assethub.html similarity index 89% rename from new/0062-lowering-existential-deposit-on-assethub.html rename to proposed/0062-lowering-existential-deposit-on-assethub.html index fe38eed..424ecfd 100644 --- a/new/0062-lowering-existential-deposit-on-assethub.html +++ b/proposed/0062-lowering-existential-deposit-on-assethub.html @@ -90,7 +90,7 @@ @@ -274,11 +274,11 @@ This implies that the new ED can support 3 asset.transfer or 4 asset.transferKe