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

docs: split primitives into own files and update to latest changes #1486

Merged
merged 1 commit into from
May 22, 2024
Merged
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
2 changes: 1 addition & 1 deletion website/versioned_docs/version-v1.2/circuits.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ The hash is computed using the `sha256` Solidity function and is then subject to
| 4th 50 bits | `numSignUps` |
| 5th 50 bits | `batchStartIndex` |

`numSignUps`, a value provided by the contract, is the number of users who have signed up. This is one less than the number of leaves inserted in the state tree (since the 0th state leaf is a [blank state leaf](/docs/core-concepts/primitives#state-leaf)). `batchStartIndex` is the ballot tree index at which the batch begins.
`numSignUps`, a value provided by the contract, is the number of users who have signed up. This is one less than the number of leaves inserted in the state tree (since the 0th state leaf is a blank state leaf). `batchStartIndex` is the ballot tree index at which the batch begins.

For instance, if `numSignUps` is 25 and the batch index is `5`, and all other values are 0, the following is the `packedVals` representation in hexadecimal:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: MACI Ballot
description: A short introduction of the main primitives used by MACI
sidebar_label: MACI Ballot
sidebar_position: 4
---

A Ballot represents a particular user's votes in a poll, as well as their next valid nonce. It is akin to a voting slip, which belongs to only one voter and contains a list of their choices.

| Symbol | Name | Comments |
| --------- | -------------------------- | -------------------------------------------------------------------------- |
| $blt_{v}$ | An array of vote weights | $blt_{v[i]}$ refers to the vote weights assigned to vote option $i$ |
| $blt_n$ | The current nonce | Starts from 0 and increments, so the first valid command must have nonce 1 |
| $blt_d$ | The vote option tree depth | The depth of the vote option tree |

The hash $blt$ is computed as such:

1. Compute the Merkle root of $blt_v$, arity 5, of a tree of depth $blt_d$; let this value be $blt_r$
2. Compute $poseidon_2([blt_n, blt_r])$
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Coordinator local processing
description: How does the coordinator process and tally messages locally
sidebar_label: Coordinator local processing
sidebar_position: 3
sidebar_position: 7
---

# Coordinator local processing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: Hashing and Encryption
description: A short introduction of the main primitives used by MACI
sidebar_label: Hashing and Encryption
sidebar_position: 2
---

### Hash Functions

MACI uses the Poseidon hash function, which is proven to be very efficient in ZK applications. Poseidon accepts $n$ inputs and produces 1 output:

$y = poseidon_n([x_1, x_2, ..., x_n])$

Also, SHA256 is used to compress public inputs to a circuit into a single field element in the finite field $F$ mod $p$.

### Message Encryption

In order to encrypt messages, MACI uses Poseidon in DuplexSponge [mode](https://dusk.network/uploads/Encryption-with-Poseidon.pdf). This provides an encryption function and a decryption function:

- $C$ as $poseidonEncrypt(k_s[0], k_s[1], N, l, t[])$
- $poseidonDecrypt(k_s[0], k_s[1], N, l, C)$

In more details,

- $k_s$ is the shared key, a point on the Baby Jubjub curve
- $N$ is the nonce, which we hardcode to 0
- $l$ is the length of the plaintext $t[]$

The implementation can be found [here](https://github.com/privacy-scaling-explorations/zk-kit/tree/main/packages/poseidon-cipher).

### Shared Key Generation

The ECDH algorithm is used to generate a shared key, which is then used to encrypt each message. This allows to create messages which are only decryptable by the coordinator and the person sending the message.

In more details:

- The coordinator's public key $cPk$ is known to all. Their private key $cSk$ is secret.

- When the user publishes a message (i.e. casts a vote), they generate an ephemeral keypair with private key $eSk$ and public key $ePk$.

- The user generates the shared key $k$ using the coordinator's public key $cPk$ and the user's ephemeral private key $eSk$.

- The user encrypts the command and signature with $k$ to form a message.

- The user sends their ephemeral public key $ePk$ along with the ciphertext. The coordinator can recover the same shared key using their private key $cSk$ and the given ephemeral public key $ePk$.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
title: MACI key change
description: How key change messages work
sidebar_label: Key change
sidebar_position: 4
sidebar_position: 8
---

# MACI Key Change

MACI's voters are identified by their MACI public key. Together with their private key, they can sign and submit messages to live Polls.

As MACI's main property is to provide collusion resistance in digital voting applications, it is important to have a mechanism for a user to change their voting key, should this become compromised, or they wish to revoke past actions.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: MACI Keys
description: A short introduction of MACI's keys
sidebar_label: Maci Keys
sidebar_position: 1
---

## Elliptic Curves

MACI uses the Baby Jubjub Elliptic [Curve](https://iden3-docs.readthedocs.io/en/latest/_downloads/33717d75ab84e11313cc0d8a090b636f/Baby-Jubjub.pdf). The `p` scalar field of choosing is:

$p=21888242871839275222246405745257275088548364400416034343698204186575808495617$

with generator:

$995203441582195749578291179787384436505546430278305826713579947235728471134$
$5472060717959818805561601436314318772137091100104008585924551046643952123905$

and within the finite field with modulo $p$.

## Key Pairs

MACI uses Node.js's `crypto.randomBytes(32)` function to generate a cryptographically strong pseudorandom 32-byte value. This value is the seed used to generate a maci public key. A public key is a point on the Baby Jubjub [curve](https://iden3-docs.readthedocs.io/en/latest/_downloads/33717d75ab84e11313cc0d8a090b636f/Baby-Jubjub.pdf), which is deterministically derived from a private key `s`.

A public key is generated with the following function from [zk-kit](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/eddsa-poseidon/src/eddsa-poseidon.ts#L75):

```ts
export function derivePublicKey(privateKey: Buffer | Uint8Array | string): Point<bigint> {
const s = deriveSecretScalar(privateKey);

return mulPointEscalar(Base8, s);
}
```

In more details, the function does the following:

1. Derive a scalar value from the seed (private key).

```ts
export function deriveSecretScalar(privateKey: Buffer | Uint8Array | string): bigint {
// Convert the private key to buffer.
privateKey = checkPrivateKey(privateKey);

let hash = blake(privateKey);

hash = hash.slice(0, 32);
hash = pruneBuffer(hash);

return scalar.shiftRight(leBufferToBigInt(hash), BigInt(3)) % subOrder;
}
```

2. Perform a scalar multiplication of the base point `Base8` with the scalar value `s`.

Now we have a public key, which is a point on the Baby Jubjub curve. In TypeScript, this is an array of two bigint values, representing the x and y coordinates of the point.

## Serialization

In order to easily store and transmit maci keys, these are serialized to a string.

A public key if first packed, then converted to a hex string. This string is then prefixed with `macipk.`.

For private keys (well the seed really), the value is converted to a hex string, padded to be of 64 characters, and prefixed with `macisk.`.

For instance, given a seed of `27514007781052885036162808648019893362811628316940391612960868886926452498447`, we have a public key of:

```json
{
"x": 7849177681360672621257726786949079749092629607596162839195961972852243798387,
"y": 6476520406570543146511284735472598280851241629796745672331248892171436291770
}
```

Serialized, these will look like **macipk.0e5194a54562ea4d440ac6a0049a41d4b600e3eb0bf54486e7a5f7e27521f6ba** and **macisk.3cd46064ea59936f82efb384059dd4f5b6b8e5c7546614caf7c1c3be0daea00f**.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: MACI Commands and Messages
description: A short introduction of MACI's commands and messages
sidebar_label: MACI Commands and Messages
sidebar_position: 3
---

## Command

A command represents an action that a user may take, such as casting a vote in a poll or changing their public key if bribed. It is made up of the following parameters:

| Symbol | Name | Size | Description |
| ------------ | ----------------------- | ---- | --------------------------------------------------------------------------------------------------- |
| $cm_i$ | State index | 50 | State leaf index where the signing key is located |
| $cm_{p_{x}}$ | Public key x-coordinate | 253 | If no change is necessary this parameter should reflect the current public key's x-coordinate |
| $cm_{p_{y}}$ | Public key y-coordinate | 253 | If no change is necessary this parameter should reflect the current public key's y-coordinate |
| $cm_{i_{v}}$ | Vote option index | 50 | Option state leaf index of preference to assign the vote for |
| $cm_w$ | Voting weight | 50 | Voice credit balance allocation, this is an arbitrary value dependent on a user's available credits |
| $cm_n$ | Nonce | 50 | State leaf's index of actions committed plus one |
| $cm_{id}$ | Poll id | 50 | The poll's identifier to cast in regard to |
| $cm_s$ | Salt | 253 | An entropy value to inhibit brute force attacks |

## Message

A message is an encrypted command using the shared key $k_s$ between the voter and the coordinator. The plaintext $t$ is computed as such:

$t = [p, cm_{p_{x}}, cm_{p_{y}}, cm_s, R8[0], R8[1], S]$

While the message can be computed with the formula below:

$M$ = ${poseidonEncrypt}(k_s[0], k_s[1], cm_n, 7, t)$

### Decrypting a message

To decrypt a message using $k_s$ we have the following:

$[p, R8[0], R8[1], cm_s]$ = ${poseidonDecrypt}(M, k_s[0], k_s[1], cm_n, 7)$

To unpack $p$ to its original five parameters, it must be separated into 50 bit values from the parent 250 bit value. To extract 50 bits at byte $n$, we:

1. initialise 50 bits
2. shift left by $n$ bits
3. bitwise AND with $p$
4. shift right by $n$ bits
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Merkle Trees
description: A short introduction of the main primitives used by MACI
sidebar_label: Merkle Trees
sidebar_position: 5
---

MACI uses different types of merkle trees to store and manage data. On chain, a [LazyIMT](https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/lazy-imt) is used to store user's state leaves, and an [AccQueue](https://github.com/privacy-scaling-explorations/maci/blob/dev/contracts/contracts/trees/AccQueue.sol) to store user's messages.

## Accumulator queue

This contract holds [messages](/docs/developers-references/smart-contracts/Poll#publishmessage) sent by users. When a leaf is inserted into the `AccQueue`, the merkle root is not updated yet, instead the leaf is updated or the root of a subtree is re-computed. The smart contract exposes three functions:

- `enqueue(leaf)`: Enqueues a leaf into a subtree
four out of five times it is invoked, an enqueue operation may or may not require the contract to perform a hash function. When it does, only up to $t_d$ required number of hashes need to be computed
- `mergeSubRoots()`: Merge all subtree roots into the shortest possible Merkle tree to fit
Before computing the main Merkle root, it is necessary to compute the smallSRTroot (the smallest subroot tree root). This is the Merkle root of a tree which is small enough to fit all the subroots
function which allows the coordinator to specify the number of queue operations to execute. The entire tree may be merged in a single transaction, or it may not.
- `merge()`: Calculate the Merkle root of all the leaves at height $d_t$

## LazyIMT

A LazyIMT is a Merkle tree that is updated lazily. It is used to [store the state leaves](/docs/developers-references/smart-contracts/MACI#signup) of the users. The "lazy" tree performs the minimum number of hashes necessary to insert elements in a tree. This means if there is only a left element the parent hash is not calculated until a corresponding right element exists, to avoid having an intermediate hash that will change in the future. This tree is designed for roots that are infrequently accessed onchain.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
title: MACI Poll Types
description: Which type of polls you can run on MACI
sidebar_label: MACI poll types
sidebar_position: 21
sidebar_position: 9
---

# MACI Poll Types

MACI allows to conduct polls in both a quadratic voting and non quadratic voting fashion. One should be aware that the only distinction between the two happens when messages are processed and votes tallied. On top of that, the Tally smart contract has been split into two different ones, with the non quadratic voting version one being slightly smaller, due to the need of one less function.

This document will explain how to use each of these options.
Expand All @@ -15,7 +13,7 @@ This document will explain how to use each of these options.

MACI has always worked with quadratic voting. Users signing up to MACI are assigned a number of voice credits based on certain conditions (enforced by the [initial voice credit proxy contract](https://github.com/privacy-scaling-explorations/maci/blob/dev/contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol)), and after each vote, the number of voice credits is reduced by the square of the weight of the vote casted. For instance, if the vote weight is 5, a user must have at least 25 voice credits to cast the vote.

To run a poll with quadratic voting, the coordinator must set the `useQuadraticVoting` parameter to `true` when creating the MACI instance. This will make the MACI instance use the `Tally` smart contract, which is the one that has been used since the beginning of MACI.
To run a poll with quadratic voting, the coordinator must deploy the Poll with the mode set to quadratic voting.

Using MACI's cli, one can create a MACI instance with quadratic voting by running the following command:

Expand Down
Loading
Loading