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 library is used to make tests of circomlib circuits.
This project demonstrates an advanced Hardhat use case, integrating other tools commonly used alongside Hardhat in the ecosystem.
In the src directory the package includes these programs, in the
test directory includes its own tests and, in the tools directory includes programs to precompute some needed parameters.
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.
You can install `circomlibjs` with the following command:
Try running some of the following tasks:
```text
npm install -g circomlibjs
```
```shell
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",
"type": "module",
"main": "./build/main.cjs",
"module": "./main.js",
"exports": {
"import": "./main.js",
"require": "./build/main.cjs"
},
"version": "0.0.8",
"description": "Javascript library to work with circomlib",
"main": "index.js",
"scripts": {
"test": "mocha",
"poseidonOptimizeConstants": "node tools/poseidon_optimize_constants.js"
"poseidonOptimizeConstants": "node tools/poseidon_optimize_constants.js",
"build": "rollup -c rollup.cjs.config.js"
},
"repository": {
"type": "git",
@ -28,13 +35,13 @@
"homepage": "https://github.com/iden3/circomlibjs#readme",
"devDependencies": {
"chai": "^4.3.4",
"ganache-cli": "^6.12.2"
"ganache-cli": "^6.12.2",
"mocha": "^9.1.3"
},
"dependencies": {
"ethers": "^5.5.1",
"blake-hash": "^2.0.0",
"blake2b": "^2.1.3",
"ffjavascript": "^0.2.38",
"web3": "^1.6.0",
"web3-utils": "^1.6.0"
"ffjavascript": "^0.2.39"
}
}

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;
const Scalar = require("ffjavascript").Scalar;
const utils = require("ffjavascript").utils;
import { getCurveFromName, Scalar } from "ffjavascript";
exports.addPoint = addPoint;
exports.mulPointEscalar = mulPointEscalar;
exports.inCurve = inCurve;
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;
export default async function buildBabyJub() {
const bn128 = await getCurveFromName("bn128");
return new BabyJub(bn128.Fr);
}
function mulPointEscalar(base, e) {
let res = [F.e("0"),F.e("1")];
let rem = e;
let exp = base;
class BabyJub {
constructor(F) {
this.F = F;
this.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
this.pm1d2 = Scalar.div(Scalar.sub(this.p, Scalar.e(1)), Scalar.e(2));
while (! Scalar.isZero(rem)) {
if (Scalar.isOdd(rem)) {
res = addPoint(res, exp);
this.Generator = [
F.e("995203441582195749578291179787384436505546430278305826713579947235728471134"),
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;
}
function inSubgroup(P) {
if (!inCurve(P)) return false;
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;
inSubgroup(P) {
const F = this.F;
if (!this.inCurve(P)) return false;
const res= this.mulPointEscalar(P, this.subOrder);
return (F.isZero(res[0]) && F.eq(res[1], F.one));
}
return buff;
}
function unpackPoint(_buff) {
const buff = Buffer.from(_buff);
let sign = false;
const P = new Array(2);
if (buff[31] & 0x80) {
sign = true;
buff[31] = buff[31] & 0x7F;
inCurve(P) {
const F = this.F;
const x2 = F.square(P[0]);
const y2 = F.square(P[1]);
if (!F.eq(
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(
F.sub(F.one, y2),
F.sub(exports.A, F.mul(exports.D, y2))));
unpackPoint(buff) {
const F = this.F;
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");
const Scalar = require("ffjavascript").Scalar;
const F1Field = require("ffjavascript").F1Field;
const babyJub = require("./babyjub");
const utils = require("ffjavascript").utils;
const pedersenHash = require("./pedersenHash").hash;
const mimc7 = require("./mimc7");
const poseidon = require("./poseidon.js");
const mimcsponge = require("./mimcsponge");
import { Scalar } from "ffjavascript";
import buildBabyJub from "./babyjub.js";
import buildPedersenHash from "./pedersenhash.js";
import buildMimc7 from "./mimc7.js";
import buildPoseidon from "./poseidon.js";
import buildMimcSponge from "./mimcsponge.js";
import createBlakeHash from "blake-hash";
exports.prv2pub= prv2pub;
exports.sign = sign;
exports.signMiMC = signMiMC;
exports.signPoseidon = signPoseidon;
exports.signMiMCSponge = signMiMCSponge;
exports.verify = verify;
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);
export default async function buildEddsa() {
const babyJub = await buildBabyJub("bn128");
const pedersenHash = await buildPedersenHash();
const mimc7 = await buildMimc7();
const poseidon = await buildPoseidon();
const mimcSponge = await buildMimcSponge();
return new Eddsa(babyJub, pedersenHash, mimc7, poseidon, mimcSponge);
}
function pruneBuffer(_buff) {
const buff = Buffer.from(_buff);
buff[0] = buff[0] & 0xF8;
buff[31] = buff[31] & 0x7F;
buff[31] = buff[31] | 0x40;
return buff;
}
class Eddsa {
function prv2pub(prv) {
const sBuff = pruneBuffer(createBlakeHash("blake512").update(prv).digest().slice(0,32));
let s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s,3));
return A;
}
constructor(babyJub, pedersenHash, mimc7, poseidon, mimcSponge) {
this.babyJub = babyJub;
this.pedersenHash = pedersenHash;
this.mimc7 = mimc7;
this.poseidon = poseidon;
this.mimcSponge = mimcSponge;
this.F = babyJub.F;
}
function sign(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
pruneBuffer(buff) {
buff[0] = buff[0] & 0xF8;
buff[31] = buff[31] & 0x7F;
buff[31] = buff[31] | 0x40;
return buff;
}
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msg])).digest();
let r = utils.leBuff2int(rBuff);
const Fr = new F1Field(babyJub.subOrder);
r = Fr.e(r);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const R8p = babyJub.packPoint(R8);
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
};
}
prv2pub(prv) {
const F = this.babyJub.F;
const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
let s = Scalar.fromRprLE(sBuff, 0, 32);
const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s,3));
return A;
}
function signMiMC(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
signPedersen(prv, msg) {
const F = this.babyJub.F;
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));
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32));
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest();
let r = utils.leBuff2int(rBuff);
const Fr = new F1Field(babyJub.subOrder);
r = Fr.e(r);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const hm = mimc7.multiHash([R8[0], R8[1], A[0], A[1], msg]);
const S = Fr.add(r , Fr.mul(hm, s));
return {
R8: R8,
S: S
};
}
const composeBuff = new Uint8Array(32 + msg.length);
composeBuff.set(sBuff.slice(32), 0);
composeBuff.set(msg, 32);
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 R8p = this.babyJub.packPoint(R8);
const Ap = this.babyJub.packPoint(A);
function signMiMCSponge(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
const composeBuff2 = new Uint8Array(64 + msg.length);
composeBuff2.set(R8p, 0);
composeBuff2.set(Ap, 32);
composeBuff2.set(msg, 64);
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32));
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest();
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
};
}
const hmBuff = this.pedersenHash.hash(composeBuff2);
const hm = Scalar.fromRprLE(hmBuff, 0, 32);
function signPoseidon(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
const S = Scalar.mod(
Scalar.add(
r,
Scalar.mul(hm, s)
),
this.babyJub.subOrder
)
return {
R8: R8,
S: S
};
}
const msgBuff = ensureBuffer(utils.leInt2Buff(msg, 32));
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msgBuff])).digest();
let r = utils.leBuff2int(rBuff);
const Fr = new F1Field(babyJub.subOrder);
r = Fr.e(r);
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
};
}
signMiMC(prv, msg) {
const F = this.babyJub.F;
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 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 Ap = babyJub.packPoint(A);
const hmBuff = pedersenHash(Buffer.concat([R8p, Ap, msg]));
const hm = utils.leBuff2int(hmBuff);
const composeBuff = new Uint8Array(32 + msg.length);
composeBuff.set(sBuff.slice(32), 0);
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);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S);
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm,8));
Pright = babyJub.addPoint(sig.R8, Pright);
const hm = this.mimc7.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
};
}
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
signMiMCSponge(prv, msg) {
const F = this.babyJub.F;
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) {
// 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 composeBuff = new Uint8Array(32 + msg.length);
composeBuff.set(sBuff.slice(32), 0);
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);
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);
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm, 8));
Pright = babyJub.addPoint(sig.R8, Pright);
signPoseidon(prv, msg) {
const F = this.babyJub.F;
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;
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
function 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 (!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 hm = poseidon([sig.R8[0], sig.R8[1], A[0], A[1], msg]);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S);
let Pright = babyJub.mulPointEscalar(A, Scalar.mul(hm, 8));
Pright = babyJub.addPoint(sig.R8, Pright);
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
function 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 (!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 hm = mimcsponge.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]);
const Pleft = babyJub.mulPointEscalar(babyJub.Base8, sig.S);
let Pright = babyJub.mulPointEscalar(A, hm.times(bigInt("8")));
Pright = babyJub.addPoint(sig.R8, Pright);
if (!babyJub.F.eq(Pleft[0],Pright[0])) return false;
if (!babyJub.F.eq(Pleft[1],Pright[1])) return false;
return true;
}
function packSignature(sig) {
const R8p = babyJub.packPoint(sig.R8);
const Sp = utils.leInt2Buff(sig.S, 32);
return Buffer.concat([R8p, Sp]);
}
function unpackSignature(sigBuff) {
return {
R8: babyJub.unpackPoint(sigBuff.slice(0,32)),
S: utils.leBuff2int(sigBuff.slice(32,64))
};
const composeBuff = new Uint8Array(32 + msg.length);
composeBuff.set(sBuff.slice(32), 0);
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);
const hm = this.poseidon([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
};
}
verifyPedersen(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 (Scalar.geq(sig.S, this.babyJub.subOrder)) return false;
const R8p = this.babyJub.packPoint(sig.R8);
const Ap = this.babyJub.packPoint(A);
const composeBuff2 = new Uint8Array(64 + msg.length);
composeBuff2.set(R8p, 0);
composeBuff2.set(Ap, 32);
composeBuff2.set(msg, 64);
const hmBuff = this.pedersenHash.hash(composeBuff2);
const hm = Scalar.fromRprLE(hmBuff, 0, 32);
const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S);
let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hm,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;
}
verifyMiMC(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.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() {
this.code = [];
this.labels = {};
@ -26,7 +27,7 @@ class Contract {
while (genLoadedLength!=setLoaderLength) {
setLoaderLength = genLoadedLength;
C = new module.exports();
C = new Contract();
C.codesize();
C.push(setLoaderLength);
C.push(0);
@ -38,7 +39,7 @@ class Contract {
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); }
@ -154,22 +155,25 @@ class Contract {
}
push(data) {
if (typeof data === "number") {
let isNeg;
if (data<0) {
isNeg = true;
data = -data;
if ((typeof data !== "string") || (data.slice(0,2) != "0x")) {
let v = Scalar.e(data);
if (Scalar.isNegative(v)) {
v = Scalar.add(Scalar.shl(Scalar.e(1), 256), v);
}
data = data.toString(16);
if (data.length % 2 == 1) data = "0" + data;
data = "0x" + data;
if (isNeg) data = "-"+data;
let S = Scalar.toString(v, 16);
if (S.length % 2) S = "0"+S;
S = "0x" +S;
data = S;
}
const d = Web3Utils.hexToBytes(Web3Utils.toHex(data));
const d = ethers.utils.arrayify(data);
if (d.length == 0 || d.length > 32) {
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) {
@ -204,5 +208,3 @@ class Contract {
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;
const ZqField = require("ffjavascript").ZqField;
import {getCurveFromName, Scalar} from "ffjavascript";
const Web3Utils = require("web3-utils");
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
exports.F = F;
import ethers from "ethers";
const SEED = "mimc";
const NROUNDS = 91;
exports.getIV = (seed) => {
if (typeof seed === "undefined") seed = SEED;
const c = Web3Utils.keccak256(seed+"_iv");
const cn = Scalar.fromString(Web3Utils.toBN(c).toString());
const iv = Scalar.mod(cn, F.p);
return iv;
};
export default async function buildMimc7() {
const bn128 = await getCurveFromName("bn128");
return new Mimc7(bn128.Fr);
}
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()));
const c2 = Web3Utils.padLeft(Web3Utils.toHex(n1), 64);
cts[i] = Scalar.fromString(Web3Utils.toBN(c2).toString());
class Mimc7 {
constructor (F) {
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) =>{
const x_in = F.e(_x_in);
const k = F.e(_k);
let r;
for (let i=0; i<NROUNDS; i++) {
const c = cts[i];
const t = (i==0) ? F.add(x_in, k) : F.add(F.add(r, k), c);
r = F.pow(t, 7);
}
return F.add(r, k);
};
getConstants(seed, nRounds) {
const F = this.F;
if (typeof seed === "undefined") seed = SEED;
if (typeof nRounds === "undefined") nRounds = NROUNDS;
const cts = new Array(nRounds);
let c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(SEED));
for (let i=1; i<nRounds; i++) {
c = ethers.utils.keccak256(c);
exports.multiHash = (arr, key) => {
let r;
if (typeof(key) === "undefined") {
r = F.zero;
} else {
r = key;
cts[i] = F.e(c);
}
cts[0] = F.e(0);
return cts;
}
for (let i=0; i<arr.length; i++) {
r = F.add(
F.add(
r,
arr[i]
),
exports.hash(F.e(arr[i]), r)
);
hash (_x_in, _k) {
const F = this.F;
const x_in = F.e(_x_in);
const k = F.e(_k);
let 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+
//
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();
@ -51,7 +51,7 @@ function createCode(seed, n) {
C.mulmod(); // r=t^7 k q
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(0); // 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();
}
module.exports.abi = [
export const abi = [
{
"constant": true,
"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";
@ -9,5 +9,5 @@ if (typeof process.argv[2] != "undefined") {
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
const Web3Utils = require("web3-utils");
const ZqField = require("ffjavascript").ZqField;
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
import { Scalar, getCurveFromName } from "ffjavascript";
import ethers from "ethers";
const SEED = "mimcsponge";
const NROUNDS = 220;
exports.getIV = (seed) => {
if (typeof seed === "undefined") seed = SEED;
const c = Web3Utils.keccak256(seed+"_iv");
const cn = Scalar.fromString(Web3Utils.toBN(c).toString());
const iv = cn.mod(F.p);
return iv;
};
export default async function buildMimcSponge() {
const bn128 = await getCurveFromName("bn128");
return new MimcSponge(bn128.Fr);
}
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()));
const c2 = Web3Utils.padLeft(Web3Utils.toHex(n1), 64);
cts[i] = F.e(Web3Utils.toBN(c2).toString());
class MimcSponge {
constructor (F) {
this.F = F;
this.cts = this.getConstants(SEED, NROUNDS);
}
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) =>{
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 = cts[i];
const t = (i==0) ? F.add(xL, k) : F.add(F.add(xL, k), c);
const xR_tmp = F.e(xR);
if (i < (NROUNDS - 1)) {
xR = xL;
xL = F.add(xR_tmp, F.pow(t, 5));
getConstants (seed, nRounds) {
const F = this.F;
if (typeof seed === "undefined") seed = SEED;
if (typeof nRounds === "undefined") nRounds = NROUNDS;
const cts = new Array(nRounds);
let c = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(SEED));;
for (let i=1; i<nRounds; i++) {
c = ethers.utils.keccak256(c);
cts[i] = F.e(c);
}
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 {
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+
//
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();
@ -51,7 +51,7 @@ function createCode(seed, n) {
for (let i=0; i<n-1; i++) {
if (i < n-2) {
ci = Web3Utils.keccak256(ci);
ci = ethers.utils.keccak256(ci);
} else {
ci = "0x00";
}
@ -89,7 +89,7 @@ function createCode(seed, n) {
return C.createTxData();
}
module.exports.abi = [
export const abi = [
{
"constant": true,
"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;
let S = "[\n";
const cts = mimcsponge.getConstants();
for (let i=0; i<nRounds; i++) {
S = S + cts[i].toString();
if (i<nRounds-1) S = S + ",";
S=S+"\n";
async function run() {
const mimcsponge = await buildMimcSponge();
const nRounds = 220;
let S = "[\n";
const cts = mimcsponge.getConstants();
for (let i=0; i<nRounds; i++) {
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";
@ -9,5 +9,5 @@ if (typeof process.argv[2] != "undefined") {
nRounds = 220;
}
console.log(mimcGenContract.createCode(SEED, nRounds));
console.log(createCode(SEED, nRounds));

View File

@ -1,119 +1,129 @@
const babyJub = require("./babyjub");
const createBlakeHash = require("blake-hash");
const blake2b = require("blake2b");
const Scalar = require("ffjavascript").Scalar;
import buildBabyJub from "./babyjub.js";
import blake2b from "blake2b";
import createBlakeHash from "blake-hash";
import { Scalar } from "ffjavascript";
const GENPOINT_PREFIX = "PedersenGenerator";
const windowSize = 4;
const nWindowsPerSegment = 50;
exports.hash = pedersenHash;
exports.getBasePoint = getBasePoint;
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());
}
export default async function buildPedersenHash() {
const babyJub = await buildBabyJub();
return new PedersenHash(babyJub);
}
function pedersenHash(msg, options) {
options = options || {};
options.baseHash = options.baseHash || "blake";
const bitsPerSegment = windowSize*nWindowsPerSegment;
const bits = buffer2bits(msg);
class PedersenHash {
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];
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;
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());
}
let escalar = Scalar.e(0);
let exp = Scalar.e(1);
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++;
}
hash(msg, options) {
options = options || {};
options.baseHash = options.baseHash || "blake";
const babyJub = this.babyJub;
const bitsPerSegment = windowSize*nWindowsPerSegment;
const bits = this.buffer2bits(msg);
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) {
if (bits[o]) {
acc = Scalar.neg(acc);
let escalar = Scalar.e(0);
let exp = Scalar.e(1);
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)) {
escalar = Scalar.add( escalar, babyJub.subOrder);
return babyJub.packPoint(accP);
}
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);
}
let bases = [];
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++;
padLeftZeros(idx, n) {
let sidx = "" + idx;
while (sidx.length<n) sidx = "0"+sidx;
return sidx;
}
const p8 = babyJub.mulPointEscalar(p, 8);
if (!babyJub.inSubgroup(p8)) {
throw new Error("Point not in curve");
/*
Input a buffer
Returns an array of booleans. 0 is LSB of first byte and so on.
*/
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;
if (typeof process.argv[3] != "undefined") {
baseHash = process.argv[3];
} else {
baseHash = "blake";
}
run().then(()=> {
process.exit(0);
}, (err) => {
console.log(err.stack);
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
// 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)
// 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)));
// Optimization is taken from https://github.com/filecoin-project/neptune
function poseidon(inputs) {
assert(inputs.length > 0);
assert(inputs.length <= N_ROUNDS_P.length);
import assert from "assert";
import { getCurveFromName } from "ffjavascript";
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];
import poseidonConstants from "./poseidon_constants_opt.js";
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)
);
function unsringifyConstants(Fr, o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
return Fr.e(o);
} else if ((typeof(o) == "string") && (/^0x[0-9a-fA-F]+$/.test(o) )) {
return Fr.e(o);
} else if (Array.isArray(o)) {
return o.map(unsringifyConstants.bind(null, Fr));
} else if (typeof o == "object") {
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+
//
const Contract = require("./evmasm");
const { unstringifyBigInts } = require("ffjavascript").utils;
const Web3Utils = require("web3-utils");
import Contract from "./evmasm.js";
import { utils } from "ffjavascript";
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_P = [56, 57, 56, 60, 60, 63, 64, 63];
@ -17,7 +20,7 @@ function toHex256(a) {
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");
const t = nInputs + 1;
@ -101,10 +104,10 @@ function createCode(nInputs) {
C.calldataload();
C.div();
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.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.or();
C.jmpi("start");
@ -155,7 +158,7 @@ function createCode(nInputs) {
return C.createTxData();
}
function generateABI(nInputs) {
export function generateABI(nInputs) {
return [
{
"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) {
console.log("Usage: node poseidon_gencontract.js [numberOfInputs]");
@ -9,5 +9,5 @@ const nInputs = Number(process.argv[2]);
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
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
import assert from "assert";
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
// Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const { C, M } = unstringifyBigInts(require("./poseidon_constants.json"));
import poseidonConstants from "./poseidon_constants.js";
// 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)
);
function unsringifyConstants(Fr, o) {
if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) {
return Fr.e(o);
} else if ((typeof(o) == "string") && (/^0x[0-9a-fA-F]+$/.test(o) )) {
return Fr.e(o);
} else if (Array.isArray(o)) {
return o.map(unsringifyConstants.bind(null, Fr));
} else if (typeof o == "object") {
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;
}
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;
const SMTMemDB = require("./smt_memdb");
const {hash0, hash1, F} = require("./smt_hashes_poseidon");
import { Scalar } from "ffjavascript";
import SMTMemDB from "./smt_memdb.js";
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.root = root;
this.hash0 = hash0;
this.hash1 = hash1;
this.F = F;
}
_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);
@ -18,10 +29,10 @@ class SMT {
}
async update(_key, _newValue) {
const key = Scalar.e(_key);
const F = this.F;
const key = F.e(_key);
const newValue = F.e(_newValue);
const resFind = await this.find(key);
const res = {};
res.oldRoot = this.root;
@ -34,8 +45,8 @@ class SMT {
const ins = [];
const dels = [];
let rtOld = hash1(key, resFind.foundValue);
let rtNew = hash1(key, newValue);
let rtOld = this.hash1(key, resFind.foundValue);
let rtNew = this.hash1(key, newValue);
ins.push([rtNew, [1, key, newValue ]]);
dels.push(rtOld);
@ -50,8 +61,8 @@ class SMT {
oldNode = [rtOld, sibling];
newNode = [rtNew, sibling];
}
rtOld = hash0(oldNode[0], oldNode[1]);
rtNew = hash0(newNode[0], newNode[1]);
rtOld = this.hash0(oldNode[0], oldNode[1]);
rtNew = this.hash0(newNode[0], newNode[1]);
dels.push(rtOld);
ins.push([rtNew, newNode]);
}
@ -67,7 +78,8 @@ class SMT {
}
async delete(_key) {
const key = Scalar.e(_key);
const F = this.F;
const key = F.e(_key);
const resFind = await this.find(key);
if (!resFind.found) throw new Error("Key does not exists");
@ -80,7 +92,7 @@ class SMT {
const dels = [];
const ins = [];
let rtOld = hash1(key, resFind.foundValue);
let rtOld = this.hash1(key, resFind.foundValue);
let rtNew;
dels.push(rtOld);
@ -118,9 +130,9 @@ class SMT {
}
const oldSibling = resFind.siblings[level];
if (keyBits[level]) {
rtOld = hash0(oldSibling, rtOld);
rtOld = this.hash0(oldSibling, rtOld);
} else {
rtOld = hash0(rtOld, oldSibling);
rtOld = this.hash0(rtOld, oldSibling);
}
dels.push(rtOld);
if (!F.isZero(newSibling)) {
@ -135,7 +147,7 @@ class SMT {
} else {
newNode = [rtNew, newSibling];
}
rtNew = hash0(newNode[0], newNode[1]);
rtNew = this.hash0(newNode[0], newNode[1]);
ins.push([rtNew, newNode]);
}
}
@ -152,7 +164,8 @@ class SMT {
}
async insert(_key, _value) {
const key = Scalar.e(_key);
const F = this.F;
const key = F.e(_key);
const value = F.e(_value);
let addedOne = false;
const res = {};
@ -173,7 +186,7 @@ class SMT {
for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) {
res.siblings.push(F.zero);
}
rtOld = hash1(resFind.notFoundKey, resFind.notFoundValue);
rtOld = this.hash1(resFind.notFoundKey, resFind.notFoundValue);
res.siblings.push(rtOld);
addedOne = true;
mixed = false;
@ -185,7 +198,7 @@ class SMT {
const inserts = [];
const dels = [];
let rt = hash1(key, value);
let rt = this.hash1(key, value);
inserts.push([rt,[1, key, value]] );
for (let i=res.siblings.length-1; i>=0; i--) {
@ -195,9 +208,9 @@ class SMT {
if (mixed) {
const oldSibling = resFind.siblings[i];
if (newKeyBits[i]) {
rtOld = hash0(oldSibling, rtOld);
rtOld = this.hash0(oldSibling, rtOld);
} else {
rtOld = hash0(rtOld, oldSibling);
rtOld = this.hash0(rtOld, oldSibling);
}
dels.push(rtOld);
}
@ -205,10 +218,10 @@ class SMT {
let newRt;
if (newKeyBits[i]) {
newRt = hash0(res.siblings[i], rt);
newRt = this.hash0(res.siblings[i], rt);
inserts.push([newRt,[res.siblings[i], rt]] );
} else {
newRt = hash0(rt, res.siblings[i]);
newRt = this.hash0(rt, res.siblings[i]);
inserts.push([newRt,[rt, res.siblings[i]]] );
}
rt = newRt;
@ -232,12 +245,14 @@ class SMT {
return res;
}
async find(key) {
async find(_key) {
const key = this.F.e(_key);
const keyBits = this._splitBits(key);
return await this._find(key, keyBits, this.root, 0);
}
async _find(key, keyBits, root, level) {
const F = this.F;
if (typeof root === "undefined") root = this.root;
let res;
@ -284,18 +299,11 @@ class SMT {
}
}
async function loadFromFile(fileName) {
}
async function newMemEmptyTrie() {
const db = new SMTMemDB();
export async function newMemEmptyTrie() {
const {hash0, hash1,F} = await getHashes();
const db = new SMTMemDB(F);
const rt = await db.getRoot();
const smt = new SMT(db, rt);
const smt = new SMT(db, rt, hash0, hash1, F);
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) {
return mimc7.multiHash(left, right);
};
import buildMimc7 from "./mimc7.js";
exports.hash1 = function(key, value) {
return mimc7.multiHash([key, value], bigInt.one);
};
export default async function getHashes() {
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 @@
const ZqField = require("ffjavascript").ZqField;
const Scalar = require("ffjavascript").Scalar;
const poseidon = require("./poseidon");
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
import buildPoseidon from "./poseidon.js";
import { getCurveFromName } from "ffjavascript";
exports.hash0 = function (left, right) {
return poseidon([left, right]);
};
exports.hash1 = function(key, value) {
return poseidon([key, value, F.one]);
};
exports.F = F;
export default async function getHashes() {
const bn128 = await getCurveFromName("bn128");
const poseidon = await buildPoseidon();
return {
hash0: function (left, right) {
return poseidon([left, right]);
},
hash1: function(key, value) {
return poseidon([key, value, bn128.Fr.one]);
},
F: bn128.Fr
}
}

View File

@ -1,14 +1,8 @@
const Scalar = require("ffjavascript").Scalar;
const ZqField = require("ffjavascript").ZqField;
// Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
class SMTMemDb {
constructor() {
export default class SMTMemDb {
constructor(F) {
this.nodes = {};
this.root = F.zero;
this.F = F;
}
async getRoot() {
@ -16,14 +10,15 @@ class SMTMemDb {
}
_key2str(k) {
// const keyS = bigInt(key).leInt2Buff(32).toString("hex");
const keyS = k.toString();
const F = this.F;
const keyS = this.F.toString(k);
return keyS;
}
_normalize(n) {
const F = this.F;
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");
const babyjub = require("../src/babyjub.js");
const Scalar = require("ffjavascript").Scalar;
import chai from "chai";
import buildBabyjub from "../src/babyjub.js";
import { Scalar } from "ffjavascript";
const assert = chai.assert;
@ -14,9 +14,16 @@ function buff2hex(buff) {
}
describe("Baby Jub js test", function () {
let babyjub;
this.timeout(100000);
before(async () => {
babyjub = await buildBabyjub();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should add point (0,1) and (0,1)", () => {
const p1 = [
@ -84,10 +91,10 @@ describe("Baby Jub js test", function () {
const r = babyjub.mulPointEscalar(p, 3);
let r2 = babyjub.addPoint(p, p);
r2 = babyjub.addPoint(r2, p);
assert.equal(r2[0].toString(), r[0].toString());
assert.equal(r2[1].toString(), r[1].toString());
assert.equal(r[0].toString(), "19372461775513343691590086534037741906533799473648040012278229434133483800898");
assert.equal(r[1].toString(), "9458658722007214007257525444427903161243386465067105737478306991484593958249");
assert(babyjub.F.eq(r2[0], r[0]));
assert(babyjub.F.eq(r2[1], r[1]));
assert(babyjub.F.eq(r[0], babyjub.F.e("19372461775513343691590086534037741906533799473648040012278229434133483800898")));
assert(babyjub.F.eq(r[1], babyjub.F.e("9458658722007214007257525444427903161243386465067105737478306991484593958249")));
});
it("should mulPointEscalar 1", () => {
@ -97,8 +104,8 @@ describe("Baby Jub js test", function () {
];
const r = babyjub.mulPointEscalar(p, Scalar.fromString("14035240266687799601661095864649209771790948434046947201833777492504781204499"));
assert.equal(r[0].toString(), "17070357974431721403481313912716834497662307308519659060910483826664480189605");
assert.equal(r[1].toString(), "4014745322800118607127020275658861516666525056516280575712425373174125159339");
assert(babyjub.F.eq(r[0], babyjub.F.e("17070357974431721403481313912716834497662307308519659060910483826664480189605")));
assert(babyjub.F.eq(r[1], babyjub.F.e("4014745322800118607127020275658861516666525056516280575712425373174125159339")));
});
it("should mulPointEscalar 2", () => {
@ -108,8 +115,8 @@ describe("Baby Jub js test", function () {
];
const r = babyjub.mulPointEscalar(p, Scalar.fromString("20819045374670962167435360035096875258406992893633759881276124905556507972311"));
assert.equal(r[0].toString(), "13563888653650925984868671744672725781658357821216877865297235725727006259983");
assert.equal(r[1].toString(), "8442587202676550862664528699803615547505326611544120184665036919364004251662");
assert(babyjub.F.eq(r[0], babyjub.F.e("13563888653650925984868671744672725781658357821216877865297235725727006259983")));
assert(babyjub.F.eq(r[1], babyjub.F.e("8442587202676550862664528699803615547505326611544120184665036919364004251662")));
});
it("should inCurve 1", () => {
@ -152,8 +159,8 @@ describe("Baby Jub js test", function () {
const buf = babyjub.packPoint(p);
assert.equal(buff2hex(buf), "53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85");
const p2 = babyjub.unpackPoint(buf);
assert.equal(p2[0].toString(), "17777552123799933955779906779655732241715742912184938656739573121738514868268");
assert.equal(p2[1].toString(), "2626589144620713026669568689430873010625803728049924121243784502389097019475");
assert(babyjub.F.eq(p2[0], babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268")));
assert(babyjub.F.eq(p2[1], babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475")));
});
it("should packPoint - unpackPoint 2", () => {
@ -164,7 +171,7 @@ describe("Baby Jub js test", function () {
const buf = babyjub.packPoint(p);
assert.equal(buff2hex(buf), "e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709");
const p2 = babyjub.unpackPoint(buf);
assert.equal(p2[0].toString(), "6890855772600357754907169075114257697580319025794532037257385534741338397365");
assert.equal(p2[1].toString(), "4338620300185947561074059802482547481416142213883829469920100239455078257889");
assert(babyjub.F.eq(p2[0], babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365")));
assert(babyjub.F.eq(p2[1], babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889")));
});
});

View File

@ -1,26 +1,68 @@
const chai = require("chai");
const eddsa = require("../src/eddsa.js");
const babyJub = require("../src/babyjub.js");
import chai from "chai";
import { Scalar } from "ffjavascript";
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 () {
let eddsa;
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", () => {
const msgBuf = Buffer.from("00010203040506070809", "hex");
const msg = utils.leBuff2int(msgBuf);
const F = eddsa.babyJub.F;
const msgBuf = fromHexString("000102030405060708090000");
const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0));
// const prvKey = crypto.randomBytes(32);
@ -28,23 +70,23 @@ describe("EdDSA js test", function () {
const pubKey = eddsa.prv2pub(prvKey);
assert.equal(pubKey[0].toString(),
"13277427435165878497778222415993513565335242147425444199013288855685581939618");
assert.equal(pubKey[1].toString(),
"13622229784656158136036771217484571176836296686641868549125388198837476602820");
assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
const pPubKey = babyJub.packPoint(pubKey);
const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signMiMC(prvKey, msg);
assert.equal(signature.R8[0].toString(),
"11384336176656855268977457483345535180380036354188103142384839473266348197733");
assert.equal(signature.R8[1].toString(),
"15383486972088797283337779941324724402501462225528836549661220478783371668959");
assert.equal(signature.S.toString(),
"2523202440825208709475937830811065542425109372212752003460238913256192595070");
// 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("2523202440825208709475937830811065542425109372212752003460238913256192595070")));
const pSignature = eddsa.packSignature(signature);
assert.equal(buff2hex(pSignature), ""+
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405");
@ -54,35 +96,75 @@ describe("EdDSA js test", function () {
});
it("Sign (using Poseidon) a single 10 bytes from 0 to 9", () => {
const msgBuf = Buffer.from("00010203040506070809", "hex");
const msg = utils.leBuff2int(msgBuf);
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.equal(pubKey[0].toString(),
"13277427435165878497778222415993513565335242147425444199013288855685581939618");
assert.equal(pubKey[1].toString(),
"13622229784656158136036771217484571176836296686641868549125388198837476602820");
assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618")));
assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820")));
const pPubKey = babyJub.packPoint(pubKey);
const pPubKey = eddsa.babyJub.packPoint(pubKey);
const signature = eddsa.signPoseidon(prvKey, msg);
assert.equal(signature.R8[0].toString(),
"11384336176656855268977457483345535180380036354188103142384839473266348197733");
assert.equal(signature.R8[1].toString(),
"15383486972088797283337779941324724402501462225528836549661220478783371668959");
assert.equal(signature.S.toString(),
"1672775540645840396591609181675628451599263765380031905495115170613215233181");
// 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("1672775540645840396591609181675628451599263765380031905495115170613215233181")));
const pSignature = eddsa.packSignature(signature);
assert.equal(buff2hex(pSignature), ""+
// console.log(toHexString(pSignature));
assert.equal(toHexString(pSignature), ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203");
const uSignature = eddsa.unpackSignature(pSignature);
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");
const Web3 = require("web3");
const chai = require("chai");
const mimcGenContract = require("../src/mimcsponge_gencontract.js");
const mimcjs = require("../src/mimcsponge.js");
import chai from "chai";
import {createCode, abi} from "../src/mimcsponge_gencontract.js";
import ethers from "ethers";
import ganache from "ganache-cli";
import buildMimcSponge from "../src/mimcsponge.js";
const assert = chai.assert;
@ -11,33 +12,44 @@ const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
const SEED = "mimcsponge";
describe("MiMC Sponge Smart contract test", () => {
let testrpc;
let web3;
let mimc;
let accounts;
let mimcJS;
let account;
before(async () => {
web3 = new Web3(ganache.provider(), null, { transactionConfirmationBlocks: 1 });
accounts = await web3.eth.getAccounts();
const provider = new ethers.providers.Web3Provider(ganache.provider());
account = provider.getSigner(0);
mimcJS = await buildMimcSponge();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should deploy the contract", async () => {
const C = new web3.eth.Contract(mimcGenContract.abi);
mimc = await C.deploy({
data: mimcGenContract.createCode(SEED, 220)
}).send({
gas: 3500000,
from: accounts[0]
});
const C = new ethers.ContractFactory(
abi,
createCode(SEED, 220),
account
);
mimc = await C.deploy();
});
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());
assert.equal(res.xR.toString(), res2.xR.toString());
const res = await mimc["MiMCSponge"](1,2, 3);
// 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 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 () => {
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 () => {
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");
const Web3 = require("web3");
const chai = require("chai");
const poseidonGenContract = require("../src/poseidon_gencontract.js");
const poseidon = require("../src/poseidon.js");
import chai from "chai";
import {createCode, generateABI} from "../src/poseidon_gencontract.js";
import buildPoseidon from "../src/poseidon.js";
import ethers from "ethers";
import ganache from "ganache-cli";
const assert = chai.assert;
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
@ -12,53 +12,57 @@ describe("Poseidon Smart contract test", function () {
let web3;
let poseidon6;
let poseidon3;
let accounts;
let poseidon;
let account;
this.timeout(100000);
before(async () => {
web3 = new Web3(ganache.provider(), null, { transactionConfirmationBlocks: 1 });
accounts = await web3.eth.getAccounts();
const provider = new ethers.providers.Web3Provider(ganache.provider());
account = provider.getSigner(0);
poseidon = await buildPoseidon();
});
after(async () => {
globalThis.curve_bn128.terminate();
});
it("Should deploy the contract", async () => {
const C6 = new web3.eth.Contract(poseidonGenContract.generateABI(5));
const C3 = new web3.eth.Contract(poseidonGenContract.generateABI(2));
const C6 = new ethers.ContractFactory(
generateABI(5),
createCode(5),
account
);
const C3 = new ethers.ContractFactory(
generateABI(2),
createCode(2),
account
);
poseidon6 = await C6.deploy({
data: poseidonGenContract.createCode(5)
}).send({
gas: 5000000,
from: accounts[0]
});
poseidon3 = await C3.deploy({
data: poseidonGenContract.createCode(2)
}).send({
gas: 5000000,
from: accounts[0]
});
poseidon6 = await C6.deploy();
poseidon3 = await C3.deploy();
});
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));
const res2 = poseidon([1,2, 0, 0, 0]);
// 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 () => {
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));
const res2 = poseidon([1,2]);
// 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;
const Scalar = require("ffjavascript").Scalar;
exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const Fr = new F1Field(exports.p);
const smt = require("../src/smt.js");
import buildBabyjub from "../src/babyjub.js";
import {newMemEmptyTrie} from "../src/smt.js";
const assert = chai.assert;
describe("SMT Javascript test", function () {
let Fr;
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 () => {
const tree = await smt.newMemEmptyTrie();
const tree = await newMemEmptyTrie();
const key1 = Fr.e(111);
const value1 = Fr.e(222);
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 () => {
const keys = [Fr.e(8), Fr.e(9), Fr.e(32)];
const values = [Fr.e(88), Fr.e(99), Fr.e(3232)];
const tree1 = await smt.newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie();
const tree3 = await smt.newMemEmptyTrie();
const tree4 = await smt.newMemEmptyTrie();
const tree5 = await smt.newMemEmptyTrie();
const tree6 = await smt.newMemEmptyTrie();
const tree1 = await newMemEmptyTrie();
const tree2 = await newMemEmptyTrie();
const tree3 = await newMemEmptyTrie();
const tree4 = await newMemEmptyTrie();
const tree5 = await newMemEmptyTrie();
const tree6 = await newMemEmptyTrie();
await tree1.insert(keys[0],values[0]);
await tree1.insert(keys[1],values[1]);
@ -126,7 +128,7 @@ describe("SMT Javascript test", function () {
}
return rArr;
}
const tree = await smt.newMemEmptyTrie();
const tree = await newMemEmptyTrie();
const arr = [];
const N = 100;
for (let i=0; i<N; i++) {
@ -146,8 +148,8 @@ describe("SMT Javascript test", function () {
});
it("Should test update", async () => {
const tree1 = await smt.newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie();
const tree1 = await newMemEmptyTrie();
const tree2 = await newMemEmptyTrie();
await tree1.insert(8,88);
await tree1.insert(9,99,);
@ -165,7 +167,7 @@ describe("SMT Javascript test", function () {
});
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.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");
const path = require("path");
const Scalar = require("ffjavascript").Scalar;
const ZqField = require("ffjavascript").ZqField;
const { unstringifyBigInts } = require("ffjavascript").utils;
import fs from "fs";
import path from "path";
import {Scalar, ZqField, utils} from "ffjavascript";
const { unstringifyBigInts } = utils;
// 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_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);
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(()=> {