mirror of
https://github.com/tornadocash/circomlibjs.git
synced 2025-01-05 19:26:00 +01:00
Transfer from circomlib
This commit is contained in:
commit
3d29629a83
65
.gitignore
vendored
Normal file
65
.gitignore
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
tmp
|
||||
|
||||
.DS_Store
|
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
||||
#circomlibjs
|
||||
|
||||
Javascript library to work with circomlib circuits
|
||||
|
9070
package-lock.json
generated
Normal file
9070
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
package.json
Normal file
39
package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "circomlibjs",
|
||||
"version": "0.0.1",
|
||||
"description": "Javascript library to work with circomlibç",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/iden3/circomlibjs.git"
|
||||
},
|
||||
"keywords": [
|
||||
"circom",
|
||||
"circomlib",
|
||||
"iden3",
|
||||
"snarkjs",
|
||||
"snark",
|
||||
"zero",
|
||||
"knowledge"
|
||||
],
|
||||
"author": "Jordi Baylina",
|
||||
"license": "GPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/iden3/circomlibjs/issues"
|
||||
},
|
||||
"homepage": "https://github.com/iden3/circomlibjs#readme",
|
||||
"devDependencies": {
|
||||
"chai": "^4.3.4",
|
||||
"ganache-cli": "^6.12.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"blake-hash": "^2.0.0",
|
||||
"blake2b": "^2.1.3",
|
||||
"ffjavascript": "^0.2.38",
|
||||
"web3": "^1.6.0",
|
||||
"web3-utils": "^1.6.0"
|
||||
}
|
||||
}
|
128
src/babyjub.js
Normal file
128
src/babyjub.js
Normal file
@ -0,0 +1,128 @@
|
||||
const F1Field = require("ffjavascript").F1Field;
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const utils = require("ffjavascript").utils;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function mulPointEscalar(base, e) {
|
||||
let res = [F.e("0"),F.e("1")];
|
||||
let rem = e;
|
||||
let exp = base;
|
||||
|
||||
while (! Scalar.isZero(rem)) {
|
||||
if (Scalar.isOdd(rem)) {
|
||||
res = addPoint(res, exp);
|
||||
}
|
||||
exp = addPoint(exp, exp);
|
||||
rem = Scalar.shiftRight(rem, 1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
P[1] = utils.leBuff2int(buff);
|
||||
if (Scalar.gt(P[1], exports.p)) return null;
|
||||
|
||||
const y2 = F.square(P[1]);
|
||||
|
||||
let x = F.sqrt(F.div(
|
||||
F.sub(F.one, y2),
|
||||
F.sub(exports.A, F.mul(exports.D, y2))));
|
||||
|
||||
if (x == null) return null;
|
||||
|
||||
if (sign) x = F.neg(x);
|
||||
|
||||
P[0] = x;
|
||||
|
||||
return P;
|
||||
}
|
233
src/eddsa.js
Normal file
233
src/eddsa.js
Normal file
@ -0,0 +1,233 @@
|
||||
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");
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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 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
|
||||
};
|
||||
}
|
||||
|
||||
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 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
|
||||
};
|
||||
}
|
||||
|
||||
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 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 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 hm = mimc7.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, 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 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))
|
||||
};
|
||||
}
|
||||
|
||||
|
208
src/evmasm.js
Normal file
208
src/evmasm.js
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2018 Jordi Baylina
|
||||
// License: LGPL-3.0+
|
||||
//
|
||||
|
||||
|
||||
const Web3Utils = require("web3-utils");
|
||||
|
||||
class Contract {
|
||||
constructor() {
|
||||
this.code = [];
|
||||
this.labels = {};
|
||||
this.pendingLabels = {};
|
||||
}
|
||||
|
||||
createTxData() {
|
||||
let C;
|
||||
|
||||
// Check all labels are defined
|
||||
const pendingLabels = Object.keys(this.pendingLabels);
|
||||
if (pendingLabels.length>0) {
|
||||
throw new Error("Lables not defined: "+ pendingLabels.join(", "));
|
||||
}
|
||||
|
||||
let setLoaderLength = 0;
|
||||
let genLoadedLength = -1;
|
||||
|
||||
while (genLoadedLength!=setLoaderLength) {
|
||||
setLoaderLength = genLoadedLength;
|
||||
C = new module.exports();
|
||||
C.codesize();
|
||||
C.push(setLoaderLength);
|
||||
C.push(0);
|
||||
C.codecopy();
|
||||
|
||||
C.push(this.code.length);
|
||||
C.push(0);
|
||||
C.return();
|
||||
genLoadedLength = C.code.length;
|
||||
}
|
||||
|
||||
return Web3Utils.bytesToHex(C.code.concat(this.code));
|
||||
}
|
||||
|
||||
stop() { this.code.push(0x00); }
|
||||
add() { this.code.push(0x01); }
|
||||
mul() { this.code.push(0x02); }
|
||||
sub() { this.code.push(0x03); }
|
||||
div() { this.code.push(0x04); }
|
||||
sdiv() { this.code.push(0x05); }
|
||||
mod() { this.code.push(0x06); }
|
||||
smod() { this.code.push(0x07); }
|
||||
addmod() { this.code.push(0x08); }
|
||||
mulmod() { this.code.push(0x09); }
|
||||
exp() { this.code.push(0x0a); }
|
||||
signextend() { this.code.push(0x0b); }
|
||||
|
||||
lt() { this.code.push(0x10); }
|
||||
gt() { this.code.push(0x11); }
|
||||
slt() { this.code.push(0x12); }
|
||||
sgt() { this.code.push(0x13); }
|
||||
eq() { this.code.push(0x14); }
|
||||
iszero() { this.code.push(0x15); }
|
||||
and() { this.code.push(0x16); }
|
||||
or() { this.code.push(0x17); }
|
||||
shor() { this.code.push(0x18); }
|
||||
not() { this.code.push(0x19); }
|
||||
byte() { this.code.push(0x1a); }
|
||||
|
||||
keccak() { this.code.push(0x20); }
|
||||
sha3() { this.code.push(0x20); } // alias
|
||||
|
||||
address() { this.code.push(0x30); }
|
||||
balance() { this.code.push(0x31); }
|
||||
origin() { this.code.push(0x32); }
|
||||
caller() { this.code.push(0x33); }
|
||||
callvalue() { this.code.push(0x34); }
|
||||
calldataload() { this.code.push(0x35); }
|
||||
calldatasize() { this.code.push(0x36); }
|
||||
calldatacopy() { this.code.push(0x37); }
|
||||
codesize() { this.code.push(0x38); }
|
||||
codecopy() { this.code.push(0x39); }
|
||||
gasprice() { this.code.push(0x3a); }
|
||||
extcodesize() { this.code.push(0x3b); }
|
||||
extcodecopy() { this.code.push(0x3c); }
|
||||
returndatasize() { this.code.push(0x3d); }
|
||||
returndatacopy() { this.code.push(0x3e); }
|
||||
|
||||
blockhash() { this.code.push(0x40); }
|
||||
coinbase() { this.code.push(0x41); }
|
||||
timestamp() { this.code.push(0x42); }
|
||||
number() { this.code.push(0x43); }
|
||||
difficulty() { this.code.push(0x44); }
|
||||
gaslimit() { this.code.push(0x45); }
|
||||
|
||||
pop() { this.code.push(0x50); }
|
||||
mload() { this.code.push(0x51); }
|
||||
mstore() { this.code.push(0x52); }
|
||||
mstore8() { this.code.push(0x53); }
|
||||
sload() { this.code.push(0x54); }
|
||||
sstore() { this.code.push(0x55); }
|
||||
|
||||
_pushLabel(label) {
|
||||
if (typeof this.labels[label] != "undefined") {
|
||||
this.push(this.labels[label]);
|
||||
} else {
|
||||
this.pendingLabels[label] = this.pendingLabels[label] || [];
|
||||
this.pendingLabels[label].push(this.code.length);
|
||||
this.push("0x000000");
|
||||
}
|
||||
}
|
||||
|
||||
_fillLabel(label) {
|
||||
if (!this.pendingLabels[label]) return;
|
||||
|
||||
let dst = this.labels[label];
|
||||
|
||||
const dst3 = [dst >> 16, (dst >> 8) & 0xFF, dst & 0xFF];
|
||||
|
||||
this.pendingLabels[label].forEach((p) => {
|
||||
for (let i=0; i<3; i++) {
|
||||
this.code[p+i+1] = dst3[i];
|
||||
}
|
||||
});
|
||||
|
||||
delete this.pendingLabels[label];
|
||||
}
|
||||
|
||||
|
||||
jmp(label) {
|
||||
if (typeof label !== "undefined") {
|
||||
this._pushLabel(label);
|
||||
}
|
||||
this.code.push(0x56);
|
||||
}
|
||||
|
||||
jmpi(label) {
|
||||
if (typeof label !== "undefined") {
|
||||
this._pushLabel(label);
|
||||
}
|
||||
this.code.push(0x57);
|
||||
}
|
||||
|
||||
pc() { this.code.push(0x58); }
|
||||
msize() { this.code.push(0x59); }
|
||||
gas() { this.code.push(0x5a); }
|
||||
label(name) {
|
||||
if (typeof this.labels[name] != "undefined") {
|
||||
throw new Error("Label already defined");
|
||||
}
|
||||
this.labels[name] = this.code.length;
|
||||
this.code.push(0x5b);
|
||||
|
||||
this._fillLabel(name);
|
||||
}
|
||||
|
||||
push(data) {
|
||||
if (typeof data === "number") {
|
||||
let isNeg;
|
||||
if (data<0) {
|
||||
isNeg = true;
|
||||
data = -data;
|
||||
}
|
||||
data = data.toString(16);
|
||||
if (data.length % 2 == 1) data = "0" + data;
|
||||
data = "0x" + data;
|
||||
if (isNeg) data = "-"+data;
|
||||
}
|
||||
const d = Web3Utils.hexToBytes(Web3Utils.toHex(data));
|
||||
if (d.length == 0 || d.length > 32) {
|
||||
throw new Error("Assertion failed");
|
||||
}
|
||||
this.code = this.code.concat([0x5F + d.length], d);
|
||||
}
|
||||
|
||||
dup(n) {
|
||||
if (n < 0 || n >= 16) {
|
||||
throw new Error("Assertion failed");
|
||||
}
|
||||
this.code.push(0x80 + n);
|
||||
}
|
||||
|
||||
swap(n) {
|
||||
if (n < 1 || n > 16) {
|
||||
throw new Error("Assertion failed");
|
||||
}
|
||||
this.code.push(0x8f + n);
|
||||
}
|
||||
|
||||
log0() { this.code.push(0xa0); }
|
||||
log1() { this.code.push(0xa1); }
|
||||
log2() { this.code.push(0xa2); }
|
||||
log3() { this.code.push(0xa3); }
|
||||
log4() { this.code.push(0xa4); }
|
||||
|
||||
create() { this.code.push(0xf0); }
|
||||
call() { this.code.push(0xf1); }
|
||||
callcode() { this.code.push(0xf2); }
|
||||
return() { this.code.push(0xf3); }
|
||||
delegatecall() { this.code.push(0xf4); }
|
||||
|
||||
staticcall() { this.code.push(0xfa); }
|
||||
revert() { this.code.push(0xfd); }
|
||||
invalid() { this.code.push(0xfe); }
|
||||
selfdestruct() { this.code.push(0xff); }
|
||||
}
|
||||
|
||||
module.exports = Contract;
|
||||
|
582
src/g2_gencontract.js
Normal file
582
src/g2_gencontract.js
Normal file
@ -0,0 +1,582 @@
|
||||
// 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;
|
66
src/mimc7.js
Normal file
66
src/mimc7.js
Normal file
@ -0,0 +1,66 @@
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
|
||||
const Web3Utils = require("web3-utils");
|
||||
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
|
||||
exports.F = F;
|
||||
|
||||
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 = cn.mod(F.p);
|
||||
return iv;
|
||||
};
|
||||
|
||||
exports.getConstants = (seed, nRounds) => {
|
||||
if (typeof seed === "undefined") seed = SEED;
|
||||
if (typeof nRounds === "undefined") nRounds = NROUNDS;
|
||||
const cts = new Array(nRounds);
|
||||
let c = Web3Utils.keccak256(SEED);
|
||||
for (let i=1; i<nRounds; i++) {
|
||||
c = Web3Utils.keccak256(c);
|
||||
|
||||
const n1 = Web3Utils.toBN(c).mod(Web3Utils.toBN(F.p.toString()));
|
||||
const c2 = Web3Utils.padLeft(Web3Utils.toHex(n1), 64);
|
||||
cts[i] = Scalar.fromString(Web3Utils.toBN(c2).toString());
|
||||
}
|
||||
cts[0] = F.e(0);
|
||||
return cts;
|
||||
};
|
||||
|
||||
const cts = exports.getConstants(SEED, 91);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
exports.multiHash = (arr, key) => {
|
||||
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]
|
||||
),
|
||||
exports.hash(F.e(arr[i]), r)
|
||||
);
|
||||
}
|
||||
return r;
|
||||
};
|
114
src/mimc_gencontract.js
Normal file
114
src/mimc_gencontract.js
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2018 Jordi Baylina
|
||||
// License: LGPL-3.0+
|
||||
//
|
||||
|
||||
const Web3Utils = require("web3-utils");
|
||||
|
||||
const Contract = require("./evmasm");
|
||||
|
||||
function createCode(seed, n) {
|
||||
|
||||
let ci = Web3Utils.keccak256(seed);
|
||||
|
||||
const C = new Contract();
|
||||
|
||||
C.push(0x44);
|
||||
C.push("0x00");
|
||||
C.push("0x00");
|
||||
C.calldatacopy();
|
||||
C.push("0x0100000000000000000000000000000000000000000000000000000000");
|
||||
C.push("0x00");
|
||||
C.mload();
|
||||
C.div();
|
||||
C.push("0xd15ca109"); // MiMCpe7(uint256,uint256)
|
||||
// C.push("0x8c42199e"); // MiMCpe7(uint256,uint256,uint256)
|
||||
C.eq();
|
||||
C.jmpi("start");
|
||||
C.invalid();
|
||||
|
||||
C.label("start");
|
||||
C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q
|
||||
C.push("0x24");
|
||||
C.mload(); // k q
|
||||
|
||||
|
||||
C.dup(1); // q k q
|
||||
C.dup(0); // q q k q
|
||||
C.push("0x04");
|
||||
C.mload(); // x q q k q
|
||||
C.dup(3); // k x q q k q
|
||||
C.addmod(); // t=x+k q k q
|
||||
C.dup(1); // q t q k q
|
||||
C.dup(0); // q q t q k q
|
||||
C.dup(2); // t q q t q k q
|
||||
C.dup(0); // t t q q t q k q
|
||||
C.mulmod(); // a=t^2 q t q k q
|
||||
C.dup(1); // q a q t q k q
|
||||
C.dup(1); // a q a q t q k q
|
||||
C.dup(0); // a a q a q t q k q
|
||||
C.mulmod(); // b=t^4 a q t q k q
|
||||
C.mulmod(); // c=t^6 t q k q
|
||||
C.mulmod(); // r=t^7 k q
|
||||
|
||||
for (let i=0; i<n-1; i++) {
|
||||
ci = Web3Utils.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
|
||||
C.swap(3); // r q q q k q
|
||||
C.push(ci); // c r q q k q
|
||||
C.addmod(); // s=c+r q q k q
|
||||
C.dup(3); // k s q q k q
|
||||
C.addmod(); // t=s+k q k q
|
||||
C.dup(1); // q t q k q
|
||||
C.dup(0); // q q t q k q
|
||||
C.dup(2); // t q q t q k q
|
||||
C.dup(0); // t t q q t q k q
|
||||
C.mulmod(); // a=t^2 q t q k q
|
||||
C.dup(1); // q a q t q k q
|
||||
C.dup(1); // a q a q t q k q
|
||||
C.dup(0); // a a q a q t q k q
|
||||
C.mulmod(); // b=t^4 a q t q k q
|
||||
C.mulmod(); // c=t^6 t q k q
|
||||
C.mulmod(); // r=t^7 k q
|
||||
}
|
||||
|
||||
C.addmod(); // res=t^7+k
|
||||
C.push("0x00");
|
||||
C.mstore(); // Save it to pos 0;
|
||||
C.push("0x20");
|
||||
C.push("0x00");
|
||||
C.return();
|
||||
|
||||
return C.createTxData();
|
||||
}
|
||||
|
||||
module.exports.abi = [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "in_x",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "in_k",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "MiMCpe7",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out_x",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.createCode = createCode;
|
||||
|
||||
|
3
src/mimc_print_iv.js
Normal file
3
src/mimc_print_iv.js
Normal file
@ -0,0 +1,3 @@
|
||||
const mimc7 = require("./mimc7.js");
|
||||
|
||||
console.log("IV: "+mimc7.getIV().toString());
|
13
src/mimc_printconstants.js
Normal file
13
src/mimc_printconstants.js
Normal file
@ -0,0 +1,13 @@
|
||||
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);
|
13
src/mimc_printcontract.js
Normal file
13
src/mimc_printcontract.js
Normal file
@ -0,0 +1,13 @@
|
||||
const mimcGenContract = require("./mimc_gencontract");
|
||||
|
||||
const SEED = "mimc";
|
||||
|
||||
let nRounds;
|
||||
if (typeof process.argv[2] != "undefined") {
|
||||
nRounds = parseInt(process.argv[2]);
|
||||
} else {
|
||||
nRounds = 91;
|
||||
}
|
||||
|
||||
console.log(mimcGenContract.createCode(SEED, nRounds));
|
||||
|
86
src/mimcsponge.js
Normal file
86
src/mimcsponge.js
Normal file
@ -0,0 +1,86 @@
|
||||
const Scalar = require("ffjavascript").Scalar
|
||||
const Web3Utils = require("web3-utils");
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
cts[0] = F.e(0);
|
||||
cts[cts.length - 1] = F.e(0);
|
||||
return cts;
|
||||
};
|
||||
|
||||
const cts = exports.getConstants(SEED, NROUNDS);
|
||||
|
||||
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));
|
||||
} else {
|
||||
xR = F.add(xR_tmp, F.pow(t, 5));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
};
|
128
src/mimcsponge_gencontract.js
Normal file
128
src/mimcsponge_gencontract.js
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2018 Jordi Baylina
|
||||
// License: LGPL-3.0+
|
||||
//
|
||||
|
||||
const Web3Utils = require("web3-utils");
|
||||
|
||||
const Contract = require("./evmasm");
|
||||
|
||||
function createCode(seed, n) {
|
||||
|
||||
let ci = Web3Utils.keccak256(seed);
|
||||
|
||||
const C = new Contract();
|
||||
|
||||
C.push(0x64);
|
||||
C.push("0x00");
|
||||
C.push("0x00");
|
||||
C.calldatacopy();
|
||||
C.push("0x0100000000000000000000000000000000000000000000000000000000");
|
||||
C.push("0x00");
|
||||
C.mload();
|
||||
C.div();
|
||||
C.push("0x3f1a1187"); // MiMCSponge(uint256,uint256,uint256)
|
||||
C.eq();
|
||||
C.jmpi("start");
|
||||
C.invalid();
|
||||
|
||||
C.label("start");
|
||||
C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q
|
||||
C.push("0x44");
|
||||
C.mload(); // k q
|
||||
C.push("0x04");
|
||||
C.mload(); // xL k q
|
||||
C.dup(2); // q xL k q
|
||||
C.push("0x24");
|
||||
C.mload(); // xR q xL k q
|
||||
C.dup(1); // q xR q xL k q
|
||||
C.dup(0); // q q xR q xL k q
|
||||
C.dup(4); // xL q q xR q xL k q
|
||||
C.dup(6); // k xL q q xR q xL k q
|
||||
C.addmod(); // t=k+xL q xR q xL k q
|
||||
C.dup(1); // q t q xR q xL k q
|
||||
C.dup(0); // q q t q xR q xL k q
|
||||
C.dup(2); // t q q t q xR q xL k q
|
||||
C.dup(0); // t t q q t q xR q xL k q
|
||||
C.mulmod(); // b=t^2 q t q xR q xL k q
|
||||
C.dup(0); // b b q t q xR q xL k q
|
||||
C.mulmod(); // c=t^4 t q xR q xL k q
|
||||
C.mulmod(); // d=t^5 xR q xL k q
|
||||
C.addmod(); // e=t^5+xR xL k q (for next round: xL xR k q)
|
||||
|
||||
for (let i=0; i<n-1; i++) {
|
||||
if (i < n-2) {
|
||||
ci = Web3Utils.keccak256(ci);
|
||||
} else {
|
||||
ci = "0x00";
|
||||
}
|
||||
C.swap(1); // xR xL k q
|
||||
C.dup(3); // q xR xL k q
|
||||
C.dup(3); // k q xR xL k q
|
||||
C.dup(1); // q k q xR xL k q
|
||||
C.dup(4); // xL q k q xR xL k q
|
||||
C.push(ci); // ci xL q k q xR xL k q
|
||||
C.addmod(); // a=ci+xL k q xR xL k q
|
||||
C.addmod(); // t=a+k xR xL k q
|
||||
C.dup(4); // q t xR xL k q
|
||||
C.swap(1); // t q xR xL k q
|
||||
C.dup(1); // q t q xR xL k q
|
||||
C.dup(0); // q q t q xR xL k q
|
||||
C.dup(2); // t q q t q xR xL k q
|
||||
C.dup(0); // t t q q t q xR xL k q
|
||||
C.mulmod(); // b=t^2 q t q xR xL k q
|
||||
C.dup(0); // b b q t q xR xL k q
|
||||
C.mulmod(); // c=t^4 t q xR xL k q
|
||||
C.mulmod(); // d=t^5 xR xL k q
|
||||
C.dup(4); // q d xR xL k q
|
||||
C.swap(2); // xR d q xL k q
|
||||
C.addmod(); // e=t^5+xR xL k q (for next round: xL xR k q)
|
||||
}
|
||||
|
||||
C.push("0x20");
|
||||
C.mstore(); // Save it to pos 0;
|
||||
C.push("0x00");
|
||||
C.mstore(); // Save it to pos 1;
|
||||
C.push("0x40");
|
||||
C.push("0x00");
|
||||
C.return();
|
||||
|
||||
return C.createTxData();
|
||||
}
|
||||
|
||||
module.exports.abi = [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "xL_in",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "xR_in",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "k",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "MiMCSponge",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "xL",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "xR",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.createCode = createCode;
|
||||
|
||||
|
13
src/mimcsponge_printconstants.js
Normal file
13
src/mimcsponge_printconstants.js
Normal file
@ -0,0 +1,13 @@
|
||||
const mimcsponge = require("./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";
|
||||
}
|
||||
S = S + "]\n";
|
||||
|
||||
console.log(S);
|
13
src/mimcsponge_printcontract.js
Normal file
13
src/mimcsponge_printcontract.js
Normal file
@ -0,0 +1,13 @@
|
||||
const mimcGenContract = require("./mimcsponge_gencontract");
|
||||
|
||||
const SEED = "mimcsponge";
|
||||
|
||||
let nRounds;
|
||||
if (typeof process.argv[2] != "undefined") {
|
||||
nRounds = parseInt(process.argv[2]);
|
||||
} else {
|
||||
nRounds = 220;
|
||||
}
|
||||
|
||||
console.log(mimcGenContract.createCode(SEED, nRounds));
|
||||
|
121
src/pedersenHash.js
Normal file
121
src/pedersenHash.js
Normal file
@ -0,0 +1,121 @@
|
||||
const babyJub = require("./babyjub");
|
||||
const createBlakeHash = require("blake-hash");
|
||||
const blake2b = require("blake2b");
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
function pedersenHash(msg, options) {
|
||||
options = options || {};
|
||||
options.baseHash = options.baseHash || "blake";
|
||||
const bitsPerSegment = windowSize*nWindowsPerSegment;
|
||||
const bits = buffer2bits(msg);
|
||||
|
||||
const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1;
|
||||
|
||||
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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (Scalar.lt(escalar, 0)) {
|
||||
escalar = Scalar.add( escalar, babyJub.subOrder);
|
||||
}
|
||||
|
||||
accP = babyJub.addPoint(accP, babyJub.mulPointEscalar(getBasePoint(options.baseHash, s), escalar));
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
const p8 = babyJub.mulPointEscalar(p, 8);
|
||||
|
||||
if (!babyJub.inSubgroup(p8)) {
|
||||
throw new Error("Point not in curve");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
21
src/pedersen_printbases.js
Normal file
21
src/pedersen_printbases.js
Normal file
@ -0,0 +1,21 @@
|
||||
const pedersenHash = require("./pedersenHash.js");
|
||||
|
||||
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(baseHash, i);
|
||||
console.log(`[${p[0]},${p[1]}]`);
|
||||
}
|
80
src/poseidon.js
Normal file
80
src/poseidon.js
Normal file
@ -0,0 +1,80 @@
|
||||
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)));
|
||||
|
||||
function poseidon(inputs) {
|
||||
assert(inputs.length > 0);
|
||||
assert(inputs.length < N_ROUNDS_P.length - 1);
|
||||
|
||||
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 F.normalize(state[0]);
|
||||
}
|
||||
|
||||
module.exports = poseidon;
|
||||
|
||||
|
||||
|
206
src/poseidon_constants.json
Normal file
206
src/poseidon_constants.json
Normal file
File diff suppressed because one or more lines are too long
24806
src/poseidon_constants_opt.json
Normal file
24806
src/poseidon_constants_opt.json
Normal file
File diff suppressed because it is too large
Load Diff
208
src/poseidon_gencontract.js
Normal file
208
src/poseidon_gencontract.js
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2018 Jordi Baylina
|
||||
// License: LGPL-3.0+
|
||||
//
|
||||
|
||||
const Contract = require("./evmasm");
|
||||
const { unstringifyBigInts } = require("ffjavascript").utils;
|
||||
const Web3Utils = require("web3-utils");
|
||||
|
||||
const { C:K, M } = unstringifyBigInts(require("./poseidon_constants.json"));
|
||||
|
||||
const N_ROUNDS_F = 8;
|
||||
const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63];
|
||||
|
||||
function toHex256(a) {
|
||||
let S = a.toString(16);
|
||||
while (S.length < 64) S="0"+S;
|
||||
return "0x" + S;
|
||||
}
|
||||
|
||||
function createCode(nInputs) {
|
||||
|
||||
if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8");
|
||||
const t = nInputs + 1;
|
||||
const nRoundsF = N_ROUNDS_F;
|
||||
const nRoundsP = N_ROUNDS_P[t - 2];
|
||||
|
||||
const C = new Contract();
|
||||
|
||||
function saveM() {
|
||||
for (let i=0; i<t; i++) {
|
||||
for (let j=0; j<t; j++) {
|
||||
C.push(toHex256(M[t-2][i][j]));
|
||||
C.push((1+i*t+j)*32);
|
||||
C.mstore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ark(r) { // st, q
|
||||
for (let i=0; i<t; i++) {
|
||||
C.dup(t); // q, st, q
|
||||
C.push(toHex256(K[t-2][r*t+i])); // K, q, st, q
|
||||
C.dup(2+i); // st[i], K, q, st, q
|
||||
C.addmod(); // newSt[i], st, q
|
||||
C.swap(1 + i); // xx, st, q
|
||||
C.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function sigma(p) {
|
||||
// sq, q
|
||||
C.dup(t); // q, st, q
|
||||
C.dup(1+p); // st[p] , q , st, q
|
||||
C.dup(1); // q, st[p] , q , st, q
|
||||
C.dup(0); // q, q, st[p] , q , st, q
|
||||
C.dup(2); // st[p] , q, q, st[p] , q , st, q
|
||||
C.dup(0); // st[p] , st[p] , q, q, st[p] , q , st, q
|
||||
C.mulmod(); // st2[p], q, st[p] , q , st, q
|
||||
C.dup(0); // st2[p], st2[p], q, st[p] , q , st, q
|
||||
C.mulmod(); // st4[p], st[p] , q , st, q
|
||||
C.mulmod(); // st5[p], st, q
|
||||
C.swap(1+p);
|
||||
C.pop(); // newst, q
|
||||
}
|
||||
|
||||
function mix() {
|
||||
C.label("mix");
|
||||
for (let i=0; i<t; i++) {
|
||||
for (let j=0; j<t; j++) {
|
||||
if (j==0) {
|
||||
C.dup(i+t); // q, newSt, oldSt, q
|
||||
C.push((1+i*t+j)*32);
|
||||
C.mload(); // M, q, newSt, oldSt, q
|
||||
C.dup(2+i+j); // oldSt[j], M, q, newSt, oldSt, q
|
||||
C.mulmod(); // acc, newSt, oldSt, q
|
||||
} else {
|
||||
C.dup(1+i+t); // q, acc, newSt, oldSt, q
|
||||
C.push((1+i*t+j)*32);
|
||||
C.mload(); // M, q, acc, newSt, oldSt, q
|
||||
C.dup(3+i+j); // oldSt[j], M, q, acc, newSt, oldSt, q
|
||||
C.mulmod(); // aux, acc, newSt, oldSt, q
|
||||
C.dup(2+i+t); // q, aux, acc, newSt, oldSt, q
|
||||
C.swap(2); // acc, aux, q, newSt, oldSt, q
|
||||
C.addmod(); // acc, newSt, oldSt, q
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i=0; i<t; i++) {
|
||||
C.swap((t -i) + (t -i-1));
|
||||
C.pop();
|
||||
}
|
||||
C.push(0);
|
||||
C.mload();
|
||||
C.jmp();
|
||||
}
|
||||
|
||||
|
||||
// Check selector
|
||||
C.push("0x0100000000000000000000000000000000000000000000000000000000");
|
||||
C.push(0);
|
||||
C.calldataload();
|
||||
C.div();
|
||||
C.dup(0);
|
||||
C.push(Web3Utils.keccak256(`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.eq();
|
||||
C.or();
|
||||
C.jmpi("start");
|
||||
C.invalid();
|
||||
|
||||
C.label("start");
|
||||
|
||||
saveM();
|
||||
|
||||
C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q
|
||||
|
||||
// Load t values from the call data.
|
||||
// The function has a single array param param
|
||||
// [Selector (4)] [item1 (32)] [item2 (32)] ....
|
||||
// Stack positions 0-nInputs.
|
||||
for (let i=0; i<nInputs; i++) {
|
||||
C.push(0x04+(0x20*(nInputs-i-1)));
|
||||
C.calldataload();
|
||||
}
|
||||
|
||||
C.push(0);
|
||||
|
||||
for (let i=0; i<nRoundsF+nRoundsP; i++) {
|
||||
ark(i);
|
||||
if ((i<nRoundsF/2) || (i>=nRoundsP+nRoundsF/2)) {
|
||||
for (let j=0; j<t; j++) {
|
||||
sigma(j);
|
||||
}
|
||||
} else {
|
||||
sigma(0);
|
||||
}
|
||||
const strLabel = "aferMix"+i;
|
||||
C._pushLabel(strLabel);
|
||||
C.push(0);
|
||||
C.mstore();
|
||||
C.jmp("mix");
|
||||
C.label(strLabel);
|
||||
}
|
||||
|
||||
C.push("0x00");
|
||||
C.mstore(); // Save it to pos 0;
|
||||
C.push("0x20");
|
||||
C.push("0x00");
|
||||
C.return();
|
||||
|
||||
mix();
|
||||
|
||||
return C.createTxData();
|
||||
}
|
||||
|
||||
function generateABI(nInputs) {
|
||||
return [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": `bytes32[${nInputs}]`,
|
||||
"name": "input",
|
||||
"type": `bytes32[${nInputs}]`
|
||||
}
|
||||
],
|
||||
"name": "poseidon",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": `uint256[${nInputs}]`,
|
||||
"name": "input",
|
||||
"type": `uint256[${nInputs}]`
|
||||
}
|
||||
],
|
||||
"name": "poseidon",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
module.exports.generateABI = generateABI;
|
||||
module.exports.createCode = createCode;
|
||||
|
||||
|
302
src/poseidon_optimize_constants.js
Normal file
302
src/poseidon_optimize_constants.js
Normal file
@ -0,0 +1,302 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
const { unstringifyBigInts } = require("ffjavascript").utils;
|
||||
|
||||
|
||||
// Version to write in hexadecimal
|
||||
function stringifyBigInts(o) {
|
||||
if ((typeof(o) == "bigint") || o.eq !== undefined) {
|
||||
return "0x"+o.toString(16);
|
||||
} else if (o instanceof Uint8Array) {
|
||||
return Scalar.fromRprLE(o, 0);
|
||||
} else if (Array.isArray(o)) {
|
||||
return o.map(stringifyBigInts);
|
||||
} else if (typeof o == "object") {
|
||||
const res = {};
|
||||
const keys = Object.keys(o);
|
||||
keys.forEach( (k) => {
|
||||
res[k] = stringifyBigInts(o[k]);
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
const { C, M } = unstringifyBigInts(require("./poseidon_constants.json"));
|
||||
|
||||
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];
|
||||
|
||||
|
||||
function matrix_inverse(Fr, A) {
|
||||
const m = A.length;
|
||||
const B=[];
|
||||
for (let i=0; i<m; i++) {
|
||||
if (A[i].length != m) throw new Error("Matrix is not square");
|
||||
B[i] = [];
|
||||
for (let j=0; j<m; j++) B[i][j] = A[i][j];
|
||||
for (let j=0; j<m; j++) B[i][m+j] = i==j ? Fr.one : Fr.zero;
|
||||
}
|
||||
const n=2*m;
|
||||
let h=0; // curRow
|
||||
let k=0; // curColumn
|
||||
|
||||
// Triangularize
|
||||
while ((h<m)&&(k<n)) {
|
||||
// Find the pivot
|
||||
let i_max;
|
||||
for (i_max=h; (i_max<m)&&(Fr.isZero(B[i_max][k])); i_max++);
|
||||
if (i_max==m) throw new Error("Matrix is not inveritible");
|
||||
|
||||
[B[h], B[i_max]] = [B[i_max], B[h]];
|
||||
|
||||
const inv_pivot = Fr.inv(B[h][k]);
|
||||
|
||||
for (let i=h+1; i<m; i++) {
|
||||
const f = Fr.mul(B[i][k], inv_pivot);
|
||||
B[i][k] = Fr.zero;
|
||||
for (let j=k+1; j<n; j++) B[i][j] = Fr.sub(B[i][j], Fr.mul(B[h][j], f));
|
||||
}
|
||||
|
||||
B[h][k] = Fr.one;
|
||||
for (let j=k+1; j<n; j++) B[h][j] = Fr.mul(B[h][j], inv_pivot);
|
||||
h++;
|
||||
k++;
|
||||
}
|
||||
|
||||
for (let i=m-2; i>=0; i--) {
|
||||
for (let i2=i+1; i2<m; i2++) {
|
||||
const f = B[i][i2];
|
||||
for (let k=m; k<n; k++) B[i][k] = Fr.sub(B[i][k], Fr.mul(f,B[i2][k]));
|
||||
B[i][i2] = Fr.zero;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i=0; i<m; i++) {
|
||||
B[i] = B[i].slice(-m);
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
function matrix_print(Fr, A) {
|
||||
for (let i=0; i<A.length; i++) {
|
||||
let S = "";
|
||||
for (let j=0;j<A[i].length; j++) {
|
||||
if (j>0) S = S + ", ";
|
||||
S = S + Fr.toString(A[i][j]);
|
||||
}
|
||||
console.log(S);
|
||||
}
|
||||
}
|
||||
|
||||
function vec_print(Fr, v) {
|
||||
for (let i=0; i<v.length; i++) {
|
||||
console.log(Fr.toString(v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
function vec_mul_matrix(Fr, v, A) {
|
||||
checkSquare(A);
|
||||
if (v.length != A.length) throw new Error("Invalid vec mul sizes");
|
||||
const res = [];
|
||||
for (let j=0; j<v.length; j++) {
|
||||
res[j] = Fr.zero;
|
||||
for (let i=0; i<A.length; i++) {
|
||||
res[j] = Fr.add(res[j], Fr.mul(v[i], A[i][j]));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function matrix_mul_vec(Fr, A, v) {
|
||||
checkSquare(A);
|
||||
if (v.length != A.length) throw new Error("Invalid vec mul sizes");
|
||||
const res = [];
|
||||
for (let i=0; i<v.length; i++) {
|
||||
res[i] = Fr.zero;
|
||||
for (let j=0; j<A.length; j++) {
|
||||
res[i] = Fr.add(res[i], Fr.mul(A[i][j], v[j]));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function matrix_mul(Fr, A, B) {
|
||||
checkSquare(A);
|
||||
if (A.length != B.length) throw new Error("multiplication different sizes");
|
||||
const res = [];
|
||||
for (let i=0; i<A.length; i++) {
|
||||
res[i] = [];
|
||||
for (let j=0; j<A.length; j++) {
|
||||
res[i][j] = Fr.zero;
|
||||
}
|
||||
}
|
||||
for (let i=0; i<A.length; i++) {
|
||||
for (let j=0; j<A.length; j++) {
|
||||
for (let k=0; k<A.length; k++) {
|
||||
res[i][j] = Fr.add( res[i][j], Fr.mul(A[i][k], B[k][j]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function matrix_traspose(A) {
|
||||
checkSquare(A);
|
||||
const res = [];
|
||||
for (let i=0; i<A.length; i++) {
|
||||
res[i] = [];
|
||||
for (let j=0; j<A.length; j++) {
|
||||
res[i][j] = A[j][i];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function checkSquare(A) {
|
||||
const n= A.length;
|
||||
for (let i=0; i<A.length; i++) {
|
||||
if (A[i].length != n) throw new Error("Matrix is not square");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function convertConstants(Fr, t, C, M) {
|
||||
let res = [];
|
||||
const nRoundsF = N_ROUNDS_F;
|
||||
const nRoundsP = N_ROUNDS_P[t - 2];
|
||||
|
||||
const Minv = matrix_inverse(Fr, M);
|
||||
|
||||
for (let k=0; k<t; k++) res.push(C[k]);
|
||||
|
||||
for (let r=0; r<nRoundsF/2-1; r++) {
|
||||
const cr = C.slice((r+1)*t, (r+1)*t+t);
|
||||
const crt = vec_mul_matrix(Fr, cr, Minv);
|
||||
for (let k=0; k<t; k++) res.push(crt[k]);
|
||||
}
|
||||
|
||||
const partialConst = [];
|
||||
let acc = [];
|
||||
for (let k=0; k<t; k++) acc[k] = C[(nRoundsF/2+ nRoundsP -1 +1)*t+k];
|
||||
|
||||
for (let r=nRoundsF/2+nRoundsP-1; r>=nRoundsF/2; r--) {
|
||||
const accp = vec_mul_matrix(Fr, acc, Minv);
|
||||
partialConst.push(accp[0]);
|
||||
accp[0] = Fr.zero;
|
||||
for (let k=0; k<t; k++) acc[k] = Fr.add(accp[k], C[(r-1+1)*t+k]);
|
||||
}
|
||||
|
||||
const accp = vec_mul_matrix(Fr, acc, Minv);
|
||||
for (let k=0; k<t; k++) res.push(accp[k]);
|
||||
|
||||
for (let i=0; i<partialConst.length; i++) res.push(partialConst[partialConst.length-1-i]);
|
||||
|
||||
for (let r=nRoundsF/2 + nRoundsP; r< nRoundsF+nRoundsP-1; r++) {
|
||||
const cr = C.slice((r+1)*t, (r+1)*t+t);
|
||||
const crt = vec_mul_matrix(Fr, cr, Minv);
|
||||
for (let k=0; k<t; k++) res.push(crt[k]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
/*
|
||||
m_{0,0} m_{0,1} m_{0,2} ........... m_{0,t-1}
|
||||
w_0 1 0 ........... 0
|
||||
w_1 0 1 ........... 0
|
||||
. . . . .
|
||||
. . . . .
|
||||
w_{t-2} 0 0 ........... 1
|
||||
*/
|
||||
// Returns sparse matrix with format: [m_{0,0}, m_0, ..... m_{t-2}, m_{0,1}, ... m_{0, t-1}]
|
||||
|
||||
function sparseFactorize(Fr, m) {
|
||||
|
||||
const m_hat = [];
|
||||
const mp = [];
|
||||
const w = [];
|
||||
for (let i=0; i<m.length; i++) {
|
||||
mp[i] = [];
|
||||
if (i<m.length-1) m_hat[i] = [];
|
||||
for (let j=0; j<m.length; j++) {
|
||||
if ((i>0) &&(j>0)) {
|
||||
m_hat[i-1][j-1] = m[i][j];
|
||||
mp[i][j] = m[i][j];
|
||||
} else {
|
||||
mp[i][j] = ((i==0)&&(j==0)) ? Fr.one : Fr.zero;
|
||||
}
|
||||
if ((i>0)&&(j==0)) w[i-1] = m[i][j];
|
||||
}
|
||||
}
|
||||
const m_hat_inv = matrix_inverse(Fr, m_hat);
|
||||
const wp = matrix_mul_vec(Fr, m_hat_inv, w);
|
||||
|
||||
const S = [];
|
||||
|
||||
S.push(m[0][0]);
|
||||
for (let k=0; k<wp.length; k++) S.push(wp[k]);
|
||||
for (let k=1; k<m.length; k++) S.push(m[0][k]);
|
||||
|
||||
return [mp, S];
|
||||
}
|
||||
|
||||
function calculatePS(Fr, t, M) {
|
||||
const nRoundsP = N_ROUNDS_P[t - 2];
|
||||
const sparse = [];
|
||||
let m=M;
|
||||
|
||||
for (let i=0; i<nRoundsP; i++) {
|
||||
const [mp, mpp] = sparseFactorize(Fr, m);
|
||||
sparse.push(mpp);
|
||||
m = matrix_mul(Fr, M, mp);
|
||||
}
|
||||
const P = m;
|
||||
const S = [];
|
||||
|
||||
for (let i=0; i<sparse.length; i++) {
|
||||
for (let k=0; k<sparse[sparse.length-1-i].length; k++) {
|
||||
S.push(sparse[sparse.length-1-i][k]);
|
||||
}
|
||||
}
|
||||
|
||||
return [P, S];
|
||||
}
|
||||
|
||||
|
||||
async function run() {
|
||||
// Prime bn128
|
||||
const Fr = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
|
||||
|
||||
let opt = {
|
||||
C: [],
|
||||
M: [],
|
||||
P: [],
|
||||
S: []
|
||||
};
|
||||
for (let i=1; i<=16; i++) {
|
||||
const oM = matrix_traspose(M[i-1]);
|
||||
const oC = convertConstants(Fr, i+1, C[i-1], oM);
|
||||
const [oP, oS] = calculatePS(Fr, i+1, oM);
|
||||
opt.C.push(oC);
|
||||
opt.M.push(oM);
|
||||
opt.P.push(oP);
|
||||
opt.S.push(oS);
|
||||
}
|
||||
|
||||
opt = stringifyBigInts(opt);
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, "poseidon_constants_opt.json"), JSON.stringify(opt, null, 1), "utf8");
|
||||
}
|
||||
|
||||
run().then(()=> {
|
||||
process.exit(0);
|
||||
}, (err) => {
|
||||
// console.log(err);
|
||||
console.log(err.stack);
|
||||
process.exit(1);
|
||||
});
|
13
src/poseidon_printcontract.js
Normal file
13
src/poseidon_printcontract.js
Normal file
@ -0,0 +1,13 @@
|
||||
const poseidonGenContract = require("./poseidon_gencontract");
|
||||
|
||||
if (process.argv.length != 3) {
|
||||
console.log("Usage: node poseidon_gencontract.js [numberOfInputs]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const nInputs = Number(process.argv[2]);
|
||||
|
||||
console.log(nInputs);
|
||||
|
||||
console.log(poseidonGenContract.createCode(nInputs));
|
||||
|
22
src/poseidon_printmatrix.js
Normal file
22
src/poseidon_printmatrix.js
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
const Poseidon = require("./poseidon.js");
|
||||
|
||||
const M = Poseidon.getMatrix();
|
||||
|
||||
let S = "[\n ";
|
||||
|
||||
for (let i=0; i<M.length; i++) {
|
||||
const LC = M[i];
|
||||
S = S + "[\n";
|
||||
for (let j=0; j<LC.length; j++) {
|
||||
S = S + " " + M[i][j].toString();
|
||||
if (j<LC.length-1) S = S + ",";
|
||||
S = S + "\n";
|
||||
}
|
||||
S = S + " ]";
|
||||
if (i<M.length-1) S = S + ",";
|
||||
}
|
||||
S=S+ "\n]\n";
|
||||
|
||||
console.log(S);
|
46
src/poseidon_slow.js
Normal file
46
src/poseidon_slow.js
Normal file
@ -0,0 +1,46 @@
|
||||
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"));
|
||||
|
||||
// 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"));
|
||||
|
||||
// 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 - 1);
|
||||
|
||||
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 F.normalize(state[0]);
|
||||
}
|
||||
|
||||
module.exports = poseidon;
|
301
src/smt.js
Normal file
301
src/smt.js
Normal file
@ -0,0 +1,301 @@
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const SMTMemDB = require("./smt_memdb");
|
||||
const {hash0, hash1, F} = require("./smt_hashes_poseidon");
|
||||
|
||||
class SMT {
|
||||
|
||||
constructor(db, root) {
|
||||
this.db = db;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
_splitBits(_key) {
|
||||
const res = Scalar.bits(_key);
|
||||
|
||||
while (res.length<256) res.push(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async update(_key, _newValue) {
|
||||
const key = Scalar.e(_key);
|
||||
const newValue = F.e(_newValue);
|
||||
|
||||
|
||||
const resFind = await this.find(key);
|
||||
const res = {};
|
||||
res.oldRoot = this.root;
|
||||
res.oldKey = key;
|
||||
res.oldValue = resFind.foundValue;
|
||||
res.newKey = key;
|
||||
res.newValue = newValue;
|
||||
res.siblings = resFind.siblings;
|
||||
|
||||
const ins = [];
|
||||
const dels = [];
|
||||
|
||||
let rtOld = hash1(key, resFind.foundValue);
|
||||
let rtNew = hash1(key, newValue);
|
||||
ins.push([rtNew, [1, key, newValue ]]);
|
||||
dels.push(rtOld);
|
||||
|
||||
const keyBits = this._splitBits(key);
|
||||
for (let level = resFind.siblings.length-1; level >=0; level--) {
|
||||
let oldNode, newNode;
|
||||
const sibling = resFind.siblings[level];
|
||||
if (keyBits[level]) {
|
||||
oldNode = [sibling, rtOld];
|
||||
newNode = [sibling, rtNew];
|
||||
} else {
|
||||
oldNode = [rtOld, sibling];
|
||||
newNode = [rtNew, sibling];
|
||||
}
|
||||
rtOld = hash0(oldNode[0], oldNode[1]);
|
||||
rtNew = hash0(newNode[0], newNode[1]);
|
||||
dels.push(rtOld);
|
||||
ins.push([rtNew, newNode]);
|
||||
}
|
||||
|
||||
res.newRoot = rtNew;
|
||||
|
||||
await this.db.multiDel(dels);
|
||||
await this.db.multiIns(ins);
|
||||
await this.db.setRoot(rtNew);
|
||||
this.root = rtNew;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async delete(_key) {
|
||||
const key = Scalar.e(_key);
|
||||
|
||||
const resFind = await this.find(key);
|
||||
if (!resFind.found) throw new Error("Key does not exists");
|
||||
|
||||
const res = {
|
||||
siblings: [],
|
||||
delKey: key,
|
||||
delValue: resFind.foundValue
|
||||
};
|
||||
|
||||
const dels = [];
|
||||
const ins = [];
|
||||
let rtOld = hash1(key, resFind.foundValue);
|
||||
let rtNew;
|
||||
dels.push(rtOld);
|
||||
|
||||
let mixed;
|
||||
if (resFind.siblings.length > 0) {
|
||||
const record = await this.db.get(resFind.siblings[resFind.siblings.length - 1]);
|
||||
if ((record.length == 3)&&(F.eq(record[0], F.one))) {
|
||||
mixed = false;
|
||||
res.oldKey = record[1];
|
||||
res.oldValue = record[2];
|
||||
res.isOld0 = false;
|
||||
rtNew = resFind.siblings[resFind.siblings.length - 1];
|
||||
} else if (record.length == 2) {
|
||||
mixed = true;
|
||||
res.oldKey = key;
|
||||
res.oldValue = F.zero;
|
||||
res.isOld0 = true;
|
||||
rtNew = F.zero;
|
||||
} else {
|
||||
throw new Error("Invalid node. Database corrupted");
|
||||
}
|
||||
} else {
|
||||
rtNew = F.zero;
|
||||
res.oldKey = key;
|
||||
res.oldValue = F.zero;
|
||||
res.isOld0 = true;
|
||||
}
|
||||
|
||||
const keyBits = this._splitBits(key);
|
||||
|
||||
for (let level = resFind.siblings.length-1; level >=0; level--) {
|
||||
let newSibling = resFind.siblings[level];
|
||||
if ((level == resFind.siblings.length-1)&&(!res.isOld0)) {
|
||||
newSibling = F.zero;
|
||||
}
|
||||
const oldSibling = resFind.siblings[level];
|
||||
if (keyBits[level]) {
|
||||
rtOld = hash0(oldSibling, rtOld);
|
||||
} else {
|
||||
rtOld = hash0(rtOld, oldSibling);
|
||||
}
|
||||
dels.push(rtOld);
|
||||
if (!F.isZero(newSibling)) {
|
||||
mixed = true;
|
||||
}
|
||||
|
||||
if (mixed) {
|
||||
res.siblings.unshift(resFind.siblings[level]);
|
||||
let newNode;
|
||||
if (keyBits[level]) {
|
||||
newNode = [newSibling, rtNew];
|
||||
} else {
|
||||
newNode = [rtNew, newSibling];
|
||||
}
|
||||
rtNew = hash0(newNode[0], newNode[1]);
|
||||
ins.push([rtNew, newNode]);
|
||||
}
|
||||
}
|
||||
|
||||
await this.db.multiIns(ins);
|
||||
await this.db.setRoot(rtNew);
|
||||
this.root = rtNew;
|
||||
await this.db.multiDel(dels);
|
||||
|
||||
res.newRoot = rtNew;
|
||||
res.oldRoot = rtOld;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async insert(_key, _value) {
|
||||
const key = Scalar.e(_key);
|
||||
const value = F.e(_value);
|
||||
let addedOne = false;
|
||||
const res = {};
|
||||
res.oldRoot = this.root;
|
||||
const newKeyBits = this._splitBits(key);
|
||||
|
||||
let rtOld;
|
||||
|
||||
const resFind = await this.find(key);
|
||||
|
||||
if (resFind.found) throw new Error("Key already exists");
|
||||
|
||||
res.siblings = resFind.siblings;
|
||||
let mixed;
|
||||
|
||||
if (!resFind.isOld0) {
|
||||
const oldKeyits = this._splitBits(resFind.notFoundKey);
|
||||
for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) {
|
||||
res.siblings.push(F.zero);
|
||||
}
|
||||
rtOld = hash1(resFind.notFoundKey, resFind.notFoundValue);
|
||||
res.siblings.push(rtOld);
|
||||
addedOne = true;
|
||||
mixed = false;
|
||||
} else if (res.siblings.length >0) {
|
||||
mixed = true;
|
||||
rtOld = F.zero;
|
||||
}
|
||||
|
||||
const inserts = [];
|
||||
const dels = [];
|
||||
|
||||
let rt = hash1(key, value);
|
||||
inserts.push([rt,[1, key, value]] );
|
||||
|
||||
for (let i=res.siblings.length-1; i>=0; i--) {
|
||||
if ((i<res.siblings.length-1)&&(!F.isZero(res.siblings[i]))) {
|
||||
mixed = true;
|
||||
}
|
||||
if (mixed) {
|
||||
const oldSibling = resFind.siblings[i];
|
||||
if (newKeyBits[i]) {
|
||||
rtOld = hash0(oldSibling, rtOld);
|
||||
} else {
|
||||
rtOld = hash0(rtOld, oldSibling);
|
||||
}
|
||||
dels.push(rtOld);
|
||||
}
|
||||
|
||||
|
||||
let newRt;
|
||||
if (newKeyBits[i]) {
|
||||
newRt = hash0(res.siblings[i], rt);
|
||||
inserts.push([newRt,[res.siblings[i], rt]] );
|
||||
} else {
|
||||
newRt = hash0(rt, res.siblings[i]);
|
||||
inserts.push([newRt,[rt, res.siblings[i]]] );
|
||||
}
|
||||
rt = newRt;
|
||||
}
|
||||
|
||||
if (addedOne) res.siblings.pop();
|
||||
while ((res.siblings.length>0) && (F.isZero(res.siblings[res.siblings.length-1]))) {
|
||||
res.siblings.pop();
|
||||
}
|
||||
res.oldKey = resFind.notFoundKey;
|
||||
res.oldValue = resFind.notFoundValue;
|
||||
res.newRoot = rt;
|
||||
res.isOld0 = resFind.isOld0;
|
||||
|
||||
|
||||
await this.db.multiIns(inserts);
|
||||
await this.db.setRoot(rt);
|
||||
this.root = rt;
|
||||
await this.db.multiDel(dels);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async find(key) {
|
||||
const keyBits = this._splitBits(key);
|
||||
return await this._find(key, keyBits, this.root, 0);
|
||||
}
|
||||
|
||||
async _find(key, keyBits, root, level) {
|
||||
if (typeof root === "undefined") root = this.root;
|
||||
|
||||
let res;
|
||||
if (F.isZero(root)) {
|
||||
res = {
|
||||
found: false,
|
||||
siblings: [],
|
||||
notFoundKey: key,
|
||||
notFoundValue: F.zero,
|
||||
isOld0: true
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
const record = await this.db.get(root);
|
||||
|
||||
if ((record.length==3)&&(F.eq(record[0],F.one))) {
|
||||
if (F.eq(record[1],key)) {
|
||||
res = {
|
||||
found: true,
|
||||
siblings: [],
|
||||
foundValue: record[2],
|
||||
isOld0: false
|
||||
};
|
||||
} else {
|
||||
res = {
|
||||
found: false,
|
||||
siblings: [],
|
||||
notFoundKey: record[1],
|
||||
notFoundValue: record[2],
|
||||
isOld0: false
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (keyBits[level] == 0) {
|
||||
res = await this._find(key, keyBits, record[0], level+1);
|
||||
res.siblings.unshift(record[1]);
|
||||
} else {
|
||||
res = await this._find(key, keyBits, record[1], level+1);
|
||||
res.siblings.unshift(record[0]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFromFile(fileName) {
|
||||
|
||||
}
|
||||
|
||||
async function newMemEmptyTrie() {
|
||||
const db = new SMTMemDB();
|
||||
const rt = await db.getRoot();
|
||||
const smt = new SMT(db, rt);
|
||||
return smt;
|
||||
}
|
||||
|
||||
module.exports.loadFromFile = loadFromFile;
|
||||
module.exports.newMemEmptyTrie = newMemEmptyTrie;
|
||||
module.exports.SMT = SMT;
|
||||
module.exports.SMTMemDB = SMTMemDB;
|
12
src/smt_hashes_mimc.js
Normal file
12
src/smt_hashes_mimc.js
Normal file
@ -0,0 +1,12 @@
|
||||
const mimc7 = require("./mimc7");
|
||||
const bigInt = require("big-integer");
|
||||
|
||||
exports.hash0 = function (left, right) {
|
||||
return mimc7.multiHash(left, right);
|
||||
};
|
||||
|
||||
exports.hash1 = function(key, value) {
|
||||
return mimc7.multiHash([key, value], bigInt.one);
|
||||
};
|
||||
|
||||
exports.F = mimc7.F;
|
18
src/smt_hashes_poseidon.js
Normal file
18
src/smt_hashes_poseidon.js
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
|
||||
const poseidon = require("./poseidon");
|
||||
|
||||
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
|
||||
|
||||
|
||||
exports.hash0 = function (left, right) {
|
||||
return poseidon([left, right]);
|
||||
};
|
||||
|
||||
exports.hash1 = function(key, value) {
|
||||
return poseidon([key, value, F.one]);
|
||||
};
|
||||
|
||||
exports.F = F;
|
63
src/smt_memdb.js
Normal file
63
src/smt_memdb.js
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
const ZqField = require("ffjavascript").ZqField;
|
||||
|
||||
// Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
|
||||
const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
|
||||
|
||||
class SMTMemDb {
|
||||
constructor() {
|
||||
this.nodes = {};
|
||||
this.root = F.zero;
|
||||
}
|
||||
|
||||
async getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
_key2str(k) {
|
||||
// const keyS = bigInt(key).leInt2Buff(32).toString("hex");
|
||||
const keyS = k.toString();
|
||||
return keyS;
|
||||
}
|
||||
|
||||
_normalize(n) {
|
||||
for (let i=0; i<n.length; i++) {
|
||||
n[i] = F.e(n[i]);
|
||||
}
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
const keyS = this._key2str(key);
|
||||
return this.nodes[keyS];
|
||||
}
|
||||
|
||||
async multiGet(keys) {
|
||||
const promises = [];
|
||||
for (let i=0; i<keys.length; i++) {
|
||||
promises.push(this.get(keys[i]));
|
||||
}
|
||||
return await Promise.all(promises);
|
||||
}
|
||||
|
||||
async setRoot(rt) {
|
||||
this.root = rt;
|
||||
}
|
||||
|
||||
async multiIns(inserts) {
|
||||
for (let i=0; i<inserts.length; i++) {
|
||||
const keyS = this._key2str(inserts[i][0]);
|
||||
this._normalize(inserts[i][1]);
|
||||
this.nodes[keyS] = inserts[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
async multiDel(dels) {
|
||||
for (let i=0; i<dels.length; i++) {
|
||||
const keyS = this._key2str(dels[i]);
|
||||
delete this.nodes[keyS];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SMTMemDb;
|
170
test/babyjub.js
Normal file
170
test/babyjub.js
Normal file
@ -0,0 +1,170 @@
|
||||
const chai = require("chai");
|
||||
const babyjub = require("../src/babyjub.js");
|
||||
const Scalar = require("ffjavascript").Scalar;
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
// const bigInt = require("big-integer");
|
||||
|
||||
function buff2hex(buff) {
|
||||
function i2hex(i) {
|
||||
return ('0' + i.toString(16)).slice(-2);
|
||||
}
|
||||
return Array.from(buff).map(i2hex).join('');
|
||||
}
|
||||
|
||||
describe("Baby Jub js test", function () {
|
||||
|
||||
this.timeout(100000);
|
||||
|
||||
it("Should add point (0,1) and (0,1)", () => {
|
||||
|
||||
const p1 = [
|
||||
babyjub.F.e(0),
|
||||
babyjub.F.e(1)];
|
||||
const p2 = [
|
||||
babyjub.F.e(0),
|
||||
babyjub.F.e(1)
|
||||
];
|
||||
|
||||
const out = babyjub.addPoint(p1, p2);
|
||||
assert(babyjub.F.eq(out[0], babyjub.F.zero));
|
||||
assert(babyjub.F.eq(out[1], babyjub.F.one));
|
||||
});
|
||||
|
||||
it("Should base be 8*generator", () => {
|
||||
let res;
|
||||
res = babyjub.addPoint(babyjub.Generator, babyjub.Generator);
|
||||
res = babyjub.addPoint(res, res);
|
||||
res = babyjub.addPoint(res, res);
|
||||
|
||||
assert(babyjub.F.eq(res[0], babyjub.Base8[0]));
|
||||
assert(babyjub.F.eq(res[1], babyjub.Base8[1]));
|
||||
});
|
||||
|
||||
it("Should add 2 same numbers", () => {
|
||||
|
||||
const p1 = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
const p2 = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
|
||||
const out = babyjub.addPoint(p1, p2);
|
||||
assert(babyjub.F.eq(out[0], babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365")));
|
||||
assert(babyjub.F.eq(out[1], babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889")));
|
||||
});
|
||||
|
||||
it("Should add 2 different numbers", () => {
|
||||
|
||||
const p1 = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
const p2 = [
|
||||
babyjub.F.e("16540640123574156134436876038791482806971768689494387082833631921987005038935"),
|
||||
babyjub.F.e("20819045374670962167435360035096875258406992893633759881276124905556507972311"),
|
||||
];
|
||||
|
||||
const out = babyjub.addPoint(p1, p2);
|
||||
assert(babyjub.F.eq(out[0], babyjub.F.e("7916061937171219682591368294088513039687205273691143098332585753343424131937")));
|
||||
assert(babyjub.F.eq(out[1], babyjub.F.e("14035240266687799601661095864649209771790948434046947201833777492504781204499")));
|
||||
|
||||
});
|
||||
|
||||
it("should mulPointEscalar 0", () => {
|
||||
const p = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
it("should mulPointEscalar 1", () => {
|
||||
const p = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
|
||||
const r = babyjub.mulPointEscalar(p, Scalar.fromString("14035240266687799601661095864649209771790948434046947201833777492504781204499"));
|
||||
assert.equal(r[0].toString(), "17070357974431721403481313912716834497662307308519659060910483826664480189605");
|
||||
assert.equal(r[1].toString(), "4014745322800118607127020275658861516666525056516280575712425373174125159339");
|
||||
});
|
||||
|
||||
it("should mulPointEscalar 2", () => {
|
||||
const p = [
|
||||
babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"),
|
||||
babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"),
|
||||
];
|
||||
|
||||
const r = babyjub.mulPointEscalar(p, Scalar.fromString("20819045374670962167435360035096875258406992893633759881276124905556507972311"));
|
||||
assert.equal(r[0].toString(), "13563888653650925984868671744672725781658357821216877865297235725727006259983");
|
||||
assert.equal(r[1].toString(), "8442587202676550862664528699803615547505326611544120184665036919364004251662");
|
||||
});
|
||||
|
||||
it("should inCurve 1", () => {
|
||||
const p = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
assert(babyjub.inCurve(p));
|
||||
});
|
||||
|
||||
it("should inCurve 2", () => {
|
||||
const p = [
|
||||
babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"),
|
||||
babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"),
|
||||
];
|
||||
assert(babyjub.inCurve(p));
|
||||
});
|
||||
|
||||
it("should inSubgroup 1", () => {
|
||||
const p = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
assert(babyjub.inSubgroup(p));
|
||||
});
|
||||
|
||||
it("should inSubgroup 2", () => {
|
||||
const p = [
|
||||
babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"),
|
||||
babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"),
|
||||
];
|
||||
assert(babyjub.inSubgroup(p));
|
||||
});
|
||||
|
||||
it("should packPoint - unpackPoint 1", () => {
|
||||
const p = [
|
||||
babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"),
|
||||
babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"),
|
||||
];
|
||||
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");
|
||||
});
|
||||
|
||||
it("should packPoint - unpackPoint 2", () => {
|
||||
const p = [
|
||||
babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"),
|
||||
babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"),
|
||||
];
|
||||
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");
|
||||
});
|
||||
});
|
88
test/eddsa.js
Normal file
88
test/eddsa.js
Normal file
@ -0,0 +1,88 @@
|
||||
const chai = require("chai");
|
||||
|
||||
const eddsa = require("../src/eddsa.js");
|
||||
const babyJub = require("../src/babyjub.js");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
const utils = require("ffjavascript").utils;
|
||||
|
||||
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 () {
|
||||
|
||||
this.timeout(100000);
|
||||
|
||||
it("Sign (using Mimc7) a single 10 bytes from 0 to 9", () => {
|
||||
const msgBuf = Buffer.from("00010203040506070809", "hex");
|
||||
const msg = utils.leBuff2int(msgBuf);
|
||||
|
||||
// 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");
|
||||
|
||||
const pPubKey = 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");
|
||||
|
||||
const pSignature = eddsa.packSignature(signature);
|
||||
assert.equal(buff2hex(pSignature), ""+
|
||||
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
|
||||
"7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405");
|
||||
|
||||
const uSignature = eddsa.unpackSignature(pSignature);
|
||||
assert(eddsa.verifyMiMC(msg, uSignature, pubKey));
|
||||
|
||||
});
|
||||
|
||||
it("Sign (using Poseidon) a single 10 bytes from 0 to 9", () => {
|
||||
const msgBuf = Buffer.from("00010203040506070809", "hex");
|
||||
const msg = utils.leBuff2int(msgBuf);
|
||||
|
||||
const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex");
|
||||
|
||||
const pubKey = eddsa.prv2pub(prvKey);
|
||||
|
||||
assert.equal(pubKey[0].toString(),
|
||||
"13277427435165878497778222415993513565335242147425444199013288855685581939618");
|
||||
assert.equal(pubKey[1].toString(),
|
||||
"13622229784656158136036771217484571176836296686641868549125388198837476602820");
|
||||
|
||||
const pPubKey = 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");
|
||||
|
||||
const pSignature = eddsa.packSignature(signature);
|
||||
assert.equal(buff2hex(pSignature), ""+
|
||||
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
|
||||
"9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203");
|
||||
|
||||
const uSignature = eddsa.unpackSignature(pSignature);
|
||||
assert(eddsa.verifyPoseidon(msg, uSignature, pubKey));
|
||||
|
||||
});
|
||||
});
|
48
test/mimccontract.js
Normal file
48
test/mimccontract.js
Normal file
@ -0,0 +1,48 @@
|
||||
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());
|
||||
});
|
||||
});
|
||||
|
43
test/mimcspongecontract.js
Normal file
43
test/mimcspongecontract.js
Normal file
@ -0,0 +1,43 @@
|
||||
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");
|
||||
|
||||
|
||||
const assert = chai.assert;
|
||||
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;
|
||||
|
||||
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, 220)
|
||||
}).send({
|
||||
gas: 3500000,
|
||||
from: accounts[0]
|
||||
});
|
||||
});
|
||||
|
||||
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());
|
||||
});
|
||||
});
|
||||
|
15
test/poseidon.js
Normal file
15
test/poseidon.js
Normal file
@ -0,0 +1,15 @@
|
||||
const chai = require("chai");
|
||||
const assert = chai.assert;
|
||||
|
||||
const poseidon = require("../src/poseidon.js");
|
||||
|
||||
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));
|
||||
});
|
||||
it("Should check constrain reference implementation poseidonperm_x5_254_5", async () => {
|
||||
const res2 = poseidon([1,2,3,4]);
|
||||
assert.equal("299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465", res2.toString(16));
|
||||
});
|
||||
});
|
65
test/poseidoncontract.js
Normal file
65
test/poseidoncontract.js
Normal file
@ -0,0 +1,65 @@
|
||||
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");
|
||||
|
||||
const assert = chai.assert;
|
||||
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
|
||||
|
||||
describe("Poseidon Smart contract test", function () {
|
||||
let testrpc;
|
||||
let web3;
|
||||
let poseidon6;
|
||||
let poseidon3;
|
||||
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 C6 = new web3.eth.Contract(poseidonGenContract.generateABI(5));
|
||||
const C3 = new web3.eth.Contract(poseidonGenContract.generateABI(2));
|
||||
|
||||
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]
|
||||
});
|
||||
});
|
||||
|
||||
it("Should calculate the poseidon correctly t=6", async () => {
|
||||
|
||||
const res = await poseidon6.methods.poseidon([1,2, 0, 0, 0]).call();
|
||||
|
||||
// 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());
|
||||
});
|
||||
it("Should calculate the poseidon correctly t=3", async () => {
|
||||
|
||||
const res = await poseidon3.methods.poseidon([1,2]).call();
|
||||
|
||||
// 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());
|
||||
});
|
||||
|
||||
});
|
||||
|
176
test/smt.js
Normal file
176
test/smt.js
Normal file
@ -0,0 +1,176 @@
|
||||
const chai = require("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");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
describe("SMT Javascript test", function () {
|
||||
this.timeout(100000);
|
||||
before( async () => {
|
||||
});
|
||||
|
||||
it("Should insert 2 elements and empty them", async () => {
|
||||
const tree = await smt.newMemEmptyTrie();
|
||||
const key1 = Fr.e(111);
|
||||
const value1 = Fr.e(222);
|
||||
const key2 = Fr.e(333);
|
||||
const value2 = Fr.e(444);
|
||||
|
||||
await tree.insert(key1,value1);
|
||||
await tree.insert(key2,value2);
|
||||
await tree.delete(key2);
|
||||
await tree.delete(key1);
|
||||
|
||||
assert(Fr.isZero(tree.root));
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
await tree1.insert(keys[0],values[0]);
|
||||
await tree1.insert(keys[1],values[1]);
|
||||
await tree1.insert(keys[2],values[2]);
|
||||
|
||||
await tree2.insert(keys[0],values[0]);
|
||||
await tree2.insert(keys[2],values[2]);
|
||||
await tree2.insert(keys[1],values[1]);
|
||||
|
||||
await tree3.insert(keys[1],values[1]);
|
||||
await tree3.insert(keys[0],values[0]);
|
||||
await tree3.insert(keys[2],values[2]);
|
||||
|
||||
await tree4.insert(keys[1],values[1]);
|
||||
await tree4.insert(keys[2],values[2]);
|
||||
await tree4.insert(keys[0],values[0]);
|
||||
|
||||
await tree5.insert(keys[2],values[2]);
|
||||
await tree5.insert(keys[0],values[0]);
|
||||
await tree5.insert(keys[1],values[1]);
|
||||
|
||||
await tree6.insert(keys[2],values[2]);
|
||||
await tree6.insert(keys[1],values[1]);
|
||||
await tree6.insert(keys[0],values[0]);
|
||||
|
||||
assert(Fr.eq(tree1.root, tree2.root));
|
||||
assert(Fr.eq(tree2.root, tree3.root));
|
||||
assert(Fr.eq(tree3.root, tree4.root));
|
||||
assert(Fr.eq(tree4.root, tree5.root));
|
||||
assert(Fr.eq(tree5.root, tree6.root));
|
||||
|
||||
assert.equal(Object.keys(tree1.db.nodes).length, Object.keys(tree2.db.nodes).length);
|
||||
assert.equal(Object.keys(tree2.db.nodes).length, Object.keys(tree3.db.nodes).length);
|
||||
assert.equal(Object.keys(tree3.db.nodes).length, Object.keys(tree4.db.nodes).length);
|
||||
assert.equal(Object.keys(tree4.db.nodes).length, Object.keys(tree5.db.nodes).length);
|
||||
assert.equal(Object.keys(tree5.db.nodes).length, Object.keys(tree6.db.nodes).length);
|
||||
|
||||
await tree1.delete(keys[0]);
|
||||
await tree1.delete(keys[1]);
|
||||
await tree2.delete(keys[1]);
|
||||
await tree2.delete(keys[0]);
|
||||
assert(Fr.eq(tree1.root, tree2.root));
|
||||
|
||||
await tree3.delete(keys[0]);
|
||||
await tree3.delete(keys[2]);
|
||||
await tree4.delete(keys[2]);
|
||||
await tree4.delete(keys[0]);
|
||||
assert(Fr.eq(tree3.root, tree4.root));
|
||||
|
||||
await tree5.delete(keys[1]);
|
||||
await tree5.delete(keys[2]);
|
||||
await tree6.delete(keys[2]);
|
||||
await tree6.delete(keys[1]);
|
||||
assert(Fr.eq(tree5.root, tree6.root));
|
||||
|
||||
await tree1.delete(keys[2]);
|
||||
await tree2.delete(keys[2]);
|
||||
await tree3.delete(keys[1]);
|
||||
await tree4.delete(keys[1]);
|
||||
await tree5.delete(keys[0]);
|
||||
await tree6.delete(keys[0]);
|
||||
|
||||
assert(Fr.isZero(tree1.root));
|
||||
assert(Fr.isZero(tree2.root));
|
||||
assert(Fr.isZero(tree3.root));
|
||||
assert(Fr.isZero(tree4.root));
|
||||
assert(Fr.isZero(tree5.root));
|
||||
assert(Fr.isZero(tree6.root));
|
||||
|
||||
assert.equal(Object.keys(tree1.db.nodes).length, 0);
|
||||
assert.equal(Object.keys(tree2.db.nodes).length, 0);
|
||||
assert.equal(Object.keys(tree3.db.nodes).length, 0);
|
||||
assert.equal(Object.keys(tree4.db.nodes).length, 0);
|
||||
assert.equal(Object.keys(tree5.db.nodes).length, 0);
|
||||
assert.equal(Object.keys(tree6.db.nodes).length, 0);
|
||||
});
|
||||
|
||||
it("Insert and remove 100 numbers randomly", async () => {
|
||||
function perm(a) {
|
||||
const arr = a.slice();
|
||||
const rArr = [];
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
let rIdx = Math.floor(Math.random() * (arr.length - i));
|
||||
rArr.push(arr[rIdx]);
|
||||
arr[rIdx] = arr[arr.length - i - 1];
|
||||
}
|
||||
return rArr;
|
||||
}
|
||||
const tree = await smt.newMemEmptyTrie();
|
||||
const arr = [];
|
||||
const N = 100;
|
||||
for (let i=0; i<N; i++) {
|
||||
arr.push(Fr.e(i));
|
||||
}
|
||||
const insArr = perm(arr);
|
||||
for (let i=0; i<N; i++) {
|
||||
await tree.insert(insArr[i], i);
|
||||
}
|
||||
const delArr = perm(insArr);
|
||||
for (let i=0; i<N; i++) {
|
||||
await tree.delete(delArr[i]);
|
||||
}
|
||||
|
||||
assert(Fr.isZero(tree.root));
|
||||
assert.equal(Object.keys(tree.db.nodes).length, 0);
|
||||
});
|
||||
|
||||
it("Should test update", async () => {
|
||||
const tree1 = await smt.newMemEmptyTrie();
|
||||
const tree2 = await smt.newMemEmptyTrie();
|
||||
|
||||
await tree1.insert(8,88);
|
||||
await tree1.insert(9,99,);
|
||||
await tree1.insert(32,3232);
|
||||
|
||||
await tree2.insert(8,888);
|
||||
await tree2.insert(9,999);
|
||||
await tree2.insert(32,323232);
|
||||
|
||||
await tree1.update(8, 888);
|
||||
await tree1.update(9, 999);
|
||||
await tree1.update(32, 323232);
|
||||
|
||||
assert(Fr.eq(tree1.root, tree2.root));
|
||||
});
|
||||
|
||||
it("Should test update with same key-value", async () => {
|
||||
const tree1 = await smt.newMemEmptyTrie();
|
||||
|
||||
await tree1.insert(8,88);
|
||||
await tree1.update(8,88);
|
||||
|
||||
const res = await tree1.db.get(tree1.root);
|
||||
assert.notEqual(res, undefined);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user