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

feat: scripts for MPC + removing MPC from the worker process 🔨 #633

Merged
merged 2 commits into from
May 4, 2022
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
9 changes: 7 additions & 2 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ module.exports = {
USE_STUBS: process.env.USE_STUBS === 'true',
VK_IDS: { deposit: 0, single_transfer: 1, double_transfer: 2, withdraw: 3 }, // used as an enum to mirror the Shield contracts enum for vk types. The keys of this object must correspond to a 'folderpath' (the .zok file without the '.zok' bit)
TIMBER_HEIGHT: 32,

MAX_PUBLIC_VALUES: {
ERCADDRESS: 2n ** 161n - 1n,
COMMITMENT: 2n ** 249n - 1n,
NULLIFIER: 2n ** 249n - 1n,
},
// the various parameters needed to describe the Babyjubjub curve that we use for El-Gamal
// BABYJUBJUB
// Montgomery EC form is y^2 = x^3 + Ax^2 + Bx
Expand All @@ -112,7 +116,8 @@ module.exports = {
},
MAX_QUEUE: 5,
MPC: {
RADIX_FILES_URL: 'https://nightfallv3-proving-files.s3.eu-west-1.amazonaws.com/radix',
MPC_PARAMS_URL:
'https://nightfallv3-proving-files.s3.eu-west-1.amazonaws.com/phase2/mpc_params',
},
ENVIRONMENTS: {
mainnet: {
Expand Down
32 changes: 0 additions & 32 deletions doc/mpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,3 @@ The MPC envolves two phases:
it ensures that if at least one participant of the ceremony is honest, then the result is forcibly
trustablke.
- Phase 2, which is circuit-specific and needs to be generated upon deployment.

## Radix files

The radix files are the MPC params that are calculated from the latest response file from PPOT.
These radix files have specific depths depending on the number of constraints for each circuit, and
can take a while to compute. They're also quite big in size so we shouldn't commit them to git.
Instead, we store them in a publicly accessible S3 bucket, and the `zokrates-worker` fetches them
synchonously if they're not present in the docker volume.

Does this mean you have to trust these radix files? No! Because you can start the process on your
own, and create your own radix files. You don't need to trust anybody.

## How can I not trust people

You are able to bring your own keys to the table. For that, you need to get the latest response from
the [PPOT ceremony](https://github.com/weijiekoh/perpetualpowersoftau) and download the
`new_challenge` file, rename it to `challenge` and run the little `mpc.sh` script you'll find
[here](https://github.com/EYBlockchain/nightfall_3/blob/master/zokrates-worker/src/mpc.sh). This
script will spit out a bunch of radix files like `phase1radix2m2`, `phase1radix2m3`...
`phase1radix2m(n)` where `n` should be the depth of the circuit we're using.

This means number of constraints, which you can get by running `zokrates compile` to compile the
circuits, and then `zokrates inspect` on the compiled circuits, which should spit out the number of
constraints. You should pick a value of `n` for which `2^n` is bigger than the number of
constraints. For example, at the time of writing, the compiled `deposit` circuit has `84766`
constraints so we need to pick up the `phase1radix2m17` as `2^16 < 84766 < 2^17`.

You should rename these radix files to the name of the circuits, and host them somewhere. IPFS, S3
buckets, your own webserver, whatever you want.

Lastly, don't forget to modify the env variable `RADIX_FILES_URL` to point to the URL where you'll
get the radix files.
18 changes: 11 additions & 7 deletions nightfall-client/src/classes/commitment.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ class Commitment {
compressedPkd,
salt,
});
this.hash = sha256([
this.preimage.ercAddress,
this.preimage.tokenId,
this.preimage.value,
this.preimage.compressedPkd,
this.preimage.salt,
]);
// we truncate the hash down to 31 bytes but store it in a 32 byte variable
// this is consistent to what we do in the ZKP circuits
this.hash = generalise(
sha256([
this.preimage.ercAddress,
this.preimage.tokenId,
this.preimage.value,
this.preimage.compressedPkd,
this.preimage.salt,
]).hex(32, 31),
);
}

// sometimes (e.g. going over http) the general-number class is inconvenient
Expand Down
4 changes: 3 additions & 1 deletion nightfall-client/src/classes/nullifier.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class Nullifier {
nsk,
commitment: commitment.hash,
});
this.hash = sha256([this.preimage.nsk, this.preimage.commitment]);
// we truncate the hash down to 31 bytes but store it in a 32 byte variable
// this is consistent to what we do in the ZKP circuits
this.hash = generalise(sha256([this.preimage.nsk, this.preimage.commitment]).hex(32, 31));
}
}

Expand Down
9 changes: 7 additions & 2 deletions nightfall-client/src/services/transfer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ async function transfer(transferParams) {

// compress the secrets to save gas
const compressedSecrets = Secrets.compressSecrets(secrets);

const commitmentTreeInfo = await Promise.all(oldCommitments.map(c => getSiblingInfo(c)));
const localSiblingPaths = commitmentTreeInfo.map(l => {
const path = l.siblingPath.path.map(p => p.value);
Expand Down Expand Up @@ -166,7 +165,13 @@ async function transfer(transferParams) {
secrets.cipherText.flat().map(text => text.field(BN128_GROUP_ORDER)),
...secrets.squareRootsElligator2.map(sqroot => sqroot.field(BN128_GROUP_ORDER)),
],
compressedSecrets.map(text => generalise(text.hex(32, 31)).field(BN128_GROUP_ORDER)),
compressedSecrets.map(text => {
const bin = text.binary.padStart(256, '0');
const parity = bin[0];
const ordinate = bin.slice(1);
const fields = [parity, new GN(ordinate, 'binary').field(BN128_GROUP_ORDER)];
return fields;
}),
].flat(Infinity);

logger.debug(`witness input is ${witness.join(' ')}`);
Expand Down
22 changes: 15 additions & 7 deletions nightfall-deployer/circuits/double_transfer.zok
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct Secrets {
field sqrtMessage4
}

struct CompressedPoint {
bool parity
field ordinate
}

def main(\
private field[2] fErcAddress,\
private OldCommitmentPreimage[2] oldCommitment,\
Expand All @@ -53,7 +58,7 @@ def main(\
private field[2][32] path,\
private field[2] order,\
private Secrets secrets,\
field[8] compressedCipherText\
CompressedPoint[8] compressedCipherText\
)->():

BabyJubJubParams context = curveParams()
Expand Down Expand Up @@ -155,7 +160,6 @@ def main(\
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(newCommitmentHash[1][0])[8..32])

// check the old commitments are valid
// old commitments are private inputs, so they are u32[8] and not truncated
for u32 i in 0..2 do
sha = sha256of1280([\
...ercAddress[i],\
Expand All @@ -164,7 +168,11 @@ def main(\
...pkdU32,\
...oldCommitment[i].salt\
])
assert(sha == oldCommitment[i].hash)
// assert(sha == oldCommitment[i].hash)
// last 224 bits:
assert(sha[1..8] == oldCommitment[i].hash[1..8])
// first 24 bits:
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(oldCommitment[i].hash[0])[8..32])
endfor

// And the encryption of the transaction (extend the value up to 256 bits)
Expand All @@ -178,10 +186,10 @@ def main(\
// there is likely a compiler bug with zokrates 0.6.4 which makes using spreads (e.g. [8..256]) inside a function (e.g. assert()) very slow
u32 j = 2*i
bool[256] compressed256 = edwardsCompress([secrets.cipherText[j], secrets.cipherText[j+1]])
bool[256] compressedCheck256 = field_to_bool_256(compressedCipherText[i])
bool[248] compressed = compressed256[8..256]
bool[248] compressedCheck = compressedCheck256[8..256]
assert(compressed == compressedCheck)
bool parity = compressedCipherText[i].parity
bool[256] ordinate = field_to_bool_256(compressedCipherText[i].ordinate)
bool[256] compressedCheck256 = [ parity, ...ordinate[1..256] ]
assert(compressed256 == compressedCheck256)
endfor

// check that the old commitments are in the merkle tree
Expand Down
12 changes: 10 additions & 2 deletions nightfall-deployer/circuits/double_transfer_stub.zok
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ struct Secrets {
field sqrtMessage4
}

struct CompressedPoint {
bool parity
field ordinate
}

def main(\
private field[2] fErcAddress,\
private OldCommitmentPreimage[2] oldCommitment,\
Expand All @@ -36,11 +41,12 @@ def main(\
private field[2][32] path,\
private field[2] order,\
private Secrets secrets,\
field[8] compressedCipherText\
CompressedPoint[8] compressedCipherText\
)->():

field u = 0
u32 v = 0x00000000
bool b = true
for u32 j in 0..2 do
u = u + fErcAddress[j] + fNewCommitmentHash[j] + fNullifier[j] - root[j]
for u32 i in 0..8 do
Expand All @@ -56,7 +62,8 @@ def main(\

u32 w = 0x00000000
for u32 i in 0..8 do
u = u + compressedCipherText[i]
u = u + compressedCipherText[i].ordinate
b = b && compressedCipherText[i].parity
w = w + secrets.ephemeralKey1[i] +\
secrets.ephemeralKey2[i] +\
secrets.ephemeralKey3[i] +\
Expand All @@ -83,5 +90,6 @@ def main(\
assert(v == v)
assert(u == u)
assert(w == w)
assert(b == b)

return
22 changes: 15 additions & 7 deletions nightfall-deployer/circuits/single_transfer.zok
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct Secrets {
field sqrtMessage4
}

struct CompressedPoint {
bool parity
field ordinate
}

def main(\
private field fErcAddress,\
private OldCommitmentPreimage oldCommitment,\
Expand All @@ -53,7 +58,7 @@ def main(\
private field[32] path,\
private field order,\
private Secrets secrets,\
field[8] compressedCipherText\
CompressedPoint[8] compressedCipherText\
)->():

BabyJubJubParams context = curveParams()
Expand Down Expand Up @@ -110,15 +115,18 @@ def main(\
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(newCommitmentHash[0])[8..32])

// check the old commitment is valid
// old commitments are private inputs, so they are u32[8] and not truncated
sha = sha256of1280([\
...ercAddress,\
...oldCommitment.id,\
...oldCommitment.value,\
...pkdU32,\
...oldCommitment.salt\
])
assert(sha == oldCommitment.hash)
// assert(sha == oldCommitment.hash)
// last 224 bits:
assert(sha[1..8] == oldCommitment.hash[1..8])
// first 24 bits:
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(oldCommitment.hash[0])[8..32])

// And the encryption of the transaction (extend the value up to 256 bits)
assert(secrets.cipherText == enc4(ercAddress, oldCommitment.id, oldCommitment.value, newCommitment.salt, newCommitment.pkdRecipient, secrets.ephemeralKey1, secrets.ephemeralKey2, secrets.ephemeralKey3, secrets.ephemeralKey4, secrets.sqrtMessage1, secrets.sqrtMessage2, secrets.sqrtMessage3, secrets.sqrtMessage4))
Expand All @@ -130,10 +138,10 @@ def main(\
// there is likely a compiler bug with zokrates 0.6.4 which makes using spreads (e.g. [8..256]) inside a function (e.g. assert()) very slow
u32 j = 2*i
bool[256] compressed256 = edwardsCompress([secrets.cipherText[j], secrets.cipherText[j+1]])
bool[256] compressedCheck256 = field_to_bool_256(compressedCipherText[i])
bool[248] compressed = compressed256[8..256]
bool[248] compressedCheck = compressedCheck256[8..256]
assert(compressed == compressedCheck)
bool parity = compressedCipherText[i].parity
bool[256] ordinate = field_to_bool_256(compressedCipherText[i].ordinate)
bool[256] compressedCheck256 = [ parity, ...ordinate[1..256] ]
assert(compressed256 == compressedCheck256)
endfor

// check that the old commitment is in the merkle tree (path[0] should be the root)
Expand Down
12 changes: 10 additions & 2 deletions nightfall-deployer/circuits/single_transfer_stub.zok
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ struct Secrets {
field sqrtMessage4
}

struct CompressedPoint {
bool parity
field ordinate
}

def main(\
private field fErcAddress,\
private OldCommitmentPreimage oldCommitment,\
Expand All @@ -35,11 +40,12 @@ def main(\
private field[32] path,\
private field order,\
private Secrets secrets,\
field[8] compressedCipherText\
CompressedPoint[8] compressedCipherText\
)->():

field u = fErcAddress + fNewCommitmentHash + fNullifier - root
u32 v = 0x00000000
bool b = true
for u32 i in 0..8 do
v = v + oldCommitment.id[i] +\
oldCommitment.value[i] +\
Expand All @@ -52,7 +58,8 @@ def main(\

u32 w = 0x00000000
for u32 i in 0..8 do
u = u + compressedCipherText[i]
u = u + compressedCipherText[i].ordinate
b = b && compressedCipherText[i].parity
w = w + secrets.ephemeralKey1[i] +\
secrets.ephemeralKey2[i] +\
secrets.ephemeralKey3[i] +\
Expand All @@ -71,5 +78,6 @@ def main(\
assert(v == v)
assert(u == u)
assert(w == w)
assert(b == b)

return
9 changes: 6 additions & 3 deletions nightfall-deployer/circuits/withdraw.zok
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ def main(\
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(nullifier[0])[8..32])

// check the old commitment is valid
// old commitments are private inputs, so they are u32[8] and not truncated
assert(oldCommitment.hash == sha256of1280([\
sha = sha256of1280([\
...ercAddress,\
...id,\
...value,\
...pkdU32,\
...oldCommitment.salt\
]))
])
// last 224 bits:
assert(sha[1..8] == oldCommitment.hash[1..8])
// first 24 bits:
assert(u32_to_bool_32(sha[0])[8..32] == u32_to_bool_32(oldCommitment.hash[0])[8..32])

// check that the old commitment is in the merkle tree
field mimcHash = u32_8_to_field(oldCommitment.hash)
Expand Down
15 changes: 15 additions & 0 deletions nightfall-deployer/contracts/Challenges.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ contract Challenges is Stateful, Key_Registry, Config {
) external onlyBootChallenger {
checkCommit(msg.data);
state.isBlockReal(blockL2, transactions, blockNumberL2);
// first check the transaction and block do not overflow
ChallengesUtil.libCheckOverflows(
blockL2,
transactions[transactionIndex]
);
// now we need to check that the proof is correct
ChallengesUtil.libCheckCompressedProof(
transactions[transactionIndex].proof,
Expand Down Expand Up @@ -189,6 +194,11 @@ contract Challenges is Stateful, Key_Registry, Config {
transactions[transactionIndex].historicRootBlockNumberL2[0] ==
blockNumberL2ContainingHistoricRoot
);
// first check the transaction and block do not overflow
ChallengesUtil.libCheckOverflows(
blockL2,
transactions[transactionIndex]
);
// now we need to check that the proof is correct
ChallengesUtil.libCheckCompressedProof(
transactions[transactionIndex].proof,
Expand Down Expand Up @@ -236,6 +246,11 @@ contract Challenges is Stateful, Key_Registry, Config {
blockNumberL2ContainingHistoricRoot[1],
'Incorrect historic root block'
);
// first check the transaction and block do not overflow
ChallengesUtil.libCheckOverflows(
blockL2,
transactions[transactionIndex]
);
// now we need to check that the proof is correct
ChallengesUtil.libChallengeProofVerification(
transactions[transactionIndex],
Expand Down
Loading