Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a simple HTML page to compute proof size and time from parameters #22

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/alba.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.row {
display: flex;
flex-wrap: wrap;
flex-direction: row;
padding: 0.2em;
}

.col {
display: flex;
flex-direction: column;
padding: 0.2em;
}

.col-md-2 {
display: flex;
flex: 2;
}

.col-md-3 {
display: flex;
flex: 0 0 auto;
}

.col-md-4 {
display: flex;
flex: 4;
}

.col-md-8 {
display: flex;
flex: 8;
}

label {
width: 100%;
height: 1.4em;
display: block;
}

input {
text-align: right;
font-family: monospace;
}

.help {
display: none;
}
132 changes: 132 additions & 0 deletions docs/alba.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
document.addEventListener('DOMContentLoaded', () => {

const lam = 128;

const node = document.getElementById('main_chart');
const n_p = document.getElementById('n_p');
const n_f = document.getElementById('n_f');
const item = document.getElementById('item');
const proof_size = document.getElementById('proof_size');
const n = document.getElementById('n');
const proof_proba = document.getElementById('proof_proba');

function U(n_p, n_f) {
return Math.ceil((lam + Math.log2(lam) + 5 - Math.log2(Math.log2(Math.E))) / Math.log2(n_p / n_f));
};

function probabilityOfProof(u, n_p, n_f, x) {
// from https://github.com/cardano-scaling/alba/discussions/17

S_1_bot = n_p / (17 ** 2 / (9 * Math.log2(Math.E)) * u ** 2) - 7 < 1;
S_2_bot = n_p / (17 ** 2 / (9 * Math.log2(Math.E)) * u ** 2) - 2 < 1;

if (!S_1_bot && !S_2_bot) {
throw Error("S_1_bot or S_2_bot");
}
Comment on lines +23 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Currently, this will just silently log this error to the console, potentially confusing users. Maybe use alert or sth like that?
  • One could also implement the remaining two cases in the proof of Corollary 3 in order to handle large n_p values (where this error is currently thrown).

(Doesn't necessarily have to happen in this PR)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I copy/pasted your code. I think it's good enough for now but obviously throwing exceptions in user-facing code is not great. I don't like alert() but can find a good substitute.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you throw an error and not update the d/q/r parameters depending on S1 and S2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for this simulation we can just use the parameters for the "small case". They in fact work for all cases, it's just, the prover will not have a good worst-case running time. Then we don't need to worry about S_1_bot, etc.

We can borrow the math code from Raphael's implementation.
https://github.com/cardano-scaling/alba/pull/33/files#diff-b0bad697f839908964cc4e445cd9c87a76b4a5f84da347b24a426b61b7d09322R127


const d = Math.ceil(32 * Math.log(12) * u);
const q = 2 * Math.log(12) / d;
const r = Math.ceil(lam);

return Math.min(r * (x / n_p) ** u * d * q, 1);
};

// Returns updated labels and data
function updatedData() {
const data = [];
const labels = [];
const n_p_v = Number(n_p.value);
const n_f_v = Number(n_f.value);
const u = U(n_p_v, n_f_v);
// number of points
const length = (n_p_v - n_f_v) / 10;

for (var i = 0; i < length; i++) {
const y = (i * 10) + n_f_v;
labels.push(y);
data.push(probabilityOfProof(u, n_p_v, n_f_v, y));
}

return [labels, data];
}

const [labels, data] = updatedData();

// throughput chart
const chart = new Chart(node, {
title: {
text: "Probabiliy of making a proof"
},
data: {
// N.B.: Make sure colors are picked from an inclusive color palette.
// See for instance: https://medium.com/@allieofisher/inclusive-color-palettes-for-the-web-bbfe8cf2410e
labels,
datasets: [
{
type: 'line',
label: "proof probability",
data,
function: probabilityOfProof,
backgroundColor: '#6FDE6E',
borderColor: '#6FDE6E',
fill: false
}
]
},
options: {
scales: {
y: {
type: 'logarithmic',
min: 0,
ticks: {
stepSize: 0.0001,
autoSkip: true,
callback: (val) => (val.toExponential(1))
}
},
x: {
type: 'linear',
title: {
text: 'n',
display: true
},
}
}
}
});

function updateProofSize() {
const n_p_v = Number(n_p.value);
const n_f_v = Number(n_f.value);
const single = Number(item.value);
const u = U(n_p_v, n_f_v);
proof_size.value = u * single;
}

function updateProofProba() {
const n_p_v = Number(n_p.value);
const n_f_v = Number(n_f.value);
const n_v = Number(n.value);
const u = U(n_p_v, n_f_v);
const proba = probabilityOfProof(u, n_p_v, n_f_v, n_v);
// expected prover time is n_p + O (u^2), we neglect the n_p part here
This conversation was marked as resolved.
Show resolved Hide resolved
proof_proba.value = proba.toExponential(4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused about what this does.

}

function updateChart() {
const [labels, data] = updatedData();
chart.data.labels = labels;
chart.data.datasets[0].data = data;
chart.update();
updateProofSize();
updateProofProba();
};

n_p.addEventListener('change', updateChart);
n_f.addEventListener('change', updateChart);
item.addEventListener('change', updateChart);
n.addEventListener('change', updateProofProba);

updateProofSize();
updateProofProba();
});
2 changes: 2 additions & 0 deletions docs/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ title = "ALBA"

[output.html]
mathjax-support = true
additional-css = ["alba.css"]
additional-js = ["alba.js", "chart.js"]
20 changes: 20 additions & 0 deletions docs/chart.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Summary

- [Introduction](./intro.md)
- [How it works?](./algorithm.md)
- [Benchmarks](./benchmarks.md)
- [Simulation](./simulation.md)
39 changes: 39 additions & 0 deletions docs/src/algorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Proving

Here is an alternative graphical representation of how ALBA certificates are produced, using the basic construction. We start with a set denote \\(S_p\\) of elements such that each is _unique_ and its availability can be asserted by a verifier through some predicate (called \\(R\\)) in the paper.

> **Note**: The \\(S_p\\) set can be smaller than the expected maximum number of elements we want to build a proof on, with a lower bound on \\(n_f\\) the number of expected honest elements.

![Initial set](proving-0.jpg)

The first step is to construct the cartesian product of each element of \\(S_p\\), here called \\(a\\) through \\(k\\), with all the integers lower than some parameter \\(d\\), yielding a set of size \\(\|S_p\| \times d\\)

![Initial tupling](proving-1.jpg)

From this set, we pseudo-randomly select \\(n_p\\) pairs using a "random oracle" \\(\mathbb{H}\\). \\(\mathbb{H}\\) is effectively just a hash function and to select the adequate number of pairs we pick those with a hash value modulo \\(n_p\\) equals to 0, yielding a set of size roughly \\(n_p\\) of pair of an integer and some item in \\(S_p\\).

> **Note**: The ratio \\(n_p / n_f\\) is a key factor when setting the needed length of the proof.

![Selecting pairs](proving-2.jpg)

We then expand the set again, creating triples of an integer and 2 elements from \\(S_p\\) through a cartesian product between the selected pairs and the initial set, yielding a set of size \\(\|S_p\| \times n_p\\).

![Expanding set](proving-3.jpg)

And we again pseudo-randomly select \\(n_p\\) elements from this new set using the same function \\(\mathbb{H}\\), yielding a set of triples of approximate size \\(n_p\\)

![Selecting triples](proving-4.jpg)

This process is iterated \\(n\\) times according to the protocol parameters (see the paper or [the code](https://github.com/cardano-scaling/alba/blob/8893e4b2de2cb9d74f135ec4535fbfca6acf83d3/src/ALBA.hs#L162) for details on how these parameters are computed), yielding a set of (roughly) \\(n_p\\) tuples of length \\(n+1\\) where each tuple is some "random" integer along with \\(n\\) items from \\(S_p\\).

The last stage consists in applying a function \\(\mathbb{H}_1\\) to select _one_ element from this set which is the final _proof_.

![Selecting proof](proving-5.jpg)

## Verifying

Verifying a given proof \\(P\\) is straightforward. Given the known share of honest items \\(n_p\\) and a security parameter \\(\lambda\\) (set at 128 in the paper) controlling the exponentially decaying probability a proof with dishonest items can be constructed, the verifier needs to check:

* The sequence of hashes from the items in the proof are all 0 modulo \\(n_p\\),
* The value of \\(H_1\\) for the whole sequence is 0 modulo some derived parameter \\(q\\),
* And of course that each item in the sequence is valid w.r.t. the predicate \\(R\\).
45 changes: 9 additions & 36 deletions docs/src/intro.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,17 @@
# Introduction

_Approximate Lower Bound Arguments_ are a form of cryptographic certificates that allow a _prover_ to convince a _verifier_ they know some large set of elements by providing only a small subset of those elements. This excellent [X thread](https://x.com/Quantumplation/status/1783188333046255997) provides a good intuition on why ALBAs are useful and how they work, and details of the theory behind this construction are beyond the scope of this introduction and can be found in the paper.
## Definition

### Proving
_Approximate Lower Bound Arguments_ are a form of cryptographic certificates that allow a _prover_ to convince a _verifier_ they know some set of elements by providing only a small subset of those elements. More formally, given some set of elements, and some bounds \\(n_p\\) and \\(n_f\\), where \\(n_p > n_f\\), about the size of this set, ALBA provides an algorithm that allows to build a _proof_ they know _at least_ \\(n_f\\) elements, where the proof size is a constant value derived from the \\(\frac{n_p}{n_f}\\) ratio.

Here is an alternative graphical representation of how ALBA certificates are produced, using the basic construction. We start with a set denote \\(S_p\\) of elements such that each is _unique_ and its availability can be asserted by a verifier through some predicate (called \\(R\\)) in the paper.
The algorithm provides the following guarantees, given the prover has \\(S_p\\) elements and a security parameter \\(\lambda\\):

> **Note**: The \\(S_p\\) set can be smaller than the expected maximum number of elements we want to build a proof on, with a lower bound on \\(n_p\\) the number of expected honest elements.
* if \\(|S_p| \geq n_p\\) then the prover has a probability lower than \\(2^{-\lambda}\\) of _failure_ to build a proof, or in other words they are sure to find one,
* if \\(|S_p| \leq n_f\\) then the prover has a probability lower than \\(2^{-\lambda}\\) of _success_ to build a proof,
* in between those 2 bounds, the probability of being able to build a proof drops exponentially (see our [simulation](./simulation.md) page for a graphical exploration of this probability).

![Initial set](proving-0.jpg)
Details of the theory behind this construction are beyond the scope of this introduction and can be found in the paper.

The first step is to construct the cartesian product of each element of \\(S_p\\), here called \\(a\\) through \\(k\\), with all the integers lower than some parameter \\(d\\), yielding a set of size \\(\|S_p\| \times d\\)
## Usefulness

![Initial tupling](proving-1.jpg)

From this set, we pseudo-randomly select \\(n_p\\) pairs using a "random oracle" \\(\mathbb{H}\\). \\(\mathbb{H}\\) is effectively just a hash function and to select the adequate number of pairs we pick those with a hash value modulo \\(n_p\\) equals to 0, yielding a set of size roughly \\(n_p\\) of pair of an integer and some item in \\(S_p\\).

> **Note**: The ratio \\(n_p / n_f\\) is a key factor when setting the needed length of the proof.

![Selecting pairs](proving-2.jpg)

We then expand the set again, creating triples of an integer and 2 elements from \\(S_p\\) through a cartesian product between the selected pairs and the initial set, yielding a set of size \\(\|S_p\| \times n_p\\).

![Expanding set](proving-3.jpg)

And we again pseudo-randomly select \\(n_p\\) elements from this new set using the same function \\(\mathbb{H}\\), yielding a set of triples of approximate size \\(n_p\\)

![Selecting triples](proving-4.jpg)

This process is iterated \\(n\\) times according to the protocol parameters (see the paper or [the code](https://github.com/cardano-scaling/alba/blob/8893e4b2de2cb9d74f135ec4535fbfca6acf83d3/src/ALBA.hs#L162) for details on how these parameters are computed), yielding a set of (roughly) \\(n_p\\) tuples of length \\(n+1\\) where each tuple is some "random" integer along with \\(n\\) items from \\(S_p\\).

The last stage consists in applying a function \\(\mathbb{H}_1\\) to select _one_ element from this set which is the final _proof_.

![Selecting proof](proving-5.jpg)


### Verifying

[Verifying](https://github.com/cardano-scaling/alba/blob/8893e4b2de2cb9d74f135ec4535fbfca6acf83d3/src/ALBA.hs#L259) a given proof \\(P\\) is straightforward. Given the known share of honest items \\(n_p\\) and a security parameter \\(\lambda\\) (set at 128 in the paper) controlling the exponentially decaying probability a proof with dishonest items can be constructed, the verifier needs to check:

* The sequence of hashes from the items in the proof are all 0 modulo \\(n_p\\),
* The value of \\(H_1\\) for the whole sequence is 0 modulo some derived parameter \\(q\\),
* And of course that each item in the sequence is valid w.r.t. the predicate \\(R\\).
This excellent [X thread](https://x.com/Quantumplation/status/1783188333046255997) provides a good intuition on why ALBAs is useful and how it works.
83 changes: 83 additions & 0 deletions docs/src/simulation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# ALBA Parameters Simulation

ALBA provides strong security guarantees about the capability to generate a proof given some number of items \\(n\\):

* a honest prover with at least \\(n_p\\) items has a probability of
failing to generate a proof lower than \\(2^{-128}\\),
* symetrically, a (dishonest) prover with at most \\(n_f\\) items
has probability of succeeding to generate a proof lower than
\\(2^{-128}\\).

This page provides a simple and user-friendly way of estimating how
various ALBA parameters impact the size of the proof and probability
of having a proof, given a number of items \\(n\\) varying between
the two parameters' bounds. It is expected to be a quick way to
estimate what would be "good" values for those parameters in a
specific context, depending on security requirements and expected
adversarial share.

One can define:

* \\(n_p\\): The size of the _honest set_ of items.
* \\(n_f\\): The size of the _faulty set_ of items. \\(n_f\\) must be lower than \\(n_p\\).
* Item size: The size of a single item, this is used to compute the full size of a proof.
* \\(n\\): The number of items actually available to an adversary, or put differently, how much adversarial stake should we expect.

From these parameters, we display a chart showing the probability of
an adversary being able to compute a single valid proof, given some
This conversation was marked as resolved.
Show resolved Hide resolved
number of items \\(n\\) available between \\(n_p\\) and
\\(n_f\\). The formula used, initially proposed in this [GitHub
discussion](https://github.com/cardano-scaling/alba/discussions/17)
is:

\\[
r \cdot {\left( \frac{n}{n_p} \right)}^u\cdot qd
\\]

where _u_, _r_, _q_, and _d_ are defined in the aforementioned
section of the paper. Note the vertical axis' scale is logarithmic.

We also display two specific values:

* the size of the proof for the given \\(n_p/n_f\\) ratio and individual
item size, which is given by the formula from section 3.2.2,
Corollary 3, from the [ALBA
paper](https://iohk.io/en/research/library/papers/approximate-lower-bound-arguments/),
* the probability of having a proof for some specific number of \\(n\\) (computed.

<div class="col col-md-8">
<div class="row">
<div id="chart" class="row" style="width: 100%; margin: auto;" >
<canvas id="main_chart"></canvas>
</div>
</div>
<div class="row g-3" style="margin: auto;">
<div class="col col-md-3">
<label for="n_p" class="form-label">\(n_p\)<span class="help" data-bs-toggle="tooltip" title="Expected number of honest set of items.">?</span></label>
<input type="number" step="10" class="form-control" id="n_p" value="800">
</div>
<div class="col col-md-3">
<label for="n_f" class="form-label">\(n_f\)<span class="help" data-bs-toggle="tooltip" title="Expected number of faulty set of items.">?</span></label>
<input type="number" step="10" class="form-control" id="n_f" value="400">
</div>
<div class="col col-md-3">
<label for="item" class="form-label">Item size<span class="help" data-bs-toggle="tooltip" title="Size of a single item.">?</span></label>
<input type="number" step="10" class="form-control" id="item" value="600">
</div>
<div class="col col-md-3">
<label for="proof_size" class="form-label">Proof size<span class="help" data-bs-toggle="tooltip" title="Total size of proof.">?</span></label>
<input type="number" class="form-control" id="proof_size" disabled>
</div>
</div>
<div class="row g-3" style="width: 95%; margin: auto;">
<div class="col col-md-3">
<label for="n" class="form-label">\(n\)<span class="help" data-bs-toggle="tooltip" title="Number of items actually available.">?</span></label>
<input type="number" step="10" class="form-control" id="n" value="700">
</div>
<div class="col col-md-3">
<label for="proof_proba" class="form-label">Proof probability<span class="help" data-bs-toggle="tooltip" title="Probability of having a proof for some number of items.">?</span></label>
<input type="number" class="form-control" id="proof_proba" disabled>
</div>
<div class="col-md-3"/>
</div>
</div>
Loading