mirror of
https://github.com/pezkuwichain/pezkuwi-fellows.git
synced 2026-05-30 17:21:00 +00:00
deploy: 4d0a344326
This commit is contained in:
+233
-231
@@ -305,7 +305,7 @@ detailing proposed changes to the technical implementation of the Polkadot netwo
|
|||||||
</li>
|
</li>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#2-stakeholders">2. Stakeholders</a>
|
<li><a href="proposed/0026-sassafras-consensus.html#2-stakeholders">2. Stakeholders</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#21-blockchain-developers">2.1. Blockchain Developers</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#21-blockchain-core-developers">2.1. Blockchain Core Developers</a></li>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -359,7 +359,7 @@ detailing proposed changes to the technical implementation of the Polkadot netwo
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></li>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#122-deployment-strategies">12.2. Deployment Strategies</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#122-deployment-strategies">12.2. Deployment Strategies</a></li>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#123-zk-snark-srs">12.3. ZK-SNARK SRS</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#123-zk-snark-parameters">12.3. ZK-SNARK Parameters</a></li>
|
||||||
<li><a href="proposed/0026-sassafras-consensus.html#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></li>
|
<li><a href="proposed/0026-sassafras-consensus.html#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -381,9 +381,9 @@ authorities set while ensuring that the identity of authorities assigned to
|
|||||||
the slots remains undisclosed until the slot is actively claimed during block
|
the slots remains undisclosed until the slot is actively claimed during block
|
||||||
production.</p>
|
production.</p>
|
||||||
<h2 id="1-motivation"><a class="header" href="#1-motivation">1. Motivation</a></h2>
|
<h2 id="1-motivation"><a class="header" href="#1-motivation">1. Motivation</a></h2>
|
||||||
<p>Sassafras Protocol has been rigorously detailed in a comprehensive
|
<p>Sassafras Protocol has been rigorously described in a comprehensive
|
||||||
<a href="https://eprint.iacr.org/2023/031.pdf">research paper</a> authored by the
|
<a href="https://eprint.iacr.org/2023/031.pdf">research paper</a> authored by the
|
||||||
<a href="https://web3.foundation">Web3 foundation</a> research team.</p>
|
<a href="https://web3.foundation">Web3 Foundation</a> research team.</p>
|
||||||
<p>This RFC is primarily intended to detail the critical implementation aspects
|
<p>This RFC is primarily intended to detail the critical implementation aspects
|
||||||
vital for ensuring interoperability and to clarify certain aspects that are
|
vital for ensuring interoperability and to clarify certain aspects that are
|
||||||
left open by the research paper and thus subject to interpretation during
|
left open by the research paper and thus subject to interpretation during
|
||||||
@@ -396,65 +396,51 @@ this RFC should be considered authoritative to eliminate ambiguities and ensure
|
|||||||
interoperability.</p>
|
interoperability.</p>
|
||||||
<h3 id="12-supporting-sassafras-for-polkadot"><a class="header" href="#12-supporting-sassafras-for-polkadot">1.2. Supporting Sassafras for Polkadot</a></h3>
|
<h3 id="12-supporting-sassafras-for-polkadot"><a class="header" href="#12-supporting-sassafras-for-polkadot">1.2. Supporting Sassafras for Polkadot</a></h3>
|
||||||
<p>Beyond promoting interoperability, this RFC also aims to facilitate the
|
<p>Beyond promoting interoperability, this RFC also aims to facilitate the
|
||||||
implementation of Sassafras within the Polkadot ecosystem.</p>
|
implementation of Sassafras within the greater Polkadot ecosystem.</p>
|
||||||
<p>Although the specifics of deployment strategies are beyond the scope of this
|
<p>Although the specifics of deployment strategies are beyond the scope of this
|
||||||
document, it lays the groundwork for the integration of Sassafras into the
|
document, it lays the groundwork for the integration of Sassafras.</p>
|
||||||
greater Polkadot ecosystem.</p>
|
|
||||||
<h2 id="2-stakeholders"><a class="header" href="#2-stakeholders">2. Stakeholders</a></h2>
|
<h2 id="2-stakeholders"><a class="header" href="#2-stakeholders">2. Stakeholders</a></h2>
|
||||||
<h3 id="21-blockchain-developers"><a class="header" href="#21-blockchain-developers">2.1. Blockchain Developers</a></h3>
|
<p>The protocol has a central role in the next generation block authoring consensus
|
||||||
|
systems.</p>
|
||||||
|
<h3 id="21-blockchain-core-developers"><a class="header" href="#21-blockchain-core-developers">2.1. Blockchain Core Developers</a></h3>
|
||||||
<p>Developers responsible for creating blockchains who intend to leverage the
|
<p>Developers responsible for creating blockchains who intend to leverage the
|
||||||
benefits offered by the Sassafras Protocol.</p>
|
benefits offered by the Sassafras Protocol.</p>
|
||||||
<h3 id="22-polkadot-ecosystem-contributors"><a class="header" href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></h3>
|
<h3 id="22-polkadot-ecosystem-contributors"><a class="header" href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></h3>
|
||||||
<p>Developers contributing to the Polkadot ecosystem, both relay-chain and
|
<p>Developers contributing to the Polkadot ecosystem, both relay-chain and
|
||||||
para-chains.</p>
|
para-chains.</p>
|
||||||
<p>The protocol will have a central role in the next generation block authoring
|
|
||||||
consensus systems.</p>
|
|
||||||
<h2 id="3-notation"><a class="header" href="#3-notation">3. Notation</a></h2>
|
<h2 id="3-notation"><a class="header" href="#3-notation">3. Notation</a></h2>
|
||||||
<p>This section outlines the notation and conventions adopted throughout this
|
<p>This section outlines the notation adopted throughout this document to ensure
|
||||||
document to ensure clarity and consistency.</p>
|
clarity and consistency.</p>
|
||||||
<h3 id="31-data-structures-definitions"><a class="header" href="#31-data-structures-definitions">3.1. Data Structures Definitions</a></h3>
|
<h3 id="31-data-structures-definitions"><a class="header" href="#31-data-structures-definitions">3.1. Data Structures Definitions</a></h3>
|
||||||
<p>Data structures are mostly defined using standard <a href="https://en.wikipedia.org/wiki/ASN.1">ASN.1</a>,
|
<p>Data structures are mostly defined using standard <a href="https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx">ASN.1</a>
|
||||||
syntax with few exceptions.</p>
|
syntax with few exceptions.</p>
|
||||||
<p>To ensure interoperability of serialized structures, the order of the fields
|
<p>To ensure interoperability of serialized structures, the order of the fields
|
||||||
must match the definitions found within this specification.</p>
|
must match the definitions found within this specification.</p>
|
||||||
<h3 id="32-types-alias"><a class="header" href="#32-types-alias">3.2. Types Alias</a></h3>
|
<h3 id="32-types-alias"><a class="header" href="#32-types-alias">3.2. Types Alias</a></h3>
|
||||||
<p>We define some types alias to make ASN.1 syntax more intuitive.</p>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Unsigned integer: <code>Unsigned ::= INTEGER (0..MAX)</code></li>
|
<li>Unsigned integer: <code>Unsigned ::= INTEGER (0..MAX)</code></li>
|
||||||
<li>n bits unsigned integer: <code>Unsigned<n> ::= INTEGER (0..2^n - 1)</code>
|
<li>n-bit unsigned integer: <code>Unsigned<n> ::= INTEGER (0..2^n - 1)</code>
|
||||||
<ul>
|
<ul>
|
||||||
<li>8 bits unsigned integer (octet) <code>Unsigned8 ::= Unsigned<8></code></li>
|
<li>8-bit unsigned integer (octet) <code>Unsigned8 ::= Unsigned<8></code></li>
|
||||||
<li>32 bits unsigned integer: <code>Unsigned32 ::= Unsigned<32></code></li>
|
<li>32-bit unsigned integer: <code>Unsigned32 ::= Unsigned<32></code></li>
|
||||||
<li>64 bits unsigned integer: <code>Unsigned64 ::= Unsigned<64></code></li>
|
<li>64-bit unsigned integer: <code>Unsigned64 ::= Unsigned<64></code></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>Non-homogeneous sequence (struct/tuple): <code>Sequence ::= SEQUENCE</code></li>
|
<li>Non-homogeneous sequence (struct/tuple): <code>Sequence ::= SEQUENCE</code></li>
|
||||||
<li>Homogeneous sequence (vector): <code>Sequence<T> ::= SEQUENCE OF T</code>
|
<li>Variable length homogeneous sequence (vector): <code>Sequence<T> ::= SEQUENCE OF T</code></li>
|
||||||
E.g. <code>Sequence<Unsigned> ::= SEQUENCE OF Unsigned</code></li>
|
|
||||||
<li>Fixed length homogeneous sequence (array): <code>Sequence<T,n> ::= Sequence<T> (SIZE(n))</code></li>
|
<li>Fixed length homogeneous sequence (array): <code>Sequence<T,n> ::= Sequence<T> (SIZE(n))</code></li>
|
||||||
<li>Octet string alias: <code>OctetString ::= Sequence<Unsigned8></code></li>
|
<li>Variable length octet-string: <code>OctetString ::= Sequence<Unsigned8></code></li>
|
||||||
<li>Fixed length octet string: <code>OctetString<n> ::= Sequence<Unsigned8, n></code></li>
|
<li>Fixed length octet-string: <code>OctetString<n> ::= Sequence<Unsigned8, n></code></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="32-pseudo-code"><a class="header" href="#32-pseudo-code">3.2. Pseudo-Code</a></h3>
|
<h3 id="32-pseudo-code"><a class="header" href="#32-pseudo-code">3.2. Pseudo-Code</a></h3>
|
||||||
<p>It is convenient to make use of code snippets as part of the protocol
|
<p>It is convenient to make use of code snippets as part of the protocol
|
||||||
description. As a convention, the code is formatted in a style similar to
|
description. As a convention, the code is formatted in a style similar to
|
||||||
<em>Rust</em>, and can make use of the following set of predefined functions:</p>
|
<em>Rust</em>, and can make use of the following set of predefined procedures:</p>
|
||||||
|
<h4 id="sequences"><a class="header" href="#sequences">Sequences</a></h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p><code>ENCODE(x: T) -> OctetString</code>: Encodes <code>x</code> as an <code>OctetString</code> according to
|
<p><code>CONCAT(x₀: OctetString, ..., xₖ: OctetString) -> OctetString</code>: Concatenates the
|
||||||
<a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
input octet-strings as a new octet string.</p>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>DECODE<T>(x: OctetString) -> T</code>: Decodes <code>x</code> as a type <code>T</code> object according
|
|
||||||
to <a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>BLAKE2(n: Unsigned, x: OctetString) -> OctetString<n></code>: Standard <em>Blake2b</em> hash
|
|
||||||
of <code>x</code> with output truncated to <code>n</code> bytes.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>CONCAT(x₀: OctetString, ..., xₖ: OctetString) -> OctetString</code>: Concatenate the
|
|
||||||
inputs octets as a new octet string.</p>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><code>LENGTH(s: Sequence) -> Unsigned</code>: The number of elements in the sequence <code>s</code>.</p>
|
<p><code>LENGTH(s: Sequence) -> Unsigned</code>: The number of elements in the sequence <code>s</code>.</p>
|
||||||
@@ -469,50 +455,65 @@ inputs octets as a new octet string.</p>
|
|||||||
<p><code>POP(s: Sequence<T>) -> T</code>: extract and returns the last element of the sequence <code>s</code>.</p>
|
<p><code>POP(s: Sequence<T>) -> T</code>: extract and returns the last element of the sequence <code>s</code>.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h4 id="codec"><a class="header" href="#codec">Codec</a></h4>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><code>ENCODE(x: T) -> OctetString</code>: Encodes <code>x</code> as an <code>OctetString</code> according to
|
||||||
|
<a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>DECODE<T>(x: OctetString) -> T</code>: Decodes <code>x</code> as a type <code>T</code> object according
|
||||||
|
to <a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4 id="other"><a class="header" href="#other">Other</a></h4>
|
||||||
|
<ul>
|
||||||
|
<li><code>BLAKE2(x: OctetString) -> OctetString<32></code>: Standard <em>Blake2b</em> hash
|
||||||
|
of <code>x</code> with 256-bit digest.</li>
|
||||||
|
</ul>
|
||||||
<h3 id="33-incremental-introduction-of-types-and-functions"><a class="header" href="#33-incremental-introduction-of-types-and-functions">3.3. Incremental Introduction of Types and Functions</a></h3>
|
<h3 id="33-incremental-introduction-of-types-and-functions"><a class="header" href="#33-incremental-introduction-of-types-and-functions">3.3. Incremental Introduction of Types and Functions</a></h3>
|
||||||
<p>More types and helper functions are introduced incrementally as they become
|
<p>More types and helper functions are introduced incrementally as they become
|
||||||
relevant within the document's context.</p>
|
relevant within the document's context.</p>
|
||||||
<h2 id="4-protocol-introduction"><a class="header" href="#4-protocol-introduction">4. Protocol Introduction</a></h2>
|
<h2 id="4-protocol-introduction"><a class="header" href="#4-protocol-introduction">4. Protocol Introduction</a></h2>
|
||||||
<p>The timeline is segmented into a sequentially ordered sequence of <strong>slots</strong>.
|
<p>The timeline is segmented into a sequentially ordered sequence of <strong>slots</strong>.
|
||||||
This entire sequence of slots is then further partitioned into distinct segments
|
This entire sequence of slots is further partitioned into distinct segments
|
||||||
known as <strong>epochs</strong>.</p>
|
known as <strong>epochs</strong>.</p>
|
||||||
<p>Sassafras protocol aims to map each slot within a <em>target</em> epoch to the
|
<p>Sassafras aims to map each slot within a <em>target epoch</em> to the authorities
|
||||||
designated authorities for that epoch, utilizing a ticketing system.</p>
|
scheduled for that epoch, utilizing a ticketing system.</p>
|
||||||
<p>The core protocol operation can be roughly divided into four phases.</p>
|
<p>The core protocol operation can be roughly divided into four phases.</p>
|
||||||
<h3 id="41-submission-of-candidate-tickets"><a class="header" href="#41-submission-of-candidate-tickets">4.1. Submission of Candidate Tickets</a></h3>
|
<h3 id="41-submission-of-candidate-tickets"><a class="header" href="#41-submission-of-candidate-tickets">4.1. Submission of Candidate Tickets</a></h3>
|
||||||
<p>Each of the authorities scheduled for the target epoch generate and submits
|
<p>Each authority scheduled for the target epoch generates and shares a set of
|
||||||
a set of candidate tickets. Every ticket has an unbiasable pseudo random score
|
candidate tickets. Every ticket has an <em>unbiasable</em> pseudo random score and is
|
||||||
and is bundled with an anonymous proof of validity.</p>
|
bundled with an anonymous proof of validity.</p>
|
||||||
<h3 id="42-validation-of-candidate-tickets"><a class="header" href="#42-validation-of-candidate-tickets">4.2. Validation of Candidate Tickets</a></h3>
|
<h3 id="42-validation-of-candidate-tickets"><a class="header" href="#42-validation-of-candidate-tickets">4.2. Validation of Candidate Tickets</a></h3>
|
||||||
<p>Each candidate ticket undergoes a validation process for the associated validity
|
<p>Each candidate ticket undergoes a validation process for the associated validity
|
||||||
proof and compliance with other protocol-specific constraints. Valid tickets
|
proof and compliance with other protocol-specific constraints. Valid tickets
|
||||||
are persisted on-chain.</p>
|
are persisted on-chain.</p>
|
||||||
<h3 id="43-tickets-slots-binding"><a class="header" href="#43-tickets-slots-binding">4.3. Tickets Slots Binding</a></h3>
|
<h3 id="43-tickets-slots-binding"><a class="header" href="#43-tickets-slots-binding">4.3. Tickets Slots Binding</a></h3>
|
||||||
<p>After collecting all valid candidate tickets and before the beginning of the
|
<p>After collecting all valid candidate tickets and before the beginning of the
|
||||||
target epoch, a deterministic method is used to uniquely associate a subset of
|
<em>target epoch</em>, a deterministic method is used to uniquely associate a subset of
|
||||||
these tickets with the slots of the target epoch.</p>
|
these tickets to the slots of the <em>target epoch</em>.</p>
|
||||||
<h3 id="44-claim-of-ticket-ownership"><a class="header" href="#44-claim-of-ticket-ownership">4.4. Claim of Ticket Ownership</a></h3>
|
<h3 id="44-claim-of-ticket-ownership"><a class="header" href="#44-claim-of-ticket-ownership">4.4. Claim of Ticket Ownership</a></h3>
|
||||||
<p>During block production phase of the target epoch, block's author is required
|
<p>During block production phase of <em>target epoch</em>, the author is required to prove
|
||||||
to prove ownership of the ticket associated to the block's slot. This step
|
ownership of the ticket associated to the block's slot. This step discloses the
|
||||||
discloses the identity of the ticket owner.</p>
|
identity of the ticket owner.</p>
|
||||||
<h2 id="5-bandersnatch-vrfs-cryptographic-primitives"><a class="header" href="#5-bandersnatch-vrfs-cryptographic-primitives">5. Bandersnatch VRFs Cryptographic Primitives</a></h2>
|
<h2 id="5-bandersnatch-vrfs-cryptographic-primitives"><a class="header" href="#5-bandersnatch-vrfs-cryptographic-primitives">5. Bandersnatch VRFs Cryptographic Primitives</a></h2>
|
||||||
<p>It is important to note that this section is not intended to serve as an
|
<p>This section is not intended to serve as an exhaustive exploration of the
|
||||||
exhaustive exploration of the mathematically intensive foundations of the
|
mathematically intensive foundations of the cryptographic primitive. Rather, its
|
||||||
cryptographic primitive. Rather, its primary aim is to offer a concise and
|
primary aim is to offer a concise and accessible explanation of the primitives
|
||||||
accessible explanation of the primitive's role and usage which is relevant
|
role and interface which is relevant within the scope of the protocol. For a more
|
||||||
within the scope of the protocol. For a more detailed explanation, refer to
|
detailed explanation, refer to the <a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs</a>
|
||||||
the <a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRF</a>
|
|
||||||
technical specification</p>
|
technical specification</p>
|
||||||
<p>Bandersnatch VRF comes in two flavors:</p>
|
<p>Bandersnatch VRF comes in two variants:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><em>Bare</em> VRF: Extension to the IETF ECVRF <a href="https://datatracker.ietf.org/doc/rfc9381/">RFC 9381</a>,</li>
|
<li><em>Bare</em> VRF: Extension to the IETF ECVRF <a href="https://datatracker.ietf.org/doc/rfc9381/">RFC 9381</a>,</li>
|
||||||
<li><em>Ring</em> VRF: Provides anonymous signatures by leveraging a <em>zk-SNARK</em>.</li>
|
<li><em>Ring</em> VRF: Anonymous signatures leveraging <em>zk-SNARK</em>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Together with the <em>input</em>, which determines the signed VRF <em>output</em>, both the
|
<p>Together with the <em>input</em>, which determines the VRF <em>output</em>, both variants
|
||||||
flavors offer the capability to sign some arbitrary additional data (<em>extra</em>)
|
offer the capability to sign some arbitrary additional data (<em>extra</em>) which
|
||||||
which doesn't contribute to the VRF output.</p>
|
doesn't contribute to the VRF output.</p>
|
||||||
<h3 id="51-bare-vrf-interface"><a class="header" href="#51-bare-vrf-interface">5.1 Bare VRF Interface</a></h3>
|
<h3 id="51-bare-vrf-interface"><a class="header" href="#51-bare-vrf-interface">5.1 Bare VRF Interface</a></h3>
|
||||||
<p>Function to construct a <code>VrfSignature</code>.</p>
|
<p>VRF signature construction.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_sign(
|
</span> fn vrf_sign(
|
||||||
@@ -521,8 +522,8 @@ which doesn't contribute to the VRF output.</p>
|
|||||||
extra: OctetString,
|
extra: OctetString,
|
||||||
) -> VrfSignature
|
) -> VrfSignature
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function for signature verification returning a Boolean value indicating the
|
<p>VRF signature verification. Returns a Boolean indicating the validity of the
|
||||||
validity of the signature (<code>1</code> on success):</p>
|
signature (<code>1</code> on success).</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_verify(
|
</span> fn vrf_verify(
|
||||||
@@ -532,7 +533,7 @@ validity of the signature (<code>1</code> on success):</p>
|
|||||||
signature: VrfSignature
|
signature: VrfSignature
|
||||||
) -> Unsigned<1>;
|
) -> Unsigned<1>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from <em>input</em> and <em>secret</em>:</p>
|
<p>VRF <em>output</em> derivation from <em>input</em> and <em>secret</em>.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_output(
|
</span> fn vrf_output(
|
||||||
@@ -540,24 +541,24 @@ validity of the signature (<code>1</code> on success):</p>
|
|||||||
input: OctetString,
|
input: OctetString,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from a <em>signature</em>:</p>
|
<p>VRF <em>output</em> derivation from a VRF signature.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_signed_output(
|
</span> fn vrf_signed_output(
|
||||||
signature: VrfSignature,
|
signature: VrfSignature,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Note that the following condition is always satisfied:</p>
|
<p>The following condition is always satisfied:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = vrf_sign(secret, input, extra);
|
</span> let signature = vrf_sign(secret, input, extra);
|
||||||
vrf_output(secret, input) == vrf_signed_output(signature)
|
vrf_output(secret, input) == vrf_signed_output(signature)
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>In this document, <code>SecretKey</code>, <code>PublicKey</code> and <code>VrfSignature</code> types are
|
<p><code>SecretKey</code>, <code>PublicKey</code> and <code>VrfSignature</code> types are intentionally left
|
||||||
intentionally left undefined. Their definitions can be found in the Bandersnatch
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
||||||
VRF specification.</p>
|
and related documents.</p>
|
||||||
<h4 id="542-ring-vrf-interface"><a class="header" href="#542-ring-vrf-interface">5.4.2. Ring VRF Interface</a></h4>
|
<h4 id="542-ring-vrf-interface"><a class="header" href="#542-ring-vrf-interface">5.4.2. Ring VRF Interface</a></h4>
|
||||||
<p>Function to construct <code>RingVrfSignature</code>.</p>
|
<p>Ring VRF signature construction.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_sign(
|
</span> fn ring_vrf_sign(
|
||||||
@@ -567,9 +568,9 @@ VRF specification.</p>
|
|||||||
extra: OctetString,
|
extra: OctetString,
|
||||||
) -> RingVrfSignature;
|
) -> RingVrfSignature;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function for signature verification returning a Boolean value
|
<p>Ring VRF signature verification. Returns a Boolean indicating the validity
|
||||||
indicating the validity of the signature (<code>1</code> on success).
|
of the signature (<code>1</code> on success). Note that verification doesn't require the
|
||||||
Note that verification doesn't require the signer's public key.</p>
|
signer's public key.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_verify(
|
</span> fn ring_vrf_verify(
|
||||||
@@ -579,23 +580,23 @@ Note that verification doesn't require the signer's public key.</p>
|
|||||||
signature: RingVrfSignature,
|
signature: RingVrfSignature,
|
||||||
) -> Unsigned<1>;
|
) -> Unsigned<1>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from a ring <em>signature</em>:</p>
|
<p>VRF <em>output</em> derivation from a ring VRF <em>signature</em>.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_signed_output(
|
</span> fn ring_vrf_signed_output(
|
||||||
signature: RingVrfSignature,
|
signature: RingVrfSignature,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Note that the following condition is always satisfied:</p>
|
<p>The following condition is always satisfied:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = vrf_sign(secret, input, extra);
|
</span> let signature = vrf_sign(secret, input, extra);
|
||||||
let ring_signature = ring_vrf_sign(secret, prover, input, extra);
|
let ring_signature = ring_vrf_sign(secret, prover, input, extra);
|
||||||
vrf_signed_output(signature) == ring_vrf_signed_output(ring_signature);
|
vrf_signed_output(signature) == ring_vrf_signed_output(ring_signature);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>In this document, the types <code>RingProver</code>, <code>RingVerifier</code>, and <code>RingVrfSignature</code>
|
<p><code>RingProver</code>, <code>RingVerifier</code>, and <code>RingVrfSignature</code> are intentionally left
|
||||||
are intentionally left undefined. Their definitions can be found in the
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
||||||
Bandersnatch VRF specification and related documents.</p>
|
and related documents.</p>
|
||||||
<h2 id="6-sassafras-protocol"><a class="header" href="#6-sassafras-protocol">6. Sassafras Protocol</a></h2>
|
<h2 id="6-sassafras-protocol"><a class="header" href="#6-sassafras-protocol">6. Sassafras Protocol</a></h2>
|
||||||
<h4 id="61-protocol-configuration"><a class="header" href="#61-protocol-configuration">6.1. Protocol Configuration</a></h4>
|
<h4 id="61-protocol-configuration"><a class="header" href="#61-protocol-configuration">6.1. Protocol Configuration</a></h4>
|
||||||
<p>The <code>ProtocolConfiguration</code> type contains some parameters to tweak the
|
<p>The <code>ProtocolConfiguration</code> type contains some parameters to tweak the
|
||||||
@@ -611,20 +612,21 @@ tickets validation. It is defined as:</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>epoch_length</code>: number of slots for each epoch.</li>
|
<li><code>epoch_length</code>: Number of slots for each epoch.</li>
|
||||||
<li><code>attempts_number</code>: maximum number of tickets that each authority is allowed to submit.</li>
|
<li><code>attempts_number</code>: Maximum number of tickets that each authority is allowed to submit.</li>
|
||||||
<li><code>redundancy_factor</code>: expected ratio between epoch's slots and the cumulative
|
<li><code>redundancy_factor</code>: Expected ratio between the cumulative number of valid
|
||||||
number of valid tickets which can be submitted by the set of epoch authorities.</li>
|
tickets which can be submitted by the scheduled authorities and the epoch's
|
||||||
|
duration in slots.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The <code>attempts_number</code> influences the anonymity of block producers. As all
|
<p>The <code>attempts_number</code> influences the anonymity of block producers. As all
|
||||||
published tickets have a <strong>public</strong> attempt number less than <code>attempts_number</code>,
|
published tickets have a <strong>public</strong> attempt number less than <code>attempts_number</code>,
|
||||||
all the tickets which share the attempt number value must belong to different
|
all the tickets which share the attempt number value must belong to different
|
||||||
block producers, which reduces anonymity late as we approach the epoch tail.
|
block producers, which reduces anonymity late as we approach the epoch tail.
|
||||||
Bigger values guarantee more anonymity but also more computation.</p>
|
Bigger values guarantee more anonymity but also more computation.</p>
|
||||||
<p>Details about how exactly these parameters drives the ticket validity
|
<p>Details about how these parameters drive the tickets validity probability can be
|
||||||
probability can be found in section <a href="proposed/0026-sassafras-consensus.html#652-tickets-threshold">6.5.2</a>.</p>
|
found in section <a href="proposed/0026-sassafras-consensus.html#652-tickets-threshold">6.5.2</a>.</p>
|
||||||
<h3 id="62-header-digest-log"><a class="header" href="#62-header-digest-log">6.2. Header Digest Log</a></h3>
|
<h3 id="62-header-digest-log"><a class="header" href="#62-header-digest-log">6.2. Header Digest Log</a></h3>
|
||||||
<p>Each block's header contains a <code>Digest</code> log, which is defined as an ordered
|
<p>Each block header contains a <code>Digest</code> log, which is defined as an ordered
|
||||||
sequence of <code>DigestItem</code>s:</p>
|
sequence of <code>DigestItem</code>s:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
@@ -636,26 +638,26 @@ sequence of <code>DigestItem</code>s:</p>
|
|||||||
Digest ::= Sequence<DigestItem>
|
Digest ::= Sequence<DigestItem>
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>Digest</code> sequence is used to propagate information required for the
|
<p>The <code>Digest</code> sequence is used to propagate information required for the
|
||||||
correct protocol progress. The information within each <code>DigestItem</code> is opaque
|
correct protocol progress. Outside the protocol's context, the information
|
||||||
outside the protocol's context and is represented as a SCALE-encoded version of
|
within each <code>DigestItem</code> is opaque and maps to some SCALE-encoded
|
||||||
protocol-specific structures.</p>
|
protocol-specific structure.</p>
|
||||||
<p>For Sassafras related entries, the <code>DiegestItem</code>s <code>id</code> is set to the ASCII
|
<p>For Sassafras related items, the <code>DiegestItem</code>s <code>id</code> is set to the ASCII
|
||||||
string <code>"SASS"</code>.</p>
|
string <code>"SASS"</code></p>
|
||||||
<p>Possible digest entries for Sassafras:</p>
|
<p>Possible digest items for Sassafras:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Epoch change signal: Contains information about the next epoch. This is
|
<li>Epoch change signal: Information about next epoch. This is mandatory for the
|
||||||
mandatory for the first block of a new epoch.</li>
|
first block of a new epoch.</li>
|
||||||
<li>Epoch tickets signal: Contains the sequence of tickets for claiming slots
|
<li>Epoch tickets signal: Sequence of tickets for claiming slots in the next
|
||||||
in the next epoch. This is mandatory for the first block in the <em>epoch's tail</em></li>
|
epoch. This is mandatory for the first block in the <em>epoch's tail</em></li>
|
||||||
<li>Slot claim info: Additional data required for block verification. This is mandatory
|
<li>Slot claim info: Additional data required for block verification. This is mandatory
|
||||||
and must be the second-to-last entry in the log.</li>
|
for each block and must be the second-to-last entry in the log.</li>
|
||||||
<li>Seal: Block signature added by the block author. This is mandatory and must be
|
<li>Seal: Block signature added by the block author. This is mandatory for each block
|
||||||
the last entry in the log.</li>
|
and must be the last entry in the log.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>If any of the digest entries are found in the wrong place or in a block where
|
<p>If any digest entry is unexpected, not found where mandatory or found in the
|
||||||
they are not specified as mandatory, then the block is considered invalid.</p>
|
wrong position, then the block is considered invalid.</p>
|
||||||
<h3 id="63-on-chain-randomness"><a class="header" href="#63-on-chain-randomness">6.3. On-Chain Randomness</a></h3>
|
<h3 id="63-on-chain-randomness"><a class="header" href="#63-on-chain-randomness">6.3. On-Chain Randomness</a></h3>
|
||||||
<p>On-Chain, we maintain a sequence of four randomness entries.</p>
|
<p>A sequence of four randomness entries is maintained on-chain.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> RandomnessBuffer ::= Sequence<OctetString<32>, 4>
|
</span> RandomnessBuffer ::= Sequence<OctetString<32>, 4>
|
||||||
@@ -663,32 +665,31 @@ they are not specified as mandatory, then the block is considered invalid.</p>
|
|||||||
<p>During epoch <code>N</code>:</p>
|
<p>During epoch <code>N</code>:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>The first entry is the current randomness accumulator value and incorporates
|
<p>The first entry is the current <em>randomness accumulator</em> and incorporates
|
||||||
verifiable random elements from all previously executed blocks. The
|
verifiable random elements from all previously executed blocks. The
|
||||||
exact randomness accumulation procedure is described in section
|
accumulation procedure is described in section <a href="proposed/0026-sassafras-consensus.html#610-randomness-accumulator">6.10</a>.</p>
|
||||||
<a href="proposed/0026-sassafras-consensus.html#610-randomness-accumulator">6.10</a>.</p>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The second entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The second entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N+2</code>.</p>
|
targeting epoch <code>N+2</code>.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N-1</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N-1</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N+1</code> (the next epoch).</p>
|
targeting epoch <code>N+1</code> (the next epoch).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N-2</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N-2</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N</code> (the current epoch).</p>
|
targeting epoch <code>N</code> (the current epoch).</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The buffer's entries are updated <strong>after</strong> block execution.</p>
|
<p>The buffer's entries are updated <strong>after</strong> each block execution.</p>
|
||||||
<h3 id="64-epoch-change-signal"><a class="header" href="#64-epoch-change-signal">6.4. Epoch Change Signal</a></h3>
|
<h3 id="64-epoch-change-signal"><a class="header" href="#64-epoch-change-signal">6.4. Epoch Change Signal</a></h3>
|
||||||
<p>The first block produced during epoch <code>N</code> must include a descriptor for some
|
<p>The first block produced during epoch <code>N</code> must include a descriptor for some
|
||||||
of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
||||||
<p>This descriptor is defined as:</p>
|
<p>This signal descriptor is defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> NextEpochDescriptor ::= Sequence {
|
</span> NextEpochDescriptor ::= Sequence {
|
||||||
@@ -699,14 +700,13 @@ of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
|||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>randomness</code>: Randomness accumulator snapshot relevant for validation of
|
<li><code>randomness</code>: Randomness accumulator snapshot relevant for validation of
|
||||||
next epoch blocks. In other words, the randomness used to construct the tickets
|
next epoch blocks. In other words, randomness used to construct the tickets
|
||||||
targeting epoch <code>N+1</code>.</li>
|
targeting epoch <code>N+1</code>.</li>
|
||||||
<li><code>authorities</code>: List of authorities scheduled for next epoch.</li>
|
<li><code>authorities</code>: List of authorities scheduled for next epoch.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>This descriptor is <code>SCALE</code> encoded and embedded in a <code>DigestItem</code> of the
|
<p>This descriptor is <code>SCALE</code> encoded and embedded in a <code>DigestItem</code>.</p>
|
||||||
<code>Digest</code> log.</p>
|
|
||||||
<h4 id="641-startup-parameters"><a class="header" href="#641-startup-parameters">6.4.1. Startup Parameters</a></h4>
|
<h4 id="641-startup-parameters"><a class="header" href="#641-startup-parameters">6.4.1. Startup Parameters</a></h4>
|
||||||
<p>Some of the initial parameters by the first epoch (<code>#0</code>), are set through
|
<p>Some of the initial parameters used by the first epoch (<code>#0</code>), are set through
|
||||||
the genesis configuration, which is defined as:</p>
|
the genesis configuration, which is defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
@@ -714,9 +714,9 @@ the genesis configuration, which is defined as:</p>
|
|||||||
authorities: Sequence<PublicKey>,
|
authorities: Sequence<PublicKey>,
|
||||||
}
|
}
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The on-chain <code>RandomnessBuffer</code> is initialized <strong>after</strong> the genesis block.
|
<p>The on-chain <code>RandomnessBuffer</code> is initialized <strong>after</strong> the genesis block
|
||||||
The first entry is set as the <em>Blake2b</em> hash of the genesis block, each of
|
construction. The first buffer entry is set as the <em>Blake2b</em> hash of the genesis
|
||||||
the following entry is set as the <em>Blake2b</em> hash of the previous entry.</p>
|
block, each of the other entries is set as the <em>Blake2b</em> hash of the previous entry.</p>
|
||||||
<p>Since block <code>#0</code> is generated by each node as part of the genesis process, the
|
<p>Since block <code>#0</code> is generated by each node as part of the genesis process, the
|
||||||
first block that an authority explicitly produces for epoch <code>#0</code> is block <code>#1</code>.
|
first block that an authority explicitly produces for epoch <code>#0</code> is block <code>#1</code>.
|
||||||
Therefore, block <code>#1</code> is required to contain the <code>NextEpochDescriptor</code> for the
|
Therefore, block <code>#1</code> is required to contain the <code>NextEpochDescriptor</code> for the
|
||||||
@@ -729,10 +729,11 @@ following epoch.</p>
|
|||||||
<h3 id="65-tickets-creation-and-submission"><a class="header" href="#65-tickets-creation-and-submission">6.5. Tickets Creation and Submission</a></h3>
|
<h3 id="65-tickets-creation-and-submission"><a class="header" href="#65-tickets-creation-and-submission">6.5. Tickets Creation and Submission</a></h3>
|
||||||
<p>During epoch <code>N</code>, each authority scheduled for epoch <code>N+2</code> constructs a set
|
<p>During epoch <code>N</code>, each authority scheduled for epoch <code>N+2</code> constructs a set
|
||||||
of tickets which may be eligible (<a href="proposed/0026-sassafras-consensus.html#652-tickets-threshold">6.5.2</a>) for on-chain
|
of tickets which may be eligible (<a href="proposed/0026-sassafras-consensus.html#652-tickets-threshold">6.5.2</a>) for on-chain
|
||||||
submission via the relayers, which are the authorities scheduled for epoch <code>N+1</code>.</p>
|
submission.</p>
|
||||||
<p>These tickets are constructed using the on-chain randomness snapshot taken
|
<p>These tickets are constructed using the on-chain randomness snapshot taken
|
||||||
<strong>before</strong> the execution of the first block of epoch <code>N</code> together with other
|
<strong>before</strong> the execution of the first block of epoch <code>N</code> together with other
|
||||||
parameters and aims to secure ownership of one or more slots of epoch <code>N+2</code>.</p>
|
parameters and aims to secure ownership of one or more slots of epoch <code>N+2</code>
|
||||||
|
(<em>target epoch</em>).</p>
|
||||||
<p>Each authority is allowed to submit a maximum number of tickets, constrained by
|
<p>Each authority is allowed to submit a maximum number of tickets, constrained by
|
||||||
<code>attempts_number</code> field of the <code>ProtocolConfiguration</code>.</p>
|
<code>attempts_number</code> field of the <code>ProtocolConfiguration</code>.</p>
|
||||||
<p>The ideal timing for the candidate authority to start constructing the tickets
|
<p>The ideal timing for the candidate authority to start constructing the tickets
|
||||||
@@ -741,11 +742,12 @@ once the last block of epoch <code>N-1</code> is either probabilistically or, ev
|
|||||||
deterministically finalized. This delay is suggested to prevent wasting
|
deterministically finalized. This delay is suggested to prevent wasting
|
||||||
resources creating tickets that will be unusable if a different chain branch is
|
resources creating tickets that will be unusable if a different chain branch is
|
||||||
chosen as canonical.</p>
|
chosen as canonical.</p>
|
||||||
<p>As said, during epoch <code>N</code>, tickets relayers collect (offchain) tickets targeting
|
<p>Tickets generated during epoch <code>N</code> are shared with the <em>tickets relayers</em>,
|
||||||
epoch <code>N+2</code>. When epoch <code>N+1</code> starts, the collected tickets are submitted
|
which are the authorities scheduled for epoch <code>N+1</code>. Relayers validate and
|
||||||
on-chain by relayers (which are the authorities scheduled for epoch <code>N+1</code>) as
|
collect (off-chain) the tickets targeting epoch <code>N+2</code>.</p>
|
||||||
"<em>inherent extrinsic</em>"s, a special type of mandatory transaction inserted by the
|
<p>When epoch <code>N+1</code> starts, collected tickets are submitted on-chain by relayers
|
||||||
block author at the beginning of the block's transactions sequence.</p>
|
as <em>inherent extrinsics</em>, a special type of transaction inserted by the block
|
||||||
|
author at the beginning of the block's transactions sequence.</p>
|
||||||
<h4 id="651-ticket-identifier"><a class="header" href="#651-ticket-identifier">6.5.1. Ticket Identifier</a></h4>
|
<h4 id="651-ticket-identifier"><a class="header" href="#651-ticket-identifier">6.5.1. Ticket Identifier</a></h4>
|
||||||
<p>Each ticket has an associated identifier defined as:</p>
|
<p>Each ticket has an associated identifier defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
@@ -753,27 +755,27 @@ block author at the beginning of the block's transactions sequence.</p>
|
|||||||
</span> TicketId ::= OctetString<32>;
|
</span> TicketId ::= OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The value of <code>TicketId</code> is completely determined by the output of Bandersnatch
|
<p>The value of <code>TicketId</code> is completely determined by the output of Bandersnatch
|
||||||
VRFs with the following <strong>unbiasable</strong> input:</p>
|
VRFs given the following <strong>unbiasable</strong> input:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_vrf_input = CONCAT(
|
</span> let ticket_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(attempt)
|
BYTES(attempt)
|
||||||
);
|
);
|
||||||
|
|
||||||
let ticket_id = vrf_output(AUTHORITY_SECRET_KEY, ticket_vrf_input);
|
let ticket_id = vrf_output(authority_secret_key, ticket_vrf_input);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>target_randomness</code>: element of <code>RandomnessBuffer</code> which contains the randomness
|
<li><code>target_epoch_randomness</code>: element of <code>RandomnessBuffer</code> which contains the
|
||||||
for the epoch the ticket is targeting.</li>
|
randomness for the epoch the ticket is targeting.</li>
|
||||||
<li><code>attempt</code>: value going from <code>0</code> to the configured <code>attempts_number - 1</code>.</li>
|
<li><code>attempt</code>: value going from <code>0</code> to the configured <code>attempts_number - 1</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4 id="652-tickets-threshold"><a class="header" href="#652-tickets-threshold">6.5.2. Tickets Threshold</a></h4>
|
<h4 id="652-tickets-threshold"><a class="header" href="#652-tickets-threshold">6.5.2. Tickets Threshold</a></h4>
|
||||||
<p>A <code>TicketId</code> value is valid for on-chain submission if its value, when interpreted
|
<p>A ticket is valid for on-chain submission if its <code>TicketId</code> value, when
|
||||||
as a big-endian 256-bit integer normalized as a float within the range <code>[0..1]</code>,
|
interpreted as a big-endian 256-bit integer normalized as a float within the
|
||||||
is less than the ticket threshold computed as:</p>
|
range <code>[0..1]</code>, is less than the ticket threshold computed as:</p>
|
||||||
<pre><code>T = (r·s)/(a·v)
|
<pre><code>T = (r·s)/(a·v)
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
@@ -782,31 +784,24 @@ is less than the ticket threshold computed as:</p>
|
|||||||
<li><code>s</code>: epoch's slots number</li>
|
<li><code>s</code>: epoch's slots number</li>
|
||||||
<li><code>r</code>: redundancy factor</li>
|
<li><code>r</code>: redundancy factor</li>
|
||||||
<li><code>a</code>: attempts number</li>
|
<li><code>a</code>: attempts number</li>
|
||||||
<li><code>T</code>: ticket threshold value (<code>0 ≤ T ≤ 1</code>)</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p>In an epoch with <code>s</code> slots, the goal is to achieve an expected number of tickets
|
<p>In an epoch with <code>s</code> slots, the goal is to achieve an expected number of valid
|
||||||
for block production equal to <code>r·s</code>.</p>
|
tickets equal to <code>r·s</code>.</p>
|
||||||
<p>It's crucial to ensure that the probability of having fewer than <code>s</code> winning
|
<p>It's crucial to ensure that the probability of having fewer than <code>s</code> winning
|
||||||
tickets is very low, even in scenarios where up to <code>1/3</code> of the authorities
|
tickets is very low, even in scenarios where up to <code>1/3</code> of the authorities
|
||||||
might be offline.</p>
|
might be offline. To accomplish this, we first define the winning probability of
|
||||||
<p>To accomplish this, we first define the winning probability of a single ticket
|
a single ticket as <code>T = (r·s)/(a·v)</code>.</p>
|
||||||
as <code>T = (r·s)/(a·v)</code>.</p>
|
<p>Let <code>n</code> be the <strong>actual</strong> number of participating authorities, where <code>v·2/3 ≤ n ≤ v</code>.
|
||||||
<p>Let <code>n</code> be the actual number of participating authorities, where <code>v·2/3 ≤ n ≤ v</code>.</p>
|
These <code>n</code> authorities each make <code>a</code> attempts, for a total of <code>a·n</code> attempts.</p>
|
||||||
<p>These <code>n</code> authorities each make <code>a</code> attempts, for a total of <code>a·n</code> attempts.</p>
|
|
||||||
<p>Let <code>X</code> be the random variable associated to the number of winning tickets, then
|
<p>Let <code>X</code> be the random variable associated to the number of winning tickets, then
|
||||||
its expected value is:</p>
|
its expected value is <code>E[X] = T·a·n = (r·s·n)/v</code>. By setting <code>r = 2</code>, we get
|
||||||
<pre><code>E[X] = T·a·n = (r·s·n)/v
|
<code>s·4/3 ≤ E[X] ≤ s·2</code>. Using <em>Bernestein's inequality</em> we get <code>Pr[X < s] ≤ e^(-s/21)</code>.</p>
|
||||||
</code></pre>
|
|
||||||
<p>By setting <code>r = 2</code>, we get</p>
|
|
||||||
<pre><code>s·4/3 ≤ E[X] ≤ s·2
|
|
||||||
</code></pre>
|
|
||||||
<p>Using <em>Bernestein's inequality</em> we get <code>Pr[X < s] ≤ e^(-s/21)</code>.</p>
|
|
||||||
<p>For instance, with <code>s = 600</code> this results in <code>Pr[X < s] < 4·10⁻¹³</code>.
|
<p>For instance, with <code>s = 600</code> this results in <code>Pr[X < s] < 4·10⁻¹³</code>.
|
||||||
Consequently, this approach offers considerable tolerance for offline nodes and
|
Consequently, this approach offers considerable tolerance for offline nodes and
|
||||||
ensures that all slots are likely to be filled with tickets.</p>
|
ensures that all slots are likely to be filled with tickets.</p>
|
||||||
<p>For more details about threshold formula please refer to the
|
<p>For more details about threshold formula refer to
|
||||||
<a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS#probabilities-and-parameters">probabilities and parameters</a>
|
<a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS#probabilities-and-parameters">probabilities and parameters</a>
|
||||||
paragraph in the Web3 foundation description of the protocol.</p>
|
paragraph in the Web3 Foundation description of the protocol.</p>
|
||||||
<h4 id="653-ticket-envelope"><a class="header" href="#653-ticket-envelope">6.5.3. Ticket Envelope</a></h4>
|
<h4 id="653-ticket-envelope"><a class="header" href="#653-ticket-envelope">6.5.3. Ticket Envelope</a></h4>
|
||||||
<p>Each ticket candidate is represented by a <code>TicketEnvelope</code>:</p>
|
<p>Each ticket candidate is represented by a <code>TicketEnvelope</code>:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
@@ -820,10 +815,10 @@ paragraph in the Web3 foundation description of the protocol.</p>
|
|||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>attempt</code>: Index associated to the ticket.</li>
|
<li><code>attempt</code>: Index associated to the ticket.</li>
|
||||||
<li><code>extra</code>: additional data for user-defined applications.</li>
|
<li><code>extra</code>: Additional data available for user-defined applications.</li>
|
||||||
<li><code>signature</code>: ring signature of the envelope data.</li>
|
<li><code>signature</code>: Ring VRF signature of the envelope data (<code>attempt</code> and <code>extra</code>).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The envelope data must be signed using Bandersnatch Ring VRF (<a href="proposed/0026-sassafras-consensus.html#542-ring-vrf-interface">5.4.2</a>).</p>
|
<p>Envelope data is signed using Bandersnatch Ring VRF (<a href="proposed/0026-sassafras-consensus.html#542-ring-vrf-interface">5.4.2</a>).</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = ring_vrf_sign(
|
</span> let signature = ring_vrf_sign(
|
||||||
@@ -835,13 +830,12 @@ paragraph in the Web3 foundation description of the protocol.</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>ticket_vrf_input</code> defined as in <a href="proposed/0026-sassafras-consensus.html#651-ticket-identifier">6.5.1</a>.</p>
|
<p>With <code>ticket_vrf_input</code> defined as in <a href="proposed/0026-sassafras-consensus.html#651-ticket-identifier">6.5.1</a>.</p>
|
||||||
<h3 id="66-on-chain-tickets-validation"><a class="header" href="#66-on-chain-tickets-validation">6.6. On-chain Tickets Validation</a></h3>
|
<h3 id="66-on-chain-tickets-validation"><a class="header" href="#66-on-chain-tickets-validation">6.6. On-chain Tickets Validation</a></h3>
|
||||||
<p>All the actions in the steps described by this paragraph are executed by
|
|
||||||
on-chain code.</p>
|
|
||||||
<p>Validation rules:</p>
|
<p>Validation rules:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<p>Ring signature is verified using the <code>ring_verifier</code> derived by the static
|
<p>Ring VRF signature is verified using the <code>ring_verifier</code> derived by the
|
||||||
ring context parameters and the next epoch authorities public keys.</p>
|
constant ring context parameters (SNARK SRS) and the next epoch authorities
|
||||||
|
public keys.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><code>TicketId</code> is locally computed from the <code>RingVrfSignature</code> and its value
|
<p><code>TicketId</code> is locally computed from the <code>RingVrfSignature</code> and its value
|
||||||
@@ -849,14 +843,17 @@ is checked to be less than tickets' threshold.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>On-chain tickets submission can't occur within a block part of the
|
<p>On-chain tickets submission can't occur within a block part of the
|
||||||
<em>epoch's tail</em>, which encompasses a configurable number of the slots at the end
|
<em>epoch's tail</em>, which encompasses a configurable number of slots at the end
|
||||||
of the epoch. This constraint is to give time to the on-chain tickets to
|
of the epoch. This constraint is to give time to persisted on-chain tickets
|
||||||
be probabilistically (or even better deterministically) finalized and thus
|
to be probabilistically (or even better deterministically) finalized and thus
|
||||||
further reduce the fork chances at the beginning of the target epoch.</p>
|
to further reduce the fork chances at the beginning of the target epoch.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>All tickets which are proposed within a block must be valid and all of them
|
<p>All tickets which are proposed within a block must be valid and all of them
|
||||||
must end up in the on-chain queue.</p>
|
must end up being persisted on-chain. Because the total number of tickets
|
||||||
|
persisted on-chain is limited by to the epoch's length, this may require to
|
||||||
|
drop some of the previously persisted tickets. We remove tickets with greater
|
||||||
|
<code>TicketId</code> value first.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>No tickets duplicates are allowed.</p>
|
<p>No tickets duplicates are allowed.</p>
|
||||||
@@ -868,12 +865,12 @@ must end up in the on-chain queue.</p>
|
|||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_vrf_input = CONCAT(
|
</span> let ticket_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(envelope.attempt)
|
BYTES(envelope.attempt)
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = ring_vrf_verify(
|
let result = ring_vrf_verify(
|
||||||
verifier,
|
ring_verifier,
|
||||||
ticket_vrf_input,
|
ticket_vrf_input,
|
||||||
envelope.extra,
|
envelope.extra,
|
||||||
envelope.ring_signature
|
envelope.ring_signature
|
||||||
@@ -896,20 +893,21 @@ their <code>TicketId</code>, interpreted as a 256-bit big-endian unsigned intege
|
|||||||
|
|
||||||
Tickets ::= Sequence<TicketBody>
|
Tickets ::= Sequence<TicketBody>
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The on-chain tickets sequence bound is set as the epoch length according to the
|
<p>The on-chain tickets sequence length bound is set equal to the epoch length
|
||||||
protocol configuration.</p>
|
in slots according to the protocol configuration.</p>
|
||||||
<h3 id="67-ticket-slot-binding"><a class="header" href="#67-ticket-slot-binding">6.7. Ticket-Slot Binding</a></h3>
|
<h3 id="67-ticket-slot-binding"><a class="header" href="#67-ticket-slot-binding">6.7. Ticket-Slot Binding</a></h3>
|
||||||
<p>Before the beginning of the claiming phase (i.e. what we've called the target
|
<p>Before the beginning of the <em>target epoch</em>, the on-chain sequence of tickets
|
||||||
epoch), the on-chain list of tickets must be associated to the next epoch's
|
must be associated to epoch's slots such that there is at most one ticket per
|
||||||
slots such that there is at most one ticket per slot.</p>
|
slot.</p>
|
||||||
<p>Given an ordered sequence of tickets <code>[t₀, t₁, ..., tₙ]</code>, the tickets are
|
<p>Given an ordered sequence of tickets <code>[t₀, t₁, ..., tₙ]</code>, the tickets are
|
||||||
associated according to the following <strong>outside-in</strong> strategy:</p>
|
associated according to the following <strong>outside-in</strong> strategy:</p>
|
||||||
<pre><code> slot_index : [ 0, 1, 2, 3 , ... ]
|
<pre><code> slot_index : [ 0, 1, 2, 3 , ... ]
|
||||||
tickets : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
|
tickets : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Here <code>slot-index</code> is a relative value computed as: <code>slot_index = slot - epoch_start_slot</code>.</p>
|
<p>Here <code>slot_index</code> is the slot number relative to the epoch's first slot:
|
||||||
|
<code>slot_index = slot - epoch_first_slot</code>.</p>
|
||||||
<p>The association between tickets and a slots is recorded on-chain and thus
|
<p>The association between tickets and a slots is recorded on-chain and thus
|
||||||
is public. What remains confidential is the identity of the ticket's author, and
|
is public. What remains confidential is the ticket's author identity, and
|
||||||
consequently, who is enabled to claim the corresponding slot. This information
|
consequently, who is enabled to claim the corresponding slot. This information
|
||||||
is known only to the ticket's author.</p>
|
is known only to the ticket's author.</p>
|
||||||
<p>If the number of published tickets is less than the number of epoch's slots,
|
<p>If the number of published tickets is less than the number of epoch's slots,
|
||||||
@@ -917,7 +915,7 @@ some <em>orphan</em> slots at the end of the epoch will remain unbounded to any
|
|||||||
For <em>orphan</em> slots claiming strategy refer to <a href="proposed/0026-sassafras-consensus.html#682-secondary-method">6.8.2</a>.
|
For <em>orphan</em> slots claiming strategy refer to <a href="proposed/0026-sassafras-consensus.html#682-secondary-method">6.8.2</a>.
|
||||||
Note that this fallback situation always apply to the first two epochs after genesis.</p>
|
Note that this fallback situation always apply to the first two epochs after genesis.</p>
|
||||||
<h3 id="68-slot-claim"><a class="header" href="#68-slot-claim">6.8. Slot Claim</a></h3>
|
<h3 id="68-slot-claim"><a class="header" href="#68-slot-claim">6.8. Slot Claim</a></h3>
|
||||||
<p>With tickets bounded to the target epoch slots, every designated authority
|
<p>With tickets bounded to the <em>target epoch</em> slots, every designated authority
|
||||||
acquires the information about the slots for which they are required to produce
|
acquires the information about the slots for which they are required to produce
|
||||||
a block.</p>
|
a block.</p>
|
||||||
<p>The procedure for slot claiming depends on whether a given slot has an
|
<p>The procedure for slot claiming depends on whether a given slot has an
|
||||||
@@ -927,28 +925,30 @@ resorts to the secondary method as a fallback.</p>
|
|||||||
<h4 id="681-primary-method"><a class="header" href="#681-primary-method">6.8.1. Primary Method</a></h4>
|
<h4 id="681-primary-method"><a class="header" href="#681-primary-method">6.8.1. Primary Method</a></h4>
|
||||||
<p>An authority, can claim a slot using the primary method if it is the legit
|
<p>An authority, can claim a slot using the primary method if it is the legit
|
||||||
owner of the ticket associated to the given slot.</p>
|
owner of the ticket associated to the given slot.</p>
|
||||||
<p>Let <code>target_randomness</code> be the entry in <code>RandomnessBuffer</code> relative to the epoch
|
<p>Let <code>target_epoch_randomness</code> be the entry in <code>RandomnessBuffer</code> relative to
|
||||||
the block is targeting and <code>attempt</code> be the attempt used to construct the ticket
|
the epoch the block is targeting and <code>attempt</code> be the attempt used to construct
|
||||||
associated to the slot to claim, the VRF input for slot claiming is constructed as:</p>
|
the ticket associated to the slot to claim, the VRF input for slot claiming is
|
||||||
|
constructed as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal_vrf_input = CONCAT(
|
</span> let seal_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(attempt)
|
BYTES(attempt)
|
||||||
);
|
);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>seal_vrf_input</code>, when signed with the correct authority secret key, must
|
<p>The <code>seal_vrf_input</code>, when signed with the correct authority secret key, must
|
||||||
generate the same <code>TicketId</code> associated on-chain to the target slot.</p>
|
generate the same <code>TicketId</code> which has been associated to the target slot
|
||||||
|
according to the on-chain state.</p>
|
||||||
<h4 id="682-secondary-method"><a class="header" href="#682-secondary-method">6.8.2. Secondary Method</a></h4>
|
<h4 id="682-secondary-method"><a class="header" href="#682-secondary-method">6.8.2. Secondary Method</a></h4>
|
||||||
<p>Given that the authorities registered on-chain are kept on-chain in an ordered
|
<p>Given that the authorities scheduled for the <em>target epoch</em> are kept on-chain in
|
||||||
list, the index of the authority which has the privilege to claim an <em>orphan</em>
|
an ordered sequence, the index of the authority which has the privilege to claim an
|
||||||
slot is given by the following procedure:</p>
|
<em>orphan</em> slot is given by the following procedure:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let hash_input = CONCAT(
|
</span> let hash_input = CONCAT(
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
relative_slot_index,
|
ENCODE(relative_slot_index),
|
||||||
);
|
);
|
||||||
let hash = BLAKE2(hash_input);
|
let hash = BLAKE2(hash_input);
|
||||||
let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
|
let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
|
||||||
@@ -956,13 +956,11 @@ slot is given by the following procedure:</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>relative_slot_index</code> the slot offset relative to the target epoch's start
|
<p>With <code>relative_slot_index</code> the slot offset relative to the target epoch's start
|
||||||
and <code>authorities</code> the sequence of target epoch authorities.</p>
|
and <code>authorities</code> the sequence of target epoch authorities.</p>
|
||||||
<p>Let <code>randomness_buffer</code> be the instance of <code>RandomnessBuffer</code> stored in on-chain state
|
|
||||||
then the VRF input for secondary slot claiming is constructed as:</p>
|
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal_vrf_input = CONCAT(
|
</span> let seal_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_fallback_seal"),
|
BYTES("sassafras_fallback_seal"),
|
||||||
target_randomness
|
target_epoch_randomness
|
||||||
);
|
);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<h4 id="683-claim-data"><a class="header" href="#683-claim-data">6.8.3. Claim Data</a></h4>
|
<h4 id="683-claim-data"><a class="header" href="#683-claim-data">6.8.3. Claim Data</a></h4>
|
||||||
@@ -987,11 +985,11 @@ the randomness source signature is generated as follows:</p>
|
|||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let randomness_vrf_input = CONCAT(
|
</span> let randomness_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_randomness"),
|
BYTES("sassafras_randomness"),
|
||||||
vrf_output(AUTHORITY_SECRET_KEY, seal_vrf_input)
|
vrf_output(authority_secret_key, seal_vrf_input)
|
||||||
);
|
);
|
||||||
|
|
||||||
let randomness_source = vrf_sign(
|
let randomness_source = vrf_sign(
|
||||||
AUTHORITY_SECRET_KEY,
|
authority_secret_key,
|
||||||
randomness_vrf_input,
|
randomness_vrf_input,
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -1004,23 +1002,25 @@ the randomness source signature is generated as follows:</p>
|
|||||||
|
|
||||||
PUSH(block_header.digest, ENCODE(claim));
|
PUSH(block_header.digest, ENCODE(claim));
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>ClaimData</code> instance is <em>SCALE</em> encoded and pushed as the second-to-last
|
<p>The <code>ClaimData</code> object is <em>SCALE</em> encoded and pushed as the second-to-last
|
||||||
element of the header digest log.</p>
|
element of the header digest log.</p>
|
||||||
<h4 id="684-block-seal"><a class="header" href="#684-block-seal">6.8.4. Block Seal</a></h4>
|
<h4 id="684-block-seal"><a class="header" href="#684-block-seal">6.8.4. Block Seal</a></h4>
|
||||||
<p>A block is finally sealed as follows:</p>
|
<p>A block is finally sealed as follows:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal = vrf_sign(
|
</span> let unsealed_header_byets = ENCODE(block_header);
|
||||||
AUTHORITY_SECRET_KEY,
|
|
||||||
|
let seal = vrf_sign(
|
||||||
|
authority_secret_key,
|
||||||
seal_vrf_input,
|
seal_vrf_input,
|
||||||
ENCODE(block_header),
|
unsealed_header_bytes
|
||||||
);
|
);
|
||||||
|
|
||||||
PUSH(block_header.digest, ENCODE(seal));
|
PUSH(block_header.digest, ENCODE(seal));
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>block_header</code> the block's header without the seal digest log entry.</p>
|
<p>With <code>block_header</code> the block's header without the seal digest log entry.</p>
|
||||||
<p>The <code>seal</code> object is a <code>VrfSignature</code>, which is <em>SCALE</em> encoded and pushed as
|
<p>The <code>seal</code> object is a <code>VrfSignature</code>, which is <em>SCALE</em> encoded and pushed as
|
||||||
the <strong>last</strong> entry of the block's header digest log.</p>
|
the <strong>last</strong> entry of the header digest log.</p>
|
||||||
<h3 id="69-slot-claim-verification"><a class="header" href="#69-slot-claim-verification">6.9. Slot Claim Verification</a></h3>
|
<h3 id="69-slot-claim-verification"><a class="header" href="#69-slot-claim-verification">6.9. Slot Claim Verification</a></h3>
|
||||||
<p>The last entry is extracted from the header digest log, and is SCALE decoded as
|
<p>The last entry is extracted from the header digest log, and is SCALE decoded as
|
||||||
a <code>VrfSignature</code> object. The unsealed header is then SCALE encoded in order to be
|
a <code>VrfSignature</code> object. The unsealed header is then SCALE encoded in order to be
|
||||||
@@ -1037,22 +1037,25 @@ used by the block author.</p>
|
|||||||
let unsealed_header_bytes = ENCODE(header);
|
let unsealed_header_bytes = ENCODE(header);
|
||||||
let claim_data = DECODE<ClaimData>(POP(header.digest));
|
let claim_data = DECODE<ClaimData>(POP(header.digest));
|
||||||
|
|
||||||
let public_key = GET(authorities, claim_data.authority_index);
|
let authority_public_key = GET(authorities, claim_data.authority_index);
|
||||||
|
|
||||||
// Verify seal signature
|
// Verify seal signature
|
||||||
let result = vrf_verify(
|
let result = vrf_verify(
|
||||||
public_key,
|
authority_public_key,
|
||||||
seal_vrf_input,
|
seal_vrf_input,
|
||||||
unsealed_header_bytes,
|
unsealed_header_bytes,
|
||||||
seal_signature
|
seal_signature
|
||||||
);
|
);
|
||||||
ASSERT(result == 1);
|
ASSERT(result == 1);
|
||||||
|
|
||||||
let randomness_vrf_input = vrf_signed_output(seal_signature);
|
let randomness_vrf_input = CONCAT(
|
||||||
|
BYTES("sassafras_randomness"),
|
||||||
|
vrf_signed_output(seal_signature)
|
||||||
|
);
|
||||||
|
|
||||||
// Verify per-block entropy source signature
|
// Verify per-block entropy source signature
|
||||||
let result = vrf_verify(
|
let result = vrf_verify(
|
||||||
public_key,
|
authority_public_key,
|
||||||
randomness_vrf_input,
|
randomness_vrf_input,
|
||||||
[],
|
[],
|
||||||
claim_data.randomness_source
|
claim_data.randomness_source
|
||||||
@@ -1070,7 +1073,8 @@ based on whether the slot is associated with a ticket according to the on-chain
|
|||||||
state.</p>
|
state.</p>
|
||||||
<h3 id="691-primary-method"><a class="header" href="#691-primary-method">6.9.1. Primary Method</a></h3>
|
<h3 id="691-primary-method"><a class="header" href="#691-primary-method">6.9.1. Primary Method</a></h3>
|
||||||
<p>For slots tied to a ticket, the primary verification method is employed.
|
<p>For slots tied to a ticket, the primary verification method is employed.
|
||||||
This method verifies ticket ownership using the <code>TicketId</code> associated to the slot.</p>
|
This method verifies ticket ownership using the <code>TicketId</code> associated to the
|
||||||
|
slot.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_id = vrf_signed_output(seal_signature);
|
</span> let ticket_id = vrf_signed_output(seal_signature);
|
||||||
@@ -1079,14 +1083,13 @@ This method verifies ticket ownership using the <code>TicketId</code> associated
|
|||||||
<p>With <code>expected_ticket_id</code> the ticket identifier committed on-chain in the
|
<p>With <code>expected_ticket_id</code> the ticket identifier committed on-chain in the
|
||||||
associated <code>TicketBody</code>.</p>
|
associated <code>TicketBody</code>.</p>
|
||||||
<h4 id="692-secondary-method"><a class="header" href="#692-secondary-method">6.9.2. Secondary Method</a></h4>
|
<h4 id="692-secondary-method"><a class="header" href="#692-secondary-method">6.9.2. Secondary Method</a></h4>
|
||||||
<p>If the slot doesn't have any associated ticket then the <code>authority_index</code> contained in
|
<p>If the slot doesn't have any associated ticket, then the <code>authority_index</code>
|
||||||
the <code>ClaimData</code> must match the one returned by the procedure outlined in section
|
contained in the <code>ClaimData</code> must match the one returned by the procedure
|
||||||
<a href="proposed/0026-sassafras-consensus.html#682-secondary-method">6.8.2</a>.</p>
|
outlined in section <a href="proposed/0026-sassafras-consensus.html#682-secondary-method">6.8.2</a>.</p>
|
||||||
<h3 id="610-randomness-accumulator"><a class="header" href="#610-randomness-accumulator">6.10. Randomness Accumulator</a></h3>
|
<h3 id="610-randomness-accumulator"><a class="header" href="#610-randomness-accumulator">6.10. Randomness Accumulator</a></h3>
|
||||||
<p>The randomness accumulator is updated using the <code>randomness_source</code> signature found
|
<p>The randomness accumulator is updated using the <code>randomness_source</code> signature
|
||||||
within the <code>ClaimData</code> object.</p>
|
found within the <code>ClaimData</code> object. In particular, fresh randomness is derived
|
||||||
<p>In particular, fresh randomness is derived and accumulated <strong>after</strong> block
|
and accumulated <strong>after</strong> block execution as follows:</p>
|
||||||
execution as follows:</p>
|
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let fresh_randomness = vrf_signed_output(claim.randomness_source);
|
</span> let fresh_randomness = vrf_signed_output(claim.randomness_source);
|
||||||
@@ -1095,30 +1098,29 @@ execution as follows:</p>
|
|||||||
<h2 id="7-drawbacks"><a class="header" href="#7-drawbacks">7. Drawbacks</a></h2>
|
<h2 id="7-drawbacks"><a class="header" href="#7-drawbacks">7. Drawbacks</a></h2>
|
||||||
<p>None</p>
|
<p>None</p>
|
||||||
<h2 id="8-testing-security-and-privacy"><a class="header" href="#8-testing-security-and-privacy">8. Testing, Security, and Privacy</a></h2>
|
<h2 id="8-testing-security-and-privacy"><a class="header" href="#8-testing-security-and-privacy">8. Testing, Security, and Privacy</a></h2>
|
||||||
<p>It is critical that implementations of this RFC undergo thorough testing on
|
<p>It is critical that implementations of this RFC undergo thorough rigorous
|
||||||
test networks.</p>
|
testing. A security audit may be desirable to ensure the implementation does not
|
||||||
<p>A security audit may be desirable to ensure the implementation does not
|
introduce emergent side effects.</p>
|
||||||
introduce unwanted side effects.</p>
|
|
||||||
<h2 id="9-performance-ergonomics-and-compatibility"><a class="header" href="#9-performance-ergonomics-and-compatibility">9. Performance, Ergonomics, and Compatibility</a></h2>
|
<h2 id="9-performance-ergonomics-and-compatibility"><a class="header" href="#9-performance-ergonomics-and-compatibility">9. Performance, Ergonomics, and Compatibility</a></h2>
|
||||||
<h3 id="91-performance"><a class="header" href="#91-performance">9.1. Performance</a></h3>
|
<h3 id="91-performance"><a class="header" href="#91-performance">9.1. Performance</a></h3>
|
||||||
<p>Adopting Sassafras consensus marks a significant improvement in reducing the
|
<p>Adopting Sassafras consensus marks a significant improvement in reducing the
|
||||||
frequency of short-lived forks.</p>
|
frequency of short-lived forks which are eliminated by design.</p>
|
||||||
<p>Forks are eliminated by design. Forks may only result from network disruptions
|
<p>Forks may only result from network disruption or protocol attacks. In such
|
||||||
or protocol attacks. In such cases, the choice of which fork to follow upon
|
cases, the choice of which fork to follow upon recovery is clear-cut, with only
|
||||||
recovery is clear-cut, with only one valid option.</p>
|
one valid option.</p>
|
||||||
<h3 id="92-ergonomics"><a class="header" href="#92-ergonomics">9.2. Ergonomics</a></h3>
|
<h3 id="92-ergonomics"><a class="header" href="#92-ergonomics">9.2. Ergonomics</a></h3>
|
||||||
<p>No specific considerations.</p>
|
<p>No specific considerations.</p>
|
||||||
<h3 id="93-compatibility"><a class="header" href="#93-compatibility">9.3. Compatibility</a></h3>
|
<h3 id="93-compatibility"><a class="header" href="#93-compatibility">9.3. Compatibility</a></h3>
|
||||||
<p>The adoption of Sassafras affects the native client and thus can't be introduced
|
<p>The adoption of Sassafras affects the native client and thus can't be introduced
|
||||||
just via a runtime upgrade.</p>
|
via a "simple" runtime upgrade.</p>
|
||||||
<p>A deployment strategy should be carefully engineered for live networks.</p>
|
<p>A deployment strategy should be carefully engineered for live networks. This
|
||||||
<p>This subject is left open for a dedicated RFC.</p>
|
subject is left open for a dedicated RFC.</p>
|
||||||
<h2 id="10-prior-art-and-references"><a class="header" href="#10-prior-art-and-references">10. Prior Art and References</a></h2>
|
<h2 id="10-prior-art-and-references"><a class="header" href="#10-prior-art-and-references">10. Prior Art and References</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS">Sassafras layman introduction</a></li>
|
<li><a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS">Sassafras layman introduction</a></li>
|
||||||
<li><a href="https://eprint.iacr.org/2023/031.pdf">Sassafras research paper</a></li>
|
<li><a href="https://eprint.iacr.org/2023/031.pdf">Sassafras research paper</a></li>
|
||||||
<li><a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs specification</a>.</li>
|
<li><a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs specification</a></li>
|
||||||
<li><a href="https://github.com/davxy/ark-ec-vrfs">Bandersnatch VRFs reference implementation</a>.</li>
|
<li><a href="https://github.com/davxy/ark-ec-vrfs">Bandersnatch VRFs reference implementation</a></li>
|
||||||
<li><a href="https://eprint.iacr.org/2023/002.pdf">W3F Ring VRF research paper</a></li>
|
<li><a href="https://eprint.iacr.org/2023/002.pdf">W3F Ring VRF research paper</a></li>
|
||||||
<li><a href="https://github.com/paritytech/substrate/issues/11515">Sassafras reference implementation tracking issue</a></li>
|
<li><a href="https://github.com/paritytech/substrate/issues/11515">Sassafras reference implementation tracking issue</a></li>
|
||||||
<li><a href="https://github.com/paritytech/substrate/pull/11879">Sassafras reference implementation main PR</a></li>
|
<li><a href="https://github.com/paritytech/substrate/pull/11879">Sassafras reference implementation main PR</a></li>
|
||||||
@@ -1131,41 +1133,41 @@ protocol, several crucial topics remain to be addressed in future RFCs.</p>
|
|||||||
<h3 id="121-interactions-with-on-chain-code"><a class="header" href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></h3>
|
<h3 id="121-interactions-with-on-chain-code"><a class="header" href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Storage</strong> organization and static configuration.</p>
|
<p><strong>Storage</strong>: Types, organization and genesis configuration.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Outbound Interfaces</strong>: Interfaces that the host environment provides to the
|
<p><strong>Host interface</strong>: Interface that the hosting environment exposes to on-chain
|
||||||
on-chain code, typically known as <em>Host Functions</em>.</p>
|
code (also known as <em>host functions</em>).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Unrecorded Inbound Interfaces</strong>. Interfaces that the on-chain code provides
|
<p><strong>Unrecorded on-chain interface</strong>. Interface that on-chain code exposes to the
|
||||||
to the host environment, typically known as <em>Runtime APIs</em>.</p>
|
hosting environment (also known as <em>runtime API</em>).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Transactional Inbound Interfaces</strong>. Interfaces that the on-chain code provides
|
<p><strong>Transactional on-chain interface</strong>. Interface that on-chain code exposes
|
||||||
to the world to alter the chain state, typically known as <em>Transactions</em>
|
to the World to alter the state (also known as <em>transactions</em> or
|
||||||
(or <em>extrinsics</em> in the <em>Polkadot</em> ecosystem)</p>
|
<em>extrinsics</em> in the <em>Polkadot</em> ecosystem).</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="122-deployment-strategies"><a class="header" href="#122-deployment-strategies">12.2. Deployment Strategies</a></h3>
|
<h3 id="122-deployment-strategies"><a class="header" href="#122-deployment-strategies">12.2. Deployment Strategies</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Protocol Migration</strong>. Exploring how this protocol can seamlessly replace an
|
<li><strong>Protocol Migration</strong>. Investigate of how Sassafras can seamlessly replace
|
||||||
already operational instance of another protocol. Future RFCs may focus on
|
an already operational instance of another protocol. Future RFCs may focus on
|
||||||
deployment strategies to facilitate a smooth transition.</li>
|
deployment strategies to facilitate a smooth transition.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="123-zk-snark-srs"><a class="header" href="#123-zk-snark-srs">12.3. ZK-SNARK SRS</a></h3>
|
<h3 id="123-zk-snark-parameters"><a class="header" href="#123-zk-snark-parameters">12.3. ZK-SNARK Parameters</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Procedure</strong>: Determining the procedure for the <em>zk-SNARK</em> SRS (Structured
|
<li><strong>Parameters Setup</strong>: Determine the setup procedure for the <em>zk-SNARK</em> SRS
|
||||||
Reference String) initialization. Future RFCs may provide insights into
|
(Structured Reference String) initialization. Future RFCs may provide insights
|
||||||
whether this process should include an ad-hoc initialization ceremony or if
|
into whether this process should include an ad-hoc initialization ceremony or
|
||||||
we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).</li>
|
if we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="124-anonymous-submission-of-tickets"><a class="header" href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></h3>
|
<h3 id="124-anonymous-submission-of-tickets"><a class="header" href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Mixnet Integration</strong>: Submitting tickets directly to the relay can pose a
|
<li><strong>Mixnet Integration</strong>: Submitting tickets directly to the relay can pose a
|
||||||
risk of potential deanonymization through traffic analysis. Subsequent RFCs
|
risk of potential deanonymization through traffic analysis. Subsequent RFCs
|
||||||
may investigate the potential for incorporating Mixnet protocol or other
|
may investigate the potential for incorporating <em>mix network</em> protocol or
|
||||||
privacy-enhancing mechanisms to address this concern.</li>
|
other privacy-enhancing mechanisms to address this concern.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div style="break-before: page; page-break-before: always;"></div><p><a href="https://github.com/polkadot-fellows/RFCs/pull/89">(source)</a></p>
|
<div style="break-before: page; page-break-before: always;"></div><p><a href="https://github.com/polkadot-fellows/RFCs/pull/89">(source)</a></p>
|
||||||
<p><strong>Table of Contents</strong></p>
|
<p><strong>Table of Contents</strong></p>
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li><a href="#2-stakeholders">2. Stakeholders</a>
|
<li><a href="#2-stakeholders">2. Stakeholders</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#21-blockchain-developers">2.1. Blockchain Developers</a></li>
|
<li><a href="#21-blockchain-core-developers">2.1. Blockchain Core Developers</a></li>
|
||||||
<li><a href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></li>
|
<li><a href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></li>
|
<li><a href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></li>
|
||||||
<li><a href="#122-deployment-strategies">12.2. Deployment Strategies</a></li>
|
<li><a href="#122-deployment-strategies">12.2. Deployment Strategies</a></li>
|
||||||
<li><a href="#123-zk-snark-srs">12.3. ZK-SNARK SRS</a></li>
|
<li><a href="#123-zk-snark-parameters">12.3. ZK-SNARK Parameters</a></li>
|
||||||
<li><a href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></li>
|
<li><a href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -264,9 +264,9 @@ authorities set while ensuring that the identity of authorities assigned to
|
|||||||
the slots remains undisclosed until the slot is actively claimed during block
|
the slots remains undisclosed until the slot is actively claimed during block
|
||||||
production.</p>
|
production.</p>
|
||||||
<h2 id="1-motivation"><a class="header" href="#1-motivation">1. Motivation</a></h2>
|
<h2 id="1-motivation"><a class="header" href="#1-motivation">1. Motivation</a></h2>
|
||||||
<p>Sassafras Protocol has been rigorously detailed in a comprehensive
|
<p>Sassafras Protocol has been rigorously described in a comprehensive
|
||||||
<a href="https://eprint.iacr.org/2023/031.pdf">research paper</a> authored by the
|
<a href="https://eprint.iacr.org/2023/031.pdf">research paper</a> authored by the
|
||||||
<a href="https://web3.foundation">Web3 foundation</a> research team.</p>
|
<a href="https://web3.foundation">Web3 Foundation</a> research team.</p>
|
||||||
<p>This RFC is primarily intended to detail the critical implementation aspects
|
<p>This RFC is primarily intended to detail the critical implementation aspects
|
||||||
vital for ensuring interoperability and to clarify certain aspects that are
|
vital for ensuring interoperability and to clarify certain aspects that are
|
||||||
left open by the research paper and thus subject to interpretation during
|
left open by the research paper and thus subject to interpretation during
|
||||||
@@ -279,65 +279,51 @@ this RFC should be considered authoritative to eliminate ambiguities and ensure
|
|||||||
interoperability.</p>
|
interoperability.</p>
|
||||||
<h3 id="12-supporting-sassafras-for-polkadot"><a class="header" href="#12-supporting-sassafras-for-polkadot">1.2. Supporting Sassafras for Polkadot</a></h3>
|
<h3 id="12-supporting-sassafras-for-polkadot"><a class="header" href="#12-supporting-sassafras-for-polkadot">1.2. Supporting Sassafras for Polkadot</a></h3>
|
||||||
<p>Beyond promoting interoperability, this RFC also aims to facilitate the
|
<p>Beyond promoting interoperability, this RFC also aims to facilitate the
|
||||||
implementation of Sassafras within the Polkadot ecosystem.</p>
|
implementation of Sassafras within the greater Polkadot ecosystem.</p>
|
||||||
<p>Although the specifics of deployment strategies are beyond the scope of this
|
<p>Although the specifics of deployment strategies are beyond the scope of this
|
||||||
document, it lays the groundwork for the integration of Sassafras into the
|
document, it lays the groundwork for the integration of Sassafras.</p>
|
||||||
greater Polkadot ecosystem.</p>
|
|
||||||
<h2 id="2-stakeholders"><a class="header" href="#2-stakeholders">2. Stakeholders</a></h2>
|
<h2 id="2-stakeholders"><a class="header" href="#2-stakeholders">2. Stakeholders</a></h2>
|
||||||
<h3 id="21-blockchain-developers"><a class="header" href="#21-blockchain-developers">2.1. Blockchain Developers</a></h3>
|
<p>The protocol has a central role in the next generation block authoring consensus
|
||||||
|
systems.</p>
|
||||||
|
<h3 id="21-blockchain-core-developers"><a class="header" href="#21-blockchain-core-developers">2.1. Blockchain Core Developers</a></h3>
|
||||||
<p>Developers responsible for creating blockchains who intend to leverage the
|
<p>Developers responsible for creating blockchains who intend to leverage the
|
||||||
benefits offered by the Sassafras Protocol.</p>
|
benefits offered by the Sassafras Protocol.</p>
|
||||||
<h3 id="22-polkadot-ecosystem-contributors"><a class="header" href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></h3>
|
<h3 id="22-polkadot-ecosystem-contributors"><a class="header" href="#22-polkadot-ecosystem-contributors">2.2. Polkadot Ecosystem Contributors</a></h3>
|
||||||
<p>Developers contributing to the Polkadot ecosystem, both relay-chain and
|
<p>Developers contributing to the Polkadot ecosystem, both relay-chain and
|
||||||
para-chains.</p>
|
para-chains.</p>
|
||||||
<p>The protocol will have a central role in the next generation block authoring
|
|
||||||
consensus systems.</p>
|
|
||||||
<h2 id="3-notation"><a class="header" href="#3-notation">3. Notation</a></h2>
|
<h2 id="3-notation"><a class="header" href="#3-notation">3. Notation</a></h2>
|
||||||
<p>This section outlines the notation and conventions adopted throughout this
|
<p>This section outlines the notation adopted throughout this document to ensure
|
||||||
document to ensure clarity and consistency.</p>
|
clarity and consistency.</p>
|
||||||
<h3 id="31-data-structures-definitions"><a class="header" href="#31-data-structures-definitions">3.1. Data Structures Definitions</a></h3>
|
<h3 id="31-data-structures-definitions"><a class="header" href="#31-data-structures-definitions">3.1. Data Structures Definitions</a></h3>
|
||||||
<p>Data structures are mostly defined using standard <a href="https://en.wikipedia.org/wiki/ASN.1">ASN.1</a>,
|
<p>Data structures are mostly defined using standard <a href="https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx">ASN.1</a>
|
||||||
syntax with few exceptions.</p>
|
syntax with few exceptions.</p>
|
||||||
<p>To ensure interoperability of serialized structures, the order of the fields
|
<p>To ensure interoperability of serialized structures, the order of the fields
|
||||||
must match the definitions found within this specification.</p>
|
must match the definitions found within this specification.</p>
|
||||||
<h3 id="32-types-alias"><a class="header" href="#32-types-alias">3.2. Types Alias</a></h3>
|
<h3 id="32-types-alias"><a class="header" href="#32-types-alias">3.2. Types Alias</a></h3>
|
||||||
<p>We define some types alias to make ASN.1 syntax more intuitive.</p>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Unsigned integer: <code>Unsigned ::= INTEGER (0..MAX)</code></li>
|
<li>Unsigned integer: <code>Unsigned ::= INTEGER (0..MAX)</code></li>
|
||||||
<li>n bits unsigned integer: <code>Unsigned<n> ::= INTEGER (0..2^n - 1)</code>
|
<li>n-bit unsigned integer: <code>Unsigned<n> ::= INTEGER (0..2^n - 1)</code>
|
||||||
<ul>
|
<ul>
|
||||||
<li>8 bits unsigned integer (octet) <code>Unsigned8 ::= Unsigned<8></code></li>
|
<li>8-bit unsigned integer (octet) <code>Unsigned8 ::= Unsigned<8></code></li>
|
||||||
<li>32 bits unsigned integer: <code>Unsigned32 ::= Unsigned<32></code></li>
|
<li>32-bit unsigned integer: <code>Unsigned32 ::= Unsigned<32></code></li>
|
||||||
<li>64 bits unsigned integer: <code>Unsigned64 ::= Unsigned<64></code></li>
|
<li>64-bit unsigned integer: <code>Unsigned64 ::= Unsigned<64></code></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>Non-homogeneous sequence (struct/tuple): <code>Sequence ::= SEQUENCE</code></li>
|
<li>Non-homogeneous sequence (struct/tuple): <code>Sequence ::= SEQUENCE</code></li>
|
||||||
<li>Homogeneous sequence (vector): <code>Sequence<T> ::= SEQUENCE OF T</code>
|
<li>Variable length homogeneous sequence (vector): <code>Sequence<T> ::= SEQUENCE OF T</code></li>
|
||||||
E.g. <code>Sequence<Unsigned> ::= SEQUENCE OF Unsigned</code></li>
|
|
||||||
<li>Fixed length homogeneous sequence (array): <code>Sequence<T,n> ::= Sequence<T> (SIZE(n))</code></li>
|
<li>Fixed length homogeneous sequence (array): <code>Sequence<T,n> ::= Sequence<T> (SIZE(n))</code></li>
|
||||||
<li>Octet string alias: <code>OctetString ::= Sequence<Unsigned8></code></li>
|
<li>Variable length octet-string: <code>OctetString ::= Sequence<Unsigned8></code></li>
|
||||||
<li>Fixed length octet string: <code>OctetString<n> ::= Sequence<Unsigned8, n></code></li>
|
<li>Fixed length octet-string: <code>OctetString<n> ::= Sequence<Unsigned8, n></code></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="32-pseudo-code"><a class="header" href="#32-pseudo-code">3.2. Pseudo-Code</a></h3>
|
<h3 id="32-pseudo-code"><a class="header" href="#32-pseudo-code">3.2. Pseudo-Code</a></h3>
|
||||||
<p>It is convenient to make use of code snippets as part of the protocol
|
<p>It is convenient to make use of code snippets as part of the protocol
|
||||||
description. As a convention, the code is formatted in a style similar to
|
description. As a convention, the code is formatted in a style similar to
|
||||||
<em>Rust</em>, and can make use of the following set of predefined functions:</p>
|
<em>Rust</em>, and can make use of the following set of predefined procedures:</p>
|
||||||
|
<h4 id="sequences"><a class="header" href="#sequences">Sequences</a></h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p><code>ENCODE(x: T) -> OctetString</code>: Encodes <code>x</code> as an <code>OctetString</code> according to
|
<p><code>CONCAT(x₀: OctetString, ..., xₖ: OctetString) -> OctetString</code>: Concatenates the
|
||||||
<a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
input octet-strings as a new octet string.</p>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>DECODE<T>(x: OctetString) -> T</code>: Decodes <code>x</code> as a type <code>T</code> object according
|
|
||||||
to <a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>BLAKE2(n: Unsigned, x: OctetString) -> OctetString<n></code>: Standard <em>Blake2b</em> hash
|
|
||||||
of <code>x</code> with output truncated to <code>n</code> bytes.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p><code>CONCAT(x₀: OctetString, ..., xₖ: OctetString) -> OctetString</code>: Concatenate the
|
|
||||||
inputs octets as a new octet string.</p>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><code>LENGTH(s: Sequence) -> Unsigned</code>: The number of elements in the sequence <code>s</code>.</p>
|
<p><code>LENGTH(s: Sequence) -> Unsigned</code>: The number of elements in the sequence <code>s</code>.</p>
|
||||||
@@ -352,50 +338,65 @@ inputs octets as a new octet string.</p>
|
|||||||
<p><code>POP(s: Sequence<T>) -> T</code>: extract and returns the last element of the sequence <code>s</code>.</p>
|
<p><code>POP(s: Sequence<T>) -> T</code>: extract and returns the last element of the sequence <code>s</code>.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h4 id="codec"><a class="header" href="#codec">Codec</a></h4>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><code>ENCODE(x: T) -> OctetString</code>: Encodes <code>x</code> as an <code>OctetString</code> according to
|
||||||
|
<a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><code>DECODE<T>(x: OctetString) -> T</code>: Decodes <code>x</code> as a type <code>T</code> object according
|
||||||
|
to <a href="https://github.com/paritytech/parity-scale-codec">SCALE</a> codec.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h4 id="other"><a class="header" href="#other">Other</a></h4>
|
||||||
|
<ul>
|
||||||
|
<li><code>BLAKE2(x: OctetString) -> OctetString<32></code>: Standard <em>Blake2b</em> hash
|
||||||
|
of <code>x</code> with 256-bit digest.</li>
|
||||||
|
</ul>
|
||||||
<h3 id="33-incremental-introduction-of-types-and-functions"><a class="header" href="#33-incremental-introduction-of-types-and-functions">3.3. Incremental Introduction of Types and Functions</a></h3>
|
<h3 id="33-incremental-introduction-of-types-and-functions"><a class="header" href="#33-incremental-introduction-of-types-and-functions">3.3. Incremental Introduction of Types and Functions</a></h3>
|
||||||
<p>More types and helper functions are introduced incrementally as they become
|
<p>More types and helper functions are introduced incrementally as they become
|
||||||
relevant within the document's context.</p>
|
relevant within the document's context.</p>
|
||||||
<h2 id="4-protocol-introduction"><a class="header" href="#4-protocol-introduction">4. Protocol Introduction</a></h2>
|
<h2 id="4-protocol-introduction"><a class="header" href="#4-protocol-introduction">4. Protocol Introduction</a></h2>
|
||||||
<p>The timeline is segmented into a sequentially ordered sequence of <strong>slots</strong>.
|
<p>The timeline is segmented into a sequentially ordered sequence of <strong>slots</strong>.
|
||||||
This entire sequence of slots is then further partitioned into distinct segments
|
This entire sequence of slots is further partitioned into distinct segments
|
||||||
known as <strong>epochs</strong>.</p>
|
known as <strong>epochs</strong>.</p>
|
||||||
<p>Sassafras protocol aims to map each slot within a <em>target</em> epoch to the
|
<p>Sassafras aims to map each slot within a <em>target epoch</em> to the authorities
|
||||||
designated authorities for that epoch, utilizing a ticketing system.</p>
|
scheduled for that epoch, utilizing a ticketing system.</p>
|
||||||
<p>The core protocol operation can be roughly divided into four phases.</p>
|
<p>The core protocol operation can be roughly divided into four phases.</p>
|
||||||
<h3 id="41-submission-of-candidate-tickets"><a class="header" href="#41-submission-of-candidate-tickets">4.1. Submission of Candidate Tickets</a></h3>
|
<h3 id="41-submission-of-candidate-tickets"><a class="header" href="#41-submission-of-candidate-tickets">4.1. Submission of Candidate Tickets</a></h3>
|
||||||
<p>Each of the authorities scheduled for the target epoch generate and submits
|
<p>Each authority scheduled for the target epoch generates and shares a set of
|
||||||
a set of candidate tickets. Every ticket has an unbiasable pseudo random score
|
candidate tickets. Every ticket has an <em>unbiasable</em> pseudo random score and is
|
||||||
and is bundled with an anonymous proof of validity.</p>
|
bundled with an anonymous proof of validity.</p>
|
||||||
<h3 id="42-validation-of-candidate-tickets"><a class="header" href="#42-validation-of-candidate-tickets">4.2. Validation of Candidate Tickets</a></h3>
|
<h3 id="42-validation-of-candidate-tickets"><a class="header" href="#42-validation-of-candidate-tickets">4.2. Validation of Candidate Tickets</a></h3>
|
||||||
<p>Each candidate ticket undergoes a validation process for the associated validity
|
<p>Each candidate ticket undergoes a validation process for the associated validity
|
||||||
proof and compliance with other protocol-specific constraints. Valid tickets
|
proof and compliance with other protocol-specific constraints. Valid tickets
|
||||||
are persisted on-chain.</p>
|
are persisted on-chain.</p>
|
||||||
<h3 id="43-tickets-slots-binding"><a class="header" href="#43-tickets-slots-binding">4.3. Tickets Slots Binding</a></h3>
|
<h3 id="43-tickets-slots-binding"><a class="header" href="#43-tickets-slots-binding">4.3. Tickets Slots Binding</a></h3>
|
||||||
<p>After collecting all valid candidate tickets and before the beginning of the
|
<p>After collecting all valid candidate tickets and before the beginning of the
|
||||||
target epoch, a deterministic method is used to uniquely associate a subset of
|
<em>target epoch</em>, a deterministic method is used to uniquely associate a subset of
|
||||||
these tickets with the slots of the target epoch.</p>
|
these tickets to the slots of the <em>target epoch</em>.</p>
|
||||||
<h3 id="44-claim-of-ticket-ownership"><a class="header" href="#44-claim-of-ticket-ownership">4.4. Claim of Ticket Ownership</a></h3>
|
<h3 id="44-claim-of-ticket-ownership"><a class="header" href="#44-claim-of-ticket-ownership">4.4. Claim of Ticket Ownership</a></h3>
|
||||||
<p>During block production phase of the target epoch, block's author is required
|
<p>During block production phase of <em>target epoch</em>, the author is required to prove
|
||||||
to prove ownership of the ticket associated to the block's slot. This step
|
ownership of the ticket associated to the block's slot. This step discloses the
|
||||||
discloses the identity of the ticket owner.</p>
|
identity of the ticket owner.</p>
|
||||||
<h2 id="5-bandersnatch-vrfs-cryptographic-primitives"><a class="header" href="#5-bandersnatch-vrfs-cryptographic-primitives">5. Bandersnatch VRFs Cryptographic Primitives</a></h2>
|
<h2 id="5-bandersnatch-vrfs-cryptographic-primitives"><a class="header" href="#5-bandersnatch-vrfs-cryptographic-primitives">5. Bandersnatch VRFs Cryptographic Primitives</a></h2>
|
||||||
<p>It is important to note that this section is not intended to serve as an
|
<p>This section is not intended to serve as an exhaustive exploration of the
|
||||||
exhaustive exploration of the mathematically intensive foundations of the
|
mathematically intensive foundations of the cryptographic primitive. Rather, its
|
||||||
cryptographic primitive. Rather, its primary aim is to offer a concise and
|
primary aim is to offer a concise and accessible explanation of the primitives
|
||||||
accessible explanation of the primitive's role and usage which is relevant
|
role and interface which is relevant within the scope of the protocol. For a more
|
||||||
within the scope of the protocol. For a more detailed explanation, refer to
|
detailed explanation, refer to the <a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs</a>
|
||||||
the <a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRF</a>
|
|
||||||
technical specification</p>
|
technical specification</p>
|
||||||
<p>Bandersnatch VRF comes in two flavors:</p>
|
<p>Bandersnatch VRF comes in two variants:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><em>Bare</em> VRF: Extension to the IETF ECVRF <a href="https://datatracker.ietf.org/doc/rfc9381/">RFC 9381</a>,</li>
|
<li><em>Bare</em> VRF: Extension to the IETF ECVRF <a href="https://datatracker.ietf.org/doc/rfc9381/">RFC 9381</a>,</li>
|
||||||
<li><em>Ring</em> VRF: Provides anonymous signatures by leveraging a <em>zk-SNARK</em>.</li>
|
<li><em>Ring</em> VRF: Anonymous signatures leveraging <em>zk-SNARK</em>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Together with the <em>input</em>, which determines the signed VRF <em>output</em>, both the
|
<p>Together with the <em>input</em>, which determines the VRF <em>output</em>, both variants
|
||||||
flavors offer the capability to sign some arbitrary additional data (<em>extra</em>)
|
offer the capability to sign some arbitrary additional data (<em>extra</em>) which
|
||||||
which doesn't contribute to the VRF output.</p>
|
doesn't contribute to the VRF output.</p>
|
||||||
<h3 id="51-bare-vrf-interface"><a class="header" href="#51-bare-vrf-interface">5.1 Bare VRF Interface</a></h3>
|
<h3 id="51-bare-vrf-interface"><a class="header" href="#51-bare-vrf-interface">5.1 Bare VRF Interface</a></h3>
|
||||||
<p>Function to construct a <code>VrfSignature</code>.</p>
|
<p>VRF signature construction.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_sign(
|
</span> fn vrf_sign(
|
||||||
@@ -404,8 +405,8 @@ which doesn't contribute to the VRF output.</p>
|
|||||||
extra: OctetString,
|
extra: OctetString,
|
||||||
) -> VrfSignature
|
) -> VrfSignature
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function for signature verification returning a Boolean value indicating the
|
<p>VRF signature verification. Returns a Boolean indicating the validity of the
|
||||||
validity of the signature (<code>1</code> on success):</p>
|
signature (<code>1</code> on success).</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_verify(
|
</span> fn vrf_verify(
|
||||||
@@ -415,7 +416,7 @@ validity of the signature (<code>1</code> on success):</p>
|
|||||||
signature: VrfSignature
|
signature: VrfSignature
|
||||||
) -> Unsigned<1>;
|
) -> Unsigned<1>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from <em>input</em> and <em>secret</em>:</p>
|
<p>VRF <em>output</em> derivation from <em>input</em> and <em>secret</em>.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_output(
|
</span> fn vrf_output(
|
||||||
@@ -423,24 +424,24 @@ validity of the signature (<code>1</code> on success):</p>
|
|||||||
input: OctetString,
|
input: OctetString,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from a <em>signature</em>:</p>
|
<p>VRF <em>output</em> derivation from a VRF signature.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn vrf_signed_output(
|
</span> fn vrf_signed_output(
|
||||||
signature: VrfSignature,
|
signature: VrfSignature,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Note that the following condition is always satisfied:</p>
|
<p>The following condition is always satisfied:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = vrf_sign(secret, input, extra);
|
</span> let signature = vrf_sign(secret, input, extra);
|
||||||
vrf_output(secret, input) == vrf_signed_output(signature)
|
vrf_output(secret, input) == vrf_signed_output(signature)
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>In this document, <code>SecretKey</code>, <code>PublicKey</code> and <code>VrfSignature</code> types are
|
<p><code>SecretKey</code>, <code>PublicKey</code> and <code>VrfSignature</code> types are intentionally left
|
||||||
intentionally left undefined. Their definitions can be found in the Bandersnatch
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
||||||
VRF specification.</p>
|
and related documents.</p>
|
||||||
<h4 id="542-ring-vrf-interface"><a class="header" href="#542-ring-vrf-interface">5.4.2. Ring VRF Interface</a></h4>
|
<h4 id="542-ring-vrf-interface"><a class="header" href="#542-ring-vrf-interface">5.4.2. Ring VRF Interface</a></h4>
|
||||||
<p>Function to construct <code>RingVrfSignature</code>.</p>
|
<p>Ring VRF signature construction.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_sign(
|
</span> fn ring_vrf_sign(
|
||||||
@@ -450,9 +451,9 @@ VRF specification.</p>
|
|||||||
extra: OctetString,
|
extra: OctetString,
|
||||||
) -> RingVrfSignature;
|
) -> RingVrfSignature;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function for signature verification returning a Boolean value
|
<p>Ring VRF signature verification. Returns a Boolean indicating the validity
|
||||||
indicating the validity of the signature (<code>1</code> on success).
|
of the signature (<code>1</code> on success). Note that verification doesn't require the
|
||||||
Note that verification doesn't require the signer's public key.</p>
|
signer's public key.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_verify(
|
</span> fn ring_vrf_verify(
|
||||||
@@ -462,23 +463,23 @@ Note that verification doesn't require the signer's public key.</p>
|
|||||||
signature: RingVrfSignature,
|
signature: RingVrfSignature,
|
||||||
) -> Unsigned<1>;
|
) -> Unsigned<1>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Function to derive the VRF <em>output</em> from a ring <em>signature</em>:</p>
|
<p>VRF <em>output</em> derivation from a ring VRF <em>signature</em>.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> fn ring_vrf_signed_output(
|
</span> fn ring_vrf_signed_output(
|
||||||
signature: RingVrfSignature,
|
signature: RingVrfSignature,
|
||||||
) -> OctetString<32>;
|
) -> OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Note that the following condition is always satisfied:</p>
|
<p>The following condition is always satisfied:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = vrf_sign(secret, input, extra);
|
</span> let signature = vrf_sign(secret, input, extra);
|
||||||
let ring_signature = ring_vrf_sign(secret, prover, input, extra);
|
let ring_signature = ring_vrf_sign(secret, prover, input, extra);
|
||||||
vrf_signed_output(signature) == ring_vrf_signed_output(ring_signature);
|
vrf_signed_output(signature) == ring_vrf_signed_output(ring_signature);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>In this document, the types <code>RingProver</code>, <code>RingVerifier</code>, and <code>RingVrfSignature</code>
|
<p><code>RingProver</code>, <code>RingVerifier</code>, and <code>RingVrfSignature</code> are intentionally left
|
||||||
are intentionally left undefined. Their definitions can be found in the
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
||||||
Bandersnatch VRF specification and related documents.</p>
|
and related documents.</p>
|
||||||
<h2 id="6-sassafras-protocol"><a class="header" href="#6-sassafras-protocol">6. Sassafras Protocol</a></h2>
|
<h2 id="6-sassafras-protocol"><a class="header" href="#6-sassafras-protocol">6. Sassafras Protocol</a></h2>
|
||||||
<h4 id="61-protocol-configuration"><a class="header" href="#61-protocol-configuration">6.1. Protocol Configuration</a></h4>
|
<h4 id="61-protocol-configuration"><a class="header" href="#61-protocol-configuration">6.1. Protocol Configuration</a></h4>
|
||||||
<p>The <code>ProtocolConfiguration</code> type contains some parameters to tweak the
|
<p>The <code>ProtocolConfiguration</code> type contains some parameters to tweak the
|
||||||
@@ -494,20 +495,21 @@ tickets validation. It is defined as:</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>epoch_length</code>: number of slots for each epoch.</li>
|
<li><code>epoch_length</code>: Number of slots for each epoch.</li>
|
||||||
<li><code>attempts_number</code>: maximum number of tickets that each authority is allowed to submit.</li>
|
<li><code>attempts_number</code>: Maximum number of tickets that each authority is allowed to submit.</li>
|
||||||
<li><code>redundancy_factor</code>: expected ratio between epoch's slots and the cumulative
|
<li><code>redundancy_factor</code>: Expected ratio between the cumulative number of valid
|
||||||
number of valid tickets which can be submitted by the set of epoch authorities.</li>
|
tickets which can be submitted by the scheduled authorities and the epoch's
|
||||||
|
duration in slots.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The <code>attempts_number</code> influences the anonymity of block producers. As all
|
<p>The <code>attempts_number</code> influences the anonymity of block producers. As all
|
||||||
published tickets have a <strong>public</strong> attempt number less than <code>attempts_number</code>,
|
published tickets have a <strong>public</strong> attempt number less than <code>attempts_number</code>,
|
||||||
all the tickets which share the attempt number value must belong to different
|
all the tickets which share the attempt number value must belong to different
|
||||||
block producers, which reduces anonymity late as we approach the epoch tail.
|
block producers, which reduces anonymity late as we approach the epoch tail.
|
||||||
Bigger values guarantee more anonymity but also more computation.</p>
|
Bigger values guarantee more anonymity but also more computation.</p>
|
||||||
<p>Details about how exactly these parameters drives the ticket validity
|
<p>Details about how these parameters drive the tickets validity probability can be
|
||||||
probability can be found in section <a href="#652-tickets-threshold">6.5.2</a>.</p>
|
found in section <a href="#652-tickets-threshold">6.5.2</a>.</p>
|
||||||
<h3 id="62-header-digest-log"><a class="header" href="#62-header-digest-log">6.2. Header Digest Log</a></h3>
|
<h3 id="62-header-digest-log"><a class="header" href="#62-header-digest-log">6.2. Header Digest Log</a></h3>
|
||||||
<p>Each block's header contains a <code>Digest</code> log, which is defined as an ordered
|
<p>Each block header contains a <code>Digest</code> log, which is defined as an ordered
|
||||||
sequence of <code>DigestItem</code>s:</p>
|
sequence of <code>DigestItem</code>s:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
@@ -519,26 +521,26 @@ sequence of <code>DigestItem</code>s:</p>
|
|||||||
Digest ::= Sequence<DigestItem>
|
Digest ::= Sequence<DigestItem>
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>Digest</code> sequence is used to propagate information required for the
|
<p>The <code>Digest</code> sequence is used to propagate information required for the
|
||||||
correct protocol progress. The information within each <code>DigestItem</code> is opaque
|
correct protocol progress. Outside the protocol's context, the information
|
||||||
outside the protocol's context and is represented as a SCALE-encoded version of
|
within each <code>DigestItem</code> is opaque and maps to some SCALE-encoded
|
||||||
protocol-specific structures.</p>
|
protocol-specific structure.</p>
|
||||||
<p>For Sassafras related entries, the <code>DiegestItem</code>s <code>id</code> is set to the ASCII
|
<p>For Sassafras related items, the <code>DiegestItem</code>s <code>id</code> is set to the ASCII
|
||||||
string <code>"SASS"</code>.</p>
|
string <code>"SASS"</code></p>
|
||||||
<p>Possible digest entries for Sassafras:</p>
|
<p>Possible digest items for Sassafras:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Epoch change signal: Contains information about the next epoch. This is
|
<li>Epoch change signal: Information about next epoch. This is mandatory for the
|
||||||
mandatory for the first block of a new epoch.</li>
|
first block of a new epoch.</li>
|
||||||
<li>Epoch tickets signal: Contains the sequence of tickets for claiming slots
|
<li>Epoch tickets signal: Sequence of tickets for claiming slots in the next
|
||||||
in the next epoch. This is mandatory for the first block in the <em>epoch's tail</em></li>
|
epoch. This is mandatory for the first block in the <em>epoch's tail</em></li>
|
||||||
<li>Slot claim info: Additional data required for block verification. This is mandatory
|
<li>Slot claim info: Additional data required for block verification. This is mandatory
|
||||||
and must be the second-to-last entry in the log.</li>
|
for each block and must be the second-to-last entry in the log.</li>
|
||||||
<li>Seal: Block signature added by the block author. This is mandatory and must be
|
<li>Seal: Block signature added by the block author. This is mandatory for each block
|
||||||
the last entry in the log.</li>
|
and must be the last entry in the log.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>If any of the digest entries are found in the wrong place or in a block where
|
<p>If any digest entry is unexpected, not found where mandatory or found in the
|
||||||
they are not specified as mandatory, then the block is considered invalid.</p>
|
wrong position, then the block is considered invalid.</p>
|
||||||
<h3 id="63-on-chain-randomness"><a class="header" href="#63-on-chain-randomness">6.3. On-Chain Randomness</a></h3>
|
<h3 id="63-on-chain-randomness"><a class="header" href="#63-on-chain-randomness">6.3. On-Chain Randomness</a></h3>
|
||||||
<p>On-Chain, we maintain a sequence of four randomness entries.</p>
|
<p>A sequence of four randomness entries is maintained on-chain.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> RandomnessBuffer ::= Sequence<OctetString<32>, 4>
|
</span> RandomnessBuffer ::= Sequence<OctetString<32>, 4>
|
||||||
@@ -546,32 +548,31 @@ they are not specified as mandatory, then the block is considered invalid.</p>
|
|||||||
<p>During epoch <code>N</code>:</p>
|
<p>During epoch <code>N</code>:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>The first entry is the current randomness accumulator value and incorporates
|
<p>The first entry is the current <em>randomness accumulator</em> and incorporates
|
||||||
verifiable random elements from all previously executed blocks. The
|
verifiable random elements from all previously executed blocks. The
|
||||||
exact randomness accumulation procedure is described in section
|
accumulation procedure is described in section <a href="#610-randomness-accumulator">6.10</a>.</p>
|
||||||
<a href="#610-randomness-accumulator">6.10</a>.</p>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The second entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The second entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N+2</code>.</p>
|
targeting epoch <code>N+2</code>.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N-1</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N-1</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N+1</code> (the next epoch).</p>
|
targeting epoch <code>N+1</code> (the next epoch).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
<p>The third entry is the snapshot of the accumulator <strong>before</strong> the execution
|
||||||
of the first block of epoch <code>N-2</code>. This is the randomness to be used for tickets
|
of the first block of epoch <code>N-2</code>. This is the randomness used for tickets
|
||||||
targeting epoch <code>N</code> (the current epoch).</p>
|
targeting epoch <code>N</code> (the current epoch).</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The buffer's entries are updated <strong>after</strong> block execution.</p>
|
<p>The buffer's entries are updated <strong>after</strong> each block execution.</p>
|
||||||
<h3 id="64-epoch-change-signal"><a class="header" href="#64-epoch-change-signal">6.4. Epoch Change Signal</a></h3>
|
<h3 id="64-epoch-change-signal"><a class="header" href="#64-epoch-change-signal">6.4. Epoch Change Signal</a></h3>
|
||||||
<p>The first block produced during epoch <code>N</code> must include a descriptor for some
|
<p>The first block produced during epoch <code>N</code> must include a descriptor for some
|
||||||
of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
||||||
<p>This descriptor is defined as:</p>
|
<p>This signal descriptor is defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> NextEpochDescriptor ::= Sequence {
|
</span> NextEpochDescriptor ::= Sequence {
|
||||||
@@ -582,14 +583,13 @@ of the parameters to be used by the subsequent epoch (<code>N+1</code>).</p>
|
|||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>randomness</code>: Randomness accumulator snapshot relevant for validation of
|
<li><code>randomness</code>: Randomness accumulator snapshot relevant for validation of
|
||||||
next epoch blocks. In other words, the randomness used to construct the tickets
|
next epoch blocks. In other words, randomness used to construct the tickets
|
||||||
targeting epoch <code>N+1</code>.</li>
|
targeting epoch <code>N+1</code>.</li>
|
||||||
<li><code>authorities</code>: List of authorities scheduled for next epoch.</li>
|
<li><code>authorities</code>: List of authorities scheduled for next epoch.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>This descriptor is <code>SCALE</code> encoded and embedded in a <code>DigestItem</code> of the
|
<p>This descriptor is <code>SCALE</code> encoded and embedded in a <code>DigestItem</code>.</p>
|
||||||
<code>Digest</code> log.</p>
|
|
||||||
<h4 id="641-startup-parameters"><a class="header" href="#641-startup-parameters">6.4.1. Startup Parameters</a></h4>
|
<h4 id="641-startup-parameters"><a class="header" href="#641-startup-parameters">6.4.1. Startup Parameters</a></h4>
|
||||||
<p>Some of the initial parameters by the first epoch (<code>#0</code>), are set through
|
<p>Some of the initial parameters used by the first epoch (<code>#0</code>), are set through
|
||||||
the genesis configuration, which is defined as:</p>
|
the genesis configuration, which is defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
@@ -597,9 +597,9 @@ the genesis configuration, which is defined as:</p>
|
|||||||
authorities: Sequence<PublicKey>,
|
authorities: Sequence<PublicKey>,
|
||||||
}
|
}
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The on-chain <code>RandomnessBuffer</code> is initialized <strong>after</strong> the genesis block.
|
<p>The on-chain <code>RandomnessBuffer</code> is initialized <strong>after</strong> the genesis block
|
||||||
The first entry is set as the <em>Blake2b</em> hash of the genesis block, each of
|
construction. The first buffer entry is set as the <em>Blake2b</em> hash of the genesis
|
||||||
the following entry is set as the <em>Blake2b</em> hash of the previous entry.</p>
|
block, each of the other entries is set as the <em>Blake2b</em> hash of the previous entry.</p>
|
||||||
<p>Since block <code>#0</code> is generated by each node as part of the genesis process, the
|
<p>Since block <code>#0</code> is generated by each node as part of the genesis process, the
|
||||||
first block that an authority explicitly produces for epoch <code>#0</code> is block <code>#1</code>.
|
first block that an authority explicitly produces for epoch <code>#0</code> is block <code>#1</code>.
|
||||||
Therefore, block <code>#1</code> is required to contain the <code>NextEpochDescriptor</code> for the
|
Therefore, block <code>#1</code> is required to contain the <code>NextEpochDescriptor</code> for the
|
||||||
@@ -612,10 +612,11 @@ following epoch.</p>
|
|||||||
<h3 id="65-tickets-creation-and-submission"><a class="header" href="#65-tickets-creation-and-submission">6.5. Tickets Creation and Submission</a></h3>
|
<h3 id="65-tickets-creation-and-submission"><a class="header" href="#65-tickets-creation-and-submission">6.5. Tickets Creation and Submission</a></h3>
|
||||||
<p>During epoch <code>N</code>, each authority scheduled for epoch <code>N+2</code> constructs a set
|
<p>During epoch <code>N</code>, each authority scheduled for epoch <code>N+2</code> constructs a set
|
||||||
of tickets which may be eligible (<a href="#652-tickets-threshold">6.5.2</a>) for on-chain
|
of tickets which may be eligible (<a href="#652-tickets-threshold">6.5.2</a>) for on-chain
|
||||||
submission via the relayers, which are the authorities scheduled for epoch <code>N+1</code>.</p>
|
submission.</p>
|
||||||
<p>These tickets are constructed using the on-chain randomness snapshot taken
|
<p>These tickets are constructed using the on-chain randomness snapshot taken
|
||||||
<strong>before</strong> the execution of the first block of epoch <code>N</code> together with other
|
<strong>before</strong> the execution of the first block of epoch <code>N</code> together with other
|
||||||
parameters and aims to secure ownership of one or more slots of epoch <code>N+2</code>.</p>
|
parameters and aims to secure ownership of one or more slots of epoch <code>N+2</code>
|
||||||
|
(<em>target epoch</em>).</p>
|
||||||
<p>Each authority is allowed to submit a maximum number of tickets, constrained by
|
<p>Each authority is allowed to submit a maximum number of tickets, constrained by
|
||||||
<code>attempts_number</code> field of the <code>ProtocolConfiguration</code>.</p>
|
<code>attempts_number</code> field of the <code>ProtocolConfiguration</code>.</p>
|
||||||
<p>The ideal timing for the candidate authority to start constructing the tickets
|
<p>The ideal timing for the candidate authority to start constructing the tickets
|
||||||
@@ -624,11 +625,12 @@ once the last block of epoch <code>N-1</code> is either probabilistically or, ev
|
|||||||
deterministically finalized. This delay is suggested to prevent wasting
|
deterministically finalized. This delay is suggested to prevent wasting
|
||||||
resources creating tickets that will be unusable if a different chain branch is
|
resources creating tickets that will be unusable if a different chain branch is
|
||||||
chosen as canonical.</p>
|
chosen as canonical.</p>
|
||||||
<p>As said, during epoch <code>N</code>, tickets relayers collect (offchain) tickets targeting
|
<p>Tickets generated during epoch <code>N</code> are shared with the <em>tickets relayers</em>,
|
||||||
epoch <code>N+2</code>. When epoch <code>N+1</code> starts, the collected tickets are submitted
|
which are the authorities scheduled for epoch <code>N+1</code>. Relayers validate and
|
||||||
on-chain by relayers (which are the authorities scheduled for epoch <code>N+1</code>) as
|
collect (off-chain) the tickets targeting epoch <code>N+2</code>.</p>
|
||||||
"<em>inherent extrinsic</em>"s, a special type of mandatory transaction inserted by the
|
<p>When epoch <code>N+1</code> starts, collected tickets are submitted on-chain by relayers
|
||||||
block author at the beginning of the block's transactions sequence.</p>
|
as <em>inherent extrinsics</em>, a special type of transaction inserted by the block
|
||||||
|
author at the beginning of the block's transactions sequence.</p>
|
||||||
<h4 id="651-ticket-identifier"><a class="header" href="#651-ticket-identifier">6.5.1. Ticket Identifier</a></h4>
|
<h4 id="651-ticket-identifier"><a class="header" href="#651-ticket-identifier">6.5.1. Ticket Identifier</a></h4>
|
||||||
<p>Each ticket has an associated identifier defined as:</p>
|
<p>Each ticket has an associated identifier defined as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
@@ -636,27 +638,27 @@ block author at the beginning of the block's transactions sequence.</p>
|
|||||||
</span> TicketId ::= OctetString<32>;
|
</span> TicketId ::= OctetString<32>;
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The value of <code>TicketId</code> is completely determined by the output of Bandersnatch
|
<p>The value of <code>TicketId</code> is completely determined by the output of Bandersnatch
|
||||||
VRFs with the following <strong>unbiasable</strong> input:</p>
|
VRFs given the following <strong>unbiasable</strong> input:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_vrf_input = CONCAT(
|
</span> let ticket_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(attempt)
|
BYTES(attempt)
|
||||||
);
|
);
|
||||||
|
|
||||||
let ticket_id = vrf_output(AUTHORITY_SECRET_KEY, ticket_vrf_input);
|
let ticket_id = vrf_output(authority_secret_key, ticket_vrf_input);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>target_randomness</code>: element of <code>RandomnessBuffer</code> which contains the randomness
|
<li><code>target_epoch_randomness</code>: element of <code>RandomnessBuffer</code> which contains the
|
||||||
for the epoch the ticket is targeting.</li>
|
randomness for the epoch the ticket is targeting.</li>
|
||||||
<li><code>attempt</code>: value going from <code>0</code> to the configured <code>attempts_number - 1</code>.</li>
|
<li><code>attempt</code>: value going from <code>0</code> to the configured <code>attempts_number - 1</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4 id="652-tickets-threshold"><a class="header" href="#652-tickets-threshold">6.5.2. Tickets Threshold</a></h4>
|
<h4 id="652-tickets-threshold"><a class="header" href="#652-tickets-threshold">6.5.2. Tickets Threshold</a></h4>
|
||||||
<p>A <code>TicketId</code> value is valid for on-chain submission if its value, when interpreted
|
<p>A ticket is valid for on-chain submission if its <code>TicketId</code> value, when
|
||||||
as a big-endian 256-bit integer normalized as a float within the range <code>[0..1]</code>,
|
interpreted as a big-endian 256-bit integer normalized as a float within the
|
||||||
is less than the ticket threshold computed as:</p>
|
range <code>[0..1]</code>, is less than the ticket threshold computed as:</p>
|
||||||
<pre><code>T = (r·s)/(a·v)
|
<pre><code>T = (r·s)/(a·v)
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
@@ -665,31 +667,24 @@ is less than the ticket threshold computed as:</p>
|
|||||||
<li><code>s</code>: epoch's slots number</li>
|
<li><code>s</code>: epoch's slots number</li>
|
||||||
<li><code>r</code>: redundancy factor</li>
|
<li><code>r</code>: redundancy factor</li>
|
||||||
<li><code>a</code>: attempts number</li>
|
<li><code>a</code>: attempts number</li>
|
||||||
<li><code>T</code>: ticket threshold value (<code>0 ≤ T ≤ 1</code>)</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p>In an epoch with <code>s</code> slots, the goal is to achieve an expected number of tickets
|
<p>In an epoch with <code>s</code> slots, the goal is to achieve an expected number of valid
|
||||||
for block production equal to <code>r·s</code>.</p>
|
tickets equal to <code>r·s</code>.</p>
|
||||||
<p>It's crucial to ensure that the probability of having fewer than <code>s</code> winning
|
<p>It's crucial to ensure that the probability of having fewer than <code>s</code> winning
|
||||||
tickets is very low, even in scenarios where up to <code>1/3</code> of the authorities
|
tickets is very low, even in scenarios where up to <code>1/3</code> of the authorities
|
||||||
might be offline.</p>
|
might be offline. To accomplish this, we first define the winning probability of
|
||||||
<p>To accomplish this, we first define the winning probability of a single ticket
|
a single ticket as <code>T = (r·s)/(a·v)</code>.</p>
|
||||||
as <code>T = (r·s)/(a·v)</code>.</p>
|
<p>Let <code>n</code> be the <strong>actual</strong> number of participating authorities, where <code>v·2/3 ≤ n ≤ v</code>.
|
||||||
<p>Let <code>n</code> be the actual number of participating authorities, where <code>v·2/3 ≤ n ≤ v</code>.</p>
|
These <code>n</code> authorities each make <code>a</code> attempts, for a total of <code>a·n</code> attempts.</p>
|
||||||
<p>These <code>n</code> authorities each make <code>a</code> attempts, for a total of <code>a·n</code> attempts.</p>
|
|
||||||
<p>Let <code>X</code> be the random variable associated to the number of winning tickets, then
|
<p>Let <code>X</code> be the random variable associated to the number of winning tickets, then
|
||||||
its expected value is:</p>
|
its expected value is <code>E[X] = T·a·n = (r·s·n)/v</code>. By setting <code>r = 2</code>, we get
|
||||||
<pre><code>E[X] = T·a·n = (r·s·n)/v
|
<code>s·4/3 ≤ E[X] ≤ s·2</code>. Using <em>Bernestein's inequality</em> we get <code>Pr[X < s] ≤ e^(-s/21)</code>.</p>
|
||||||
</code></pre>
|
|
||||||
<p>By setting <code>r = 2</code>, we get</p>
|
|
||||||
<pre><code>s·4/3 ≤ E[X] ≤ s·2
|
|
||||||
</code></pre>
|
|
||||||
<p>Using <em>Bernestein's inequality</em> we get <code>Pr[X < s] ≤ e^(-s/21)</code>.</p>
|
|
||||||
<p>For instance, with <code>s = 600</code> this results in <code>Pr[X < s] < 4·10⁻¹³</code>.
|
<p>For instance, with <code>s = 600</code> this results in <code>Pr[X < s] < 4·10⁻¹³</code>.
|
||||||
Consequently, this approach offers considerable tolerance for offline nodes and
|
Consequently, this approach offers considerable tolerance for offline nodes and
|
||||||
ensures that all slots are likely to be filled with tickets.</p>
|
ensures that all slots are likely to be filled with tickets.</p>
|
||||||
<p>For more details about threshold formula please refer to the
|
<p>For more details about threshold formula refer to
|
||||||
<a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS#probabilities-and-parameters">probabilities and parameters</a>
|
<a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS#probabilities-and-parameters">probabilities and parameters</a>
|
||||||
paragraph in the Web3 foundation description of the protocol.</p>
|
paragraph in the Web3 Foundation description of the protocol.</p>
|
||||||
<h4 id="653-ticket-envelope"><a class="header" href="#653-ticket-envelope">6.5.3. Ticket Envelope</a></h4>
|
<h4 id="653-ticket-envelope"><a class="header" href="#653-ticket-envelope">6.5.3. Ticket Envelope</a></h4>
|
||||||
<p>Each ticket candidate is represented by a <code>TicketEnvelope</code>:</p>
|
<p>Each ticket candidate is represented by a <code>TicketEnvelope</code>:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
@@ -703,10 +698,10 @@ paragraph in the Web3 foundation description of the protocol.</p>
|
|||||||
<p>Where:</p>
|
<p>Where:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>attempt</code>: Index associated to the ticket.</li>
|
<li><code>attempt</code>: Index associated to the ticket.</li>
|
||||||
<li><code>extra</code>: additional data for user-defined applications.</li>
|
<li><code>extra</code>: Additional data available for user-defined applications.</li>
|
||||||
<li><code>signature</code>: ring signature of the envelope data.</li>
|
<li><code>signature</code>: Ring VRF signature of the envelope data (<code>attempt</code> and <code>extra</code>).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The envelope data must be signed using Bandersnatch Ring VRF (<a href="#542-ring-vrf-interface">5.4.2</a>).</p>
|
<p>Envelope data is signed using Bandersnatch Ring VRF (<a href="#542-ring-vrf-interface">5.4.2</a>).</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let signature = ring_vrf_sign(
|
</span> let signature = ring_vrf_sign(
|
||||||
@@ -718,13 +713,12 @@ paragraph in the Web3 foundation description of the protocol.</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>ticket_vrf_input</code> defined as in <a href="#651-ticket-identifier">6.5.1</a>.</p>
|
<p>With <code>ticket_vrf_input</code> defined as in <a href="#651-ticket-identifier">6.5.1</a>.</p>
|
||||||
<h3 id="66-on-chain-tickets-validation"><a class="header" href="#66-on-chain-tickets-validation">6.6. On-chain Tickets Validation</a></h3>
|
<h3 id="66-on-chain-tickets-validation"><a class="header" href="#66-on-chain-tickets-validation">6.6. On-chain Tickets Validation</a></h3>
|
||||||
<p>All the actions in the steps described by this paragraph are executed by
|
|
||||||
on-chain code.</p>
|
|
||||||
<p>Validation rules:</p>
|
<p>Validation rules:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<p>Ring signature is verified using the <code>ring_verifier</code> derived by the static
|
<p>Ring VRF signature is verified using the <code>ring_verifier</code> derived by the
|
||||||
ring context parameters and the next epoch authorities public keys.</p>
|
constant ring context parameters (SNARK SRS) and the next epoch authorities
|
||||||
|
public keys.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><code>TicketId</code> is locally computed from the <code>RingVrfSignature</code> and its value
|
<p><code>TicketId</code> is locally computed from the <code>RingVrfSignature</code> and its value
|
||||||
@@ -732,14 +726,17 @@ is checked to be less than tickets' threshold.</p>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>On-chain tickets submission can't occur within a block part of the
|
<p>On-chain tickets submission can't occur within a block part of the
|
||||||
<em>epoch's tail</em>, which encompasses a configurable number of the slots at the end
|
<em>epoch's tail</em>, which encompasses a configurable number of slots at the end
|
||||||
of the epoch. This constraint is to give time to the on-chain tickets to
|
of the epoch. This constraint is to give time to persisted on-chain tickets
|
||||||
be probabilistically (or even better deterministically) finalized and thus
|
to be probabilistically (or even better deterministically) finalized and thus
|
||||||
further reduce the fork chances at the beginning of the target epoch.</p>
|
to further reduce the fork chances at the beginning of the target epoch.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>All tickets which are proposed within a block must be valid and all of them
|
<p>All tickets which are proposed within a block must be valid and all of them
|
||||||
must end up in the on-chain queue.</p>
|
must end up being persisted on-chain. Because the total number of tickets
|
||||||
|
persisted on-chain is limited by to the epoch's length, this may require to
|
||||||
|
drop some of the previously persisted tickets. We remove tickets with greater
|
||||||
|
<code>TicketId</code> value first.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>No tickets duplicates are allowed.</p>
|
<p>No tickets duplicates are allowed.</p>
|
||||||
@@ -751,12 +748,12 @@ must end up in the on-chain queue.</p>
|
|||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_vrf_input = CONCAT(
|
</span> let ticket_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(envelope.attempt)
|
BYTES(envelope.attempt)
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = ring_vrf_verify(
|
let result = ring_vrf_verify(
|
||||||
verifier,
|
ring_verifier,
|
||||||
ticket_vrf_input,
|
ticket_vrf_input,
|
||||||
envelope.extra,
|
envelope.extra,
|
||||||
envelope.ring_signature
|
envelope.ring_signature
|
||||||
@@ -779,20 +776,21 @@ their <code>TicketId</code>, interpreted as a 256-bit big-endian unsigned intege
|
|||||||
|
|
||||||
Tickets ::= Sequence<TicketBody>
|
Tickets ::= Sequence<TicketBody>
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The on-chain tickets sequence bound is set as the epoch length according to the
|
<p>The on-chain tickets sequence length bound is set equal to the epoch length
|
||||||
protocol configuration.</p>
|
in slots according to the protocol configuration.</p>
|
||||||
<h3 id="67-ticket-slot-binding"><a class="header" href="#67-ticket-slot-binding">6.7. Ticket-Slot Binding</a></h3>
|
<h3 id="67-ticket-slot-binding"><a class="header" href="#67-ticket-slot-binding">6.7. Ticket-Slot Binding</a></h3>
|
||||||
<p>Before the beginning of the claiming phase (i.e. what we've called the target
|
<p>Before the beginning of the <em>target epoch</em>, the on-chain sequence of tickets
|
||||||
epoch), the on-chain list of tickets must be associated to the next epoch's
|
must be associated to epoch's slots such that there is at most one ticket per
|
||||||
slots such that there is at most one ticket per slot.</p>
|
slot.</p>
|
||||||
<p>Given an ordered sequence of tickets <code>[t₀, t₁, ..., tₙ]</code>, the tickets are
|
<p>Given an ordered sequence of tickets <code>[t₀, t₁, ..., tₙ]</code>, the tickets are
|
||||||
associated according to the following <strong>outside-in</strong> strategy:</p>
|
associated according to the following <strong>outside-in</strong> strategy:</p>
|
||||||
<pre><code> slot_index : [ 0, 1, 2, 3 , ... ]
|
<pre><code> slot_index : [ 0, 1, 2, 3 , ... ]
|
||||||
tickets : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
|
tickets : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Here <code>slot-index</code> is a relative value computed as: <code>slot_index = slot - epoch_start_slot</code>.</p>
|
<p>Here <code>slot_index</code> is the slot number relative to the epoch's first slot:
|
||||||
|
<code>slot_index = slot - epoch_first_slot</code>.</p>
|
||||||
<p>The association between tickets and a slots is recorded on-chain and thus
|
<p>The association between tickets and a slots is recorded on-chain and thus
|
||||||
is public. What remains confidential is the identity of the ticket's author, and
|
is public. What remains confidential is the ticket's author identity, and
|
||||||
consequently, who is enabled to claim the corresponding slot. This information
|
consequently, who is enabled to claim the corresponding slot. This information
|
||||||
is known only to the ticket's author.</p>
|
is known only to the ticket's author.</p>
|
||||||
<p>If the number of published tickets is less than the number of epoch's slots,
|
<p>If the number of published tickets is less than the number of epoch's slots,
|
||||||
@@ -800,7 +798,7 @@ some <em>orphan</em> slots at the end of the epoch will remain unbounded to any
|
|||||||
For <em>orphan</em> slots claiming strategy refer to <a href="#682-secondary-method">6.8.2</a>.
|
For <em>orphan</em> slots claiming strategy refer to <a href="#682-secondary-method">6.8.2</a>.
|
||||||
Note that this fallback situation always apply to the first two epochs after genesis.</p>
|
Note that this fallback situation always apply to the first two epochs after genesis.</p>
|
||||||
<h3 id="68-slot-claim"><a class="header" href="#68-slot-claim">6.8. Slot Claim</a></h3>
|
<h3 id="68-slot-claim"><a class="header" href="#68-slot-claim">6.8. Slot Claim</a></h3>
|
||||||
<p>With tickets bounded to the target epoch slots, every designated authority
|
<p>With tickets bounded to the <em>target epoch</em> slots, every designated authority
|
||||||
acquires the information about the slots for which they are required to produce
|
acquires the information about the slots for which they are required to produce
|
||||||
a block.</p>
|
a block.</p>
|
||||||
<p>The procedure for slot claiming depends on whether a given slot has an
|
<p>The procedure for slot claiming depends on whether a given slot has an
|
||||||
@@ -810,28 +808,30 @@ resorts to the secondary method as a fallback.</p>
|
|||||||
<h4 id="681-primary-method"><a class="header" href="#681-primary-method">6.8.1. Primary Method</a></h4>
|
<h4 id="681-primary-method"><a class="header" href="#681-primary-method">6.8.1. Primary Method</a></h4>
|
||||||
<p>An authority, can claim a slot using the primary method if it is the legit
|
<p>An authority, can claim a slot using the primary method if it is the legit
|
||||||
owner of the ticket associated to the given slot.</p>
|
owner of the ticket associated to the given slot.</p>
|
||||||
<p>Let <code>target_randomness</code> be the entry in <code>RandomnessBuffer</code> relative to the epoch
|
<p>Let <code>target_epoch_randomness</code> be the entry in <code>RandomnessBuffer</code> relative to
|
||||||
the block is targeting and <code>attempt</code> be the attempt used to construct the ticket
|
the epoch the block is targeting and <code>attempt</code> be the attempt used to construct
|
||||||
associated to the slot to claim, the VRF input for slot claiming is constructed as:</p>
|
the ticket associated to the slot to claim, the VRF input for slot claiming is
|
||||||
|
constructed as:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal_vrf_input = CONCAT(
|
</span> let seal_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_ticket_seal"),
|
BYTES("sassafras_ticket_seal"),
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
BYTES(attempt)
|
BYTES(attempt)
|
||||||
);
|
);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>seal_vrf_input</code>, when signed with the correct authority secret key, must
|
<p>The <code>seal_vrf_input</code>, when signed with the correct authority secret key, must
|
||||||
generate the same <code>TicketId</code> associated on-chain to the target slot.</p>
|
generate the same <code>TicketId</code> which has been associated to the target slot
|
||||||
|
according to the on-chain state.</p>
|
||||||
<h4 id="682-secondary-method"><a class="header" href="#682-secondary-method">6.8.2. Secondary Method</a></h4>
|
<h4 id="682-secondary-method"><a class="header" href="#682-secondary-method">6.8.2. Secondary Method</a></h4>
|
||||||
<p>Given that the authorities registered on-chain are kept on-chain in an ordered
|
<p>Given that the authorities scheduled for the <em>target epoch</em> are kept on-chain in
|
||||||
list, the index of the authority which has the privilege to claim an <em>orphan</em>
|
an ordered sequence, the index of the authority which has the privilege to claim an
|
||||||
slot is given by the following procedure:</p>
|
<em>orphan</em> slot is given by the following procedure:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let hash_input = CONCAT(
|
</span> let hash_input = CONCAT(
|
||||||
target_randomness,
|
target_epoch_randomness,
|
||||||
relative_slot_index,
|
ENCODE(relative_slot_index),
|
||||||
);
|
);
|
||||||
let hash = BLAKE2(hash_input);
|
let hash = BLAKE2(hash_input);
|
||||||
let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
|
let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
|
||||||
@@ -839,13 +839,11 @@ slot is given by the following procedure:</p>
|
|||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>relative_slot_index</code> the slot offset relative to the target epoch's start
|
<p>With <code>relative_slot_index</code> the slot offset relative to the target epoch's start
|
||||||
and <code>authorities</code> the sequence of target epoch authorities.</p>
|
and <code>authorities</code> the sequence of target epoch authorities.</p>
|
||||||
<p>Let <code>randomness_buffer</code> be the instance of <code>RandomnessBuffer</code> stored in on-chain state
|
|
||||||
then the VRF input for secondary slot claiming is constructed as:</p>
|
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal_vrf_input = CONCAT(
|
</span> let seal_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_fallback_seal"),
|
BYTES("sassafras_fallback_seal"),
|
||||||
target_randomness
|
target_epoch_randomness
|
||||||
);
|
);
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<h4 id="683-claim-data"><a class="header" href="#683-claim-data">6.8.3. Claim Data</a></h4>
|
<h4 id="683-claim-data"><a class="header" href="#683-claim-data">6.8.3. Claim Data</a></h4>
|
||||||
@@ -870,11 +868,11 @@ the randomness source signature is generated as follows:</p>
|
|||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let randomness_vrf_input = CONCAT(
|
</span> let randomness_vrf_input = CONCAT(
|
||||||
BYTES("sassafras_randomness"),
|
BYTES("sassafras_randomness"),
|
||||||
vrf_output(AUTHORITY_SECRET_KEY, seal_vrf_input)
|
vrf_output(authority_secret_key, seal_vrf_input)
|
||||||
);
|
);
|
||||||
|
|
||||||
let randomness_source = vrf_sign(
|
let randomness_source = vrf_sign(
|
||||||
AUTHORITY_SECRET_KEY,
|
authority_secret_key,
|
||||||
randomness_vrf_input,
|
randomness_vrf_input,
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -887,23 +885,25 @@ the randomness source signature is generated as follows:</p>
|
|||||||
|
|
||||||
PUSH(block_header.digest, ENCODE(claim));
|
PUSH(block_header.digest, ENCODE(claim));
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>The <code>ClaimData</code> instance is <em>SCALE</em> encoded and pushed as the second-to-last
|
<p>The <code>ClaimData</code> object is <em>SCALE</em> encoded and pushed as the second-to-last
|
||||||
element of the header digest log.</p>
|
element of the header digest log.</p>
|
||||||
<h4 id="684-block-seal"><a class="header" href="#684-block-seal">6.8.4. Block Seal</a></h4>
|
<h4 id="684-block-seal"><a class="header" href="#684-block-seal">6.8.4. Block Seal</a></h4>
|
||||||
<p>A block is finally sealed as follows:</p>
|
<p>A block is finally sealed as follows:</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let seal = vrf_sign(
|
</span> let unsealed_header_byets = ENCODE(block_header);
|
||||||
AUTHORITY_SECRET_KEY,
|
|
||||||
|
let seal = vrf_sign(
|
||||||
|
authority_secret_key,
|
||||||
seal_vrf_input,
|
seal_vrf_input,
|
||||||
ENCODE(block_header),
|
unsealed_header_bytes
|
||||||
);
|
);
|
||||||
|
|
||||||
PUSH(block_header.digest, ENCODE(seal));
|
PUSH(block_header.digest, ENCODE(seal));
|
||||||
<span class="boring">}</span></code></pre></pre>
|
<span class="boring">}</span></code></pre></pre>
|
||||||
<p>With <code>block_header</code> the block's header without the seal digest log entry.</p>
|
<p>With <code>block_header</code> the block's header without the seal digest log entry.</p>
|
||||||
<p>The <code>seal</code> object is a <code>VrfSignature</code>, which is <em>SCALE</em> encoded and pushed as
|
<p>The <code>seal</code> object is a <code>VrfSignature</code>, which is <em>SCALE</em> encoded and pushed as
|
||||||
the <strong>last</strong> entry of the block's header digest log.</p>
|
the <strong>last</strong> entry of the header digest log.</p>
|
||||||
<h3 id="69-slot-claim-verification"><a class="header" href="#69-slot-claim-verification">6.9. Slot Claim Verification</a></h3>
|
<h3 id="69-slot-claim-verification"><a class="header" href="#69-slot-claim-verification">6.9. Slot Claim Verification</a></h3>
|
||||||
<p>The last entry is extracted from the header digest log, and is SCALE decoded as
|
<p>The last entry is extracted from the header digest log, and is SCALE decoded as
|
||||||
a <code>VrfSignature</code> object. The unsealed header is then SCALE encoded in order to be
|
a <code>VrfSignature</code> object. The unsealed header is then SCALE encoded in order to be
|
||||||
@@ -920,22 +920,25 @@ used by the block author.</p>
|
|||||||
let unsealed_header_bytes = ENCODE(header);
|
let unsealed_header_bytes = ENCODE(header);
|
||||||
let claim_data = DECODE<ClaimData>(POP(header.digest));
|
let claim_data = DECODE<ClaimData>(POP(header.digest));
|
||||||
|
|
||||||
let public_key = GET(authorities, claim_data.authority_index);
|
let authority_public_key = GET(authorities, claim_data.authority_index);
|
||||||
|
|
||||||
// Verify seal signature
|
// Verify seal signature
|
||||||
let result = vrf_verify(
|
let result = vrf_verify(
|
||||||
public_key,
|
authority_public_key,
|
||||||
seal_vrf_input,
|
seal_vrf_input,
|
||||||
unsealed_header_bytes,
|
unsealed_header_bytes,
|
||||||
seal_signature
|
seal_signature
|
||||||
);
|
);
|
||||||
ASSERT(result == 1);
|
ASSERT(result == 1);
|
||||||
|
|
||||||
let randomness_vrf_input = vrf_signed_output(seal_signature);
|
let randomness_vrf_input = CONCAT(
|
||||||
|
BYTES("sassafras_randomness"),
|
||||||
|
vrf_signed_output(seal_signature)
|
||||||
|
);
|
||||||
|
|
||||||
// Verify per-block entropy source signature
|
// Verify per-block entropy source signature
|
||||||
let result = vrf_verify(
|
let result = vrf_verify(
|
||||||
public_key,
|
authority_public_key,
|
||||||
randomness_vrf_input,
|
randomness_vrf_input,
|
||||||
[],
|
[],
|
||||||
claim_data.randomness_source
|
claim_data.randomness_source
|
||||||
@@ -953,7 +956,8 @@ based on whether the slot is associated with a ticket according to the on-chain
|
|||||||
state.</p>
|
state.</p>
|
||||||
<h3 id="691-primary-method"><a class="header" href="#691-primary-method">6.9.1. Primary Method</a></h3>
|
<h3 id="691-primary-method"><a class="header" href="#691-primary-method">6.9.1. Primary Method</a></h3>
|
||||||
<p>For slots tied to a ticket, the primary verification method is employed.
|
<p>For slots tied to a ticket, the primary verification method is employed.
|
||||||
This method verifies ticket ownership using the <code>TicketId</code> associated to the slot.</p>
|
This method verifies ticket ownership using the <code>TicketId</code> associated to the
|
||||||
|
slot.</p>
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let ticket_id = vrf_signed_output(seal_signature);
|
</span> let ticket_id = vrf_signed_output(seal_signature);
|
||||||
@@ -962,14 +966,13 @@ This method verifies ticket ownership using the <code>TicketId</code> associated
|
|||||||
<p>With <code>expected_ticket_id</code> the ticket identifier committed on-chain in the
|
<p>With <code>expected_ticket_id</code> the ticket identifier committed on-chain in the
|
||||||
associated <code>TicketBody</code>.</p>
|
associated <code>TicketBody</code>.</p>
|
||||||
<h4 id="692-secondary-method"><a class="header" href="#692-secondary-method">6.9.2. Secondary Method</a></h4>
|
<h4 id="692-secondary-method"><a class="header" href="#692-secondary-method">6.9.2. Secondary Method</a></h4>
|
||||||
<p>If the slot doesn't have any associated ticket then the <code>authority_index</code> contained in
|
<p>If the slot doesn't have any associated ticket, then the <code>authority_index</code>
|
||||||
the <code>ClaimData</code> must match the one returned by the procedure outlined in section
|
contained in the <code>ClaimData</code> must match the one returned by the procedure
|
||||||
<a href="#682-secondary-method">6.8.2</a>.</p>
|
outlined in section <a href="#682-secondary-method">6.8.2</a>.</p>
|
||||||
<h3 id="610-randomness-accumulator"><a class="header" href="#610-randomness-accumulator">6.10. Randomness Accumulator</a></h3>
|
<h3 id="610-randomness-accumulator"><a class="header" href="#610-randomness-accumulator">6.10. Randomness Accumulator</a></h3>
|
||||||
<p>The randomness accumulator is updated using the <code>randomness_source</code> signature found
|
<p>The randomness accumulator is updated using the <code>randomness_source</code> signature
|
||||||
within the <code>ClaimData</code> object.</p>
|
found within the <code>ClaimData</code> object. In particular, fresh randomness is derived
|
||||||
<p>In particular, fresh randomness is derived and accumulated <strong>after</strong> block
|
and accumulated <strong>after</strong> block execution as follows:</p>
|
||||||
execution as follows:</p>
|
|
||||||
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
||||||
</span><span class="boring">fn main() {
|
</span><span class="boring">fn main() {
|
||||||
</span> let fresh_randomness = vrf_signed_output(claim.randomness_source);
|
</span> let fresh_randomness = vrf_signed_output(claim.randomness_source);
|
||||||
@@ -978,30 +981,29 @@ execution as follows:</p>
|
|||||||
<h2 id="7-drawbacks"><a class="header" href="#7-drawbacks">7. Drawbacks</a></h2>
|
<h2 id="7-drawbacks"><a class="header" href="#7-drawbacks">7. Drawbacks</a></h2>
|
||||||
<p>None</p>
|
<p>None</p>
|
||||||
<h2 id="8-testing-security-and-privacy"><a class="header" href="#8-testing-security-and-privacy">8. Testing, Security, and Privacy</a></h2>
|
<h2 id="8-testing-security-and-privacy"><a class="header" href="#8-testing-security-and-privacy">8. Testing, Security, and Privacy</a></h2>
|
||||||
<p>It is critical that implementations of this RFC undergo thorough testing on
|
<p>It is critical that implementations of this RFC undergo thorough rigorous
|
||||||
test networks.</p>
|
testing. A security audit may be desirable to ensure the implementation does not
|
||||||
<p>A security audit may be desirable to ensure the implementation does not
|
introduce emergent side effects.</p>
|
||||||
introduce unwanted side effects.</p>
|
|
||||||
<h2 id="9-performance-ergonomics-and-compatibility"><a class="header" href="#9-performance-ergonomics-and-compatibility">9. Performance, Ergonomics, and Compatibility</a></h2>
|
<h2 id="9-performance-ergonomics-and-compatibility"><a class="header" href="#9-performance-ergonomics-and-compatibility">9. Performance, Ergonomics, and Compatibility</a></h2>
|
||||||
<h3 id="91-performance"><a class="header" href="#91-performance">9.1. Performance</a></h3>
|
<h3 id="91-performance"><a class="header" href="#91-performance">9.1. Performance</a></h3>
|
||||||
<p>Adopting Sassafras consensus marks a significant improvement in reducing the
|
<p>Adopting Sassafras consensus marks a significant improvement in reducing the
|
||||||
frequency of short-lived forks.</p>
|
frequency of short-lived forks which are eliminated by design.</p>
|
||||||
<p>Forks are eliminated by design. Forks may only result from network disruptions
|
<p>Forks may only result from network disruption or protocol attacks. In such
|
||||||
or protocol attacks. In such cases, the choice of which fork to follow upon
|
cases, the choice of which fork to follow upon recovery is clear-cut, with only
|
||||||
recovery is clear-cut, with only one valid option.</p>
|
one valid option.</p>
|
||||||
<h3 id="92-ergonomics"><a class="header" href="#92-ergonomics">9.2. Ergonomics</a></h3>
|
<h3 id="92-ergonomics"><a class="header" href="#92-ergonomics">9.2. Ergonomics</a></h3>
|
||||||
<p>No specific considerations.</p>
|
<p>No specific considerations.</p>
|
||||||
<h3 id="93-compatibility"><a class="header" href="#93-compatibility">9.3. Compatibility</a></h3>
|
<h3 id="93-compatibility"><a class="header" href="#93-compatibility">9.3. Compatibility</a></h3>
|
||||||
<p>The adoption of Sassafras affects the native client and thus can't be introduced
|
<p>The adoption of Sassafras affects the native client and thus can't be introduced
|
||||||
just via a runtime upgrade.</p>
|
via a "simple" runtime upgrade.</p>
|
||||||
<p>A deployment strategy should be carefully engineered for live networks.</p>
|
<p>A deployment strategy should be carefully engineered for live networks. This
|
||||||
<p>This subject is left open for a dedicated RFC.</p>
|
subject is left open for a dedicated RFC.</p>
|
||||||
<h2 id="10-prior-art-and-references"><a class="header" href="#10-prior-art-and-references">10. Prior Art and References</a></h2>
|
<h2 id="10-prior-art-and-references"><a class="header" href="#10-prior-art-and-references">10. Prior Art and References</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS">Sassafras layman introduction</a></li>
|
<li><a href="https://research.web3.foundation/Polkadot/protocols/block-production/SASSAFRAS">Sassafras layman introduction</a></li>
|
||||||
<li><a href="https://eprint.iacr.org/2023/031.pdf">Sassafras research paper</a></li>
|
<li><a href="https://eprint.iacr.org/2023/031.pdf">Sassafras research paper</a></li>
|
||||||
<li><a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs specification</a>.</li>
|
<li><a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs specification</a></li>
|
||||||
<li><a href="https://github.com/davxy/ark-ec-vrfs">Bandersnatch VRFs reference implementation</a>.</li>
|
<li><a href="https://github.com/davxy/ark-ec-vrfs">Bandersnatch VRFs reference implementation</a></li>
|
||||||
<li><a href="https://eprint.iacr.org/2023/002.pdf">W3F Ring VRF research paper</a></li>
|
<li><a href="https://eprint.iacr.org/2023/002.pdf">W3F Ring VRF research paper</a></li>
|
||||||
<li><a href="https://github.com/paritytech/substrate/issues/11515">Sassafras reference implementation tracking issue</a></li>
|
<li><a href="https://github.com/paritytech/substrate/issues/11515">Sassafras reference implementation tracking issue</a></li>
|
||||||
<li><a href="https://github.com/paritytech/substrate/pull/11879">Sassafras reference implementation main PR</a></li>
|
<li><a href="https://github.com/paritytech/substrate/pull/11879">Sassafras reference implementation main PR</a></li>
|
||||||
@@ -1014,41 +1016,41 @@ protocol, several crucial topics remain to be addressed in future RFCs.</p>
|
|||||||
<h3 id="121-interactions-with-on-chain-code"><a class="header" href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></h3>
|
<h3 id="121-interactions-with-on-chain-code"><a class="header" href="#121-interactions-with-on-chain-code">12.1. Interactions with On-Chain Code</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Storage</strong> organization and static configuration.</p>
|
<p><strong>Storage</strong>: Types, organization and genesis configuration.</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Outbound Interfaces</strong>: Interfaces that the host environment provides to the
|
<p><strong>Host interface</strong>: Interface that the hosting environment exposes to on-chain
|
||||||
on-chain code, typically known as <em>Host Functions</em>.</p>
|
code (also known as <em>host functions</em>).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Unrecorded Inbound Interfaces</strong>. Interfaces that the on-chain code provides
|
<p><strong>Unrecorded on-chain interface</strong>. Interface that on-chain code exposes to the
|
||||||
to the host environment, typically known as <em>Runtime APIs</em>.</p>
|
hosting environment (also known as <em>runtime API</em>).</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p><strong>Transactional Inbound Interfaces</strong>. Interfaces that the on-chain code provides
|
<p><strong>Transactional on-chain interface</strong>. Interface that on-chain code exposes
|
||||||
to the world to alter the chain state, typically known as <em>Transactions</em>
|
to the World to alter the state (also known as <em>transactions</em> or
|
||||||
(or <em>extrinsics</em> in the <em>Polkadot</em> ecosystem)</p>
|
<em>extrinsics</em> in the <em>Polkadot</em> ecosystem).</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="122-deployment-strategies"><a class="header" href="#122-deployment-strategies">12.2. Deployment Strategies</a></h3>
|
<h3 id="122-deployment-strategies"><a class="header" href="#122-deployment-strategies">12.2. Deployment Strategies</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Protocol Migration</strong>. Exploring how this protocol can seamlessly replace an
|
<li><strong>Protocol Migration</strong>. Investigate of how Sassafras can seamlessly replace
|
||||||
already operational instance of another protocol. Future RFCs may focus on
|
an already operational instance of another protocol. Future RFCs may focus on
|
||||||
deployment strategies to facilitate a smooth transition.</li>
|
deployment strategies to facilitate a smooth transition.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="123-zk-snark-srs"><a class="header" href="#123-zk-snark-srs">12.3. ZK-SNARK SRS</a></h3>
|
<h3 id="123-zk-snark-parameters"><a class="header" href="#123-zk-snark-parameters">12.3. ZK-SNARK Parameters</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Procedure</strong>: Determining the procedure for the <em>zk-SNARK</em> SRS (Structured
|
<li><strong>Parameters Setup</strong>: Determine the setup procedure for the <em>zk-SNARK</em> SRS
|
||||||
Reference String) initialization. Future RFCs may provide insights into
|
(Structured Reference String) initialization. Future RFCs may provide insights
|
||||||
whether this process should include an ad-hoc initialization ceremony or if
|
into whether this process should include an ad-hoc initialization ceremony or
|
||||||
we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).</li>
|
if we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3 id="124-anonymous-submission-of-tickets"><a class="header" href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></h3>
|
<h3 id="124-anonymous-submission-of-tickets"><a class="header" href="#124-anonymous-submission-of-tickets">12.4. Anonymous Submission of Tickets.</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Mixnet Integration</strong>: Submitting tickets directly to the relay can pose a
|
<li><strong>Mixnet Integration</strong>: Submitting tickets directly to the relay can pose a
|
||||||
risk of potential deanonymization through traffic analysis. Subsequent RFCs
|
risk of potential deanonymization through traffic analysis. Subsequent RFCs
|
||||||
may investigate the potential for incorporating Mixnet protocol or other
|
may investigate the potential for incorporating <em>mix network</em> protocol or
|
||||||
privacy-enhancing mechanisms to address this concern.</li>
|
other privacy-enhancing mechanisms to address this concern.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user