mirror of
https://github.com/pezkuwichain/pezkuwi-fellows.git
synced 2026-04-27 08:37:57 +00:00
1107 lines
71 KiB
HTML
1107 lines
71 KiB
HTML
|
|
<!DOCTYPE HTML>
|
|
<html lang="en" class="polkadot" dir="ltr">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>RFC-0026: Sassafras Consensus Protocol - Polkadot Fellowship RFCs</title>
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
<meta name="description" content="An online book of RFCs approved or proposed within the Polkadot Fellowship.">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#ffffff">
|
|
|
|
<link rel="icon" href="../favicon.svg">
|
|
<link rel="shortcut icon" href="../favicon.png">
|
|
<link rel="stylesheet" href="../css/variables.css">
|
|
<link rel="stylesheet" href="../css/general.css">
|
|
<link rel="stylesheet" href="../css/chrome.css">
|
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
|
|
|
<!-- Fonts -->
|
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
|
<link rel="stylesheet" href="../fonts/fonts.css">
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
<link rel="stylesheet" href="../highlight.css">
|
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
<link rel="stylesheet" href="../theme/polkadot.css">
|
|
|
|
</head>
|
|
<body class="sidebar-visible no-js">
|
|
<div id="body-container">
|
|
<!-- Provide site root to javascript -->
|
|
<script>
|
|
var path_to_root = "../";
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "polkadot" : "polkadot";
|
|
</script>
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
<script>
|
|
try {
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
}
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
}
|
|
} catch (e) { }
|
|
</script>
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
<script>
|
|
var theme;
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
var html = document.querySelector('html');
|
|
html.classList.remove('polkadot')
|
|
html.classList.add(theme);
|
|
var body = document.querySelector('body');
|
|
body.classList.remove('no-js')
|
|
body.classList.add('js');
|
|
</script>
|
|
|
|
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
<script>
|
|
var body = document.querySelector('body');
|
|
var sidebar = null;
|
|
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
|
if (document.body.clientWidth >= 1080) {
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
sidebar = sidebar || 'visible';
|
|
} else {
|
|
sidebar = 'hidden';
|
|
}
|
|
sidebar_toggle.checked = sidebar === 'visible';
|
|
body.classList.remove('sidebar-visible');
|
|
body.classList.add("sidebar-" + sidebar);
|
|
</script>
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
<div class="sidebar-scrollbox">
|
|
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../introduction.html">Introduction</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Newly Proposed</li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Proposed</li><li class="chapter-item expanded "><a href="../proposed/0000-pre-elves_soft.html">RFC-0000: Pre-ELVES soft concensus</a></li><li class="chapter-item expanded "><a href="../proposed/0000-rewards.html">RFC-0000: Validator Rewards</a></li><li class="chapter-item expanded "><a href="../proposed/0004-remove-unnecessary-allocator-usage.html">RFC-0004: Remove the host-side runtime memory allocator</a></li><li class="chapter-item expanded "><a href="../proposed/0006-dynamic-pricing-for-bulk-coretime-sales.html">RFC-0006: Dynamic Pricing for Bulk Coretime Sales</a></li><li class="chapter-item expanded "><a href="../proposed/0034-xcm-absolute-location-account-derivation.html">RFC-34: XCM Absolute Location Account Derivation</a></li><li class="chapter-item expanded "><a href="../proposed/0035-conviction-voting-delegation-modifications.html"> RFC-0035: Conviction Voting Delegation Modifications</a></li><li class="chapter-item expanded "><a href="../proposed/0044-rent-based-registration.html">RFC-0044: Rent based registration model</a></li><li class="chapter-item expanded "><a href="../proposed/0054-remove-heap-pages.html">RFC-0054: Remove the concept of "heap pages" from the client</a></li><li class="chapter-item expanded "><a href="../proposed/0070-x-track-kusamanetwork.html">RFC-0070: X Track for @kusamanetwork</a></li><li class="chapter-item expanded "><a href="../proposed/0073-referedum-deposit-track.html">RFC-0073: Decision Deposit Referendum Track</a></li><li class="chapter-item expanded "><a href="../proposed/0074-stateful-multisig-pallet.html">RFC-0074: Stateful Multisig Pallet</a></li><li class="chapter-item expanded "><a href="../proposed/0077-increase-max-length-of-identity-pgp-fingerprint-value.html">RFC-0077: Increase maximum length of identity PGP fingerprint values from 20 bytes</a></li><li class="chapter-item expanded "><a href="../proposed/0088-broker-pallet-slashable-deposit-purchaser-reputation-reserved-cores.html">RFC-0088: Add slashable locked deposit, purchaser reputation, and reserved cores for on-chain identities to broker pallet</a></li><li class="chapter-item expanded "><a href="../proposed/00xx-secondary-marketplace-for-regions.html">RFC-0001: Secondary Market for Regions</a></li><li class="chapter-item expanded "><a href="../proposed/00xx-smart-contracts-coretime-chain.html">RFC-0002: Smart Contracts on the Coretime Chain</a></li><li class="chapter-item expanded "><a href="../proposed/0102-offchain-parachain-runtime-upgrades.html">RFC-0000: Feature Name Here</a></li><li class="chapter-item expanded "><a href="../proposed/0106-xcm-remove-fees-mode.html">RFC-0106: Remove XCM fees mode</a></li><li class="chapter-item expanded "><a href="../proposed/0111-pure-proxy-replication.html">RFC-0111: Pure Proxy Replication</a></li><li class="chapter-item expanded "><a href="../proposed/0112-compress-state-response-message-in-state-sync.html">RFC-0112: Compress the State Response Message in State Sync</a></li><li class="chapter-item expanded "><a href="../proposed/0114-secp256r1-hostfunction.html">RFC-0114: Introduce secp256r1_ecdsa_verify_prehashed Host Function to verify NIST-P256 elliptic curve signatures</a></li><li class="chapter-item expanded "><a href="../proposed/0117-unbrick-collective.html">RFC-0117: The Unbrick Collective</a></li><li class="chapter-item expanded "><a href="../proposed/0120-referenda-confirmation-by-candle-mechanism.html">RFC-0120: Referenda Confirmation by Candle Mechanism</a></li><li class="chapter-item expanded "><a href="../proposed/0124-extrinsic-version-5.html">RFC-0124: Extrinsic version 5</a></li><li class="chapter-item expanded "><a href="../proposed/0138-invulnerable-collator-election.html">RFC-0138: Election mechanism for invulnerable collators on system chains</a></li><li class="chapter-item expanded "><a href="../proposed/0145-remove-unnecessary-allocator-usage.html">RFC-0145: Remove the host-side runtime memory allocator</a></li><li class="chapter-item expanded "><a href="../proposed/0150-voting-while-delegating.html">RFC-150: Allow Voting While Delegating</a></li><li class="chapter-item expanded "><a href="../proposed/0152-decentralized-convex-preference-coretime-market-for-polkadot.html">RFC-0152: Decentralized Convex-Preference Coretime Market for Polkadot</a></li><li class="chapter-item expanded "><a href="../proposed/0154-multi-slot-aura.html">RFC-0154: AURA Multi-Slot Collation </a></li><li class="chapter-item expanded "><a href="../proposed/0155-pUSD.html">RFC-0155: pUSD (Polkadot USD over-collateralised debt token)</a></li><li class="chapter-item expanded "><a href="../proposed/RFC-114 Adjust Tipper Track Confirmation Periods.html">RFC-114: Adjust Tipper Track Confirmation Periods</a></li><li class="chapter-item expanded "><a href="../proposed/TODO-stale-nomination-reward-curve.html">RFC-TODO: Stale Nomination Reward Curve</a></li><li class="chapter-item expanded "><a href="../proposed/xxxx-improve-the-security-of-proof-of-possession.html">RFC-XXXX: Adding customized mandatory context to proof of possession statement</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Approved</li><li class="chapter-item expanded "><a href="../approved/0001-agile-coretime.html">RFC-1: Agile Coretime</a></li><li class="chapter-item expanded "><a href="../approved/0005-coretime-interface.html">RFC-5: Coretime Interface</a></li><li class="chapter-item expanded "><a href="../approved/0007-system-collator-selection.html">RFC-0007: System Collator Selection</a></li><li class="chapter-item expanded "><a href="../approved/0008-parachain-bootnodes-dht.html">RFC-0008: Store parachain bootnodes in relay chain DHT</a></li><li class="chapter-item expanded "><a href="../approved/0009-improved-net-light-client-requests.html">RFC-0009: Improved light client requests networking protocol</a></li><li class="chapter-item expanded "><a href="../approved/0010-burn-coretime-revenue.html">RFC-0010: Burn Coretime Revenue</a></li><li class="chapter-item expanded "><a href="../approved/0012-process-for-adding-new-collectives.html">RFC-0012: Process for Adding New System Collectives</a></li><li class="chapter-item expanded "><a href="../approved/0013-prepare-blockbuilder-and-core-runtime-apis-for-mbms.html">RFC-0013: Prepare Core runtime API for MBMs</a></li><li class="chapter-item expanded "><a href="../approved/0014-improve-locking-mechanism-for-parachains.html">RFC-0014: Improve locking mechanism for parachains</a></li><li class="chapter-item expanded "><a href="../approved/0017-coretime-market-redesign.html">RFC-0017: Coretime Market Redesign</a></li><li class="chapter-item expanded "><a href="../approved/0022-adopt-encointer-runtime.html">RFC-0022: Adopt Encointer Runtime</a></li><li class="chapter-item expanded "><a href="../approved/0026-sassafras-consensus.html" class="active">RFC-0026: Sassafras Consensus Protocol</a></li><li class="chapter-item expanded "><a href="../approved/0032-minimal-relay.html">RFC-0032: Minimal Relay</a></li><li class="chapter-item expanded "><a href="../approved/0042-extrinsics-state-version.html">RFC-0042: Add System version that replaces StateVersion on RuntimeVersion</a></li><li class="chapter-item expanded "><a href="../approved/0043-storage-proof-size-hostfunction.html">RFC-0043: Introduce storage_proof_size Host Function for Improved Parachain Block Utilization</a></li><li class="chapter-item expanded "><a href="../approved/0045-nft-deposits-asset-hub.html">RFC-0045: Lowering NFT Deposits on Asset Hub</a></li><li class="chapter-item expanded "><a href="../approved/0047-assignment-of-availability-chunks.html">RFC-0047: Assignment of availability chunks to validators</a></li><li class="chapter-item expanded "><a href="../approved/0048-session-keys-runtime-api.html">RFC-0048: Generate ownership proof for SessionKeys</a></li><li class="chapter-item expanded "><a href="../approved/0050-fellowship-salaries.html">RFC-0050: Fellowship Salaries</a></li><li class="chapter-item expanded "><a href="../approved/0056-one-transaction-per-notification.html">RFC-0056: Enforce only one transaction per notification</a></li><li class="chapter-item expanded "><a href="../approved/0059-nodes-capabilities-discovery.html">RFC-0059: Add a discovery mechanism for nodes based on their capabilities</a></li><li class="chapter-item expanded "><a href="../approved/0078-merkleized-metadata.html">RFC-0078: Merkleized Metadata</a></li><li class="chapter-item expanded "><a href="../approved/0084-general-transaction-extrinsic-format.html">RFC-0084: General transactions in extrinsic format</a></li><li class="chapter-item expanded "><a href="../approved/0091-dht-record-creation-time.html">RFC-0091: DHT Authority discovery record creation time</a></li><li class="chapter-item expanded "><a href="../approved/0097-unbonding_queue.html">RFC-0097: Unbonding Queue</a></li><li class="chapter-item expanded "><a href="../approved/0099-transaction-extension-version.html">RFC-0099: Introduce a transaction extension version</a></li><li class="chapter-item expanded "><a href="../approved/0100-xcm-multi-type-asset-transfer.html">RFC-0100: New XCM instruction: InitiateAssetsTransfer</a></li><li class="chapter-item expanded "><a href="../approved/0101-xcm-transact-remove-max-weight-param.html">RFC-0101: XCM Transact remove require_weight_at_most parameter</a></li><li class="chapter-item expanded "><a href="../approved/0103-introduce-core-index-commitment.html">RFC-0103: Introduce a CoreIndex commitment and a SessionIndex field in candidate receipts</a></li><li class="chapter-item expanded "><a href="../approved/0105-xcm-improved-fee-mechanism.html">RFC-0105: XCM improved fee mechanism</a></li><li class="chapter-item expanded "><a href="../approved/0107-xcm-execution-hints.html">RFC-0107: XCM Execution hints</a></li><li class="chapter-item expanded "><a href="../approved/0108-xcm-remove-testnet-ids.html">RFC-0108: Remove XCM testnet NetworkIds</a></li><li class="chapter-item expanded "><a href="../approved/0122-alias-origin-on-asset-transfers.html">RFC-0122: Asset transfers can alias XCM origin on destination to original origin</a></li><li class="chapter-item expanded "><a href="../approved/0123-pending-code-as-storage-location-for-runtime-upgrades.html">RFC-0123: Introduce :pending_code as intermediate storage key for the runtime code</a></li><li class="chapter-item expanded "><a href="../approved/0125-xcm-asset-metadata.html">RFC-0125: XCM Asset Metadata</a></li><li class="chapter-item expanded "><a href="../approved/0126-introduce-pvq.html">RFC-0126: Introduce PVQ (PolkaVM Query)</a></li><li class="chapter-item expanded "><a href="../approved/0135-compressed-blob-prefixes.html">RFC-0135: Compressed Blob Prefixes</a></li><li class="chapter-item expanded "><a href="../approved/0139-faster-erasure-coding.html">RFC-0139: Faster Erasure Coding</a></li><li class="chapter-item expanded "><a href="../approved/0146-deflationary-fee-proposal.html">RFC-0146: Deflationary Transaction Fee Model for the Relay Chain and its System Parachains</a></li><li class="chapter-item expanded "><a href="../approved/0149-rfc-1-renewal-adjustment.html">RFC-0149: Renewal Adjustment</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Stale</li></ol>
|
|
</div>
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|
</nav>
|
|
|
|
<!-- Track and set sidebar scroll position -->
|
|
<script>
|
|
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
|
sidebarScrollbox.addEventListener('click', function(e) {
|
|
if (e.target.tagName === 'A') {
|
|
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
|
}
|
|
}, { passive: true });
|
|
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
|
sessionStorage.removeItem('sidebar-scroll');
|
|
if (sidebarScrollTop) {
|
|
// preserve sidebar scroll position when navigating via links within sidebar
|
|
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
|
} else {
|
|
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
|
var activeSection = document.querySelector('#sidebar .active');
|
|
if (activeSection) {
|
|
activeSection.scrollIntoView({ block: 'center' });
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
<div id="menu-bar" class="menu-bar sticky">
|
|
<div class="left-buttons">
|
|
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</label>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="polkadot">Polkadot</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<h1 class="menu-title">Polkadot Fellowship RFCs</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script>
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<p><a href="https://github.com/polkadot-fellows/RFCs/blob/main/text/0026-sassafras-consensus.md">(source)</a></p>
|
|
<p><strong>Table of Contents</strong></p>
|
|
<ul>
|
|
<li><a href="#rfc-0026-sassafras-consensus-protocol">RFC-0026: Sassafras Consensus Protocol</a>
|
|
<ul>
|
|
<li><a href="#abstract">Abstract</a></li>
|
|
<li><a href="#1-motivation">1. Motivation</a>
|
|
<ul>
|
|
<li><a href="#11-relevance-to-implementors">1.1. Relevance to Implementors</a></li>
|
|
<li><a href="#12-supporting-sassafras-for-polkadot">1.2. Supporting Sassafras for Polkadot</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#2-stakeholders">2. Stakeholders</a>
|
|
<ul>
|
|
<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>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#3-notation">3. Notation</a>
|
|
<ul>
|
|
<li><a href="#31-data-structures-definitions">3.1. Data Structures Definitions</a></li>
|
|
<li><a href="#32-types-alias">3.2. Types Alias</a></li>
|
|
<li><a href="#32-pseudo-code">3.2. Pseudo-Code</a></li>
|
|
<li><a href="#33-incremental-introduction-of-types-and-functions">3.3. Incremental Introduction of Types and Functions</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#4-protocol-introduction">4. Protocol Introduction</a>
|
|
<ul>
|
|
<li><a href="#41-submission-of-candidate-tickets">4.1. Submission of Candidate Tickets</a></li>
|
|
<li><a href="#42-validation-of-candidate-tickets">4.2. Validation of Candidate Tickets</a></li>
|
|
<li><a href="#43-tickets-slots-binding">4.3. Tickets Slots Binding</a></li>
|
|
<li><a href="#44-claim-of-ticket-ownership">4.4. Claim of Ticket Ownership</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#5-bandersnatch-vrfs-cryptographic-primitives">5. Bandersnatch VRFs Cryptographic Primitives</a>
|
|
<ul>
|
|
<li><a href="#51-bare-vrf-interface">5.1 Bare VRF Interface</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#6-sassafras-protocol">6. Sassafras Protocol</a>
|
|
<ul>
|
|
<li><a href="#62-header-digest-log">6.2. Header Digest Log</a></li>
|
|
<li><a href="#63-on-chain-randomness">6.3. On-Chain Randomness</a></li>
|
|
<li><a href="#64-epoch-change-signal">6.4. Epoch Change Signal</a></li>
|
|
<li><a href="#65-tickets-creation-and-submission">6.5. Tickets Creation and Submission</a></li>
|
|
<li><a href="#66-on-chain-tickets-validation">6.6. On-chain Tickets Validation</a></li>
|
|
<li><a href="#67-ticket-slot-binding">6.7. Ticket-Slot Binding</a></li>
|
|
<li><a href="#68-slot-claim">6.8. Slot Claim</a></li>
|
|
<li><a href="#69-slot-claim-verification">6.9. Slot Claim Verification</a></li>
|
|
<li><a href="#691-primary-method">6.9.1. Primary Method</a></li>
|
|
<li><a href="#610-randomness-accumulator">6.10. Randomness Accumulator</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#7-drawbacks">7. Drawbacks</a></li>
|
|
<li><a href="#8-testing-security-and-privacy">8. Testing, Security, and Privacy</a></li>
|
|
<li><a href="#9-performance-ergonomics-and-compatibility">9. Performance, Ergonomics, and Compatibility</a>
|
|
<ul>
|
|
<li><a href="#91-performance">9.1. Performance</a></li>
|
|
<li><a href="#92-ergonomics">9.2. Ergonomics</a></li>
|
|
<li><a href="#93-compatibility">9.3. Compatibility</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#10-prior-art-and-references">10. Prior Art and References</a></li>
|
|
<li><a href="#11-unresolved-questions">11. Unresolved Questions</a></li>
|
|
<li><a href="#12-future-directions-and-related-material">12. Future Directions and Related Material</a>
|
|
<ul>
|
|
<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="#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>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h1 id="rfc-0026-sassafras-consensus-protocol"><a class="header" href="#rfc-0026-sassafras-consensus-protocol">RFC-0026: Sassafras Consensus Protocol</a></h1>
|
|
<div class="table-wrapper"><table><thead><tr><th></th><th></th></tr></thead><tbody>
|
|
<tr><td><strong>Start Date</strong></td><td>September 06, 2023</td></tr>
|
|
<tr><td><strong>Description</strong></td><td>Sassafras consensus protocol specification</td></tr>
|
|
<tr><td><strong>Authors</strong></td><td>Davide Galassi</td></tr>
|
|
</tbody></table>
|
|
</div>
|
|
<h2 id="abstract"><a class="header" href="#abstract">Abstract</a></h2>
|
|
<p>Sassafras is a novel consensus protocol designed to address the recurring
|
|
fork-related challenges encountered in other lottery-based protocols.</p>
|
|
<p>The protocol aims to create a mapping between each epoch's slots and the
|
|
authorities set while ensuring that the identity of authorities assigned to
|
|
the slots remains undisclosed until the slot is actively claimed during block
|
|
production.</p>
|
|
<h2 id="1-motivation"><a class="header" href="#1-motivation">1. Motivation</a></h2>
|
|
<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://web3.foundation">Web3 Foundation</a> research team.</p>
|
|
<p>This RFC is primarily intended to detail the critical implementation aspects
|
|
vital for ensuring interoperability and to clarify certain aspects that are
|
|
left open by the research paper and thus subject to interpretation during
|
|
implementation.</p>
|
|
<h3 id="11-relevance-to-implementors"><a class="header" href="#11-relevance-to-implementors">1.1. Relevance to Implementors</a></h3>
|
|
<p>This RFC focuses on providing implementors with the necessary insights into the
|
|
core protocol's operation.</p>
|
|
<p>In instances of inconsistency between this document and the research paper,
|
|
this RFC should be considered authoritative to eliminate ambiguities and ensure
|
|
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>
|
|
<p>Beyond promoting interoperability, this RFC also aims to facilitate the
|
|
implementation of Sassafras within the greater Polkadot ecosystem.</p>
|
|
<p>Although the specifics of deployment strategies are beyond the scope of this
|
|
document, it lays the groundwork for the integration of Sassafras.</p>
|
|
<h2 id="2-stakeholders"><a class="header" href="#2-stakeholders">2. Stakeholders</a></h2>
|
|
<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
|
|
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>
|
|
<p>Developers contributing to the Polkadot ecosystem, both relay-chain and
|
|
para-chains.</p>
|
|
<h2 id="3-notation"><a class="header" href="#3-notation">3. Notation</a></h2>
|
|
<p>This section outlines the notation adopted throughout this document to ensure
|
|
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>
|
|
<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>
|
|
<p>To ensure interoperability of serialized structures, the order of the fields
|
|
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>
|
|
<ul>
|
|
<li>Unsigned integer: <code>Unsigned ::= INTEGER (0..MAX)</code></li>
|
|
<li>n-bit unsigned integer: <code>Unsigned<n> ::= INTEGER (0..2^n - 1)</code>
|
|
<ul>
|
|
<li>8-bit unsigned integer (octet) <code>Unsigned8 ::= Unsigned<8></code></li>
|
|
<li>32-bit unsigned integer: <code>Unsigned32 ::= Unsigned<32></code></li>
|
|
<li>64-bit unsigned integer: <code>Unsigned64 ::= Unsigned<64></code></li>
|
|
</ul>
|
|
</li>
|
|
<li>Non-homogeneous sequence (struct/tuple): <code>Sequence ::= SEQUENCE</code></li>
|
|
<li>Variable length homogeneous sequence (vector): <code>Sequence<T> ::= SEQUENCE OF T</code></li>
|
|
<li>Fixed length homogeneous sequence (array): <code>Sequence<T,n> ::= Sequence<T> (SIZE(n))</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>
|
|
</ul>
|
|
<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
|
|
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 procedures:</p>
|
|
<h4 id="sequences"><a class="header" href="#sequences">Sequences</a></h4>
|
|
<ul>
|
|
<li>
|
|
<p><code>CONCAT(x₀: OctetString, ..., xₖ: OctetString) -> OctetString</code>: Concatenates the
|
|
input octet-strings as a new octet string.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>LENGTH(s: Sequence) -> Unsigned</code>: The number of elements in the sequence <code>s</code>.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>GET(s: Sequence<T>, i: Unsigned) -> T</code>: The <code>i</code>-th element of the sequence <code>s</code>.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>PUSH(s: Sequence<T>, x: T)</code>: Appends <code>x</code> as the new last element of the sequence <code>s</code>.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>POP(s: Sequence<T>) -> T</code>: extract and returns the last element of the sequence <code>s</code>.</p>
|
|
</li>
|
|
</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>
|
|
<p>More types and helper functions are introduced incrementally as they become
|
|
relevant within the document's context.</p>
|
|
<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>.
|
|
This entire sequence of slots is further partitioned into distinct segments
|
|
known as <strong>epochs</strong>.</p>
|
|
<p>Sassafras aims to map each slot within a <em>target epoch</em> to the authorities
|
|
scheduled for that epoch, utilizing a ticketing system.</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>
|
|
<p>Each authority scheduled for the target epoch generates and shares a set of
|
|
candidate tickets. Every ticket has an <em>unbiasable</em> pseudo random score and is
|
|
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>
|
|
<p>Each candidate ticket undergoes a validation process for the associated validity
|
|
proof and compliance with other protocol-specific constraints. Valid tickets
|
|
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>
|
|
<p>After collecting all valid candidate tickets and before the beginning of the
|
|
<em>target epoch</em>, a deterministic method is used to uniquely associate a subset of
|
|
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>
|
|
<p>During block production phase of <em>target epoch</em>, the author is required to prove
|
|
ownership of the ticket associated to the block's slot. This step discloses the
|
|
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>
|
|
<p>This section is not intended to serve as an exhaustive exploration of the
|
|
mathematically intensive foundations of the cryptographic primitive. Rather, its
|
|
primary aim is to offer a concise and accessible explanation of the primitives
|
|
role and interface which is relevant within the scope of the protocol. For a more
|
|
detailed explanation, refer to the <a href="https://github.com/davxy/bandersnatch-vrfs-spec">Bandersnatch VRFs</a>
|
|
technical specification</p>
|
|
<p>Bandersnatch VRF comes in two variants:</p>
|
|
<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>Ring</em> VRF: Anonymous signatures leveraging <em>zk-SNARK</em>.</li>
|
|
</ul>
|
|
<p>Together with the <em>input</em>, which determines the VRF <em>output</em>, both variants
|
|
offer the capability to sign some arbitrary additional data (<em>extra</em>) which
|
|
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>
|
|
<p>VRF signature construction.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn vrf_sign(
|
|
secret: SecretKey,
|
|
input: OctetString,
|
|
extra: OctetString,
|
|
) -> VrfSignature
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>VRF signature verification. Returns a Boolean indicating the validity of the
|
|
signature (<code>1</code> on success).</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn vrf_verify(
|
|
public: PublicKey,
|
|
input: OctetString,
|
|
extra: OctetString,
|
|
signature: VrfSignature
|
|
) -> Unsigned<1>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn vrf_output(
|
|
secret: SecretKey,
|
|
input: OctetString,
|
|
) -> OctetString<32>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>VRF <em>output</em> derivation from a VRF signature.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn vrf_signed_output(
|
|
signature: VrfSignature,
|
|
) -> OctetString<32>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The following condition is always satisfied:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let signature = vrf_sign(secret, input, extra);
|
|
vrf_output(secret, input) == vrf_signed_output(signature)
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p><code>SecretKey</code>, <code>PublicKey</code> and <code>VrfSignature</code> types are intentionally left
|
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
|
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>
|
|
<p>Ring VRF signature construction.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn ring_vrf_sign(
|
|
secret: SecretKey,
|
|
prover: RingProver,
|
|
input: OctetString,
|
|
extra: OctetString,
|
|
) -> RingVrfSignature;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Ring VRF signature verification. Returns a Boolean indicating the validity
|
|
of the signature (<code>1</code> on success). Note that verification doesn't require the
|
|
signer's public key.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn ring_vrf_verify(
|
|
verifier: RingVerifier,
|
|
input: OctetString,
|
|
extra: OctetString,
|
|
signature: RingVrfSignature,
|
|
) -> Unsigned<1>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> fn ring_vrf_signed_output(
|
|
signature: RingVrfSignature,
|
|
) -> OctetString<32>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The following condition is always satisfied:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let signature = vrf_sign(secret, input, extra);
|
|
let ring_signature = ring_vrf_sign(secret, prover, input, extra);
|
|
vrf_signed_output(signature) == ring_vrf_signed_output(ring_signature);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p><code>RingProver</code>, <code>RingVerifier</code>, and <code>RingVrfSignature</code> are intentionally left
|
|
undefined. Their definitions can be found in the Bandersnatch VRF specification
|
|
and related documents.</p>
|
|
<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>
|
|
<p>The <code>ProtocolConfiguration</code> type contains some parameters to tweak the
|
|
protocol behavior and primarily influences certain checks carried out during
|
|
tickets validation. It is defined as:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> ProtocolConfiguration ::= Sequence {
|
|
epoch_length: Unsigned32,
|
|
attempts_number: Unsigned8,
|
|
redundancy_factor: Unsigned8,
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Where:</p>
|
|
<ul>
|
|
<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>redundancy_factor</code>: Expected ratio between the cumulative number of valid
|
|
tickets which can be submitted by the scheduled authorities and the epoch's
|
|
duration in slots.</li>
|
|
</ul>
|
|
<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>,
|
|
all the tickets which share the attempt number value must belong to different
|
|
block producers, which reduces anonymity late as we approach the epoch tail.
|
|
Bigger values guarantee more anonymity but also more computation.</p>
|
|
<p>Details about how these parameters drive the tickets validity probability can be
|
|
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>
|
|
<p>Each block header contains a <code>Digest</code> log, which is defined as an ordered
|
|
sequence of <code>DigestItem</code>s:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> DigestItem ::= Sequence {
|
|
id: OctetString<4>,
|
|
data: OctetString
|
|
}
|
|
|
|
Digest ::= Sequence<DigestItem>
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The <code>Digest</code> sequence is used to propagate information required for the
|
|
correct protocol progress. Outside the protocol's context, the information
|
|
within each <code>DigestItem</code> is opaque and maps to some SCALE-encoded
|
|
protocol-specific structure.</p>
|
|
<p>For Sassafras related items, the <code>DiegestItem</code>s <code>id</code> is set to the ASCII
|
|
string <code>"SASS"</code></p>
|
|
<p>Possible digest items for Sassafras:</p>
|
|
<ul>
|
|
<li>Epoch change signal: Information about next epoch. This is mandatory for the
|
|
first block of a new epoch.</li>
|
|
<li>Epoch tickets signal: Sequence of tickets for claiming slots in the next
|
|
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
|
|
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 for each block
|
|
and must be the last entry in the log.</li>
|
|
</ul>
|
|
<p>If any digest entry is unexpected, not found where mandatory or found in the
|
|
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>
|
|
<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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> RandomnessBuffer ::= Sequence<OctetString<32>, 4>
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>During epoch <code>N</code>:</p>
|
|
<ul>
|
|
<li>
|
|
<p>The first entry is the current <em>randomness accumulator</em> and incorporates
|
|
verifiable random elements from all previously executed blocks. The
|
|
accumulation procedure is described in section <a href="#610-randomness-accumulator">6.10</a>.</p>
|
|
</li>
|
|
<li>
|
|
<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 used for tickets
|
|
targeting epoch <code>N+2</code>.</p>
|
|
</li>
|
|
<li>
|
|
<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 used for tickets
|
|
targeting epoch <code>N+1</code> (the next epoch).</p>
|
|
</li>
|
|
<li>
|
|
<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 used for tickets
|
|
targeting epoch <code>N</code> (the current epoch).</p>
|
|
</li>
|
|
</ul>
|
|
<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>
|
|
<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>
|
|
<p>This signal descriptor is defined as:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> NextEpochDescriptor ::= Sequence {
|
|
randomness: OctetString<32>,
|
|
authorities: Sequence<PublicKey>,
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Where:</p>
|
|
<ul>
|
|
<li><code>randomness</code>: Randomness accumulator snapshot relevant for validation of
|
|
next epoch blocks. In other words, randomness used to construct the tickets
|
|
targeting epoch <code>N+1</code>.</li>
|
|
<li><code>authorities</code>: List of authorities scheduled for next epoch.</li>
|
|
</ul>
|
|
<p>This descriptor is <code>SCALE</code> encoded and embedded in a <code>DigestItem</code>.</p>
|
|
<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 used by the first epoch (<code>#0</code>), are set through
|
|
the genesis configuration, which is defined as:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> GenesisConfig ::= Sequence {
|
|
authorities: Sequence<PublicKey>,
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The on-chain <code>RandomnessBuffer</code> is initialized <strong>after</strong> the genesis block
|
|
construction. The first buffer entry is set as the <em>Blake2b</em> hash of the genesis
|
|
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
|
|
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
|
|
following epoch.</p>
|
|
<p><code>NextEpochDescriptor</code> for epoch <code>#1</code>:</p>
|
|
<ul>
|
|
<li><code>randomness</code>: Third entry (index 2) of the randomness buffer.</li>
|
|
<li><code>authorities</code>: The same sequence as specified in the genesis configuration.</li>
|
|
</ul>
|
|
<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
|
|
of tickets which may be eligible (<a href="#652-tickets-threshold">6.5.2</a>) for on-chain
|
|
submission.</p>
|
|
<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
|
|
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
|
|
<code>attempts_number</code> field of the <code>ProtocolConfiguration</code>.</p>
|
|
<p>The ideal timing for the candidate authority to start constructing the tickets
|
|
is subject to strategy. A recommended approach is to initiate tickets creation
|
|
once the last block of epoch <code>N-1</code> is either probabilistically or, even better,
|
|
deterministically finalized. This delay is suggested to prevent wasting
|
|
resources creating tickets that will be unusable if a different chain branch is
|
|
chosen as canonical.</p>
|
|
<p>Tickets generated during epoch <code>N</code> are shared with the <em>tickets relayers</em>,
|
|
which are the authorities scheduled for epoch <code>N+1</code>. Relayers validate and
|
|
collect (off-chain) the tickets targeting epoch <code>N+2</code>.</p>
|
|
<p>When epoch <code>N+1</code> starts, collected tickets are submitted on-chain by relayers
|
|
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>
|
|
<p>Each ticket has an associated identifier defined as:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> TicketId ::= OctetString<32>;
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The value of <code>TicketId</code> is completely determined by the output of Bandersnatch
|
|
VRFs given the following <strong>unbiasable</strong> input:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let ticket_vrf_input = CONCAT(
|
|
BYTES("sassafras_ticket_seal"),
|
|
target_epoch_randomness,
|
|
BYTES(attempt)
|
|
);
|
|
|
|
let ticket_id = vrf_output(authority_secret_key, ticket_vrf_input);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Where:</p>
|
|
<ul>
|
|
<li><code>target_epoch_randomness</code>: element of <code>RandomnessBuffer</code> which contains the
|
|
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>
|
|
</ul>
|
|
<h4 id="652-tickets-threshold"><a class="header" href="#652-tickets-threshold">6.5.2. Tickets Threshold</a></h4>
|
|
<p>A ticket is valid for on-chain submission if its <code>TicketId</code> value, when
|
|
interpreted as a big-endian 256-bit integer normalized as a float within the
|
|
range <code>[0..1]</code>, is less than the ticket threshold computed as:</p>
|
|
<pre><code>T = (r·s)/(a·v)
|
|
</code></pre>
|
|
<p>Where:</p>
|
|
<ul>
|
|
<li><code>v</code>: epoch's authorities number</li>
|
|
<li><code>s</code>: epoch's slots number</li>
|
|
<li><code>r</code>: redundancy factor</li>
|
|
<li><code>a</code>: attempts number</li>
|
|
</ul>
|
|
<p>In an epoch with <code>s</code> slots, the goal is to achieve an expected number of valid
|
|
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
|
|
tickets is very low, even in scenarios where up to <code>1/3</code> of the authorities
|
|
might be offline. To accomplish this, we first define the winning probability of
|
|
a single ticket 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>.
|
|
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
|
|
its expected value is <code>E[X] = T·a·n = (r·s·n)/v</code>. By setting <code>r = 2</code>, we get
|
|
<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>
|
|
<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
|
|
ensures that all slots are likely to be filled with tickets.</p>
|
|
<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>
|
|
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>
|
|
<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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> TicketEnvelope ::= Sequence {
|
|
attempt: Unsigned8,
|
|
extra: OctetString,
|
|
signature: RingVrfSignature
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Where:</p>
|
|
<ul>
|
|
<li><code>attempt</code>: Index associated to the ticket.</li>
|
|
<li><code>extra</code>: Additional data available for user-defined applications.</li>
|
|
<li><code>signature</code>: Ring VRF signature of the envelope data (<code>attempt</code> and <code>extra</code>).</li>
|
|
</ul>
|
|
<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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let signature = ring_vrf_sign(
|
|
secret_key,
|
|
ring_prover
|
|
ticket_vrf_input,
|
|
extra,
|
|
);
|
|
<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>
|
|
<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>Validation rules:</p>
|
|
<ol>
|
|
<li>
|
|
<p>Ring VRF signature is verified using the <code>ring_verifier</code> derived by the
|
|
constant ring context parameters (SNARK SRS) and the next epoch authorities
|
|
public keys.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>TicketId</code> is locally computed from the <code>RingVrfSignature</code> and its value
|
|
is checked to be less than tickets' threshold.</p>
|
|
</li>
|
|
<li>
|
|
<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 slots at the end
|
|
of the epoch. This constraint is to give time to persisted on-chain tickets
|
|
to be probabilistically (or even better deterministically) finalized and thus
|
|
to further reduce the fork chances at the beginning of the target epoch.</p>
|
|
</li>
|
|
<li>
|
|
<p>All tickets which are proposed within a block must be valid and all of them
|
|
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>
|
|
<p>No tickets duplicates are allowed.</p>
|
|
</li>
|
|
</ol>
|
|
<p>If at least one of the checks fails then the block must be considered invalid.</p>
|
|
<p>Pseudo-code for ticket validation for steps 1 and 2:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let ticket_vrf_input = CONCAT(
|
|
BYTES("sassafras_ticket_seal"),
|
|
target_epoch_randomness,
|
|
BYTES(envelope.attempt)
|
|
);
|
|
|
|
let result = ring_vrf_verify(
|
|
ring_verifier,
|
|
ticket_vrf_input,
|
|
envelope.extra,
|
|
envelope.ring_signature
|
|
);
|
|
ASSERT(result == 1);
|
|
|
|
let ticket_id = ring_vrf_signed_output(envelope.ring_signature);
|
|
ASSERT(ticket_id < ticket_threshold);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Valid tickets are persisted on-chain in a bounded sorted sequence of
|
|
<code>TicketBody</code> objects. Items within this sequence are sorted according to
|
|
their <code>TicketId</code>, interpreted as a 256-bit big-endian unsigned integer.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> TicketBody ::= Sequence {
|
|
id: TicketId,
|
|
attempt: Unsigned8,
|
|
extra: OctetString,
|
|
}
|
|
|
|
Tickets ::= Sequence<TicketBody>
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The on-chain tickets sequence length bound is set equal to the epoch length
|
|
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>
|
|
<p>Before the beginning of the <em>target epoch</em>, the on-chain sequence of tickets
|
|
must be associated to epoch's slots such that there is at most one ticket per
|
|
slot.</p>
|
|
<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>
|
|
<pre><code> slot_index : [ 0, 1, 2, 3 , ... ]
|
|
tickets : [ t₀, tₙ, t₁, tₙ₋₁, ... ]
|
|
</code></pre>
|
|
<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
|
|
is public. What remains confidential is the ticket's author identity, and
|
|
consequently, who is enabled to claim the corresponding slot. This information
|
|
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,
|
|
some <em>orphan</em> slots at the end of the epoch will remain unbounded to any ticket.
|
|
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>
|
|
<h3 id="68-slot-claim"><a class="header" href="#68-slot-claim">6.8. Slot Claim</a></h3>
|
|
<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
|
|
a block.</p>
|
|
<p>The procedure for slot claiming depends on whether a given slot has an
|
|
associated ticket according to the on-chain state. If a slot has an associated
|
|
ticket, then the primary authoring method is used. Conversely, the protocol
|
|
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>
|
|
<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>
|
|
<p>Let <code>target_epoch_randomness</code> be the entry in <code>RandomnessBuffer</code> relative to
|
|
the epoch the block is targeting and <code>attempt</code> be the attempt used to construct
|
|
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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let seal_vrf_input = CONCAT(
|
|
BYTES("sassafras_ticket_seal"),
|
|
target_epoch_randomness,
|
|
BYTES(attempt)
|
|
);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>The <code>seal_vrf_input</code>, when signed with the correct authority secret key, must
|
|
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>
|
|
<p>Given that the authorities scheduled for the <em>target epoch</em> are kept on-chain in
|
|
an ordered sequence, the index of the authority which has the privilege to claim an
|
|
<em>orphan</em> slot is given by the following procedure:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let hash_input = CONCAT(
|
|
target_epoch_randomness,
|
|
ENCODE(relative_slot_index),
|
|
);
|
|
let hash = BLAKE2(hash_input);
|
|
let index_bytes = CONCAT(GET(hash, 0), GET(hash, 1), GET(hash, 2), GET(hash, 3));
|
|
let index = DECODE<Unsigned32>(index_bytes) % LENGTH(authorities);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<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>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let seal_vrf_input = CONCAT(
|
|
BYTES("sassafras_fallback_seal"),
|
|
target_epoch_randomness
|
|
);
|
|
<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>
|
|
<p><code>ClaimData</code> is a digest entry which contains additional information required by
|
|
the protocol to verify the block:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> ClaimData ::= Sequence {
|
|
slot: Unsigned32,
|
|
authority_index: Unsigned32,
|
|
randomness_source: VrfSignature,
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<ul>
|
|
<li><code>slot</code>: The slot number</li>
|
|
<li><code>authority_index</code>: Block's author index relative to the on-chain authorities sequence.</li>
|
|
<li><code>randomness_source</code>: VRF signature used to generate per-block randomness.</li>
|
|
</ul>
|
|
<p>Given the <code>seal_vrf_input</code> constructed using the primary or secondary method,
|
|
the randomness source signature is generated as follows:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let randomness_vrf_input = CONCAT(
|
|
BYTES("sassafras_randomness"),
|
|
vrf_output(authority_secret_key, seal_vrf_input)
|
|
);
|
|
|
|
let randomness_source = vrf_sign(
|
|
authority_secret_key,
|
|
randomness_vrf_input,
|
|
[]
|
|
);
|
|
|
|
let claim = SlotClaim {
|
|
slot,
|
|
authority_index,
|
|
randomness_source
|
|
};
|
|
|
|
PUSH(block_header.digest, ENCODE(claim));
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<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>
|
|
<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>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let unsealed_header_byets = ENCODE(block_header);
|
|
|
|
let seal = vrf_sign(
|
|
authority_secret_key,
|
|
seal_vrf_input,
|
|
unsealed_header_bytes
|
|
);
|
|
|
|
PUSH(block_header.digest, ENCODE(seal));
|
|
<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>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 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>
|
|
<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
|
|
verified.</p>
|
|
<p>The next entry is extracted from the header digest log, and is SCALE decoded as
|
|
a <code>ClaimData</code> object.</p>
|
|
<p>The validity of the two signatures is assessed using as the authority public key
|
|
corresponding to the <code>authority_index</code> found in the <code>ClaimData</code>, together with
|
|
the VRF input (which depends on primary/secondary method) and additional data
|
|
used by the block author.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let seal_signature = DECODE<VrfSignature>(POP(header.digest));
|
|
let unsealed_header_bytes = ENCODE(header);
|
|
let claim_data = DECODE<ClaimData>(POP(header.digest));
|
|
|
|
let authority_public_key = GET(authorities, claim_data.authority_index);
|
|
|
|
// Verify seal signature
|
|
let result = vrf_verify(
|
|
authority_public_key,
|
|
seal_vrf_input,
|
|
unsealed_header_bytes,
|
|
seal_signature
|
|
);
|
|
ASSERT(result == 1);
|
|
|
|
let randomness_vrf_input = CONCAT(
|
|
BYTES("sassafras_randomness"),
|
|
vrf_signed_output(seal_signature)
|
|
);
|
|
|
|
// Verify per-block entropy source signature
|
|
let result = vrf_verify(
|
|
authority_public_key,
|
|
randomness_vrf_input,
|
|
[],
|
|
claim_data.randomness_source
|
|
);
|
|
ASSERT(result == 1);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>With:</p>
|
|
<ul>
|
|
<li><code>header</code>: The block's header.</li>
|
|
<li><code>authorities</code>: Sequence of authorities for the target epoch, as recorded on-chain.</li>
|
|
<li><code>seal_vrf_input</code>: VRF input data constructed as specified in <a href="#68-slot-claim">6.8</a>.</li>
|
|
</ul>
|
|
<p>If signatures verification is successful, then the verification process diverges
|
|
based on whether the slot is associated with a ticket according to the on-chain
|
|
state.</p>
|
|
<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.
|
|
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)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let ticket_id = vrf_signed_output(seal_signature);
|
|
ASSERT(ticket_id == expected_ticket_id);
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>With <code>expected_ticket_id</code> the ticket identifier committed on-chain in the
|
|
associated <code>TicketBody</code>.</p>
|
|
<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 the <code>ClaimData</code> must match the one returned by the procedure
|
|
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>
|
|
<p>The randomness accumulator is updated using the <code>randomness_source</code> signature
|
|
found within the <code>ClaimData</code> object. In particular, fresh randomness is derived
|
|
and accumulated <strong>after</strong> block execution as follows:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span> let fresh_randomness = vrf_signed_output(claim.randomness_source);
|
|
randomness_buffer[0] = BLAKE2(CONCAT(randomness_buffer[0], fresh_randomness));
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<h2 id="7-drawbacks"><a class="header" href="#7-drawbacks">7. Drawbacks</a></h2>
|
|
<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>
|
|
<p>It is critical that implementations of this RFC undergo thorough rigorous
|
|
testing. A security audit may be desirable to ensure the implementation does not
|
|
introduce emergent 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>
|
|
<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
|
|
frequency of short-lived forks which are eliminated by design.</p>
|
|
<p>Forks may only result from network disruption or protocol attacks. In such
|
|
cases, the choice of which fork to follow upon recovery is clear-cut, with only
|
|
one valid option.</p>
|
|
<h3 id="92-ergonomics"><a class="header" href="#92-ergonomics">9.2. Ergonomics</a></h3>
|
|
<p>No specific considerations.</p>
|
|
<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
|
|
via a "simple" runtime upgrade.</p>
|
|
<p>A deployment strategy should be carefully engineered for live networks. This
|
|
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>
|
|
<ul>
|
|
<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://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://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/pull/11879">Sassafras reference implementation main PR</a></li>
|
|
</ul>
|
|
<h2 id="11-unresolved-questions"><a class="header" href="#11-unresolved-questions">11. Unresolved Questions</a></h2>
|
|
<p>None</p>
|
|
<h2 id="12-future-directions-and-related-material"><a class="header" href="#12-future-directions-and-related-material">12. Future Directions and Related Material</a></h2>
|
|
<p>While this RFC lays the groundwork and outlines the core aspects of the
|
|
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>
|
|
<ul>
|
|
<li>
|
|
<p><strong>Storage</strong>: Types, organization and genesis configuration.</p>
|
|
</li>
|
|
<li>
|
|
<p><strong>Host interface</strong>: Interface that the hosting environment exposes to on-chain
|
|
code (also known as <em>host functions</em>).</p>
|
|
</li>
|
|
<li>
|
|
<p><strong>Unrecorded on-chain interface</strong>. Interface that on-chain code exposes to the
|
|
hosting environment (also known as <em>runtime API</em>).</p>
|
|
</li>
|
|
<li>
|
|
<p><strong>Transactional on-chain interface</strong>. Interface that on-chain code exposes
|
|
to the World to alter the state (also known as <em>transactions</em> or
|
|
<em>extrinsics</em> in the <em>Polkadot</em> ecosystem).</p>
|
|
</li>
|
|
</ul>
|
|
<h3 id="122-deployment-strategies"><a class="header" href="#122-deployment-strategies">12.2. Deployment Strategies</a></h3>
|
|
<ul>
|
|
<li><strong>Protocol Migration</strong>. Investigate of how Sassafras can seamlessly replace
|
|
an already operational instance of another protocol. Future RFCs may focus on
|
|
deployment strategies to facilitate a smooth transition.</li>
|
|
</ul>
|
|
<h3 id="123-zk-snark-parameters"><a class="header" href="#123-zk-snark-parameters">12.3. ZK-SNARK Parameters</a></h3>
|
|
<ul>
|
|
<li><strong>Parameters Setup</strong>: Determine the setup procedure for the <em>zk-SNARK</em> SRS
|
|
(Structured Reference String) initialization. Future RFCs may provide insights
|
|
into whether this process should include an ad-hoc initialization ceremony or
|
|
if we can reuse an SRS from another ecosystem (e.g. Zcash or Ethereum).</li>
|
|
</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>
|
|
<ul>
|
|
<li><strong>Mixnet Integration</strong>: Submitting tickets directly to the relay can pose a
|
|
risk of potential deanonymization through traffic analysis. Subsequent RFCs
|
|
may investigate the potential for incorporating <em>mix network</em> protocol or
|
|
other privacy-enhancing mechanisms to address this concern.</li>
|
|
</ul>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
<a rel="prev" href="../approved/0022-adopt-encointer-runtime.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
<a rel="next prefetch" href="../approved/0032-minimal-relay.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
|
|
<div style="clear: both"></div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
<a rel="prev" href="../approved/0022-adopt-encointer-runtime.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
<a rel="next prefetch" href="../approved/0032-minimal-relay.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
</nav>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
window.playground_copyable = true;
|
|
</script>
|
|
|
|
|
|
<script src="../elasticlunr.min.js"></script>
|
|
<script src="../mark.min.js"></script>
|
|
<script src="../searcher.js"></script>
|
|
|
|
<script src="../clipboard.min.js"></script>
|
|
<script src="../highlight.js"></script>
|
|
<script src="../book.js"></script>
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|