Adapt to ffjavascript asyncronous

This commit is contained in:
Jordi Baylina 2021-10-30 13:57:29 +02:00
parent c71c8c1daf
commit fe43791f41
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
51 changed files with 55532 additions and 7737 deletions

3
.npmignore Normal file
View File

@ -0,0 +1,3 @@
hardhat.config.ts
scripts
test

28
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,28 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Test",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": ["test/pedersenhash.js"]
},
{
"type": "pwa-node",
"request": "launch",
"name": "Print Bases",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/pedersen_printbases.js"
}
]
}

View File

@ -1,13 +1,46 @@
#circomlibjs # Advanced Sample Hardhat Project
`circomlibjs` is a Javascript library that provides programs to compute the witness of several circuits of `circomlib`. This project demonstrates an advanced Hardhat use case, integrating other tools commonly used alongside Hardhat in the ecosystem.
This library is used to make tests of circomlib circuits.
In the src directory the package includes these programs, in the The project comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts. It also comes with a variety of other tools, preconfigured to work with the project code.
test directory includes its own tests and, in the tools directory includes programs to precompute some needed parameters.
You can install `circomlibjs` with the following command: Try running some of the following tasks:
```text ```shell
npm install -g circomlibjs npx hardhat accounts
``` npx hardhat compile
npx hardhat clean
npx hardhat test
npx hardhat node
npx hardhat help
REPORT_GAS=true npx hardhat test
npx hardhat coverage
npx hardhat run scripts/deploy.ts
TS_NODE_FILES=true npx ts-node scripts/deploy.ts
npx eslint '**/*.{js,ts}'
npx eslint '**/*.{js,ts}' --fix
npx prettier '**/*.{json,sol,md}' --check
npx prettier '**/*.{json,sol,md}' --write
npx solhint 'contracts/**/*.sol'
npx solhint 'contracts/**/*.sol' --fix
```
# Etherscan verification
To try out Etherscan verification, you first need to deploy a contract to an Ethereum network that's supported by Etherscan, such as Ropsten.
In this project, copy the .env.example file to a file named .env, and then edit it to fill in the details. Enter your Etherscan API key, your Ropsten node URL (eg from Alchemy), and the private key of the account which will send the deployment transaction. With a valid .env file in place, first deploy your contract:
```shell
hardhat run --network ropsten scripts/sample-script.ts
```
Then, copy the deployment address and paste it in to replace `DEPLOYED_CONTRACT_ADDRESS` in this command:
```shell
npx hardhat verify --network ropsten DEPLOYED_CONTRACT_ADDRESS "Hello, Hardhat!"
```
# Performance optimizations
For faster runs of your tests and scripts, consider skipping ts-node's type checking by setting the environment variable `TS_NODE_TRANSPILE_ONLY` to `1` in hardhat's environment. For more details see [the documentation](https://hardhat.org/guides/typescript.html#performance-optimizations).

26937
build/main.cjs Normal file

File diff suppressed because one or more lines are too long

6
hardhat.config.js Normal file
View File

@ -0,0 +1,6 @@
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.7.3",
};

View File

@ -1,15 +0,0 @@
exports.babyjub = require("./src/babyjub");
exports.eddsa = require("./src/eddsa");
exports.evmasm = require("./src/evmasm");
exports.mimc7 = require("./src/mimc7");
exports.mimc_gencontract = require("./src/mimc_gencontract");
exports.mimcsponge_gencontract = require("./src/mimcsponge_gencontract");
exports.mimcsponge = require("./src/mimcsponge");
exports.pedersenHash = require("./src/pedersenHash");
exports.poseidon_gencontract = require("./src/poseidon_gencontract");
exports.poseidon = require("./src/poseidon");
exports.poseidon_slow = require("./src/poseidon_slow");
exports.smt_hashes_mimc = require("./src/smt_hashes_mimc");
exports.smt_hashes_poseidon = require("./src/smt_hashes_poseidon");
exports.smt = require("./src/smt");
exports.smt_memdb = require("./src/smt_memdb");

24
main.js Normal file
View File

@ -0,0 +1,24 @@
export {default as buildBabyjub} from "./src/babyjub.js";
export {default as buildEddsa} from "./src/eddsa.js";
export {default as evmasm} from "./src/evmasm.js";
export {default as buildMimc7} from "./src/mimc7.js";
import * as _mimc7Contract from "./src/mimc7_gencontract.js";
export const mimc7Contract=_mimc7Contract;
export {default as buildMimcSponge} from "./src/mimcsponge.js";
import * as _mimcSpongeContract from "./src/mimcsponge_gencontract.js";
export const mimcSpongecontract=_mimcSpongeContract;
export {default as buildPedersenHash} from "./src/pedersenhash.js";
export {default as buildPoseidon} from "./src/poseidon.js";
import * as _poseidonContract from "./src/poseidon_gencontract.js";
export const poseidonContract=_poseidonContract;
export {default as buildPoseidonSlow} from "./src/poseidon_slow.js";
export {SMT, buildSMT, newMemEmptyTrie} from "./src/smt.js";
export { default as SMTMemDb } from "./src/smt_memdb.js";

8063
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,18 @@
{ {
"name": "circomlibjs", "name": "circomlibjs",
"type": "module",
"main": "./build/main.cjs",
"module": "./main.js",
"exports": {
"import": "./main.js",
"require": "./build/main.cjs"
},
"version": "0.0.8", "version": "0.0.8",
"description": "Javascript library to work with circomlib", "description": "Javascript library to work with circomlib",
"main": "index.js",
"scripts": { "scripts": {
"test": "mocha", "test": "mocha",
"poseidonOptimizeConstants": "node tools/poseidon_optimize_constants.js" "poseidonOptimizeConstants": "node tools/poseidon_optimize_constants.js",
"build": "rollup -c rollup.cjs.config.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -28,13 +35,13 @@
"homepage": "https://github.com/iden3/circomlibjs#readme", "homepage": "https://github.com/iden3/circomlibjs#readme",
"devDependencies": { "devDependencies": {
"chai": "^4.3.4", "chai": "^4.3.4",
"ganache-cli": "^6.12.2" "ganache-cli": "^6.12.2",
"mocha": "^9.1.3"
}, },
"dependencies": { "dependencies": {
"ethers": "^5.5.1",
"blake-hash": "^2.0.0", "blake-hash": "^2.0.0",
"blake2b": "^2.1.3", "blake2b": "^2.1.3",
"ffjavascript": "^0.2.38", "ffjavascript": "^0.2.39"
"web3": "^1.6.0",
"web3-utils": "^1.6.0"
} }
} }

16
rollup.cjs.config.js Normal file
View File

@ -0,0 +1,16 @@
import fs from "fs";
import { builtinModules as builtin } from "module";
const pkg = JSON.parse(fs.readFileSync("./package.json"));
export default {
input: "./main.js",
output: {
file: "build/main.cjs",
format: "cjs",
},
external: [
...Object.keys(pkg.dependencies),
...builtin,
]
};

View File

@ -1,128 +1,139 @@
const F1Field = require("ffjavascript").F1Field; import { getCurveFromName, Scalar } from "ffjavascript";
const Scalar = require("ffjavascript").Scalar;
const utils = require("ffjavascript").utils;
exports.addPoint = addPoint; export default async function buildBabyJub() {
exports.mulPointEscalar = mulPointEscalar; const bn128 = await getCurveFromName("bn128");
exports.inCurve = inCurve; return new BabyJub(bn128.Fr);
exports.inSubgroup = inSubgroup;
exports.packPoint = packPoint;
exports.unpackPoint = unpackPoint;
exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const F = new F1Field(exports.p);
exports.F = F;
exports.Generator = [
F.e("995203441582195749578291179787384436505546430278305826713579947235728471134"),
F.e("5472060717959818805561601436314318772137091100104008585924551046643952123905")
];
exports.Base8 = [
F.e("5299619240641551281634865583518297030282874472190772894086521144482721001553"),
F.e("16950150798460657717958625567821834550301663161624707787222815936182638968203")
];
exports.order = Scalar.fromString("21888242871839275222246405745257275088614511777268538073601725287587578984328");
exports.subOrder = Scalar.shiftRight(exports.order, 3);
exports.A = F.e("168700");
exports.D = F.e("168696");
function addPoint(a,b) {
const res = [];
/* does the equivalent of:
res[0] = bigInt((a[0]*b[1] + b[0]*a[1]) * bigInt(bigInt("1") + d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
res[1] = bigInt((a[1]*b[1] - cta*a[0]*b[0]) * bigInt(bigInt("1") - d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
*/
const beta = F.mul(a[0],b[1]);
const gamma = F.mul(a[1],b[0]);
const delta = F.mul(
F.sub(a[1], F.mul(exports.A, a[0])),
F.add(b[0], b[1])
);
const tau = F.mul(beta, gamma);
const dtau = F.mul(exports.D, tau);
res[0] = F.div(
F.add(beta, gamma),
F.add(F.one, dtau)
);
res[1] = F.div(
F.add(delta, F.sub(F.mul(exports.A,beta), gamma)),
F.sub(F.one, dtau)
);
return res;
} }
function mulPointEscalar(base, e) { class BabyJub {
let res = [F.e("0"),F.e("1")]; constructor(F) {
let rem = e; this.F = F;
let exp = base; this.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
this.pm1d2 = Scalar.div(Scalar.sub(this.p, Scalar.e(1)), Scalar.e(2));
while (! Scalar.isZero(rem)) { this.Generator = [
if (Scalar.isOdd(rem)) { F.e("995203441582195749578291179787384436505546430278305826713579947235728471134"),
res = addPoint(res, exp); F.e("5472060717959818805561601436314318772137091100104008585924551046643952123905")
];
this.Base8 = [
F.e("5299619240641551281634865583518297030282874472190772894086521144482721001553"),
F.e("16950150798460657717958625567821834550301663161624707787222815936182638968203")
];
this.order = Scalar.fromString("21888242871839275222246405745257275088614511777268538073601725287587578984328");
this.subOrder = Scalar.shiftRight(this.order, 3);
this.A = F.e("168700");
this.D = F.e("168696");
}
addPoint(a,b) {
const F = this.F;
const res = [];
/* does the equivalent of:
res[0] = bigInt((a[0]*b[1] + b[0]*a[1]) * bigInt(bigInt("1") + d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
res[1] = bigInt((a[1]*b[1] - cta*a[0]*b[0]) * bigInt(bigInt("1") - d*a[0]*b[0]*a[1]*b[1]).inverse(q)).affine(q);
*/
const beta = F.mul(a[0],b[1]);
const gamma = F.mul(a[1],b[0]);
const delta = F.mul(
F.sub(a[1], F.mul(this.A, a[0])),
F.add(b[0], b[1])
);
const tau = F.mul(beta, gamma);
const dtau = F.mul(this.D, tau);
res[0] = F.div(
F.add(beta, gamma),
F.add(F.one, dtau)
);
res[1] = F.div(
F.add(delta, F.sub(F.mul(this.A,beta), gamma)),
F.sub(F.one, dtau)
);
return res;
}
mulPointEscalar(base, e) {
const F = this.F;
let res = [F.e("0"),F.e("1")];
let rem = e;
let exp = base;
while (! Scalar.isZero(rem)) {
if (Scalar.isOdd(rem)) {
res = this.addPoint(res, exp);
}
exp = this.addPoint(exp, exp);
rem = Scalar.shiftRight(rem, 1);
} }
exp = addPoint(exp, exp);
rem = Scalar.shiftRight(rem, 1); return res;
} }
return res; inSubgroup(P) {
} const F = this.F;
if (!this.inCurve(P)) return false;
function inSubgroup(P) { const res= this.mulPointEscalar(P, this.subOrder);
if (!inCurve(P)) return false; return (F.isZero(res[0]) && F.eq(res[1], F.one));
const res= mulPointEscalar(P, exports.subOrder);
return (F.isZero(res[0]) && F.eq(res[1], F.one));
}
function inCurve(P) {
const x2 = F.square(P[0]);
const y2 = F.square(P[1]);
if (!F.eq(
F.add(F.mul(exports.A, x2), y2),
F.add(F.one, F.mul(F.mul(x2, y2), exports.D)))) return false;
return true;
}
function packPoint(P) {
const buff = utils.leInt2Buff(P[1], 32);
if (F.lt(P[0], F.zero)) {
buff[31] = buff[31] | 0x80;
} }
return buff;
}
function unpackPoint(_buff) { inCurve(P) {
const buff = Buffer.from(_buff); const F = this.F;
let sign = false; const x2 = F.square(P[0]);
const P = new Array(2); const y2 = F.square(P[1]);
if (buff[31] & 0x80) {
sign = true; if (!F.eq(
buff[31] = buff[31] & 0x7F; F.add(F.mul(this.A, x2), y2),
F.add(F.one, F.mul(F.mul(x2, y2), this.D)))) return false;
return true;
} }
P[1] = utils.leBuff2int(buff);
if (Scalar.gt(P[1], exports.p)) return null;
const y2 = F.square(P[1]); packPoint(P) {
const F = this.F;
const buff = new Uint8Array(32);
F.toRprLE(buff, 0, P[1]);
const n = F.toObject(P[0]);
if (Scalar.gt(n, this.pm1d2)) {
buff[31] = buff[31] | 0x80;
}
return buff;
}
let x = F.sqrt(F.div( unpackPoint(buff) {
F.sub(F.one, y2), const F = this.F;
F.sub(exports.A, F.mul(exports.D, y2)))); let sign = false;
const P = new Array(2);
if (buff[31] & 0x80) {
sign = true;
buff[31] = buff[31] & 0x7F;
}
P[1] = F.fromRprLE(buff, 0);
if (Scalar.gt(F.toObject(P[1]), this.p)) return null;
if (x == null) return null; const y2 = F.square(P[1]);
if (sign) x = F.neg(x); const x2 = F.div(
F.sub(F.one, y2),
F.sub(this.A, F.mul(this.D, y2))
);
P[0] = x; const x2h = F.exp(x2, F.half);
if (! F.eq(F.one, x2h)) return null;
return P; let x = F.sqrt(x2);
if (x == null) return null;
if (sign) x = F.neg(x);
P[0] = x;
return P;
}
} }

View File

@ -1,233 +1,285 @@
const createBlakeHash = require("blake-hash"); import { Scalar } from "ffjavascript";
const Scalar = require("ffjavascript").Scalar; import buildBabyJub from "./babyjub.js";
const F1Field = require("ffjavascript").F1Field; import buildPedersenHash from "./pedersenhash.js";
const babyJub = require("./babyjub"); import buildMimc7 from "./mimc7.js";
const utils = require("ffjavascript").utils; import buildPoseidon from "./poseidon.js";
const pedersenHash = require("./pedersenHash").hash; import buildMimcSponge from "./mimcsponge.js";
const mimc7 = require("./mimc7"); import createBlakeHash from "blake-hash";
const poseidon = require("./poseidon.js");
const mimcsponge = require("./mimcsponge");
export default async function buildEddsa() {
exports.prv2pub= prv2pub; const babyJub = await buildBabyJub("bn128");
exports.sign = sign; const pedersenHash = await buildPedersenHash();
exports.signMiMC = signMiMC; const mimc7 = await buildMimc7();
exports.signPoseidon = signPoseidon; const poseidon = await buildPoseidon();
exports.signMiMCSponge = signMiMCSponge; const mimcSponge = await buildMimcSponge();
exports.verify = verify; return new Eddsa(babyJub, pedersenHash, mimc7, poseidon, mimcSponge);
exports.verifyMiMC = verifyMiMC;
exports.verifyPoseidon = verifyPoseidon;
exports.verifyMiMCSponge = verifyMiMCSponge;
exports.packSignature = packSignature;
exports.unpackSignature = unpackSignature;
exports.pruneBuffer = pruneBuffer;
function ensureBuffer(_buff) {
if (Buffer.isBuffer(_buff)) return buff;
return Buffer.from(_buff);
} }
function pruneBuffer(_buff) { class Eddsa {
const buff = Buffer.from(_buff);
buff[0] = buff[0] & 0xF8;
buff[31] = buff[31] & 0x7F;
buff[31] = buff[31] | 0x40;
return buff;
}
function prv2pub(prv) { constructor(babyJub, pedersenHash, mimc7, poseidon, mimcSponge) {
const sBuff = pruneBuffer(createBlakeHash("blake512").update(prv).digest().slice(0,32)); this.babyJub = babyJub;
let s = utils.leBuff2int(sBuff); this.pedersenHash = pedersenHash;
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s,3)); this.mimc7 = mimc7;
return A; this.poseidon = poseidon;
} this.mimcSponge = mimcSponge;
this.F = babyJub.F;
}
function sign(prv, msg) { pruneBuffer(buff) {
const h1 = createBlakeHash("blake512").update(prv).digest(); buff[0] = buff[0] & 0xF8;
const sBuff = pruneBuffer(h1.slice(0,32)); buff[31] = buff[31] & 0x7F;
const s = utils.leBuff2int(sBuff); buff[31] = buff[31] | 0x40;
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3)); return buff;
}
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msg])).digest(); prv2pub(prv) {
let r = utils.leBuff2int(rBuff); const F = this.babyJub.F;
const Fr = new F1Field(babyJub.subOrder); const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
r = Fr.e(r); let s = Scalar.fromRprLE(sBuff, 0, 32);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r); const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s,3));
const R8p = babyJub.packPoint(R8); return A;
const Ap = babyJub.packPoint(A); }
const hmBuff = pedersenHash(Buffer.concat([R8p, Ap, msg]));
const hm = utils.leBuff2int(hmBuff);
const S = Fr.add(r , Fr.mul(hm, s));
return {
R8: R8,
S: S
};
}
function signMiMC(prv, msg) { signPedersen(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest(); const F = this.babyJub.F;
const sBuff = pruneBuffer(h1.slice(0,32)); const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
const s = utils.leBuff2int(sBuff); const s = Scalar.fromRprLE(sBuff, 0, 32);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3)); const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32)); const composeBuff = new Uint8Array(32 + msg.length);
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest(); composeBuff.set(sBuff.slice(32), 0);
let r = utils.leBuff2int(rBuff); composeBuff.set(msg, 32);
const Fr = new F1Field(babyJub.subOrder); const rBuff = createBlakeHash("blake512").update(Buffer.from(composeBuff)).digest();
r = Fr.e(r); let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r); const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
const hm = mimc7.multiHash([R8[0], R8[1], A[0], A[1], msg]); const R8p = this.babyJub.packPoint(R8);
const S = Fr.add(r , Fr.mul(hm, s)); const Ap = this.babyJub.packPoint(A);
return {
R8: R8,
S: S
};
}
function signMiMCSponge(prv, msg) { const composeBuff2 = new Uint8Array(64 + msg.length);
const h1 = createBlakeHash("blake512").update(prv).digest(); composeBuff2.set(R8p, 0);
const sBuff = pruneBuffer(h1.slice(0,32)); composeBuff2.set(Ap, 32);
const s = utils.leBuff2int(sBuff); composeBuff2.set(msg, 64);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32)); const hmBuff = this.pedersenHash.hash(composeBuff2);
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest(); const hm = Scalar.fromRprLE(hmBuff, 0, 32);
let r = utils.leBuff2int(rBuff);
const Fr = new F1Field(babyJub.subOrder);
r = Fr.e(r);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const hm = mimcsponge.multiHash([R8[0], R8[1], A[0], A[1], msg]);
const S = Fr.add(r , Fr.mul(hm, s));
return {
R8: R8,
S: S
};
}
function signPoseidon(prv, msg) { const S = Scalar.mod(
const h1 = createBlakeHash("blake512").update(prv).digest(); Scalar.add(
const sBuff = pruneBuffer(h1.slice(0,32)); r,
const s = utils.leBuff2int(sBuff); Scalar.mul(hm, s)
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3)); ),
this.babyJub.subOrder
)
return {
R8: R8,
S: S
};
}
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32)); signMiMC(prv, msg) {
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest(); const F = this.babyJub.F;
let r = utils.leBuff2int(rBuff); const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
const Fr = new F1Field(babyJub.subOrder); const s = Scalar.fromRprLE(sBuff, 0, 32);
r = Fr.e(r); const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const hm = poseidon([R8[0], R8[1], A[0], A[1], msg]);
const S = Fr.add(r , Fr.mul(hm, s));
return {
R8: R8,
S: S
};
}
function verify(msg, sig, A) {
// Check parameters
if (typeof sig != "object") return false;
if (!Array.isArray(sig.R8)) return false;
if (sig.R8.length!= 2) return false;
if (!babyJub.inCurve(sig.R8)) return false;
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!babyJub.inCurve(A)) return false;
if (sig.S>= babyJub.subOrder) return false;
const R8p = babyJub.packPoint(sig.R8); const composeBuff = new Uint8Array(32 + msg.length);
const Ap = babyJub.packPoint(A); composeBuff.set(sBuff.slice(32), 0);
const hmBuff = pedersenHash(Buffer.concat([R8p, Ap, msg])); F.toRprLE(composeBuff, 32, msg);
const hm = utils.leBuff2int(hmBuff); const rBuff = createBlakeHash("blake512").update(Buffer.from(composeBuff)).digest();
let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder);
const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S); const hm = this.mimc7.multiHash([R8[0], R8[1], A[0], A[1], msg]);
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm,8)); const hms = Scalar.e(this.babyJub.F.toObject(hm));
Pright = babyJub.addPoint(sig.R8, Pright); const S = Scalar.mod(
Scalar.add(
r,
Scalar.mul(hms, s)
),
this.babyJub.subOrder
)
return {
R8: R8,
S: S
};
}
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false; signMiMCSponge(prv, msg) {
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false; const F = this.babyJub.F;
return true; const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
} const s = Scalar.fromRprLE(sBuff, 0, 32);
const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
function verifyMiMC(msg, sig, A) { const composeBuff = new Uint8Array(32 + msg.length);
// Check parameters composeBuff.set(sBuff.slice(32), 0);
if (typeof sig != "object") return false; F.toRprLE(composeBuff, 32, msg);
if (!Array.isArray(sig.R8)) return false; const rBuff = createBlakeHash("blake512").update(Buffer.from(composeBuff)).digest();
if (sig.R8.length!= 2) return false; let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder);
if (!babyJub.inCurve(sig.R8)) return false; const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!babyJub.inCurve(A)) return false;
if (sig.S>= babyJub.subOrder) return false;
const hm = mimc7.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]); const hm = this.mimcSponge.multiHash([R8[0], R8[1], A[0], A[1], msg]);
const hms = Scalar.e(this.babyJub.F.toObject(hm));
const S = Scalar.mod(
Scalar.add(
r,
Scalar.mul(hms, s)
),
this.babyJub.subOrder
)
return {
R8: R8,
S: S
};
}
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S); signPoseidon(prv, msg) {
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm, 8)); const F = this.babyJub.F;
Pright = babyJub.addPoint(sig.R8, Pright); const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
const s = Scalar.fromRprLE(sBuff, 0, 32);
const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3));
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false; const composeBuff = new Uint8Array(32 + msg.length);
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false; composeBuff.set(sBuff.slice(32), 0);
return true; F.toRprLE(composeBuff, 32, msg);
} const rBuff = createBlakeHash("blake512").update(Buffer.from(composeBuff)).digest();
let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder);
const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
function verifyPoseidon(msg, sig, A) {
// Check parameters const hm = this.poseidon([R8[0], R8[1], A[0], A[1], msg]);
if (typeof sig != "object") return false; const hms = Scalar.e(this.babyJub.F.toObject(hm));
if (!Array.isArray(sig.R8)) return false; const S = Scalar.mod(
if (sig.R8.length!= 2) return false; Scalar.add(
if (!babyJub.inCurve(sig.R8)) return false; r,
if (!Array.isArray(A)) return false; Scalar.mul(hms, s)
if (A.length!= 2) return false; ),
if (!babyJub.inCurve(A)) return false; this.babyJub.subOrder
if (sig.S>= babyJub.subOrder) return false; )
return {
const hm = poseidon([sig.R8[0], sig.R8[1], A[0], A[1], msg]); R8: R8,
S: S
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S); };
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm, 8)); }
Pright = babyJub.addPoint(sig.R8, Pright);
verifyPedersen(msg, sig, A) {
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false; // Check parameters
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false; if (typeof sig != "object") return false;
return true; if (!Array.isArray(sig.R8)) return false;
} if (sig.R8.length!= 2) return false;
if (!this.babyJub.inCurve(sig.R8)) return false;
function verifyMiMCSponge(msg, sig, A) { if (!Array.isArray(A)) return false;
// Check parameters if (A.length!= 2) return false;
if (typeof sig != "object") return false; if (!this.babyJub.inCurve(A)) return false;
if (!Array.isArray(sig.R8)) return false; if (Scalar.geq(sig.S, this.babyJub.subOrder)) return false;
if (sig.R8.length!= 2) return false;
if (!babyJub.inCurve(sig.R8)) return false; const R8p = this.babyJub.packPoint(sig.R8);
if (!Array.isArray(A)) return false; const Ap = this.babyJub.packPoint(A);
if (A.length!= 2) return false;
if (!babyJub.inCurve(A)) return false;
if (sig.S>= babyJub.subOrder) return false; const composeBuff2 = new Uint8Array(64 + msg.length);
composeBuff2.set(R8p, 0);
const hm = mimcsponge.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]); composeBuff2.set(Ap, 32);
composeBuff2.set(msg, 64);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S);
let Pright = babyJub.mulPointEscalar(A, hm.times(bigInt("8")));
Pright = babyJub.addPoint(sig.R8, Pright); const hmBuff = this.pedersenHash.hash(composeBuff2);
const hm = Scalar.fromRprLE(hmBuff, 0, 32);
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false; const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
return true; let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hm,8));
} Pright = this.babyJub.addPoint(sig.R8, Pright);
function packSignature(sig) { if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false;
const R8p = babyJub.packPoint(sig.R8); if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false;
const Sp = utils.leInt2Buff(sig.S, 32); return true;
return Buffer.concat([R8p, Sp]); }
}
verifyMiMC(msg, sig, A) {
function unpackSignature(sigBuff) { // Check parameters
return { if (typeof sig != "object") return false;
R8: babyJub.unpackPoint(sigBuff.slice(0,32)), if (!Array.isArray(sig.R8)) return false;
S: utils.leBuff2int(sigBuff.slice(32,64)) if (sig.R8.length!= 2) return false;
}; if (!this.babyJub.inCurve(sig.R8)) return false;
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!this.babyJub.inCurve(A)) return false;
if (sig.S>= this.babyJub.subOrder) return false;
const hm = this.mimc7.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]);
const hms = Scalar.e(this.babyJub.F.toObject(hm));
const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8));
Pright = this.babyJub.addPoint(sig.R8, Pright);
if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
verifyPoseidon(msg, sig, A) {
// Check parameters
if (typeof sig != "object") return false;
if (!Array.isArray(sig.R8)) return false;
if (sig.R8.length!= 2) return false;
if (!this.babyJub.inCurve(sig.R8)) return false;
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!this.babyJub.inCurve(A)) return false;
if (sig.S>= this.babyJub.subOrder) return false;
const hm = this.poseidon([sig.R8[0], sig.R8[1], A[0], A[1], msg]);
const hms = Scalar.e(this.babyJub.F.toObject(hm));
const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8));
Pright = this.babyJub.addPoint(sig.R8, Pright);
if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
verifyMiMCSponge(msg, sig, A) {
// Check parameters
if (typeof sig != "object") return false;
if (!Array.isArray(sig.R8)) return false;
if (sig.R8.length!= 2) return false;
if (!this.babyJub.inCurve(sig.R8)) return false;
if (!Array.isArray(A)) return false;
if (A.length!= 2) return false;
if (!this.babyJub.inCurve(A)) return false;
if (sig.S>= this.babyJub.subOrder) return false;
const hm = this.mimcSponge.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]);
const hms = Scalar.e(this.babyJub.F.toObject(hm));
const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8));
Pright = this.babyJub.addPoint(sig.R8, Pright);
if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
packSignature(sig) {
const buff = new Uint8Array(64);
const R8p = this.babyJub.packPoint(sig.R8);
buff.set(R8p, 0)
const Sp = Scalar.toRprLE(buff, 32, sig.S, 32);
return buff;
}
unpackSignature(sigBuff) {
return {
R8: this.babyJub.unpackPoint(sigBuff.slice(0,32)),
S: Scalar.fromRprLE(sigBuff, 32, 32)
};
}
} }

View File

@ -3,9 +3,10 @@
// //
const Web3Utils = require("web3-utils"); import ethers from "ethers";
import {Scalar} from "ffjavascript";
class Contract { export default class Contract {
constructor() { constructor() {
this.code = []; this.code = [];
this.labels = {}; this.labels = {};
@ -26,7 +27,7 @@ class Contract {
while (genLoadedLength!=setLoaderLength) { while (genLoadedLength!=setLoaderLength) {
setLoaderLength = genLoadedLength; setLoaderLength = genLoadedLength;
C = new module.exports(); C = new Contract();
C.codesize(); C.codesize();
C.push(setLoaderLength); C.push(setLoaderLength);
C.push(0); C.push(0);
@ -38,7 +39,7 @@ class Contract {
genLoadedLength = C.code.length; genLoadedLength = C.code.length;
} }
return Web3Utils.bytesToHex(C.code.concat(this.code)); return ethers.utils.hexlify(C.code.concat(this.code));
} }
stop() { this.code.push(0x00); } stop() { this.code.push(0x00); }
@ -154,22 +155,25 @@ class Contract {
} }
push(data) { push(data) {
if (typeof data === "number") { if ((typeof data !== "string") || (data.slice(0,2) != "0x")) {
let isNeg; let v = Scalar.e(data);
if (data<0) { if (Scalar.isNegative(v)) {
isNeg = true; v = Scalar.add(Scalar.shl(Scalar.e(1), 256), v);
data = -data;
} }
data = data.toString(16); let S = Scalar.toString(v, 16);
if (data.length % 2 == 1) data = "0" + data; if (S.length % 2) S = "0"+S;
data = "0x" + data; S = "0x" +S;
if (isNeg) data = "-"+data; data = S;
} }
const d = Web3Utils.hexToBytes(Web3Utils.toHex(data)); const d = ethers.utils.arrayify(data);
if (d.length == 0 || d.length > 32) { if (d.length == 0 || d.length > 32) {
throw new Error("Assertion failed"); throw new Error("Assertion failed");
} }
this.code = this.code.concat([0x5F + d.length], d); const a = [];
this.code.push(0x5F + d.length);
for (let i=0; i<d.length; i++) {
this.code.push(d[i]);
}
} }
dup(n) { dup(n) {
@ -204,5 +208,3 @@ class Contract {
selfdestruct() { this.code.push(0xff); } selfdestruct() { this.code.push(0xff); }
} }
module.exports = Contract;

View File

@ -1,582 +0,0 @@
// Copyright (c) 2018 Jordi Baylina
// License: LGPL-3.0+
//
const Contract = require("./evmasm");
const G2 = require("snarkjs").bn128.G2;
function toHex256(a) {
let S = a.toString(16);
while (S.length < 64) S="0"+S;
return "0x" + S;
}
function createCode(P, w) {
const C = new Contract();
const NPOINTS = 1 << (w-1);
const VAR_POS = C.allocMem(32);
const VAR_POINTS = C.allocMem( (NPOINTS)*4*32);
const savedP = C.allocMem(32);
const savedZ3 = C.allocMem(32);
// Check selector
C.push("0x0100000000000000000000000000000000000000000000000000000000");
C.push(0);
C.calldataload();
C.div();
C.push("b65c7c74"); // mulexp(uint256)
C.eq();
C.jmpi("start");
C.invalid();
C.label("start");
storeVals();
C.push( Math.floor(255/w)*w ); // pos := 255
C.push(VAR_POS);
C.mstore();
C.push("21888242871839275222246405745257275088696311157297823662689037894645226208583");
C.push(0);
C.push(0);
C.push(0);
C.push(0);
C.push(0);
C.push(0);
C.label("begin_loop"); // ACC_X ACC_Y ACC_Z q
C.internalCall("double");
// g = (e>>pos)&MASK
C.push(4);
C.calldataload(); // e ACC_X ACC_Y ACC_Z q
C.push(VAR_POS);
C.mload(); // pos e ACC_X ACC_Y ACC_Z q
C.shr();
C.push(NPOINTS-1);
C.and(); // g ACC_X ACC_Y ACC_Z q
C.internalCall("add"); // acc_x acc_y acc_z
C.push(VAR_POS);
C.mload(); // pos acc_x acc_y acc_z
C.dup(0); // pos pos acc_x acc_y acc_z
C.push(0); // 0 pos pos acc_x acc_y acc_z
C.eq(); // eq pos acc_x acc_y acc_z
C.jmpi("after_loop"); // pos acc_x acc_y acc_z
C.push(w); // 5 pos acc_x acc_y acc_z
C.sub(); // pos acc_x acc_y acc_z
C.push(VAR_POS);
C.mstore(); // acc_x acc_y acc_z
C.jmp("begin_loop");
C.label("after_loop"); // pos acc_x acc_y acc_z
C.pop(); // acc_x acc_y acc_z
C.internalCall("affine"); // acc_x acc_y
C.push(0);
C.mstore();
C.push(20);
C.mstore();
C.push(40);
C.mstore();
C.push(60);
C.mstore();
C.push("0x80");
C.push("0x00");
C.return();
double();
addPoint();
affine();
return C.createTxData();
function add(a,b,q) {
C.dup(q);
C.dup(a+1 + 1);
C.dup(b+1 + 2);
C.addmod();
C.dup(q + 1);
C.dup(a + 2);
C.dup(b + 3);
C.addmod();
}
function sub(a,b,q) {
C.dup(q); // q
C.dup(a+1 + 1); // ai q
C.dub(q + 2); // q ai q
C.dup(b+1 + 3); // bi q ai q
C.sub(); // -bi ai q
C.addmod(); // ci
C.dup(q + 1); // q ci
C.dup(a + 2); // ar q ci
C.dup(q + 3); // q ar q ci
C.dup(b + 4); // br q ar q ci
C.sub(); // -br ar q ci
C.addmod(); // cr ci
}
function mul(a, b, q) {
C.dup(q); // q
C.dup(q + 1); // q q
C.dup(a + 2); // ar q q
C.dup(b+1 + 3); // bi ar q q
C.mulmod(); // ci1 q
C.dup(q + 2); // q ci1 q
C.dup(a+1 + 3); // ai q ci1 q
C.dup(b + 4); // ar ai q ci1 q
C.mulmod(); // ci2 ci1 q
C.addmod(); // ci
C.dup(q + 1); // q ci
C.dup(q + 2); // q q ci
C.dup(q + 3); // q q q ci
C.dup(a+1 + 4); // ai q q ci
C.dup(b+1 + 5); // bi ai q q ci
C.mulmod(); // cr2 q q ci
C.sub(); // -cr2 q ci
C.dup(q + 3); // q -cr2 q ci
C.dup(a + 4); // ar q -cr2 q ci
C.dup(b + 5); // br ar q -cr2 q ci
C.mulmod(); // cr1 -cr2 q ci
C.addmod(); // cr ci
}
function square(a, q) {
C.dup(q); // q
C.dup(q + 1); // q q
C.dup(a + 2); // ar q q
C.dup(a+1 + 3); // ai ar q q
C.mulmod(); // arai q
C.dup(0); // arai arai q
C.addmod(); // ci
C.dup(q + 1); // q ci
C.dup(q + 2); // q q ci
C.dup(q + 3); // q q q ci
C.dup(a+1 + 4); // ai q q ci
C.dup(a+1 + 5); // ai ai q q ci
C.mulmod(); // cr2 q q ci
C.sub(); // -cr2 q ci
C.dup(q + 3); // q -cr2 q ci
C.dup(a + 4); // ar q -cr2 q ci
C.dup(a + 5); // br ar q -cr2 q ci
C.mulmod(); // cr1 -cr2 q ci
C.addmod(); // cr ci
}
function add1(a, q) {
C.dup(a+1); // im
C.dup(1 + q); // q
C.dup(2 + a); // re q im
C.push(1); // 1 re q im
C.addmod();
}
function cmp(a, b) {
C.dup(a);
C.dup(b);
C.eq();
C.dup(a+1);
C.dup(a+1);
C.and();
}
function rm(a) {
if (a>0) C.swap(a);
C.pop();
if (a>0) C.swap(a);
C.pop();
}
function double() {
C.label("double"); // xR, xI, yR, yI, zR zI, q
C.dup(4);
C.iszero();
C.dup(6);
C.iszero();
C.and();
C.jumpi("enddouble"); // X Y Z q
// Z3 = 2*Y*Z // Remove Z
mul(2, 4, 6); // yz X Y Z q
rm(6); // X Y yz q
add(4, 4, 6); // 2yz X Y yz q
rm(6); // X Y Z3 q
// A = X^2
square(0,6); // A X Y Z3 q
// B = Y^2 // Remove Y
square(4,8); // B A X Y Z3 q
rm(6); // A X B Z3 q
// C = B^2
square(4,8); // C A X B Z3 q
// D = (X+B)^2-A-C // Remove X, Remove B
add(4,6, 10); // X+B C A X B Z3 q
rm(6); // C A X+B B Z3 q
rm(6); // A X+B C Z3 q
square(2,8); // (X+B)^2 A X+B C Z3 q
rm(4); // A (X+B)^2 C Z3 q
sub(2, 0, 8); // (X+B)^2-A A (X+B)^2 C Z3 q
rm(4); // A (X+B)^2-A C Z3 q
sub(2, 4, 8); // (X+B)^2-A-C A (X+B)^2-A C Z3 q
rm(4); // A D C Z3 q
// D = D+D
add(2,2, 8); // D+D A D C Z3 q
rm(4); // A D C Z3 q
// E=A+A+A
add(0, 0, 8); // 2A A D C Z3 q
add(0, 2, 10); // 3A 2A A D C Z3 q
rm(4); // 2A 3A D C Z3 q
rm(0); // E D C Z3 q
// F=E^2
square(0, 8); // F E D C Z3 q
// X3= F - 2*D // Remove F
add(4, 4, 10); // 2D F E D C Z3 q
sub(2, 0, 12); // F-2D 2D F E D C Z3 q
rm(4); // 2D X3 E D C Z3 q
rm(0); // X3 E D C Z3 q
// Y3 = E * (D - X3) - 8 * C // Remove D C E
sub(4, 0, 10); // D-X3 X3 E D C Z3 q
rm(6); // X3 E D-X3 C Z3 q
mul(2, 4, 10); // E*(D-X3) X3 E D-X3 C Z3 q
rm(6); // X3 E E*(D-X3) C Z3 q
rm(2); // X3 E*(D-X3) C Z3 q
add(4, 4, 8); // 2C X3 E*(D-X3) C Z3 q
rm(6); // X3 E*(D-X3) 2C Z3 q
add(4, 4, 8); // 4C X3 E*(D-X3) 2C Z3 q
rm(6); // X3 E*(D-X3) 4C Z3 q
add(4, 4, 8); // 8C X3 E*(D-X3) 4C Z3 q
rm(6); // X3 E*(D-X3) 8C Z3 q
sub(2, 4, 8); // E*(D-X3)-8C X3 E*(D-X3) 8C Z3 q
rm(6); // X3 E*(D-X3) Y3 Z3 q
rm(2); // X3 Y3 Z3 q
C.label("enddouble");
C.returnCall();
}
function addPoint() { // p, xR, xI, yR, yI, zR zI, q
C.dup(0); // p p X2 Y2 Z2 q
C.push(savedP);
C.mstore();
C.iszero(); // X2 Y2 Z2 q
C.jumpi("endpadd");
C.dup(4);
C.iszero();
C.dup(6);
C.iszero();
C.and();
C.jumpi("returnP"); // X2 Y2 Z2 q
// lastZ3 = (Z2+1)^2 - Z2^2
add1(4, 6); // Z2+1 X2 Y2 Z2 q
square(0, 8); // (Z2+1)^2 Z2+1 X2 Y2 Z2 q
rm(2); // (Z2+1)^2 X2 Y2 Z2 q
square(6, 8); // Z2^2 (Z2+1)^2 X2 Y2 Z2 q
sub(2, 0, 10); // (Z2+1)^2-Z2^2 Z2^2 (Z2+1)^2 X2 Y2 Z2 q
saveZ3(); // Z2^2 (Z2+1)^2 X2 Y2 Z2 q
rm(2); // Z2^2 X2 Y2 Z2 q
// U2 = X2
// S2 = Y2 // Z2^2 U2 S2 Z2 q
// U1 = X1 * Z2^2
loadX(); // X1 Z2^2 U2 S2 Z2 q
mul(0, 2, 10); // X1*Z2^2 X1 Z2^2 U2 S2 Z2 q
rm(2); // X1*Z2^2 Z2^2 U2 S2 Z2 q
mul(2, 8, 10); // Z2^3 U1 Z2^2 U2 S2 Z2 q
rm(4); // U1 Z2^3 U2 S2 Z2 q
rm(8); // Z2^3 U2 S2 U1 q
// S1 = Y1 * Z1^3
loadY(); // Y1 Z2^3 U2 S2 U1 q
mul(0, 2, 10); // S1 Y1 Z2^3 U2 S2 U1 q
rm(4); // Y1 S1 U2 S2 U1 q
rm(0); // S1 U2 S2 U1 q
cmp(0, 4); // c1 S1 U2 S2 U1 q
cmp(3, 7); // c2 c1 S1 U2 S2 U1 q
C.and(); // c2&c1 S1 U2 S2 U1 q
C.jumpi("double1"); // S1 U2 S2 U1 q
// Returns the double
// H = U2-U1 // Remove U2
C.sub(4, 8, 10); // H S1 U2 S2 U1 q
rm(4); // S1 H S2 U1 q
// // r = 2 * (S2-S1) // Remove S2
C.sub(4, 4, 8); // S1-S2 S1 H S2 U1 q
rm(6); // S1 H S1-S2 U1 q
C.add(4, 4, 8); // 2*(S1-S2) S1 H S1-S2 U1 q
rm(6); // S1 H r U1 q
// I = (2 * H)^2
C.add(2, 2, 8); // 2*H S1 H r U1 q
C.square(0, 10); // (2*H)^2 2*H S1 H r U1 q
rm(2); // I S1 H r U1 q
// V = U1 * I
mul(8, 0, 10); // V I S1 H r U1 q
rm(10); // I S1 H r V q
// J = H * I // Remove I
mul(4, 0, 10); // J I S1 H r V q
rm(2); // J S1 H r V q
// X3 = r^2 - J - 2 * V
// S1J2 = (S1*J)*2 // Remove S1
mul(2, 0, 10); // S1*J J S1 H r V q
rm(4); // J S1*J H r V q
add(2,2, 10); // (S1*J)*2 J S1*J H r V q
rm(4); // J S1J2 H r V q
// X3 = r^2 - J - 2 * V
square(6, 10); // r^2 J S1J2 H r V q
sub(0, 2, 12); // r^2-J r^2 J S1J2 H r V q
rm(2); // r^2-J J S1J2 H r V q
rm(2); // r^2-J S1J2 H r V q
add(8, 8, 10); // 2*V r^2-J S1J2 H r V q
sub(2, 0, 12); // r^2-J-2*V 2*V r^2-J S1J2 H r V q
rm(4); // 2*V X3 S1J2 H r V q
rm(0); // X3 S1J2 H r V q
// Y3 = r * (V-X3)-S1J2
sub(8, 0, 10); // V-X3 X3 S1J2 H r V q
rm(10); // X3 S1J2 H r V-X3 q
mul(6, 8, 10); // r*(V-X3) X3 S1J2 H r V-X3 q
rm(8); // X3 S1J2 H r*(V-X3) V-X3 q
rm(8); // S1J2 H r*(V-X3) X3 q
sub(4, 0, 8); // Y3 S1J2 H r*(V-X3) X3 q
rm(6); // S1J2 H Y3 X3 q
rm(0); // H Y3 X3 q
// Z3 = lastZ * H
loadZ3(); // lastZ3 H Y3 X3 q
mul(0, 2, 8); // Z3 lastZ3 H Y3 X3 q
rm(4); // lastZ3 Z3 Y3 X3 q
rm(0); // Z3 Y3 X3 q
C.swap(1);
C.swap(5);
C.swap(1);
C.swap(4); // X3 Y3 Z3 q
// returns the point in memory
C.label("returnP"); // X Y Z q
rm(0);
rm(0);
rm(0);
C.push(0);
C.push(1);
loadX();
loadY();
C.jump("endpadd");
C.label("double1"); // S1 U2 S2 U1 q
rm(0);
rm(0);
rm(0);
rm(0);
C.push(0);
C.push(1);
loadX();
loadY();
C.jump("double");
C.label("endpadd");
C.returnCall();
function loadX() {
C.push(savedP);
C.mload(); // p
C.push(32);
C.mul(); // P*32
C.push(VAR_POINTS+32);
C.add(); // P*32+32
C.dup(); // P*32+32 P*32+32
C.mload(); // im P*32+32
C.swap(1); // P*32+32 im
C.push(0x20); // 32 P*32+32 im
C.sub(); // P*32 im
C.mload(); // re im
}
function loadY() {
C.push(savedP);
C.mload(); // p
C.push(32);
C.mul(); // P*32
C.push(VAR_POINTS+32*3);
C.add(); // P*32+32
C.dup(); // P*32+32 P*32+32
C.mload(); // im P*32+32
C.swap(1); // P*32+32 im
C.push(0x20); // 32 P*32+32 im
C.sub(); // P*32 im
C.mload(); // re im
}
function loadZ3() {
C.push(savedZ3+32);
C.mload(); // p
C.push(savedZ3);
C.mload();
}
function saveZ3() {
C.push(savedZ3);
C.mstore();
C.push(savedZ3+32);
C.mstore();
}
}
function affine() { // X Y Z q
// If Z2=0 return 0
C.label("affine");
C.dup(4);
C.dup(5 + 1);
C.or();
C.jumpi("notZero"); // X Y Z q
rm(0);
rm(0);
C.push(0);
C.push(0);
C.jmp("endAffine");
C.label("notZero");
inverse2(4,6); // Z_inv X Y Z q
square(2, 8); // Z2_inv Z_inv X Y Z q
mul(0, 2, 10); // Z3_inv Z2_inv Z_inv X Y Z q
rm(4); // Z2_inv Z3_inv X Y Z q
C.push(1);
C.push(0); // 1 Z2_inv Z3_inv X Y Z q
rm(10); // Z2_inv Z3_inv X Y 1 q
mul(2, 6, 10); // YI Z2_inv Z3_inv X Y 1 q
rm(8); // Z2_inv Z3_inv X YI 1 q
mul(0, 4, 10); // XI Z2_inv Z3_inv X YI 1 q
rm(6); // Z2_inv Z3_inv XI YI 1 q
rm(0); // Z3_inv XI YI 1 q
rm(0); // XI YI 1 q
C.label("endAffine");
C.returnCall();
}
function inverse2(a, q) {
C.dup(q); // q
C.dup(q + 1); // q q
C.push(2); // 2 q q
C.sub(); // q-2 q
C.dup(q + 2); // q q-2 q
C.dup(q + 3); // q q q-2 q
C.dup(a + 4); // ar q q q-2 q
C.dup(a + 5); // ar ar q q q-2 q
C.mulmod(); // t0 q q-2 q
C.dup(q + 4); // q t0 q q-2 q
C.dup(a+1 + 5); // ai q t0 q q-2 q
C.dup(a+1 + 6); // ai ai q t0 q q-2 q
C.mulmod(); // t1 t0 q q-2 q
C.addmod(); // t2 q-2 q
C.expmod(); // t3
C.dup(q + 1); // q t3
C.dup(q + 2); // q q t3
C.dup(q + 3); // q q q t3
C.dup(1); // t3 q q q t3
C.sub(); // -t3 q q t3
C.dup(a+1 + 3); // ai -t3 q q t3
C.mulmod(); // ii q t3
C.swap(2); // t3 q ii
C.dup(a + 3); // ar t3 q ii
C.mulmod(); // ir ii
}
function storeVals() {
C.push(VAR_POINTS); // p
for (let i=0; i<NPOINTS; i++) {
const MP = G2.affine(G2.mulScalar(P, i));
for (let j=0; j<2; j++) {
for (let k=0; k<2; k++) {
C.push(toHex256(MP[j][k])); // MP[0][0] p
C.dup(1); // p MP[0][0] p
C.mstore(); // p
C.push(32); // 32 p
C.add(); // p+32
}
}
}
}
}
module.exports.abi = [
{
"constant": true,
"inputs": [
{
"name": "escalar",
"type": "uint256"
}
],
"name": "mulexp",
"outputs": [
{
"name": "",
"type": "uint256"
},
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
}
];
module.exports.createCode = createCode;

View File

@ -1,66 +1,79 @@
const Scalar = require("ffjavascript").Scalar; import {getCurveFromName, Scalar} from "ffjavascript";
const ZqField = require("ffjavascript").ZqField;
const Web3Utils = require("web3-utils");
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617")); import ethers from "ethers";
exports.F = F;
const SEED = "mimc"; const SEED = "mimc";
const NROUNDS = 91; const NROUNDS = 91;
exports.getIV = (seed) => { export default async function buildMimc7() {
if (typeof seed === "undefined") seed = SEED; const bn128 = await getCurveFromName("bn128");
const c = Web3Utils.keccak256(seed+"_iv"); return new Mimc7(bn128.Fr);
const cn = Scalar.fromString(Web3Utils.toBN(c).toString()); }
const iv = Scalar.mod(cn, F.p);
return iv;
};
exports.getConstants = (seed, nRounds) => {
if (typeof seed === "undefined") seed = SEED;
if (typeof nRounds === "undefined") nRounds = NROUNDS;
const cts = new Array(nRounds);
let c = Web3Utils.keccak256(SEED);
for (let i=1; i<nRounds; i++) {
c = Web3Utils.keccak256(c);
const n1 = Web3Utils.toBN(c).mod(Web3Utils.toBN(F.p.toString())); class Mimc7 {
const c2 = Web3Utils.padLeft(Web3Utils.toHex(n1), 64); constructor (F) {
cts[i] = Scalar.fromString(Web3Utils.toBN(c2).toString()); this.F = F;
this.cts = this.getConstants(SEED, 91);
} }
cts[0] = F.e(0);
return cts;
};
const cts = exports.getConstants(SEED, 91); getIV(seed) {
const F = this.F;
if (typeof seed === "undefined") seed = SEED;
const c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(seed+"_iv"));
const cn = Scalar.e(c);
const iv = Scalar.mod(cn, F.p);
return iv;
};
exports.hash = (_x_in, _k) =>{ getConstants(seed, nRounds) {
const x_in = F.e(_x_in); const F = this.F;
const k = F.e(_k); if (typeof seed === "undefined") seed = SEED;
let r; if (typeof nRounds === "undefined") nRounds = NROUNDS;
for (let i=0; i<NROUNDS; i++) { const cts = new Array(nRounds);
const c = cts[i]; let c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(SEED));
const t = (i==0) ? F.add(x_in, k) : F.add(F.add(r, k), c); for (let i=1; i<nRounds; i++) {
r = F.pow(t, 7); c = ethers.utils.keccak256(c);
}
return F.add(r, k);
};
exports.multiHash = (arr, key) => { cts[i] = F.e(c);
let r; }
if (typeof(key) === "undefined") { cts[0] = F.e(0);
r = F.zero; return cts;
} else {
r = key;
} }
for (let i=0; i<arr.length; i++) {
r = F.add( hash (_x_in, _k) {
F.add( const F = this.F;
r, const x_in = F.e(_x_in);
arr[i] const k = F.e(_k);
), let r;
exports.hash(F.e(arr[i]), r) for (let i=0; i<NROUNDS; i++) {
); const c = this.cts[i];
const t = (i==0) ? F.add(x_in, k) : F.add(F.add(r, k), c);
const t2 = F.square(t);
const t4 = F.square(t2);
r = F.mul(F.mul(t4, t2), t);
}
return F.add(r, k);
} }
return r;
}; multiHash(arr, key) {
const F = this.F;
let r;
if (typeof(key) === "undefined") {
r = F.zero;
} else {
r = key;
}
for (let i=0; i<arr.length; i++) {
r = F.add(
F.add(
r,
arr[i]
),
this.hash(F.e(arr[i]), r)
);
}
return r;
}
}

View File

@ -2,13 +2,13 @@
// License: LGPL-3.0+ // License: LGPL-3.0+
// //
const Web3Utils = require("web3-utils"); import ethers from "ethers";
const Contract = require("./evmasm"); import Contract from "./evmasm.js";
function createCode(seed, n) { export function createCode(seed, n) {
let ci = Web3Utils.keccak256(seed); let ci = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(seed));;
const C = new Contract(); const C = new Contract();
@ -51,7 +51,7 @@ function createCode(seed, n) {
C.mulmod(); // r=t^7 k q C.mulmod(); // r=t^7 k q
for (let i=0; i<n-1; i++) { for (let i=0; i<n-1; i++) {
ci = Web3Utils.keccak256(ci); ci = ethers.utils.keccak256(ci);
C.dup(2); // q r k q C.dup(2); // q r k q
C.dup(0); // q q r k q C.dup(0); // q q r k q
C.dup(0); // q q q r k q C.dup(0); // q q q r k q
@ -83,7 +83,7 @@ function createCode(seed, n) {
return C.createTxData(); return C.createTxData();
} }
module.exports.abi = [ export const abi = [
{ {
"constant": true, "constant": true,
"inputs": [ "inputs": [
@ -109,6 +109,5 @@ module.exports.abi = [
} }
]; ];
module.exports.createCode = createCode;

17
src/mimc7_print_iv.js Normal file
View File

@ -0,0 +1,17 @@
import buildMimc7 from "./mimc7.js";
async function run() {
const mimc7 = await buildMimc7();
console.log("IV: "+mimc7.getIV().toString());
}
run().then(()=> {
process.exit(0);
}, (err) => {
console.log(err.stack);
console.log(err.message);
process.exit(1);
});

View File

@ -0,0 +1,25 @@
import buildMimc7 from "./mimc7.js";
async function run() {
const mimc7 = await buildMimc7();
const nRounds = 91;
let S = "[\n";
const cts = mimc7.getConstants();
for (let i=0; i<nRounds; i++) {
S = S + mimc7.F.toString(cts[i]);
if (i<nRounds-1) S = S + ",";
S=S+"\n";
}
S = S + "]\n";
console.log(S);
}
run().then(()=> {
process.exit(0);
}, (err) => {
console.log(err.stack);
console.log(err.message);
process.exit(1);
});

View File

@ -1,4 +1,4 @@
const mimcGenContract = require("./mimc_gencontract"); import {createCode} from "./mimc7_gencontract.js";
const SEED = "mimc"; const SEED = "mimc";
@ -9,5 +9,5 @@ if (typeof process.argv[2] != "undefined") {
nRounds = 91; nRounds = 91;
} }
console.log(mimcGenContract.createCode(SEED, nRounds)); console.log(createCode(SEED, nRounds));

View File

@ -1,3 +0,0 @@
const mimc7 = require("./mimc7.js");
console.log("IV: "+mimc7.getIV().toString());

View File

@ -1,13 +0,0 @@
const mimc7 = require("./mimc7.js");
const nRounds = 91;
let S = "[\n";
const cts = mimc7.getConstants();
for (let i=0; i<nRounds; i++) {
S = S + cts[i].toString();
if (i<nRounds-1) S = S + ",";
S=S+"\n";
}
S = S + "]\n";
console.log(S);

View File

@ -1,86 +1,100 @@
const Scalar = require("ffjavascript").Scalar import { Scalar, getCurveFromName } from "ffjavascript";
const Web3Utils = require("web3-utils"); import ethers from "ethers";
const ZqField = require("ffjavascript").ZqField;
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
const SEED = "mimcsponge"; const SEED = "mimcsponge";
const NROUNDS = 220; const NROUNDS = 220;
exports.getIV = (seed) => { export default async function buildMimcSponge() {
if (typeof seed === "undefined") seed = SEED; const bn128 = await getCurveFromName("bn128");
const c = Web3Utils.keccak256(seed+"_iv"); return new MimcSponge(bn128.Fr);
const cn = Scalar.fromString(Web3Utils.toBN(c).toString()); }
const iv = cn.mod(F.p);
return iv;
};
exports.getConstants = (seed, nRounds) => { class MimcSponge {
if (typeof seed === "undefined") seed = SEED; constructor (F) {
if (typeof nRounds === "undefined") nRounds = NROUNDS; this.F = F;
const cts = new Array(nRounds); this.cts = this.getConstants(SEED, NROUNDS);
let c = Web3Utils.keccak256(SEED);
for (let i=1; i<nRounds; i++) {
c = Web3Utils.keccak256(c);
const n1 = Web3Utils.toBN(c).mod(Web3Utils.toBN(F.p.toString()));
const c2 = Web3Utils.padLeft(Web3Utils.toHex(n1), 64);
cts[i] = F.e(Web3Utils.toBN(c2).toString());
} }
cts[0] = F.e(0);
cts[cts.length - 1] = F.e(0);
return cts;
};
const cts = exports.getConstants(SEED, NROUNDS); getIV (seed) {
const F = this.F;
if (typeof seed === "undefined") seed = SEED;
const c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(seed+"_iv"));
const cn = Scalar.e(c);
const iv = cn.mod(F.p);
return iv;
};
exports.hash = (_xL_in, _xR_in, _k) =>{ getConstants (seed, nRounds) {
let xL = F.e(_xL_in); const F = this.F;
let xR = F.e(_xR_in); if (typeof seed === "undefined") seed = SEED;
const k = F.e(_k); if (typeof nRounds === "undefined") nRounds = NROUNDS;
for (let i=0; i<NROUNDS; i++) { const cts = new Array(nRounds);
const c = cts[i]; let c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(SEED));;
const t = (i==0) ? F.add(xL, k) : F.add(F.add(xL, k), c); for (let i=1; i<nRounds; i++) {
const xR_tmp = F.e(xR); c = ethers.utils.keccak256(c);
if (i < (NROUNDS - 1)) {
xR = xL; cts[i] = F.e(c);
xL = F.add(xR_tmp, F.pow(t, 5)); }
cts[0] = F.e(0);
cts[cts.length - 1] = F.e(0);
return cts;
};
hash(_xL_in, _xR_in, _k) {
const F = this.F;
let xL = F.e(_xL_in);
let xR = F.e(_xR_in);
const k = F.e(_k);
for (let i=0; i<NROUNDS; i++) {
const c = this.cts[i];
const t = (i==0) ? F.add(xL, k) : F.add(F.add(xL, k), c);
const t2 = F.square(t);
const t4 = F.square(t2);
const t5 = F.mul(t4, t);
const xR_tmp = F.e(xR);
if (i < (NROUNDS - 1)) {
xR = xL;
xL = F.add(xR_tmp, t5);
} else {
xR = F.add(xR_tmp, t5);
}
}
return {
xL: xL,
xR: xR
};
}
multiHash(arr, key, numOutputs) {
const F = this.F;
if (typeof(numOutputs) === "undefined") {
numOutputs = 1;
}
if (typeof(key) === "undefined") {
key = F.zero;
}
let R = F.zero;
let C = F.zero;
for (let i=0; i<arr.length; i++) {
R = F.add(R, F.e(arr[i]));
const S = this.hash(R, C, key);
R = S.xL;
C = S.xR;
}
let outputs = [R];
for (let i=1; i < numOutputs; i++) {
const S = this.hash(R, C, key);
R = S.xL;
C = S.xR;
outputs.push(R);
}
if (numOutputs == 1) {
return outputs[0];
} else { } else {
xR = F.add(xR_tmp, F.pow(t, 5)); return outputs;
} }
} }
return { }
xL: F.normalize(xL),
xR: F.normalize(xR),
};
};
exports.multiHash = (arr, key, numOutputs) => {
if (typeof(numOutputs) === "undefined") {
numOutputs = 1;
}
if (typeof(key) === "undefined") {
key = F.zero;
}
let R = F.zero;
let C = F.zero;
for (let i=0; i<arr.length; i++) {
R = F.add(R, F.e(arr[i]));
const S = exports.hash(R, C, key);
R = S.xL;
C = S.xR;
}
let outputs = [R];
for (let i=1; i < numOutputs; i++) {
const S = exports.hash(R, C, key);
R = S.xL;
C = S.xR;
outputs.push(R);
}
if (numOutputs == 1) {
return F.normalize(outputs[0]);
} else {
return outputs.map(x => F.normalize(x));
}
};

View File

@ -2,13 +2,13 @@
// License: LGPL-3.0+ // License: LGPL-3.0+
// //
const Web3Utils = require("web3-utils"); import ethers from "ethers";
const Contract = require("./evmasm"); import Contract from "./evmasm.js";
function createCode(seed, n) { export function createCode(seed, n) {
let ci = Web3Utils.keccak256(seed); let ci = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(seed));
const C = new Contract(); const C = new Contract();
@ -51,7 +51,7 @@ function createCode(seed, n) {
for (let i=0; i<n-1; i++) { for (let i=0; i<n-1; i++) {
if (i < n-2) { if (i < n-2) {
ci = Web3Utils.keccak256(ci); ci = ethers.utils.keccak256(ci);
} else { } else {
ci = "0x00"; ci = "0x00";
} }
@ -89,7 +89,7 @@ function createCode(seed, n) {
return C.createTxData(); return C.createTxData();
} }
module.exports.abi = [ export const abi = [
{ {
"constant": true, "constant": true,
"inputs": [ "inputs": [
@ -123,6 +123,3 @@ module.exports.abi = [
} }
]; ];
module.exports.createCode = createCode;

View File

@ -1,13 +1,24 @@
const mimcsponge = require("./mimcsponge.js"); import buildMimcSponge from "./mimcsponge.js";
const nRounds = 220; async function run() {
let S = "[\n"; const mimcsponge = await buildMimcSponge();
const cts = mimcsponge.getConstants(); const nRounds = 220;
for (let i=0; i<nRounds; i++) { let S = "[\n";
S = S + cts[i].toString(); const cts = mimcsponge.getConstants();
if (i<nRounds-1) S = S + ","; for (let i=0; i<nRounds; i++) {
S=S+"\n"; S = S + mimcsponge.F.toString(cts[i]);
if (i<nRounds-1) S = S + ",";
S=S+"\n";
}
S = S + "]\n";
console.log(S);
} }
S = S + "]\n";
console.log(S); run().then(()=> {
process.exit(0);
}, (err) => {
console.log(err.stack);
console.log(err.message);
process.exit(1);
});

View File

@ -1,4 +1,4 @@
const mimcGenContract = require("./mimcsponge_gencontract"); import {createCode} from "./mimcsponge_gencontract.js";
const SEED = "mimcsponge"; const SEED = "mimcsponge";
@ -9,5 +9,5 @@ if (typeof process.argv[2] != "undefined") {
nRounds = 220; nRounds = 220;
} }
console.log(mimcGenContract.createCode(SEED, nRounds)); console.log(createCode(SEED, nRounds));

View File

@ -1,119 +1,129 @@
const babyJub = require("./babyjub"); import buildBabyJub from "./babyjub.js";
const createBlakeHash = require("blake-hash"); import blake2b from "blake2b";
const blake2b = require("blake2b"); import createBlakeHash from "blake-hash";
const Scalar = require("ffjavascript").Scalar; import { Scalar } from "ffjavascript";
const GENPOINT_PREFIX = "PedersenGenerator"; const GENPOINT_PREFIX = "PedersenGenerator";
const windowSize = 4; const windowSize = 4;
const nWindowsPerSegment = 50; const nWindowsPerSegment = 50;
exports.hash = pedersenHash; export default async function buildPedersenHash() {
exports.getBasePoint = getBasePoint; const babyJub = await buildBabyJub();
return new PedersenHash(babyJub);
function baseHash(type, S) {
if (type == "blake") {
return createBlakeHash("blake256").update(S).digest();
} else if (type == "blake2b") {
return Buffer.from(blake2b(32).update(Buffer.from(S)).digest());
}
} }
function pedersenHash(msg, options) { class PedersenHash {
options = options || {};
options.baseHash = options.baseHash || "blake";
const bitsPerSegment = windowSize*nWindowsPerSegment;
const bits = buffer2bits(msg);
const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1; constructor(babyJub) {
this.babyJub = babyJub;
this.bases = [];
}
let accP = [babyJub.F.zero,babyJub.F.one]; baseHash(type, S) {
if (type == "blake") {
for (let s=0; s<nSegments; s++) { return createBlakeHash("blake256").update(S).digest();
let nWindows; } else if (type == "blake2b") {
if (s == nSegments-1) { return Buffer.from(blake2b(32).update(Buffer.from(S)).digest());
nWindows = Math.floor(((bits.length - (nSegments - 1)*bitsPerSegment) - 1) / windowSize) +1;
} else {
nWindows = nWindowsPerSegment;
} }
let escalar = Scalar.e(0); }
let exp = Scalar.e(1);
for (let w=0; w<nWindows; w++) { hash(msg, options) {
let o = s*bitsPerSegment + w*windowSize; options = options || {};
let acc = Scalar.e(1); options.baseHash = options.baseHash || "blake";
for (let b=0; ((b<windowSize-1)&&(o<bits.length)) ; b++) { const babyJub = this.babyJub;
if (bits[o]) { const bitsPerSegment = windowSize*nWindowsPerSegment;
acc = Scalar.add(acc, Scalar.shl(Scalar.e(1), b) ); const bits = this.buffer2bits(msg);
}
o++; const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1;
let accP = [babyJub.F.zero,babyJub.F.one];
for (let s=0; s<nSegments; s++) {
let nWindows;
if (s == nSegments-1) {
nWindows = Math.floor(((bits.length - (nSegments - 1)*bitsPerSegment) - 1) / windowSize) +1;
} else {
nWindows = nWindowsPerSegment;
} }
if (o<bits.length) { let escalar = Scalar.e(0);
if (bits[o]) { let exp = Scalar.e(1);
acc = Scalar.neg(acc); for (let w=0; w<nWindows; w++) {
let o = s*bitsPerSegment + w*windowSize;
let acc = Scalar.e(1);
for (let b=0; ((b<windowSize-1)&&(o<bits.length)) ; b++) {
if (bits[o]) {
acc = Scalar.add(acc, Scalar.shl(Scalar.e(1), b) );
}
o++;
} }
o++; if (o<bits.length) {
if (bits[o]) {
acc = Scalar.neg(acc);
}
o++;
}
escalar = Scalar.add(escalar, Scalar.mul(acc, exp));
exp = Scalar.shl(exp, windowSize+1);
} }
escalar = Scalar.add(escalar, Scalar.mul(acc, exp));
exp = Scalar.shl(exp, windowSize+1); if (Scalar.lt(escalar, 0)) {
escalar = Scalar.add( escalar, babyJub.subOrder);
}
accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(this.getBasePoint(options.baseHash, s), escalar));
} }
if (Scalar.lt(escalar, 0)) { return babyJub.packPoint(accP);
escalar = Scalar.add( escalar, babyJub.subOrder); }
getBasePoint(baseHashType, pointIdx) {
const babyJub = this.babyJub;
if (this.bases[pointIdx]) return this.bases[pointIdx];
let p= null;
let tryIdx = 0;
while (p==null) {
const S = GENPOINT_PREFIX + "_" + this.padLeftZeros(pointIdx, 32) + "_" + this.padLeftZeros(tryIdx, 32);
const h = this.baseHash(baseHashType, S);
h[31] = h[31] & 0xBF; // Set 255th bit to 0 (256th is the signal and 254th is the last possible bit to 1)
p = babyJub.unpackPoint(h);
tryIdx++;
} }
accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(getBasePoint(options.baseHash, s), escalar)); const p8 = babyJub.mulPointEscalar(p, 8);
if (!babyJub.inSubgroup(p8)) {
throw new Error("Point not in curve");
}
this.bases[pointIdx] = p8;
return p8;
} }
return babyJub.packPoint(accP); padLeftZeros(idx, n) {
} let sidx = "" + idx;
while (sidx.length<n) sidx = "0"+sidx;
let bases = []; return sidx;
function getBasePoint(baseHashType, pointIdx) {
if (pointIdx<bases.length) return bases[pointIdx];
let p= null;
let tryIdx = 0;
while (p==null) {
const S = GENPOINT_PREFIX + "_" + padLeftZeros(pointIdx, 32) + "_" + padLeftZeros(tryIdx, 32);
const h = baseHash(baseHashType, S);
h[31] = h[31] & 0xBF; // Set 255th bit to 0 (256th is the signal and 254th is the last possible bit to 1)
p = babyJub.unpackPoint(h);
tryIdx++;
} }
const p8 = babyJub.mulPointEscalar(p, 8); /*
Input a buffer
if (!babyJub.inSubgroup(p8)) { Returns an array of booleans. 0 is LSB of first byte and so on.
throw new Error("Point not in curve"); */
buffer2bits(buff) {
const res = new Array(buff.length*8);
for (let i=0; i<buff.length; i++) {
const b = buff[i];
res[i*8] = (b & 0x01);
res[i*8+1] = (b & 0x02) >> 1;
res[i*8+2] = (b & 0x04) >> 2;
res[i*8+3] = (b & 0x08) >> 3;
res[i*8+4] = (b & 0x10) >> 4;
res[i*8+5] = (b & 0x20) >> 5;
res[i*8+6] = (b & 0x40) >> 6;
res[i*8+7] = (b & 0x80) >> 7;
}
return res;
} }
bases[pointIdx] = p8;
return p8;
}
function padLeftZeros(idx, n) {
let sidx = "" + idx;
while (sidx.length<n) sidx = "0"+sidx;
return sidx;
}
/*
Input a buffer
Returns an array of booleans. 0 is LSB of first byte and so on.
*/
function buffer2bits(buff) {
const res = new Array(buff.length*8);
for (let i=0; i<buff.length; i++) {
const b = buff[i];
res[i*8] = b & 0x01;
res[i*8+1] = b & 0x02;
res[i*8+2] = b & 0x04;
res[i*8+3] = b & 0x08;
res[i*8+4] = b & 0x10;
res[i*8+5] = b & 0x20;
res[i*8+6] = b & 0x40;
res[i*8+7] = b & 0x80;
}
return res;
} }

View File

@ -1,21 +1,35 @@
const pedersenHash = require("./pedersenHash.js"); import buildPedersenHash from "./pedersenhash.js";
async function run() {
const pedersenHash = await buildPedersenHash();
let nBases;
if (typeof process.argv[2] != "undefined") {
nBases = parseInt(process.argv[2]);
} else {
nBases = 5;
}
let baseHash;
if (typeof process.argv[3] != "undefined") {
baseHash = process.argv[3];
} else {
baseHash = "blake";
}
for (let i=0; i < nBases; i++) {
const p = pedersenHash.getBasePoint(i);
console.log(`[${pedersenHash.babyJub.F.toString(p[0])},${pedersenHash.babyJub.F.toString(p[1])}]`);
}
let nBases;
if (typeof process.argv[2] != "undefined") {
nBases = parseInt(process.argv[2]);
} else {
nBases = 5;
} }
let baseHash; run().then(()=> {
if (typeof process.argv[3] != "undefined") { process.exit(0);
baseHash = process.argv[3]; }, (err) => {
} else { console.log(err.stack);
baseHash = "blake"; console.log(err.message);
} process.exit(1);
});
for (let i=0; i < nBases; i++) {
const p = pedersenHash.getBasePoint(baseHash, i);
console.log(`[${p[0]},${p[1]}]`);
}

View File

@ -1,80 +1,107 @@
const assert = require("assert");
const Scalar = require("ffjavascript").Scalar;
const ZqField = require("ffjavascript").ZqField;
const { unstringifyBigInts } = require("ffjavascript").utils;
// const F = new ZqField(Scalar.fromString("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")); // bls
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617")); // bn128
// Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage // Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage
// Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 // Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const opt = unstringifyBigInts(require("./poseidon_constants_opt.json"));
// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8) // Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py // Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
// And rounded up to nearest integer that divides by t // And rounded up to nearest integer that divides by t
const N_ROUNDS_F = 8;
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
const pow5 = a => F.mul(a, F.square(F.square(a, a))); // Optimization is taken from https://github.com/filecoin-project/neptune
function poseidon(inputs) { import assert from "assert";
assert(inputs.length > 0); import { getCurveFromName } from "ffjavascript";
assert(inputs.length <= N_ROUNDS_P.length);
const t = inputs.length + 1; import poseidonConstants from "./poseidon_constants_opt.js";
const nRoundsF = N_ROUNDS_F;
const nRoundsP = N_ROUNDS_P[t - 2];
const C = opt.C[t-2];
const S = opt.S[t-2];
const M = opt.M[t-2];
const P = opt.P[t-2];
let state = [F.zero, ...inputs.map(a => F.e(a))]; function unsringifyConstants(Fr, o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
state = state.map((a, i) => F.add(a, C[i])); return Fr.e(o);
} else if ((typeof(o) == "string") && (/^0x[0-9a-fA-F]+$/.test(o) )) {
for (let r = 0; r < nRoundsF/2-1; r++) { return Fr.e(o);
state = state.map(a => pow5(a)); } else if (Array.isArray(o)) {
state = state.map((a, i) => F.add(a, C[(r +1)* t +i])); return o.map(unsringifyConstants.bind(null, Fr));
state = state.map((_, i) => } else if (typeof o == "object") {
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero) if (o===null) return null;
); const res = {};
const keys = Object.keys(o);
keys.forEach( (k) => {
res[k] = unsringifyConstants(Fr, o[k]);
});
return res;
} else {
return o;
} }
state = state.map(a => pow5(a));
state = state.map((a, i) => F.add(a, C[(nRoundsF/2-1 +1)* t +i]));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(P[j][i], a)), F.zero)
);
for (let r = 0; r < nRoundsP; r++) {
state[0] = pow5(state[0]);
state[0] = F.add(state[0], C[(nRoundsF/2 +1)*t + r]);
const s0 = state.reduce((acc, a, j) => {
return F.add(acc, F.mul(S[(t*2-1)*r+j], a));
}, F.zero);
for (let k=1; k<t; k++) {
state[k] = F.add(state[k], F.mul(state[0], S[(t*2-1)*r+t+k-1] ));
}
state[0] =s0;
}
for (let r = 0; r < nRoundsF/2-1; r++) {
state = state.map(a => pow5(a));
state = state.map((a, i) => F.add(a, C[ (nRoundsF/2 +1)*t + nRoundsP + r*t + i ]));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero)
);
}
state = state.map(a => pow5(a));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero)
);
return F.normalize(state[0]);
} }
module.exports = poseidon; export default async function buildPoseidon() {
const bn128 = await getCurveFromName("bn128");
const F = bn128.Fr;
const opt = unsringifyConstants(F, poseidonConstants);
const N_ROUNDS_F = 8;
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
const pow5 = a => F.mul(a, F.square(F.square(a, a)));
function poseidon(inputs) {
assert(inputs.length > 0);
assert(inputs.length <= N_ROUNDS_P.length);
const t = inputs.length + 1;
const nRoundsF = N_ROUNDS_F;
const nRoundsP = N_ROUNDS_P[t - 2];
const C = opt.C[t-2];
const S = opt.S[t-2];
const M = opt.M[t-2];
const P = opt.P[t-2];
let state = [F.zero, ...inputs.map(a => F.e(a))];
state = state.map((a, i) => F.add(a, C[i]));
for (let r = 0; r < nRoundsF/2-1; r++) {
state = state.map(a => pow5(a));
state = state.map((a, i) => F.add(a, C[(r +1)* t +i]));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero)
);
}
state = state.map(a => pow5(a));
state = state.map((a, i) => F.add(a, C[(nRoundsF/2-1 +1)* t +i]));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(P[j][i], a)), F.zero)
);
for (let r = 0; r < nRoundsP; r++) {
state[0] = pow5(state[0]);
state[0] = F.add(state[0], C[(nRoundsF/2 +1)*t + r]);
const s0 = state.reduce((acc, a, j) => {
return F.add(acc, F.mul(S[(t*2-1)*r+j], a));
}, F.zero);
for (let k=1; k<t; k++) {
state[k] = F.add(state[k], F.mul(state[0], S[(t*2-1)*r+t+k-1] ));
}
state[0] =s0;
}
for (let r = 0; r < nRoundsF/2-1; r++) {
state = state.map(a => pow5(a));
state = state.map((a, i) => F.add(a, C[ (nRoundsF/2 +1)*t + nRoundsP + r*t + i ]));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero)
);
}
state = state.map(a => pow5(a));
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero)
);
return state[0];
}
poseidon.F = F;
return poseidon;
}

207
src/poseidon_constants.js Normal file

File diff suppressed because one or more lines are too long

24806
src/poseidon_constants_opt.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,14 @@
// License: LGPL-3.0+ // License: LGPL-3.0+
// //
const Contract = require("./evmasm"); import Contract from "./evmasm.js";
const { unstringifyBigInts } = require("ffjavascript").utils; import { utils } from "ffjavascript";
const Web3Utils = require("web3-utils"); const { unstringifyBigInts } = utils;
import ethers from "ethers";
const { C:K, M } = unstringifyBigInts(require("./poseidon_constants.json")); import poseidonConstants from "./poseidon_constants.js";
const { C:K, M } = unstringifyBigInts(poseidonConstants);
const N_ROUNDS_F = 8; const N_ROUNDS_F = 8;
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63]; const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63];
@ -17,7 +20,7 @@ function toHex256(a) {
return "0x" + S; return "0x" + S;
} }
function createCode(nInputs) { export function createCode(nInputs) {
if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8"); if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8");
const t = nInputs + 1; const t = nInputs + 1;
@ -101,10 +104,10 @@ function createCode(nInputs) {
C.calldataload(); C.calldataload();
C.div(); C.div();
C.dup(0); C.dup(0);
C.push(Web3Utils.keccak256(`poseidon(uint256[${nInputs}])`).slice(0, 10)); // poseidon(uint256[n]) C.push(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(`poseidon(uint256[${nInputs}])`)).slice(0, 10)); // poseidon(uint256[n])
C.eq(); C.eq();
C.swap(1); C.swap(1);
C.push(Web3Utils.keccak256(`poseidon(bytes32[${nInputs}])`).slice(0, 10)); // poseidon(bytes32[n]) C.push(ethers.utils.keccak256(ethers.utils.toUtf8Bytes(`poseidon(bytes32[${nInputs}])`)).slice(0, 10)); // poseidon(bytes32[n])
C.eq(); C.eq();
C.or(); C.or();
C.jmpi("start"); C.jmpi("start");
@ -155,7 +158,7 @@ function createCode(nInputs) {
return C.createTxData(); return C.createTxData();
} }
function generateABI(nInputs) { export function generateABI(nInputs) {
return [ return [
{ {
"constant": true, "constant": true,
@ -202,7 +205,5 @@ function generateABI(nInputs) {
]; ];
} }
module.exports.generateABI = generateABI;
module.exports.createCode = createCode;

View File

@ -1,4 +1,4 @@
const poseidonGenContract = require("./poseidon_gencontract"); import {createCode} from "./poseidon_gencontract.js";
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.log("Usage: node poseidon_gencontract.js [numberOfInputs]"); console.log("Usage: node poseidon_gencontract.js [numberOfInputs]");
@ -9,5 +9,5 @@ const nInputs = Number(process.argv[2]);
console.log(nInputs); console.log(nInputs);
console.log(poseidonGenContract.createCode(nInputs)); console.log(createCode(nInputs));

View File

@ -1,46 +1,72 @@
const assert = require("assert");
const Scalar = require("ffjavascript").Scalar;
const ZqField = require("ffjavascript").ZqField;
const { unstringifyBigInts } = require("ffjavascript").utils;
// Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 import assert from "assert";
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617")); import { getCurveFromName } from "ffjavascript";
// Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage import poseidonConstants from "./poseidon_constants.js";
// Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const { C, M } = unstringifyBigInts(require("./poseidon_constants.json"));
// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8) function unsringifyConstants(Fr, o) {
// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
// And rounded up to nearest integer that divides by t return Fr.e(o);
const N_ROUNDS_F = 8; } else if ((typeof(o) == "string") && (/^0x[0-9a-fA-F]+$/.test(o) )) {
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; return Fr.e(o);
} else if (Array.isArray(o)) {
const pow5 = a => F.mul(a, F.square(F.square(a, a))); return o.map(unsringifyConstants.bind(null, Fr));
} else if (typeof o == "object") {
function poseidon(inputs) { if (o===null) return null;
assert(inputs.length > 0); const res = {};
assert(inputs.length <= N_ROUNDS_P.length); const keys = Object.keys(o);
keys.forEach( (k) => {
const t = inputs.length + 1; res[k] = unsringifyConstants(Fr, o[k]);
const nRoundsF = N_ROUNDS_F; });
const nRoundsP = N_ROUNDS_P[t - 2]; return res;
} else {
let state = [F.zero, ...inputs.map(a => F.e(a))]; return o;
for (let r = 0; r < nRoundsF + nRoundsP; r++) {
state = state.map((a, i) => F.add(a, C[t - 2][r * t + i]));
if (r < nRoundsF / 2 || r >= nRoundsF / 2 + nRoundsP) {
state = state.map(a => pow5(a));
} else {
state[0] = pow5(state[0]);
}
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[t - 2][i][j], a)), F.zero)
);
} }
return F.normalize(state[0]);
} }
module.exports = poseidon; export default async function buildPoseidon() {
const bn128 = await getCurveFromName("bn128");
const F = bn128.Fr;
// Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage
// Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const {C, M} = unsringifyConstants(F, poseidonConstants);
// Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
// Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py
// And rounded up to nearest integer that divides by t
const N_ROUNDS_F = 8;
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
const pow5 = a => F.mul(a, F.square(F.square(a, a)));
function poseidon(inputs) {
assert(inputs.length > 0);
assert(inputs.length <= N_ROUNDS_P.length);
const t = inputs.length + 1;
const nRoundsF = N_ROUNDS_F;
const nRoundsP = N_ROUNDS_P[t - 2];
let state = [F.zero, ...inputs.map(a => F.e(a))];
for (let r = 0; r < nRoundsF + nRoundsP; r++) {
state = state.map((a, i) => F.add(a, C[t - 2][r * t + i]));
if (r < nRoundsF / 2 || r >= nRoundsF / 2 + nRoundsP) {
state = state.map(a => pow5(a));
} else {
state[0] = pow5(state[0]);
}
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[t - 2][i][j], a)), F.zero)
);
}
return state[0];
}
poseidon.F = F;
return poseidon;
}

View File

@ -1,16 +1,27 @@
const Scalar = require("ffjavascript").Scalar; import { Scalar } from "ffjavascript";
const SMTMemDB = require("./smt_memdb"); import SMTMemDB from "./smt_memdb.js";
const {hash0, hash1, F} = require("./smt_hashes_poseidon"); import getHashes from "./smt_hashes_poseidon.js";
class SMT { export async function buildSMT(db, root) {
constructor(db, root) { const {hash0, hash1,F} = await getHashes();
return new SMT(db, root, hash0, hash1, F);
}
export class SMT {
constructor(db, root, hash0, hash1, F) {
this.db = db; this.db = db;
this.root = root; this.root = root;
this.hash0 = hash0;
this.hash1 = hash1;
this.F = F;
} }
_splitBits(_key) { _splitBits(_key) {
const res = Scalar.bits(_key); const F = this.F;
const res = Scalar.bits(F.toObject(_key));
while (res.length<256) res.push(false); while (res.length<256) res.push(false);
@ -18,10 +29,10 @@ class SMT {
} }
async update(_key, _newValue) { async update(_key, _newValue) {
const key = Scalar.e(_key); const F = this.F;
const key = F.e(_key);
const newValue = F.e(_newValue); const newValue = F.e(_newValue);
const resFind = await this.find(key); const resFind = await this.find(key);
const res = {}; const res = {};
res.oldRoot = this.root; res.oldRoot = this.root;
@ -34,8 +45,8 @@ class SMT {
const ins = []; const ins = [];
const dels = []; const dels = [];
let rtOld = hash1(key, resFind.foundValue); let rtOld = this.hash1(key, resFind.foundValue);
let rtNew = hash1(key, newValue); let rtNew = this.hash1(key, newValue);
ins.push([rtNew, [1, key, newValue ]]); ins.push([rtNew, [1, key, newValue ]]);
dels.push(rtOld); dels.push(rtOld);
@ -50,8 +61,8 @@ class SMT {
oldNode = [rtOld, sibling]; oldNode = [rtOld, sibling];
newNode = [rtNew, sibling]; newNode = [rtNew, sibling];
} }
rtOld = hash0(oldNode[0], oldNode[1]); rtOld = this.hash0(oldNode[0], oldNode[1]);
rtNew = hash0(newNode[0], newNode[1]); rtNew = this.hash0(newNode[0], newNode[1]);
dels.push(rtOld); dels.push(rtOld);
ins.push([rtNew, newNode]); ins.push([rtNew, newNode]);
} }
@ -67,7 +78,8 @@ class SMT {
} }
async delete(_key) { async delete(_key) {
const key = Scalar.e(_key); const F = this.F;
const key = F.e(_key);
const resFind = await this.find(key); const resFind = await this.find(key);
if (!resFind.found) throw new Error("Key does not exists"); if (!resFind.found) throw new Error("Key does not exists");
@ -80,7 +92,7 @@ class SMT {
const dels = []; const dels = [];
const ins = []; const ins = [];
let rtOld = hash1(key, resFind.foundValue); let rtOld = this.hash1(key, resFind.foundValue);
let rtNew; let rtNew;
dels.push(rtOld); dels.push(rtOld);
@ -118,9 +130,9 @@ class SMT {
} }
const oldSibling = resFind.siblings[level]; const oldSibling = resFind.siblings[level];
if (keyBits[level]) { if (keyBits[level]) {
rtOld = hash0(oldSibling, rtOld); rtOld = this.hash0(oldSibling, rtOld);
} else { } else {
rtOld = hash0(rtOld, oldSibling); rtOld = this.hash0(rtOld, oldSibling);
} }
dels.push(rtOld); dels.push(rtOld);
if (!F.isZero(newSibling)) { if (!F.isZero(newSibling)) {
@ -135,7 +147,7 @@ class SMT {
} else { } else {
newNode = [rtNew, newSibling]; newNode = [rtNew, newSibling];
} }
rtNew = hash0(newNode[0], newNode[1]); rtNew = this.hash0(newNode[0], newNode[1]);
ins.push([rtNew, newNode]); ins.push([rtNew, newNode]);
} }
} }
@ -152,7 +164,8 @@ class SMT {
} }
async insert(_key, _value) { async insert(_key, _value) {
const key = Scalar.e(_key); const F = this.F;
const key = F.e(_key);
const value = F.e(_value); const value = F.e(_value);
let addedOne = false; let addedOne = false;
const res = {}; const res = {};
@ -173,7 +186,7 @@ class SMT {
for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) { for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) {
res.siblings.push(F.zero); res.siblings.push(F.zero);
} }
rtOld = hash1(resFind.notFoundKey, resFind.notFoundValue); rtOld = this.hash1(resFind.notFoundKey, resFind.notFoundValue);
res.siblings.push(rtOld); res.siblings.push(rtOld);
addedOne = true; addedOne = true;
mixed = false; mixed = false;
@ -185,7 +198,7 @@ class SMT {
const inserts = []; const inserts = [];
const dels = []; const dels = [];
let rt = hash1(key, value); let rt = this.hash1(key, value);
inserts.push([rt,[1, key, value]] ); inserts.push([rt,[1, key, value]] );
for (let i=res.siblings.length-1; i>=0; i--) { for (let i=res.siblings.length-1; i>=0; i--) {
@ -195,9 +208,9 @@ class SMT {
if (mixed) { if (mixed) {
const oldSibling = resFind.siblings[i]; const oldSibling = resFind.siblings[i];
if (newKeyBits[i]) { if (newKeyBits[i]) {
rtOld = hash0(oldSibling, rtOld); rtOld = this.hash0(oldSibling, rtOld);
} else { } else {
rtOld = hash0(rtOld, oldSibling); rtOld = this.hash0(rtOld, oldSibling);
} }
dels.push(rtOld); dels.push(rtOld);
} }
@ -205,10 +218,10 @@ class SMT {
let newRt; let newRt;
if (newKeyBits[i]) { if (newKeyBits[i]) {
newRt = hash0(res.siblings[i], rt); newRt = this.hash0(res.siblings[i], rt);
inserts.push([newRt,[res.siblings[i], rt]] ); inserts.push([newRt,[res.siblings[i], rt]] );
} else { } else {
newRt = hash0(rt, res.siblings[i]); newRt = this.hash0(rt, res.siblings[i]);
inserts.push([newRt,[rt, res.siblings[i]]] ); inserts.push([newRt,[rt, res.siblings[i]]] );
} }
rt = newRt; rt = newRt;
@ -232,12 +245,14 @@ class SMT {
return res; return res;
} }
async find(key) { async find(_key) {
const key = this.F.e(_key);
const keyBits = this._splitBits(key); const keyBits = this._splitBits(key);
return await this._find(key, keyBits, this.root, 0); return await this._find(key, keyBits, this.root, 0);
} }
async _find(key, keyBits, root, level) { async _find(key, keyBits, root, level) {
const F = this.F;
if (typeof root === "undefined") root = this.root; if (typeof root === "undefined") root = this.root;
let res; let res;
@ -284,18 +299,11 @@ class SMT {
} }
} }
async function loadFromFile(fileName) {
} export async function newMemEmptyTrie() {
const {hash0, hash1,F} = await getHashes();
async function newMemEmptyTrie() { const db = new SMTMemDB(F);
const db = new SMTMemDB();
const rt = await db.getRoot(); const rt = await db.getRoot();
const smt = new SMT(db, rt); const smt = new SMT(db, rt, hash0, hash1, F);
return smt; return smt;
} }
module.exports.loadFromFile = loadFromFile;
module.exports.newMemEmptyTrie = newMemEmptyTrie;
module.exports.SMT = SMT;
module.exports.SMTMemDB = SMTMemDB;

View File

@ -1,12 +1,16 @@
const mimc7 = require("./mimc7");
const bigInt = require("big-integer");
exports.hash0 = function (left, right) { import buildMimc7 from "./mimc7.js";
return mimc7.multiHash(left, right);
};
exports.hash1 = function(key, value) { export default async function getHashes() {
return mimc7.multiHash([key, value], bigInt.one); const mimc7 = await buildMimc7();
}; return {
hash0: function (left, right) {
return mimc7.hash(left, right);
},
hash1: function(key, value) {
return mimc7.multiHash([key, value], F.one);
},
F: mimc7.F
}
}
exports.F = mimc7.F;

View File

@ -1,18 +1,17 @@
import buildPoseidon from "./poseidon.js";
const ZqField = require("ffjavascript").ZqField; import { getCurveFromName } from "ffjavascript";
const Scalar = require("ffjavascript").Scalar;
const poseidon = require("./poseidon");
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
exports.hash0 = function (left, right) { export default async function getHashes() {
return poseidon([left, right]); const bn128 = await getCurveFromName("bn128");
}; const poseidon = await buildPoseidon();
return {
exports.hash1 = function(key, value) { hash0: function (left, right) {
return poseidon([key, value, F.one]); return poseidon([left, right]);
}; },
hash1: function(key, value) {
exports.F = F; return poseidon([key, value, bn128.Fr.one]);
},
F: bn128.Fr
}
}

View File

@ -1,14 +1,8 @@
export default class SMTMemDb {
const Scalar = require("ffjavascript").Scalar; constructor(F) {
const ZqField = require("ffjavascript").ZqField;
// Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
class SMTMemDb {
constructor() {
this.nodes = {}; this.nodes = {};
this.root = F.zero; this.root = F.zero;
this.F = F;
} }
async getRoot() { async getRoot() {
@ -16,14 +10,15 @@ class SMTMemDb {
} }
_key2str(k) { _key2str(k) {
// const keyS = bigInt(key).leInt2Buff(32).toString("hex"); const F = this.F;
const keyS = k.toString(); const keyS = this.F.toString(k);
return keyS; return keyS;
} }
_normalize(n) { _normalize(n) {
const F = this.F;
for (let i=0; i<n.length; i++) { for (let i=0; i<n.length; i++) {
n[i] = F.e(n[i]); n[i] = this.F.e(n[i]);
} }
} }
@ -60,4 +55,3 @@ class SMTMemDb {
} }
} }
module.exports = SMTMemDb;

17
src/testblake.js Normal file
View File

@ -0,0 +1,17 @@
import createBlakeHash from 'blake-hash';
import blake2b from "blake2b";
const msg = (new TextEncoder()).encode("blake256");
const msgB = Buffer.from(msg)
const toHexString = bytes =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
const h1 = createBlakeHash('blake256').digest();
const h2 = blake2b(64).digest();
console.log(toHexString(h1));
console.log(toHexString(h2));

View File

@ -1,6 +1,6 @@
const chai = require("chai"); import chai from "chai";
const babyjub = require("../src/babyjub.js"); import buildBabyjub from "../src/babyjub.js";
const Scalar = require("ffjavascript").Scalar; import { Scalar } from "ffjavascript";
const assert = chai.assert; const assert = chai.assert;
@ -14,9 +14,16 @@ function buff2hex(buff) {
} }
describe("Baby Jub js test", function () { describe("Baby Jub js test", function () {
let babyjub;
this.timeout(100000); this.timeout(100000);
before(async () => {
babyjub = await buildBabyjub();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should add point (0,1) and (0,1)", () => { it("Should add point (0,1) and (0,1)", () => {
const p1 = [ const p1 = [
@ -84,10 +91,10 @@ describe("Baby Jub js test", function () {
const r = babyjub.mulPointEscalar(p, 3); const r = babyjub.mulPointEscalar(p, 3);
let r2 = babyjub.addPoint(p, p); let r2 = babyjub.addPoint(p, p);
r2 = babyjub.addPoint(r2, p); r2 = babyjub.addPoint(r2, p);
assert.equal(r2[0].toString(), r[0].toString()); assert(babyjub.F.eq(r2[0], r[0]));
assert.equal(r2[1].toString(), r[1].toString()); assert(babyjub.F.eq(r2[1], r[1]));
assert.equal(r[0].toString(), "19372461775513343691590086534037741906533799473648040012278229434133483800898"); assert(babyjub.F.eq(r[0], babyjub.F.e("19372461775513343691590086534037741906533799473648040012278229434133483800898")));
assert.equal(r[1].toString(), "9458658722007214007257525444427903161243386465067105737478306991484593958249"); assert(babyjub.F.eq(r[1], babyjub.F.e("9458658722007214007257525444427903161243386465067105737478306991484593958249")));
}); });
it("should mulPointEscalar 1", () => { it("should mulPointEscalar 1", () => {
@ -97,8 +104,8 @@ describe("Baby Jub js test", function () {
]; ];
const r = babyjub.mulPointEscalar(p, Scalar.fromString("14035240266687799601661095864649209771790948434046947201833777492504781204499")); const r = babyjub.mulPointEscalar(p, Scalar.fromString("14035240266687799601661095864649209771790948434046947201833777492504781204499"));
assert.equal(r[0].toString(), "17070357974431721403481313912716834497662307308519659060910483826664480189605"); assert(babyjub.F.eq(r[0], babyjub.F.e("17070357974431721403481313912716834497662307308519659060910483826664480189605")));
assert.equal(r[1].toString(), "4014745322800118607127020275658861516666525056516280575712425373174125159339"); assert(babyjub.F.eq(r[1], babyjub.F.e("4014745322800118607127020275658861516666525056516280575712425373174125159339")));
}); });
it("should mulPointEscalar 2", () => { it("should mulPointEscalar 2", () => {
@ -108,8 +115,8 @@ describe("Baby Jub js test", function () {
]; ];
const r = babyjub.mulPointEscalar(p, Scalar.fromString("20819045374670962167435360035096875258406992893633759881276124905556507972311")); const r = babyjub.mulPointEscalar(p, Scalar.fromString("20819045374670962167435360035096875258406992893633759881276124905556507972311"));
assert.equal(r[0].toString(), "13563888653650925984868671744672725781658357821216877865297235725727006259983"); assert(babyjub.F.eq(r[0], babyjub.F.e("13563888653650925984868671744672725781658357821216877865297235725727006259983")));
assert.equal(r[1].toString(), "8442587202676550862664528699803615547505326611544120184665036919364004251662"); assert(babyjub.F.eq(r[1], babyjub.F.e("8442587202676550862664528699803615547505326611544120184665036919364004251662")));
}); });
it("should inCurve 1", () => { it("should inCurve 1", () => {
@ -152,8 +159,8 @@ describe("Baby Jub js test", function () {
const buf = babyjub.packPoint(p); const buf = babyjub.packPoint(p);
assert.equal(buff2hex(buf), "53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85"); assert.equal(buff2hex(buf), "53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85");
const p2 = babyjub.unpackPoint(buf); const p2 = babyjub.unpackPoint(buf);
assert.equal(p2[0].toString(), "17777552123799933955779906779655732241715742912184938656739573121738514868268"); assert(babyjub.F.eq(p2[0], babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268")));
assert.equal(p2[1].toString(), "2626589144620713026669568689430873010625803728049924121243784502389097019475"); assert(babyjub.F.eq(p2[1], babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475")));
}); });
it("should packPoint - unpackPoint 2", () => { it("should packPoint - unpackPoint 2", () => {
@ -164,7 +171,7 @@ describe("Baby Jub js test", function () {
const buf = babyjub.packPoint(p); const buf = babyjub.packPoint(p);
assert.equal(buff2hex(buf), "e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709"); assert.equal(buff2hex(buf), "e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709");
const p2 = babyjub.unpackPoint(buf); const p2 = babyjub.unpackPoint(buf);
assert.equal(p2[0].toString(), "6890855772600357754907169075114257697580319025794532037257385534741338397365"); assert(babyjub.F.eq(p2[0], babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365")));
assert.equal(p2[1].toString(), "4338620300185947561074059802482547481416142213883829469920100239455078257889"); assert(babyjub.F.eq(p2[1], babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889")));
}); });
}); });

View File

@ -1,26 +1,68 @@
const chai = require("chai"); import chai from "chai";
import { Scalar } from "ffjavascript";
const eddsa = require("../src/eddsa.js");
const babyJub = require("../src/babyjub.js");
const assert = chai.assert; const assert = chai.assert;
const utils = require("ffjavascript").utils; import buildEddsa from "../src/eddsa.js";
const fromHexString = hexString =>
new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const toHexString = bytes =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
function buff2hex(buff) {
function i2hex(i) {
return ('0' + i.toString(16)).slice(-2);
}
return Array.from(buff).map(i2hex).join('');
}
describe("EdDSA js test", function () { describe("EdDSA js test", function () {
let eddsa;
this.timeout(100000); this.timeout(100000);
before(async () => {
eddsa = await buildEddsa();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Sign (using Pedersen) a single 10 bytes from 0 to 9", () => {
const F = eddsa.babyJub.F;
const msgBuf = fromHexString("00010203040506070809");
const prvKey = fromHexString("0001020304050607080900010203040506070809000102030405060708090001");
const pubKey = eddsa.prv2pub(prvKey);
assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signPedersen(prvKey, msgBuf);
// console.log(F.toString(signature.R8[0]));
assert(F.eq(signature.R8[0], F.e("21253904451576600568378459528205653033385900307028841334532552830614710476912")));
// console.log(F.toString(signature.R8[1]));
assert(F.eq(signature.R8[1], F.e("20125634407542493427571099944365246191501563803226486072348038614369379124499")));
// console.log(Scalar.toString(signature.S));
assert(Scalar.eq(signature.S, Scalar.e("2129243915978267980511515511350111723623685317644064470882297086073041379651")));
const pSignature = eddsa.packSignature(signature);
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"138501d9e734e73f485269bcdc29a9ef2da3fac2f5c9653761d0364f95b47eac"+
"43e1a02b56ff3dacfdac040f3e8c2023dc259ba3f6880ca8ad246b4bfe1bb504");
const uSignature = eddsa.unpackSignature(pSignature);
assert(eddsa.verifyPedersen(msgBuf, uSignature, pubKey));
});
it("Sign (using Mimc7) a single 10 bytes from 0 to 9", () => { it("Sign (using Mimc7) a single 10 bytes from 0 to 9", () => {
const msgBuf = Buffer.from("00010203040506070809", "hex"); const F = eddsa.babyJub.F;
const msg = utils.leBuff2int(msgBuf); const msgBuf = fromHexString("000102030405060708090000");
const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0));
// const prvKey = crypto.randomBytes(32); // const prvKey = crypto.randomBytes(32);
@ -28,23 +70,23 @@ describe("EdDSA js test", function () {
const pubKey = eddsa.prv2pub(prvKey); const pubKey = eddsa.prv2pub(prvKey);
assert.equal(pubKey[0].toString(), assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
"13277427435165878497778222415993513565335242147425444199013288855685581939618"); assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
assert.equal(pubKey[1].toString(),
"13622229784656158136036771217484571176836296686641868549125388198837476602820");
const pPubKey = babyJub.packPoint(pubKey); const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signMiMC(prvKey, msg); const signature = eddsa.signMiMC(prvKey, msg);
assert.equal(signature.R8[0].toString(), // console.log(F.toString(signature.R8[0]));
"11384336176656855268977457483345535180380036354188103142384839473266348197733"); assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733")));
assert.equal(signature.R8[1].toString(), // console.log(F.toString(signature.R8[1]));
"15383486972088797283337779941324724402501462225528836549661220478783371668959"); assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959")));
assert.equal(signature.S.toString(), // console.log(Scalar.toString(signature.S));
"2523202440825208709475937830811065542425109372212752003460238913256192595070"); assert(Scalar.eq(signature.S, Scalar.e("2523202440825208709475937830811065542425109372212752003460238913256192595070")));
const pSignature = eddsa.packSignature(signature); const pSignature = eddsa.packSignature(signature);
assert.equal(buff2hex(pSignature), ""+
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405"); "7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405");
@ -54,35 +96,75 @@ describe("EdDSA js test", function () {
}); });
it("Sign (using Poseidon) a single 10 bytes from 0 to 9", () => { it("Sign (using Poseidon) a single 10 bytes from 0 to 9", () => {
const msgBuf = Buffer.from("00010203040506070809", "hex"); const F = eddsa.babyJub.F;
const msg = utils.leBuff2int(msgBuf); const msgBuf = fromHexString("000102030405060708090000");
const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0));
// const prvKey = crypto.randomBytes(32);
const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex");
const pubKey = eddsa.prv2pub(prvKey); const pubKey = eddsa.prv2pub(prvKey);
assert.equal(pubKey[0].toString(), assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
"13277427435165878497778222415993513565335242147425444199013288855685581939618"); assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
assert.equal(pubKey[1].toString(),
"13622229784656158136036771217484571176836296686641868549125388198837476602820");
const pPubKey = babyJub.packPoint(pubKey); const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signPoseidon(prvKey, msg); const signature = eddsa.signPoseidon(prvKey, msg);
assert.equal(signature.R8[0].toString(), // console.log(F.toString(signature.R8[0]));
"11384336176656855268977457483345535180380036354188103142384839473266348197733"); assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733")));
assert.equal(signature.R8[1].toString(), // console.log(F.toString(signature.R8[1]));
"15383486972088797283337779941324724402501462225528836549661220478783371668959"); assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959")));
assert.equal(signature.S.toString(), // console.log(Scalar.toString(signature.S));
"1672775540645840396591609181675628451599263765380031905495115170613215233181"); assert(Scalar.eq(signature.S, Scalar.e("1672775540645840396591609181675628451599263765380031905495115170613215233181")));
const pSignature = eddsa.packSignature(signature); const pSignature = eddsa.packSignature(signature);
assert.equal(buff2hex(pSignature), ""+
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203"); "9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203");
const uSignature = eddsa.unpackSignature(pSignature); const uSignature = eddsa.unpackSignature(pSignature);
assert(eddsa.verifyPoseidon(msg, uSignature, pubKey)); assert(eddsa.verifyPoseidon(msg, uSignature, pubKey));
});
it("Sign (using mimcsponge) a single 10 bytes from 0 to 9", () => {
const F = eddsa.babyJub.F;
const msgBuf = fromHexString("000102030405060708090000");
const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0));
// const prvKey = crypto.randomBytes(32);
const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex");
const pubKey = eddsa.prv2pub(prvKey);
assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signMiMCSponge(prvKey, msg);
// console.log(F.toString(signature.R8[0]));
assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733")));
// console.log(F.toString(signature.R8[1]));
assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959")));
// console.log(Scalar.toString(signature.S));
assert(Scalar.eq(signature.S, Scalar.e("1868336918738674306327358602987493427631678603535639134028485964115448322340")));
const pSignature = eddsa.packSignature(signature);
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"24599218a1c2e5290bf58b2eec37bfec1395179ed5e817f10f86c9e7f3702104");
const uSignature = eddsa.unpackSignature(pSignature);
assert(eddsa.verifyMiMCSponge(msg, uSignature, pubKey));
}); });
}); });

24
test/mimc7.js Normal file
View File

@ -0,0 +1,24 @@
import chai from "chai";
const assert = chai.assert;
import buildMimc7 from "../src/mimc7.js";
describe("Mimc7 test", function () {
let mimc7;
before(async () => {
mimc7 = await buildMimc7();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should check multihash reference 2", async () => {
const res2 = mimc7.multiHash([1,2]);
assert(mimc7.F.eq(mimc7.F.e("0x1457424af251109768c8161500816fca43bdf0f9cf10c5094008a6774baacd55"), res2));
});
it("Should check multihash reference 4", async () => {
const res2 = mimc7.multiHash([1,2,3,4]);
assert(mimc7.F.eq(mimc7.F.e("0x2c84563396117b022ab9226199f698eaeab81e03c6185d81d413f3423e8c8964"), res2));
});
});

56
test/mimc7contract.js Normal file
View File

@ -0,0 +1,56 @@
import chai from "chai";
import {createCode, abi} from "../src/mimc7_gencontract.js";
import ethers from "ethers";
import ganache from "ganache-cli";
import buildMimc7 from "../src/mimc7.js";
const assert = chai.assert;
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
const SEED = "mimc";
describe("MiMC Smart contract test", function () {
let mimc;
let mimcJS;
let account;
this.timeout(100000);
before(async () => {
const provider = new ethers.providers.Web3Provider(ganache.provider());
account = provider.getSigner(0);
mimcJS = await buildMimc7();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should deploy the contract", async () => {
const C = new ethers.ContractFactory(
abi,
createCode(SEED, 91),
account
);
mimc = await C.deploy();
});
it("Shold calculate the mimc correctly", async () => {
const res = await mimc["MiMCpe7"](1,2);
// console.log("Cir: " + bigInt(res.toString(16)).toString(16));
const res2 = mimcJS.hash(1,2);
// console.log("Ref: " + bigInt(res2).toString(16));
assert.equal(res.toString(), mimcJS.F.toString(res2));
});
});

View File

@ -1,48 +0,0 @@
const ganache = require("ganache-cli");
const Web3 = require("web3");
const chai = require("chai");
const mimcGenContract = require("../src/mimc_gencontract.js");
const mimcjs = require("../src/mimc7.js");
const assert = chai.assert;
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
const SEED = "mimc";
describe("MiMC Smart contract test", function () {
let testrpc;
let web3;
let mimc;
let accounts;
this.timeout(100000);
before(async () => {
web3 = new Web3(ganache.provider(), null, { transactionConfirmationBlocks: 1 });
accounts = await web3.eth.getAccounts();
});
it("Should deploy the contract", async () => {
const C = new web3.eth.Contract(mimcGenContract.abi);
mimc = await C.deploy({
data: mimcGenContract.createCode(SEED, 91),
arguments: []
}).send({
gas: 1500000,
gasPrice: '30000000000000',
from: accounts[0]
}).on("error", (error) => {
console.log("ERROR: "+error);
});
});
it("Shold calculate the mimic correctly", async () => {
const res = await mimc.methods.MiMCpe7(1,2).call();
const res2 = await mimcjs.hash(1,2,91);
assert.equal(res.toString(), res2.toString());
});
});

26
test/mimcsponge.js Normal file
View File

@ -0,0 +1,26 @@
import chai from "chai";
const assert = chai.assert;
import buildMimcSponge from "../src/mimcsponge.js";
describe("Mimc Sponge test", function () {
let mimcSponge;
before(async () => {
mimcSponge = await buildMimcSponge();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should check multihash reference 2", async () => {
const res2 = mimcSponge.multiHash([1,2]);
// console.log(mimcSponge.F.toString(res2,16));
assert(mimcSponge.F.eq(mimcSponge.F.e("0x2bcea035a1251603f1ceaf73cd4ae89427c47075bb8e3a944039ff1e3d6d2a6f"), res2));
});
it("Should check multihash reference 4", async () => {
const res2 = mimcSponge.multiHash([1,2,3,4]);
// console.log(mimcSponge.F.toString(res2,16));
assert(mimcSponge.F.eq(mimcSponge.F.e("0x3e86bdc4eac70bd601473c53d8233b145fe8fd8bf6ef25f0b217a1da305665c"), res2));
});
});

View File

@ -1,8 +1,9 @@
const ganache = require("ganache-cli"); import chai from "chai";
const Web3 = require("web3"); import {createCode, abi} from "../src/mimcsponge_gencontract.js";
const chai = require("chai"); import ethers from "ethers";
const mimcGenContract = require("../src/mimcsponge_gencontract.js"); import ganache from "ganache-cli";
const mimcjs = require("../src/mimcsponge.js");
import buildMimcSponge from "../src/mimcsponge.js";
const assert = chai.assert; const assert = chai.assert;
@ -11,33 +12,44 @@ const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
const SEED = "mimcsponge"; const SEED = "mimcsponge";
describe("MiMC Sponge Smart contract test", () => { describe("MiMC Sponge Smart contract test", () => {
let testrpc;
let web3;
let mimc; let mimc;
let accounts; let mimcJS;
let account;
before(async () => { before(async () => {
web3 = new Web3(ganache.provider(), null, { transactionConfirmationBlocks: 1 }); const provider = new ethers.providers.Web3Provider(ganache.provider());
accounts = await web3.eth.getAccounts();
account = provider.getSigner(0);
mimcJS = await buildMimcSponge();
});
after(async () => {
globalThis.curve_bn128.terminate();
}); });
it("Should deploy the contract", async () => { it("Should deploy the contract", async () => {
const C = new web3.eth.Contract(mimcGenContract.abi);
mimc = await C.deploy({
data: mimcGenContract.createCode(SEED, 220) const C = new ethers.ContractFactory(
}).send({ abi,
gas: 3500000, createCode(SEED, 220),
from: accounts[0] account
}); );
mimc = await C.deploy();
}); });
it("Shold calculate the mimc correctly", async () => { it("Shold calculate the mimc correctly", async () => {
const res = await mimc.methods.MiMCSponge(1,2,3).call();
const res2 = await mimcjs.hash(1,2,3);
assert.equal(res.xL.toString(), res2.xL.toString()); const res = await mimc["MiMCSponge"](1,2, 3);
assert.equal(res.xR.toString(), res2.xR.toString());
// console.log("Cir: " + bigInt(res.toString(16)).toString(16));
const res2 = mimcJS.hash(1,2, 3);
// console.log("Ref: " + bigInt(res2).toString(16));
assert.equal(res.xL.toString(), mimcJS.F.toString(res2.xL));
assert.equal(res.xR.toString(), mimcJS.F.toString(res2.xR));
}); });
}); });

30
test/pedersenhash.js Normal file
View File

@ -0,0 +1,30 @@
import chai from "chai";
const assert = chai.assert;
import buildPedersenHash from "../src/pedersenhash.js";
function buff2hex(buff) {
function i2hex(i) {
return ('0' + i.toString(16)).slice(-2);
}
return Array.from(buff).map(i2hex).join('');
}
describe("Pedersen Hash test", function () {
let pedersen;
before(async () => {
pedersen = await buildPedersenHash();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should check multihash reference 2", async () => {
const msg = (new TextEncoder()).encode("Hello");
const res2 = pedersen.hash(msg);
// console.log(buff2hex(res2));
assert.equal(buff2hex(res2), "0e90d7d613ab8b5ea7f4f8bc537db6bb0fa2e5e97bbac1c1f609ef9e6a35fd8b");
});
});

View File

@ -1,15 +1,33 @@
const chai = require("chai"); import chai from "chai";
const assert = chai.assert; const assert = chai.assert;
const poseidon = require("../src/poseidon.js"); import buildPoseidon from "../src/poseidon.js";
import buildPoseidonSlow from "../src/poseidon_slow.js";
describe("Poseidon test", function () {
let poseidon;
let poseidonSlow;
before(async () => {
poseidon = await buildPoseidon();
poseidonSlow = await buildPoseidonSlow();
});
before(async () => {
globalThis.curve_bn128.terminate();
});
describe("Poseidon Circuit test", function () {
it("Should check constrain reference implementation poseidonperm_x5_254_3", async () => { it("Should check constrain reference implementation poseidonperm_x5_254_3", async () => {
const res2 = poseidon([1,2]); const res2 = poseidon([1,2]);
assert.equal("115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a", res2.toString(16)); assert(poseidon.F.eq(poseidon.F.e("0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a"), res2));
const res3 = poseidonSlow([1, 2]);
assert(poseidonSlow.F.eq(poseidonSlow.F.e("0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a"), res3));
}); });
it("Should check constrain reference implementation poseidonperm_x5_254_5", async () => { it("Should check constrain reference implementation poseidonperm_x5_254_5", async () => {
const res2 = poseidon([1,2,3,4]); const res2 = poseidon([1,2,3,4]);
assert.equal("299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465", res2.toString(16)); assert(poseidon.F.eq(poseidon.F.e("0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465"), res2));
const res3 = poseidonSlow([1,2,3,4]);
assert(poseidonSlow.F.eq(poseidonSlow.F.e("0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465"), res3));
}); });
}); });

View File

@ -1,8 +1,8 @@
const ganache = require("ganache-cli"); import chai from "chai";
const Web3 = require("web3"); import {createCode, generateABI} from "../src/poseidon_gencontract.js";
const chai = require("chai"); import buildPoseidon from "../src/poseidon.js";
const poseidonGenContract = require("../src/poseidon_gencontract.js"); import ethers from "ethers";
const poseidon = require("../src/poseidon.js"); import ganache from "ganache-cli";
const assert = chai.assert; const assert = chai.assert;
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); }; const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
@ -12,53 +12,57 @@ describe("Poseidon Smart contract test", function () {
let web3; let web3;
let poseidon6; let poseidon6;
let poseidon3; let poseidon3;
let accounts; let poseidon;
let account;
this.timeout(100000); this.timeout(100000);
before(async () => { before(async () => {
web3 = new Web3(ganache.provider(), null, { transactionConfirmationBlocks: 1 }); const provider = new ethers.providers.Web3Provider(ganache.provider());
accounts = await web3.eth.getAccounts();
account = provider.getSigner(0);
poseidon = await buildPoseidon();
});
after(async () => {
globalThis.curve_bn128.terminate();
}); });
it("Should deploy the contract", async () => { it("Should deploy the contract", async () => {
const C6 = new web3.eth.Contract(poseidonGenContract.generateABI(5)); const C6 = new ethers.ContractFactory(
const C3 = new web3.eth.Contract(poseidonGenContract.generateABI(2)); generateABI(5),
createCode(5),
account
);
const C3 = new ethers.ContractFactory(
generateABI(2),
createCode(2),
account
);
poseidon6 = await C6.deploy({ poseidon6 = await C6.deploy();
data: poseidonGenContract.createCode(5) poseidon3 = await C3.deploy();
}).send({
gas: 5000000,
from: accounts[0]
});
poseidon3 = await C3.deploy({
data: poseidonGenContract.createCode(2)
}).send({
gas: 5000000,
from: accounts[0]
});
}); });
it("Should calculate the poseidon correctly t=6", async () => { it("Should calculate the poseidon correctly t=6", async () => {
const res = await poseidon6.methods.poseidon([1,2, 0, 0, 0]).call(); const res = await poseidon6["poseidon(uint256[5])"]([1,2, 0, 0, 0]);
// console.log("Cir: " + bigInt(res.toString(16)).toString(16)); // console.log("Cir: " + bigInt(res.toString(16)).toString(16));
const res2 = poseidon([1,2, 0, 0, 0]); const res2 = poseidon([1,2, 0, 0, 0]);
// console.log("Ref: " + bigInt(res2).toString(16)); // console.log("Ref: " + bigInt(res2).toString(16));
assert.equal(res.toString(), res2.toString()); assert.equal(res.toString(), poseidon.F.toString(res2));
}); });
it("Should calculate the poseidon correctly t=3", async () => { it("Should calculate the poseidon correctly t=3", async () => {
const res = await poseidon3.methods.poseidon([1,2]).call(); const res = await poseidon3["poseidon(uint256[2])"]([1,2]);
// console.log("Cir: " + bigInt(res.toString(16)).toString(16)); // console.log("Cir: " + bigInt(res.toString(16)).toString(16));
const res2 = poseidon([1,2]); const res2 = poseidon([1,2]);
// console.log("Ref: " + bigInt(res2).toString(16)); // console.log("Ref: " + bigInt(res2).toString(16));
assert.equal(res.toString(), res2.toString()); assert.equal(res.toString(), poseidon.F.toString(res2));
}); });
}); });

View File

@ -1,21 +1,23 @@
const chai = require("chai"); import chai from "chai";
const F1Field = require("ffjavascript").F1Field; import buildBabyjub from "../src/babyjub.js";
const Scalar = require("ffjavascript").Scalar; import {newMemEmptyTrie} from "../src/smt.js";
exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const Fr = new F1Field(exports.p);
const smt = require("../src/smt.js");
const assert = chai.assert; const assert = chai.assert;
describe("SMT Javascript test", function () { describe("SMT Javascript test", function () {
let Fr;
this.timeout(100000); this.timeout(100000);
before( async () => { before(async () => {
const babyjub = await buildBabyjub();
Fr = babyjub.F;
});
after(async () => {
globalThis.curve_bn128.terminate();
}); });
it("Should insert 2 elements and empty them", async () => { it("Should insert 2 elements and empty them", async () => {
const tree = await smt.newMemEmptyTrie(); const tree = await newMemEmptyTrie();
const key1 = Fr.e(111); const key1 = Fr.e(111);
const value1 = Fr.e(222); const value1 = Fr.e(222);
const key2 = Fr.e(333); const key2 = Fr.e(333);
@ -32,12 +34,12 @@ describe("SMT Javascript test", function () {
it("Should insert 3 elements in dferent order and should be the same", async () => { it("Should insert 3 elements in dferent order and should be the same", async () => {
const keys = [Fr.e(8), Fr.e(9), Fr.e(32)]; const keys = [Fr.e(8), Fr.e(9), Fr.e(32)];
const values = [Fr.e(88), Fr.e(99), Fr.e(3232)]; const values = [Fr.e(88), Fr.e(99), Fr.e(3232)];
const tree1 = await smt.newMemEmptyTrie(); const tree1 = await newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie(); const tree2 = await newMemEmptyTrie();
const tree3 = await smt.newMemEmptyTrie(); const tree3 = await newMemEmptyTrie();
const tree4 = await smt.newMemEmptyTrie(); const tree4 = await newMemEmptyTrie();
const tree5 = await smt.newMemEmptyTrie(); const tree5 = await newMemEmptyTrie();
const tree6 = await smt.newMemEmptyTrie(); const tree6 = await newMemEmptyTrie();
await tree1.insert(keys[0],values[0]); await tree1.insert(keys[0],values[0]);
await tree1.insert(keys[1],values[1]); await tree1.insert(keys[1],values[1]);
@ -126,7 +128,7 @@ describe("SMT Javascript test", function () {
} }
return rArr; return rArr;
} }
const tree = await smt.newMemEmptyTrie(); const tree = await newMemEmptyTrie();
const arr = []; const arr = [];
const N = 100; const N = 100;
for (let i=0; i<N; i++) { for (let i=0; i<N; i++) {
@ -146,8 +148,8 @@ describe("SMT Javascript test", function () {
}); });
it("Should test update", async () => { it("Should test update", async () => {
const tree1 = await smt.newMemEmptyTrie(); const tree1 = await newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie(); const tree2 = await newMemEmptyTrie();
await tree1.insert(8,88); await tree1.insert(8,88);
await tree1.insert(9,99,); await tree1.insert(9,99,);
@ -165,7 +167,7 @@ describe("SMT Javascript test", function () {
}); });
it("Should test update with same key-value", async () => { it("Should test update with same key-value", async () => {
const tree1 = await smt.newMemEmptyTrie(); const tree1 = await newMemEmptyTrie();
await tree1.insert(8,88); await tree1.insert(8,88);
await tree1.update(8,88); await tree1.update(8,88);

View File

@ -1,83 +0,0 @@
const bn128 = require("snarkjs").bn128;
const bigInt = require("snarkjs").bigInt;
const createBlakeHash = require("blake-hash");
const babyJub = require("../src/babyjub");
function getPoint(S) {
const F = bn128.Fr;
const h = createBlakeHash("blake256").update(S).digest();
if (h.length != 32) {
throw new Error("Invalid length")
}
let sign = false;
if (h[31] & 0x80) {
h[31] = h[31] & 0x7F;
sign = true;
}
let y = bigInt(0);
for (let i=0; i<32; i++) {
y = y.shl(8);
y = y.add(bigInt(h[i]));
}
const a = bigInt("168700");
const d = bigInt("168696");
const y2 = F.square(y);
let x = F.sqrt(F.div(
F.sub(F.one, y2),
F.sub(a, F.mul(d, y2))));
if (x == null) return null;
if (sign) x = F.neg(x);
const p = [bn128.Fr.affine(x), bn128.Fr.affine(y)];
const p8 = babyJub.mulPointEscalar(p, 8);
return p8;
}
function generatePoint(S) {
let p= null;
let idx = 0;
while (p==null) {
let sidx = "" + idx;
while (sidx.length<16) sidx = "0"+sidx;
p = getPoint(S+"_"+sidx);
idx++;
}
if (!babyJub.inCurve(p)){
throw new Error("Point not in curve");
}
return p;
}
const g = [
bigInt("5299619240641551281634865583518297030282874472190772894086521144482721001553"),
bigInt("16950150798460657717958625567821834550301663161624707787222815936182638968203")];
// Sanity check
if (!babyJub.inCurve(g)) {
throw new Error("Generator not In curve -> Some thing goes wrong...");
}
for (let i=0; i<25; i++) {
let S = "" +i;
while (S.length<16) S = "0"+S;
const P = generatePoint("Iden3_PedersenGenerator_"+S);
console.log(`[${P[0].toString()}, ${P[1].toString()}]`);
}

View File

@ -1,8 +1,7 @@
const fs = require("fs"); import fs from "fs";
const path = require("path"); import path from "path";
const Scalar = require("ffjavascript").Scalar; import {Scalar, ZqField, utils} from "ffjavascript";
const ZqField = require("ffjavascript").ZqField; const { unstringifyBigInts } = utils;
const { unstringifyBigInts } = require("ffjavascript").utils;
// Version to write in hexadecimal // Version to write in hexadecimal
@ -25,7 +24,7 @@ function stringifyBigInts(o) {
} }
} }
const { C, M } = unstringifyBigInts(require("../src/poseidon_constants.json")); const { C, M } = unstringifyBigInts(JSON.parse(fs.readFileSync(path.join("src", "poseidon_constants.json"), "utf8")));
const N_ROUNDS_F = 8; const N_ROUNDS_F = 8;
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68];
@ -290,7 +289,7 @@ async function run() {
opt = stringifyBigInts(opt); opt = stringifyBigInts(opt);
fs.writeFileSync(path.join(__dirname, "..", "src", "poseidon_constants_opt.json"), JSON.stringify(opt, null, 1), "utf8"); fs.writeFileSync(path.join( "src", "poseidon_constants_opt.js"), "export default " + JSON.stringify(opt, null, 1), "utf8");
} }
run().then(()=> { run().then(()=> {