Files
rln-docs/print.html
AtHeartEngineer df75ac01a9 deploy: 61900ff303
2022-10-07 06:32:28 +00:00

509 lines
35 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Rate-Limiting Nullifier</title>
<meta name="robots" content="noindex" />
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<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 -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
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 type="text/javascript">
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('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.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 "><a href="rln.html"><strong aria-hidden="true">1.</strong> RLN</a></li><li class="chapter-item expanded "><a href="overview.html"><strong aria-hidden="true">2.</strong> Overview</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="what_is_rln.html"><strong aria-hidden="true">2.1.</strong> What is RLN</a></li><li class="chapter-item expanded "><a href="under_the_hood.html"><strong aria-hidden="true">2.2.</strong> Under the hood</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="protocol_spec.html"><strong aria-hidden="true">2.2.1.</strong> Protocol spec</a></li><li class="chapter-item expanded "><a href="circuits.html"><strong aria-hidden="true">2.2.2.</strong> Circuits</a></li></ol></li><li class="chapter-item expanded "><a href="uses.html"><strong aria-hidden="true">2.3.</strong> Uses</a></li></ol></li><li class="chapter-item expanded "><a href="how_to_use.html"><strong aria-hidden="true">3.</strong> How to use</a></li><li><ol class="section"><li class="chapter-item expanded "><div><strong aria-hidden="true">3.1.</strong> JavaScript RLN</div></li><li class="chapter-item expanded "><div><strong aria-hidden="true">3.2.</strong> Rust RLN</div></li></ol></li><li class="chapter-item expanded "><a href="theory.html"><strong aria-hidden="true">4.</strong> Theory</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="sss.html"><strong aria-hidden="true">4.1.</strong> Shamir's Secret Sharing</a></li></ol></li><li class="chapter-item expanded "><a href="appendix.html"><strong aria-hidden="true">5.</strong> Appendix</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="terminology.html"><strong aria-hidden="true">5.1.</strong> A - Terminology</a></li><li class="chapter-item expanded "><a href="references.html"><strong aria-hidden="true">5.2.</strong> B - References</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<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 bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<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="light">Light (default)</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">Rate-Limiting Nullifier</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 type="text/javascript">
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>
<h1 id="rln"><a class="header" href="#rln">RLN</a></h1>
<p><strong>RLN</strong> (Rate-Limiting Nullifier) is a zk-gadget/protocol that enables spam prevention mechanism for anonymous environments.</p>
<p><strong>RLN</strong> is part of (<strong>PSE</strong>) <a href="https://appliedzkp.org">Privacy &amp; Scaling Explorations</a>, a multidisciplinary team supported by the Ethereum Foundation. PSE explores new use cases for zero-knowledge proofs and other cryptographic primitives.</p>
<p><img src="./images/logo.svg" alt="alt text" /></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="overview"><a class="header" href="#overview">Overview</a></h1>
<p>This section is a starting point for understanding the concepts of <strong>RLN</strong>.</p>
<p>Here we'll discuss:</p>
<ul>
<li>Basic explanation of the <strong>RLN</strong> protocol</li>
<li><strong>RLN</strong> protocol under the hood</li>
<li><strong>RLN</strong> uses</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="what-is-rate-limiting-nullifier"><a class="header" href="#what-is-rate-limiting-nullifier">What is Rate-Limiting Nullifier?</a></h1>
<p><em>This topic is a part of <a href="https://medium.com/privacy-scaling-explorations/rate-limiting-nullifier-a-spam-protection-mechanism-for-anonymous-environments-bbe4006a57d">complete overview</a> by Blagoj</em>.</p>
<hr />
<p><strong>RLN</strong> is a zero-knowledge gadget that enables spam prevention for decentralized, anonymous environments.</p>
<p>The anonymity property opens up the possibility for spam and Sybil attack vectors for certain applications, which could seriously degrade the user experience and the overall functioning of the application. For example, imagine a chat application where users are anonymous. Now, everyone can write an unlimited number of spam messages, but we don't have the ability to kick this member because the spammer is anonymous. </p>
<p><strong>RLN</strong> helps us identify and &quot;kick&quot; the spammer.</p>
<p>Moreover, <strong>RLN</strong> can be useful not only to prevent spam attacks but, in general, to limit users (in anonymous environments) in the number of actions (f.e. to vote or to make a bid).</p>
<h2 id="how-it-works"><a class="header" href="#how-it-works">How it works</a></h2>
<p>The <strong>RLN</strong> construct's functionality consists of three parts, which, when integrated together, provide spam and Sybil attack protection. These parts should be integrated by the upstream applications, which require anonymity and spam protection. The applications can be centralized or decentralized. For decentralized applications, each user maintains separate storage and compute resources for the application. The three parts are:</p>
<ul>
<li>User registration</li>
<li>User interaction</li>
<li>User removal (slashing)</li>
</ul>
<h3 id="user-registration"><a class="header" href="#user-registration">User registration</a></h3>
<p>Before registering to the application, the user needs to generate a secret key and derive an identity commitment from the secret key using the <code>Poseidon</code> hash function <code>identityCommitment = posseidonHash(secretKey)</code>.</p>
<p>The user registers to the application by providing a form of stake and their identity commitment, which is derived from the secret key. The application maintains a Merkle tree data structure (in the latest iteration of <strong>RLN</strong>, we use an Incremental Merkle Tree algorithm for gas efficiency, but the Merkle tree does not have to be on-chain), which stores the identity commitments of the registered users. Upon successful registration, the user's identity commitment is stored in a leaf of the Merkle tree, and an index is given to them, representing their position in the tree.</p>
<h3 id="user-interaction"><a class="header" href="#user-interaction">User interaction</a></h3>
<p>For each interaction that the user wants to make with the application, the user must generate a zero-knowledge proof ensuring that their identity commitment is part of the membership Merkle tree.</p>
<p>There are a number of use-cases for <strong>RLN</strong>, such as voting applications (1 vote per election), chat (one message per second), and rate-limiting cache access (CDN denial of service protection). The verifier can be a server for centralized applications or the other users for decentralized applications.</p>
<p>The general anti-spam rule is usually in the form of:
<code>Users must not make more than X interactions per epoch.</code></p>
<p>The epoch can be translated as a time interval of <code>Y</code> units of time unit <code>Z.</code> For simplicity's sake, let's transform the rule into: `Users must not send more than one message per second.</p>
<p>We can implement this using <code>Shamir's Secret Sharing</code> scheme (<a href="./sss.html"><em>read more</em></a>), which allows you to split a secret (f.e. to <code>n</code> parts) and recover it when any <code>m' of </code>n<code> parts ('m &lt;= n</code>) are presented.</p>
<p>Thus, users have to split their <code>secret_key</code> into <code>n</code> parts, and for every interaction, they have to reveal the new part of the <code>secret_key.</code> So, in addition to proving the membership in the <code>Merkle Tree,</code> users have to prove that the revealed part is truly the part of their <code>secret_key.</code></p>
<p>If they make more interactions than allowed per epoch, their secret key can be fully reconstructed.</p>
<h3 id="user-removal-slashing"><a class="header" href="#user-removal-slashing">User removal (slashing)</a></h3>
<p>The final property of the <strong>RLN</strong> mechanism is that it allows for the users to be removed from the membership tree by anyone that knows their secret key. The membership tree contains the identity commitments of all registered users. Users' identity commitment is derived from their secret key, and the secret key of the user is only revealed in a spam event (except for the scenarios where the original users want to remove themselves, which they can always do because they know their secret key). When an economic stake is present, the <strong>RLN</strong> mechanism can be implemented in a way that the spammer's stake is sent to the first user that correctly reports the spammer by providing the reconstructed secret key of the spammer as proof.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="under-the-hood"><a class="header" href="#under-the-hood">Under the hood</a></h1>
<p>This section provides deep and technical <strong>RLN</strong> overview.</p>
<p>We'll discuss:</p>
<ul>
<li>Technical side of <strong>RLN</strong> (specification demo)</li>
<li>How circuits are implemented</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="technical-side-of-rln"><a class="header" href="#technical-side-of-rln">Technical side of RLN</a></h1>
<p><em>This topic is a less strict version of specifications. If you want a more formal description, you can find specs in the <a href="./references.html">references</a>. Also, if you're unfamiliar with Shamir's Secret Sharing scheme, you can <a href="./sss.html">read it here</a>.</em></p>
<hr />
<p><strong>RLN</strong> consists of three parts:</p>
<ul>
<li>User registration</li>
<li>User interaction (signaling)</li>
<li>User removal (slashing) - additional part</li>
</ul>
<p>Well, let's discuss them.</p>
<h2 id="user-registration-1"><a class="header" href="#user-registration-1">User registration</a></h2>
<p>The first part of <strong>RLN</strong> is registration. There is nothing special in <strong>RLN</strong> registration; it's almost the same process as in other protocols/apps with anonymous environments: we need to create a Merkle Tree, and every participant must submit a <code>commitment</code> and place it in the Merkle Tree, and after that to interact with the app every participant will create a zkProof's, that they are a <em>member of the tree</em> (we use an <em>Incremental Merkle Tree</em>, as it more <em>GAS efficient</em>).</p>
<p>So, each member generates a secret key, denoted by <code>a_0</code>. Identity commitment <code>q</code> is the hash (Poseidon) of the secret key: <code>q = Poseidon(a_0)</code>.</p>
<p><strong>RLN</strong> wouldn't work if there were no punishment for spam; that's why to become a member, a user has to register and provide something at stake. So, whoever has our <code>a_0</code> can &quot;slash&quot; us. </p>
<p>The slight difference is that we must enable a <em>secret sharing</em> scheme (to split the <code>commitment</code> into parts). We need to come up with a polynomial. For simplicity we use linear polynomial (e.g. <code>f(x) = kx + b</code>). Therefore, with two points, we can reconstruct the polynomial and recover the secret. </p>
<p>Our polynomial will be: <code>A(x) = (a_0, a_1)</code>, where <code>a_1 = Poseidon(a_0, epoch)</code>.</p>
<p>Less strict: <code>A(x) = a_1 * x + a_0</code>.</p>
<p><code>epoch</code> is a simple identifier (also called <em>external nullifier</em>). And each epoch, there is a polynomial with new <code>a_1</code> and the same <code>a_0</code>. </p>
<h2 id="signalling"><a class="header" href="#signalling">Signalling</a></h2>
<p>Now that the user is registered, he wants to interact with the system. Imagine that the system is an <em>anonymous chat</em> and the interaction is the sending of messages.
So, to send a message user have to come up with <em>share</em> - the point <code>(x, y)</code> on her polynomial.
We denote: <code>x = Poseidon(message), and y = A(x)</code>. </p>
<p>Thus, if the same epoch user sends more than one message, their polynomial and, therefore, their secret (<code>a_0</code>) can be recovered.</p>
<p>Of course, we somehow must prove that our <code>share = (x, y)</code> is valid (that this is really a point on our <code>polynomial = A(x)</code>), as well as we must prove other things are valid too, that's why we use zkSNARK. An explanation of the zk-circuits can be found in the next topic.</p>
<h2 id="slashing"><a class="header" href="#slashing">Slashing</a></h2>
<p>As it's been said, if a user sends more than one message, everyone else will be able to recover his secret, slash them and take their stake.</p>
<h2 id="some-important-notes"><a class="header" href="#some-important-notes">Some important notes</a></h2>
<p>There are also <code>nullifier</code> and <code>rln_identifier</code>, which can be found in the <strong>RLN</strong> protocol/circuits.</p>
<p>So, <code>rln_identifier</code> is just a random value that's unique per <strong>RLN</strong> app. It's used for additional cross-application security - to protect the user secrets from being compromised if they use the same credentials across different <strong>RLN</strong> apps. If <code>rln_identifier</code> is not present, the user uses the same credentials and sends a message in two different <strong>RLN</strong> apps using the same epoch, then their secret key can be revealed. Adding the <code>rln_identifier</code> field, we obscure the nullifier, so this kind of attack cannot happen. The only kind of attack that is possible is if we have an entity with a global view of all messages, and they try to brute-force different combinations of x and y shares for different nullifiers.</p>
<p>Now, imagine there are a lot of users sending messages, and after each received message, we need to check if any member can be slashed. To do this, we can use all combinations of received <em>shares</em> and try to recover the polynomial, but this is a naive and non-optimal approach. Suppose we have a mechanism that will tell us about the connection between a person and their messages while not revealing their identity. In that case, we can solve this without brute-forcing all possibilities by using a public <code>nullifier</code> (<code>nullifier = Poseidon(a_1, rln_identifier)</code>), so if a user sends more than one message, it will be immediately visible to everyone.</p>
<p>Also, in our example (and <a href="https://github.com/njofce/zk-chat">zk-chat</a> implementation), we use linear polynomial, but <a href="sss.html">SSS</a> allows us to use various degree polynomials; therefore we can implement a protocol, where more than one signal (message) can be sent in per epoch. </p>
<p>To learn more, check out the <a href="https://hackmd.io/7GR5Vi28Rz2EpEmLK0E0Aw?view">specification</a>; there are also <a href="https://github.com/privacy-scaling-explorations/rln/tree/master/circuits">circuits</a> implemented for various degree polynomials too.</p>
<h1 id="diagram"><a class="header" href="#diagram">Diagram</a></h1>
<pre class="mermaid">flowchart TB
subgraph Generate Secret Key
random0(Random 32 bytes) --&gt; a_0(Secret Key)
random1(Random 32 bytes) --&gt; a_0
end
subgraph RLN
subgraph Identity Commitment
a_0 --&gt; h0(Poseidon Hash)
h0 --&gt; q(Identity Commitment)
end
subgraph Calculate Internal Nullifier
a_0 --&gt; h1(Poseidon Hash)
epoch(Epoch) --&gt; h1
h1 --&gt; a_1
rln_identifier(RLN Identifier) --&gt; h2(Poseidon Hash)
a_1 --&gt; h2
h2 --&gt; nullifier(RLN Internal Nullifier)
end
subgraph Merkle Tree
q --&gt; merkle_tree_inclusion_proof(Merkle Tree Inclusion Proof)
merkle_tree_inclusion_proof --&gt; root(ZKP of Merkle Tree Root)
end
subgraph Shamirs Secret Scheme
a_0 --&gt; plus(+)
a_1 --&gt; multiply(*)
x(Hashed Messaage) --&gt; multiply
multiply --&gt; plus
plus --&gt; share_y
end
nullifier --&gt; proof(ZKP)
root --&gt; proof
share_y --&gt; proof
end
</pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="circuits"><a class="header" href="#circuits">Circuits</a></h1>
<p><em><a href="https://vitalik.ca/general/2022/06/15/using_snarks.html">zkSNARK</a> is used in the <strong>RLN</strong> core. Therefore, we need to represent the protocol in R1CS (as we use Groth16). Circom DSL was chosen for this. This section provides an explanation of <strong>RLN</strong> circuits.</em></p>
<hr />
<p><strong>RLN</strong> circuits implement the logic described in <a href="./protocol_spec.html">previous topic</a>.</p>
<h2 id="merkle-tree-circuit"><a class="header" href="#merkle-tree-circuit">Merkle Tree circuit</a></h2>
<p>One of the key component of <strong>RLN</strong> is <em>Incremental Merkle Tree</em>.
Let's look at the <a href="https://github.com/privacy-scaling-explorations/rln/blob/master/circuits/incrementalMerkleTree.circom">implementation</a>.</p>
<p>At the beginning of the file we denote that we use second version of Circom and include two helper <em>zk-gadgets</em>:</p>
<pre><code>pragma circom 2.0.0;
include &quot;../node_modules/circomlib/circuits/poseidon.circom&quot;;
include &quot;../node_modules/circomlib/circuits/mux1.circom&quot;;
</code></pre>
<p><em>Poseidon</em> gadget is just the implementation of <em>Poseidon</em> hash function; <em>mux1</em> gadget will be described later.</p>
<p>Next, we can see two implemented gadgets:</p>
<pre><code>template PoseidonHashT3() {
var nInputs = 2;
signal input inputs[nInputs];
signal output out;
component hasher = Poseidon(nInputs);
for (var i = 0; i &lt; nInputs; i ++) {
hasher.inputs[i] &lt;== inputs[i];
}
out &lt;== hasher.out;
}
template HashLeftRight() {
signal input left;
signal input right;
signal output hash;
component hasher = PoseidonHashT3();
left ==&gt; hasher.inputs[0];
right ==&gt; hasher.inputs[1];
hash &lt;== hasher.out;
}
</code></pre>
<p>These are helper gadgets to make the code more clean. <em>Poseidon</em> gadget is implemented with the ability to take a different number of arguments. We use <code>PoseidonHashT3()</code> to initialize it like a function with two arguments. And <code>HashLeftRight</code> use <code>PoseidonHashT3</code> in more &quot;readable&quot; way: it takes two inputs <code>left</code> and <code>right</code> and outputs the result of calculation.</p>
<p>Next comes the core of Merkle Tree gadget:</p>
<pre><code>template MerkleTreeInclusionProof(n_levels) {
signal input leaf;
signal input path_index[n_levels];
signal input path_elements[n_levels][1];
signal output root;
component hashers[n_levels];
component mux[n_levels];
signal levelHashes[n_levels + 1];
levelHashes[0] &lt;== leaf;
...
root &lt;== levelHashes[n_levels];
}
</code></pre>
<p>Here we have three inputs: <code>leaf</code>, <code>path_index</code> and <code>path_elements</code></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="uses"><a class="header" href="#uses">Uses</a></h1>
<h2 id="zk-chat"><a class="header" href="#zk-chat">zk-chat</a></h2>
<p>https://github.com/njofce/zk-chat</p>
<h2 id=""><a class="header" href="#"></a></h2>
<div style="break-before: page; page-break-before: always;"></div><h1 id="how-to-use"><a class="header" href="#how-to-use">How to use</a></h1>
<p>This section provides information on how to use <strong>RLN</strong> in your project:</p>
<ul>
<li>JavaScript RLN (for <a href="https://github.com/Rate-Limiting-Nullifier/rlnjs">rln-js</a>)</li>
<li>Rust RLN (for <a href="https://github.com/vacp2p/zerokit/tree/master/rln">zerokit-rln</a>)</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="theory"><a class="header" href="#theory">Theory</a></h1>
<p>This section provides theoretical information that underpins <strong>RLN</strong>.</p>
<p>Here we'll discuss:</p>
<ul>
<li>Shamir's Secret Sharing</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="shamirs-secret-sharing-scheme"><a class="header" href="#shamirs-secret-sharing-scheme">Shamir's Secret Sharing Scheme</a></h1>
<p><em>Shamirs Secret Sharing</em> allows to split the secret to <code>n</code> parts and restore it upon presentation any <code>m</code> parts (<code>m &lt;= n</code>)</p>
<p><a href="https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing">Sharmir's Secret Sharing wikipedia</a> is a good reference to understand the concept.</p>
<p>Reconstruction 1: https://github.com/akinovak/semaphore-lib/blob/5b9bb3210192c8e508eced7ef6579fd56e635ed0/src/rln.ts#L31</p>
<pre><code class="language-js">retrievePrivateKey(x1: bigint, x2:bigint, y1:bigint, y2:bigint): Buffer | ArrayBuffer {
const slope = Fq.div(Fq.sub(y2, y1), Fq.sub(x2, x1))
const privateKey = Fq.sub(y1, Fq.mul(slope, x1));
return bigintConversion.bigintToBuf(Fq.normalize(privateKey));
}
</code></pre>
<p>Reconstruction 2: https://github.com/akinovak/semaphore-lib/blob/rln_signature_changes/test/index.ts#L250</p>
<pre><code class="language-js">async function testRlnSlashingSimulation() {
RLN.setHasher('poseidon');
const identity = RLN.genIdentity();
const privateKey = identity.keypair.privKey;
const leafIndex = 3;
const idCommitments: Array&lt;any&gt; = [];
for (let i=0; i&lt;leafIndex;i++) {
const tmpIdentity = OrdinarySemaphore.genIdentity();
const tmpCommitment: any = RLN.genIdentityCommitment(identity.keypair.privKey);
idCommitments.push(tmpCommitment);
}
idCommitments.push(RLN.genIdentityCommitment(privateKey))
const signal = 'hey hey';
const x1: bigint = OrdinarySemaphore.genSignalHash(signal);
const epoch: string = OrdinarySemaphore.genExternalNullifier('test-epoch');
const vkeyPath: string = path.join('./rln-zkeyFiles', 'verification_key.json');
const vKey = JSON.parse(fs.readFileSync(vkeyPath, 'utf-8'));
const wasmFilePath: string = path.join('./rln-zkeyFiles', 'rln.wasm');
const finalZkeyPath: string = path.join('./rln-zkeyFiles', 'rln_final.zkey');
const witnessData: IWitnessData = await RLN.genProofFromIdentityCommitments(privateKey, epoch, signal, wasmFilePath, finalZkeyPath, idCommitments, 15, BigInt(0), 2);
const a1 = RLN.calculateA1(privateKey, epoch);
const y1 = RLN.calculateY(a1, privateKey, x1);
const nullifier = RLN.genNullifier(a1);
const pubSignals = [y1, witnessData.root, nullifier, x1, epoch];
let res = await RLN.verifyProof(vKey, { proof: witnessData.fullProof.proof, publicSignals: pubSignals })
if (res === true) {
console.log(&quot;Verification OK&quot;);
} else {
console.log(&quot;Invalid proof&quot;);
return;
}
const signalSpam = &quot;let's try spamming&quot;;
const x2: bigint = OrdinarySemaphore.genSignalHash(signalSpam);
const witnessDataSpam: IWitnessData = await RLN.genProofFromIdentityCommitments(privateKey, epoch, signalSpam, wasmFilePath, finalZkeyPath, idCommitments, 15, BigInt(0), 2);
const a1Spam = RLN.calculateA1(privateKey, epoch);
const y2 = RLN.calculateY(a1Spam, privateKey, x2);
const nullifierSpam = RLN.genNullifier(a1Spam);
const pubSignalsSpam = [y2, witnessDataSpam.root, nullifierSpam, x2, epoch];
res = await RLN.verifyProof(vKey, { proof: witnessDataSpam.fullProof.proof, publicSignals: pubSignalsSpam })
if (res === true) {
console.log(&quot;Spam proof Verification OK&quot;);
} else {
console.log(&quot;Invalid proof&quot;);
return;
}
const identitySecret = RLN.calculateIdentitySecret(privateKey);
const retreivedPkey = bigintConversion.bufToBigint(RLN.retrievePrivateKey(x1, x2, y1, y2));
if(Fq.eq(identitySecret, retreivedPkey)) {
console.log(&quot;PK successfully reconstructed&quot;);
} else {
console.log(&quot;Error while reconstructing private key&quot;)
}
// TODO: Add removal from tree example
}
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="appendix"><a class="header" href="#appendix">Appendix</a></h1>
<p>The following sections contain reference material you may find useful:</p>
<ul>
<li>Terminology</li>
<li>References</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="terminology"><a class="header" href="#terminology">Terminology</a></h1>
<div class="table-wrapper"><table><thead><tr><th>Term</th><th>Description</th></tr></thead><tbody>
<tr><td>zkSNARK</td><td>Proof construction where one can prove possession of certain information, e.g. a secret key, without revealing that information, and without any interaction between the prover and verifier.</td></tr>
<tr><td>Stake</td><td>Financial or social stake required for registering in the RLN applications. Common stake examples are: locking cryptocurrency (financial), linking reputable social identity.</td></tr>
<tr><td>Identity secret</td><td>An array of two unique random components (identity nullifier and identity trapdoor), which must be kept private by the user. Secret hash and identity commitment are derived from this array.</td></tr>
<tr><td>Identity nullifier</td><td>Random 32 byte value used as component for identity secret generation.</td></tr>
<tr><td>Identity trapdoor</td><td>Random 32 byte value used as component for identity secret generation.</td></tr>
<tr><td>Identity secret hash</td><td>The hash of the identity secret, obtained using the Poseidon hash function. It is used for deriving the identity commitment of the user, and as a private input for zk proof generation. The secret hash should be kept private by the user.</td></tr>
<tr><td>Identity commitment</td><td>Hash obtained from the Identity secret hash by using the poseidon hash function. It is used by the users for registering in the protocol.</td></tr>
<tr><td>Signal</td><td>The message generated by a user. It is an arbitrary bit string that may represent a chat message, a URL request, protobuf message, etc.</td></tr>
<tr><td>Signal hash</td><td>Keccak hash of the signal, used as an input in the RLN circuit.</td></tr>
<tr><td>RLN Identifier</td><td>Random finite field value unique per RLN app. It is used for additional cross-application security. The role of the RLN identifier is protection of the user secrets being compromised if signals are being generated with the same credentials at different apps.</td></tr>
<tr><td>RLN membership tree</td><td>Merkle tree data structure, filled with identity commitments of the users. Serves as a data structure that ensures user registrations.</td></tr>
<tr><td>Merkle proof</td><td>Proof that a user is member of the RLN membership tree.</td></tr>
<tr><td>zk-gadget</td><td></td></tr>
</tbody></table>
</div><div style="break-before: page; page-break-before: always;"></div><h1 id="references"><a class="header" href="#references">References</a></h1>
<ul>
<li>
<p><a href="https://ethresear.ch/t/semaphore-rln-rate-limiting-nullifier-for-spam-prevention-in-anonymous-p2p-setting/5009">First Proposal/Idea of RLN by Barry WhiteHat</a></p>
</li>
<li>
<p><a href="https://medium.com/privacy-scaling-explorations/rate-limiting-nullifier-a-spam-protection-mechanism-for-anonymous-environments-bbe4006a57d">RLN Overview by Blagoj</a></p>
</li>
<li>
<p><a href="https://hackmd.io/@aeAuSD7mSCKofwwx445eAQ/BJcfDByNF">Demo RLN Spec</a></p>
</li>
<li>
<p><a href="https://rfc.vac.dev/spec/32/">VAC RLN Spec</a></p>
</li>
<li>
<p><a href="https://vitalik.ca/general/2016/12/10/qap.html">Understand zkSNARK</a></p>
</li>
<li>
<p><a href="https://docs.circom.io/">Circom Docs</a></p>
</li>
<li>
<p><a href="https://github.com/Rate-Limiting-Nullifier/rlnjs">rln-js</a></p>
</li>
<li>
<p><a href="https://github.com/vacp2p/zerokit">zerokit-rln</a></p>
</li>
<li>
<p><a href="https://arxiv.org/pdf/2105.06009v1.pdf">Incremental Merkle Tree paper</a></p>
</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
<script type="text/javascript" src="mermaid.min.js"></script>
<script type="text/javascript" src="mermaid-init.js"></script>
<script type="text/javascript">
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>