Rewrite Poseidon hash implementation to be compatible with reference implementation

This commit is contained in:
poma 2020-08-09 14:31:38 +03:00
parent 48efcf02ce
commit 82c2f606cc
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
7 changed files with 3649 additions and 282 deletions

@ -1,3 +1,4 @@
include "./poseidon_constants.circom";
template Sigma() { template Sigma() {
signal input in; signal input in;
@ -12,163 +13,52 @@ template Sigma() {
out <== in4*in; out <== in4*in;
} }
template Ark(t, C) { template Ark(t, C, r) {
signal input in[t]; signal input in[t];
signal output out[t]; signal output out[t];
for (var i=0; i<t; i++) { for (var i=0; i<t; i++) {
out[i] <== in[i] + C; out[i] <== in[i] + C[i + r];
} }
} }
template Mix(t, M) { template Mix(t, M) {
signal input in[t]; signal input in[t];
signal output out[t]; signal output out[t];
var lc;
var lc;
for (var i=0; i<t; i++) { for (var i=0; i<t; i++) {
lc = 0; lc = 0;
for (var j=0; j<t; j++) { for (var j=0; j<t; j++) {
lc = lc + M[i][j]*in[j]; lc += M[j][i]*in[j];
} }
out[i] <== lc; out[i] <== lc;
} }
} }
// var nRoundsF = 8; template Poseidon(nInputs) {
// var nRoundsP = 57;
// var t = 6;
template Poseidon(nInputs, t, nRoundsF, nRoundsP) {
var C = [
14397397413755236225575615486459253198602422701513067526754101844196324375522,
10405129301473404666785234951972711717481302463898292859783056520670200613128,
5179144822360023508491245509308555580251733042407187134628755730783052214509,
9132640374240188374542843306219594180154739721841249568925550236430986592615,
20360807315276763881209958738450444293273549928693737723235350358403012458514,
17933600965499023212689924809448543050840131883187652471064418452962948061619,
3636213416533737411392076250708419981662897009810345015164671602334517041153,
2008540005368330234524962342006691994500273283000229509835662097352946198608,
16018407964853379535338740313053768402596521780991140819786560130595652651567,
20653139667070586705378398435856186172195806027708437373983929336015162186471,
17887713874711369695406927657694993484804203950786446055999405564652412116765,
4852706232225925756777361208698488277369799648067343227630786518486608711772,
8969172011633935669771678412400911310465619639756845342775631896478908389850,
20570199545627577691240476121888846460936245025392381957866134167601058684375,
16442329894745639881165035015179028112772410105963688121820543219662832524136,
20060625627350485876280451423010593928172611031611836167979515653463693899374,
16637282689940520290130302519163090147511023430395200895953984829546679599107,
15599196921909732993082127725908821049411366914683565306060493533569088698214,
16894591341213863947423904025624185991098788054337051624251730868231322135455,
1197934381747032348421303489683932612752526046745577259575778515005162320212,
6172482022646932735745595886795230725225293469762393889050804649558459236626,
21004037394166516054140386756510609698837211370585899203851827276330669555417,
15262034989144652068456967541137853724140836132717012646544737680069032573006,
15017690682054366744270630371095785995296470601172793770224691982518041139766,
15159744167842240513848638419303545693472533086570469712794583342699782519832,
11178069035565459212220861899558526502477231302924961773582350246646450941231,
21154888769130549957415912997229564077486639529994598560737238811887296922114,
20162517328110570500010831422938033120419484532231241180224283481905744633719,
2777362604871784250419758188173029886707024739806641263170345377816177052018,
15732290486829619144634131656503993123618032247178179298922551820261215487562,
6024433414579583476444635447152826813568595303270846875177844482142230009826,
17677827682004946431939402157761289497221048154630238117709539216286149983245,
10716307389353583413755237303156291454109852751296156900963208377067748518748,
14925386988604173087143546225719076187055229908444910452781922028996524347508,
8940878636401797005293482068100797531020505636124892198091491586778667442523,
18911747154199663060505302806894425160044925686870165583944475880789706164410,
8821532432394939099312235292271438180996556457308429936910969094255825456935,
20632576502437623790366878538516326728436616723089049415538037018093616927643,
71447649211767888770311304010816315780740050029903404046389165015534756512,
2781996465394730190470582631099299305677291329609718650018200531245670229393,
12441376330954323535872906380510501637773629931719508864016287320488688345525,
2558302139544901035700544058046419714227464650146159803703499681139469546006,
10087036781939179132584550273563255199577525914374285705149349445480649057058,
4267692623754666261749551533667592242661271409704769363166965280715887854739,
4945579503584457514844595640661884835097077318604083061152997449742124905548,
17742335354489274412669987990603079185096280484072783973732137326144230832311,
6266270088302506215402996795500854910256503071464802875821837403486057988208,
2716062168542520412498610856550519519760063668165561277991771577403400784706,
19118392018538203167410421493487769944462015419023083813301166096764262134232,
9386595745626044000666050847309903206827901310677406022353307960932745699524,
9121640807890366356465620448383131419933298563527245687958865317869840082266,
3078975275808111706229899605611544294904276390490742680006005661017864583210,
7157404299437167354719786626667769956233708887934477609633504801472827442743,
14056248655941725362944552761799461694550787028230120190862133165195793034373,
14124396743304355958915937804966111851843703158171757752158388556919187839849,
11851254356749068692552943732920045260402277343008629727465773766468466181076,
9799099446406796696742256539758943483211846559715874347178722060519817626047,
10156146186214948683880719664738535455146137901666656566575307300522957959544,
19908645952733301583346063785055921934459499091029406575311417879963332475861,
11766105336238068471342414351862472329437473380853789942065610694000443387471,
11002137593249972174092192767251572171769044073555430468487809799220351297047,
284136377911685911941431040940403846843630064858778505937392780738953624163,
19448733709802908339787967270452055364068697565906862913410983275341804035680,
14423660424692802524250720264041003098290275890428483723270346403986712981505,
10635360132728137321700090133109897687122647659471659996419791842933639708516
];
var M = [
[
19167410339349846567561662441069598364702008768579734801591448511131028229281,
14183033936038168803360723133013092560869148726790180682363054735190196956789,
9067734253445064890734144122526450279189023719890032859456830213166173619761,
16378664841697311562845443097199265623838619398287411428110917414833007677155,
12968540216479938138647596899147650021419273189336843725176422194136033835172,
3636162562566338420490575570584278737093584021456168183289112789616069756675
],[
17034139127218860091985397764514160131253018178110701196935786874261236172431,
2799255644797227968811798608332314218966179365168250111693473252876996230317,
2482058150180648511543788012634934806465808146786082148795902594096349483974,
16563522740626180338295201738437974404892092704059676533096069531044355099628,
10468644849657689537028565510142839489302836569811003546969773105463051947124,
3328913364598498171733622353010907641674136720305714432354138807013088636408
],[
18985203040268814769637347880759846911264240088034262814847924884273017355969,
8652975463545710606098548415650457376967119951977109072274595329619335974180,
970943815872417895015626519859542525373809485973005165410533315057253476903,
19406667490568134101658669326517700199745817783746545889094238643063688871948,
17049854690034965250221386317058877242629221002521630573756355118745574274967,
4964394613021008685803675656098849539153699842663541444414978877928878266244
],[
19025623051770008118343718096455821045904242602531062247152770448380880817517,
9077319817220936628089890431129759976815127354480867310384708941479362824016,
4770370314098695913091200576539533727214143013236894216582648993741910829490,
4298564056297802123194408918029088169104276109138370115401819933600955259473,
6905514380186323693285869145872115273350947784558995755916362330070690839131,
4783343257810358393326889022942241108539824540285247795235499223017138301952
],[
16205238342129310687768799056463408647672389183328001070715567975181364448609,
8303849270045876854140023508764676765932043944545416856530551331270859502246,
20218246699596954048529384569730026273241102596326201163062133863539137060414,
1712845821388089905746651754894206522004527237615042226559791118162382909269,
13001155522144542028910638547179410124467185319212645031214919884423841839406,
16037892369576300958623292723740289861626299352695838577330319504984091062115
],[
15162889384227198851506890526431746552868519326873025085114621698588781611738,
13272957914179340594010910867091459756043436017766464331915862093201960540910,
9416416589114508529880440146952102328470363729880726115521103179442988482948,
8035240799672199706102747147502951589635001418759394863664434079699838251138,
21642389080762222565487157652540372010968704000567605990102641816691459811717,
20261355950827657195644012399234591122288573679402601053407151083849785332516
]
];
signal input inputs[nInputs]; signal input inputs[nInputs];
signal output out; signal output out;
component ark[nRoundsF + nRoundsP]; // Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8)
component sigmaF[nRoundsF][t]; // 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
var N_ROUNDS_P[8] = [56, 57, 56, 60, 60, 63, 64, 63];
var t = nInputs + 1;
var nRoundsF = 8;
var nRoundsP = N_ROUNDS_P[t - 2];
var C[t*(nRoundsF + nRoundsP)] = POSEIDON_C(t);
var M[t][t] = POSEIDON_M(t);
component ark[nRoundsF + nRoundsP - 1];
component sigmaF[nRoundsF - 1][t];
component sigmaP[nRoundsP]; component sigmaP[nRoundsP];
component mix[nRoundsF + nRoundsP]; component mix[nRoundsF + nRoundsP - 1];
var k; var k;
for (var i=0; i<(nRoundsF + nRoundsP); i++) { for (var i=0; i<nRoundsF + nRoundsP - 1; i++) {
ark[i] = Ark(t, C[i]); ark[i] = Ark(t, C, t*i);
mix[i] = Mix(t, M);
for (var j=0; j<t; j++) { for (var j=0; j<t; j++) {
if (i==0) { if (i==0) {
if (j<nInputs) { if (j<nInputs) {
@ -181,15 +71,17 @@ template Poseidon(nInputs, t, nRoundsF, nRoundsP) {
} }
} }
if ((i<(nRoundsF/2)) || (i>= (nRoundsP + nRoundsF/2))) { if (i < nRoundsF/2 || i >= nRoundsP + nRoundsF/2) {
k= i<nRoundsF/2 ? i : (i-nRoundsP); k = i < nRoundsF/2 ? i : i - nRoundsP;
mix[i] = Mix(t, M);
for (var j=0; j<t; j++) { for (var j=0; j<t; j++) {
sigmaF[k][j] = Sigma(); sigmaF[k][j] = Sigma();
sigmaF[k][j].in <== ark[i].out[j]; sigmaF[k][j].in <== ark[i].out[j];
mix[i].in[j] <== sigmaF[k][j].out; mix[i].in[j] <== sigmaF[k][j].out;
} }
} else { } else {
k= i-nRoundsF/2; k = i - nRoundsF/2;
mix[i] = Mix(t, M);
sigmaP[k] = Sigma(); sigmaP[k] = Sigma();
sigmaP[k].in <== ark[i].out[0]; sigmaP[k].in <== ark[i].out[0];
mix[i].in[0] <== sigmaP[k].out; mix[i].in[0] <== sigmaP[k].out;
@ -199,5 +91,8 @@ template Poseidon(nInputs, t, nRoundsF, nRoundsP) {
} }
} }
out <== mix[nRoundsF + nRoundsP -1].out[0]; // last round is done only for the first word, so we do it manually to save constraints
component lastSigmaF = Sigma();
lastSigmaF.in <== mix[nRoundsF + nRoundsP - 2].out[0] + C[t*(nRoundsF + nRoundsP - 1)];
out <== lastSigmaF.out;
} }

File diff suppressed because one or more lines are too long

@ -1,116 +1,50 @@
const bn128 = require("snarkjs").bn128;
const bigInt = require("snarkjs").bigInt;
const blake2b = require('blake2b');
const assert = require("assert"); const assert = require("assert");
const F = bn128.Fr; const Scalar = require("ffjavascript").Scalar;
const ZqField = require("ffjavascript").ZqField;
const { unstringifyBigInts } = require("ffjavascript").utils;
const SEED = "poseidon"; // Prime 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
const NROUNDSF = 8; const F = new ZqField(Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"));
const NROUNDSP = 57;
const T = 6;
function getPseudoRandom(seed, n) { // Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage
const res = []; // Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
let input = Buffer.from(seed); const { C, M } = unstringifyBigInts(require("./poseidon_constants.json"));
let h = blake2b(32).update(input).digest()
while (res.length<n) {
const n = F.affine(bigInt.leBuff2int(h));
res.push(n);
h = blake2b(32).update(h).digest()
}
return res; // 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];
function allDifferent(v) { const pow5 = a => F.mul(a, F.square(F.square(a, a)));
for (let i=0; i<v.length; i++) {
if (v[i].isZero()) return false; function poseidon(inputs) {
for (let j=i+1; j<v.length; j++) { assert(inputs.length > 0);
if (v[i].equals(v[j])) return false; 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 = [...inputs.map(a => F.e(a)), F.zero];
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]);
}
// no matrix multiplication in the last round
if (r < nRoundsF + nRoundsP - 1) {
state = state.map((_, i) =>
state.reduce((acc, a, j) => F.add(acc, F.mul(M[t - 2][j][i], a)), F.zero)
);
} }
} }
return true; return F.normalize(state[0]);
} }
exports.getMatrix = (t, seed, nRounds) => { module.exports = poseidon;
if (typeof seed === "undefined") seed = SEED; module.exports.F = F;
if (typeof nRounds === "undefined") nRounds = NROUNDSF + NROUNDSP;
if (typeof t === "undefined") t = T;
let nonce = "0000";
let cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
while (!allDifferent(cmatrix)) {
nonce = (Number(nonce)+1)+"";
while(nonce.length<4) nonce = "0"+nonce;
cmatrix = getPseudoRandom(seed+"_matrix_"+nonce, t*2);
}
const M = new Array(t);
for (let i=0; i<t; i++) {
M[i] = new Array(t);
for (let j=0; j<t; j++) {
M[i][j] = F.affine(F.inverse(F.sub(cmatrix[i], cmatrix[t+j])));
}
}
return M;
};
exports.getConstants = (t, seed, nRounds) => {
if (typeof seed === "undefined") seed = SEED;
if (typeof nRounds === "undefined") nRounds = NROUNDSF + NROUNDSP;
if (typeof t === "undefined") t = T;
const cts = getPseudoRandom(seed+"_constants", nRounds);
return cts;
};
function ark(state, c) {
for (let j=0; j<state.length; j++ ) {
state[j] = F.add(state[j], c);
}
}
function sigma(a) {
return F.mul(a, F.square(F.square(a,a)));
}
function mix(state, M) {
const newState = new Array(state.length);
for (let i=0; i<state.length; i++) {
newState[i] = F.zero;
for (let j=0; j<state.length; j++) {
newState[i] = F.add(newState[i], F.mul(M[i][j], state[j]) );
}
}
for (let i=0; i<state.length; i++) state[i] = newState[i];
}
exports.createHash = (t, nRoundsF, nRoundsP, seed) => {
if (typeof seed === "undefined") seed = SEED;
if (typeof nRoundsF === "undefined") nRoundsF = NROUNDSF;
if (typeof nRoundsP === "undefined") nRoundsP = NROUNDSP;
if (typeof t === "undefined") t = T;
assert(nRoundsF % 2 == 0);
const C = exports.getConstants(t, seed, nRoundsF + nRoundsP);
const M = exports.getMatrix(t, seed, nRoundsF + nRoundsP);
return function(inputs) {
let state = [];
assert(inputs.length <= t);
assert(inputs.length > 0);
for (let i=0; i<inputs.length; i++) state[i] = bigInt(inputs[i]);
for (let i=inputs.length; i<t; i++) state[i] = F.zero;
for (let i=0; i< nRoundsF + nRoundsP; i++) {
ark(state, C[i]);
if ((i<nRoundsF/2) || (i >= nRoundsF/2 + nRoundsP)) {
for (let j=0; j<t; j++) state[j] = sigma(state[j]);
} else {
state[0] = sigma(state[0]);
}
mix(state, M);
}
return F.affine(state[0]);
};
};

3449
src/poseidon_constants.json Normal file

File diff suppressed because it is too large Load Diff

@ -1,3 +1,3 @@
include "../../circuits/poseidon.circom" include "../../circuits/poseidon.circom"
component main = Poseidon(2, 6, 8, 57); component main = Poseidon(2);

@ -0,0 +1,3 @@
include "../../circuits/poseidon.circom"
component main = Poseidon(4);

@ -1,60 +1,52 @@
const chai = require("chai"); const chai = require("chai");
const path = require("path"); const path = require("path");
const snarkjs = require("snarkjs"); const tester = require("circom").tester;
const compiler = require("circom");
var blake2b = require('blake2b');
const poseidon = require("../src/poseidon.js"); const poseidon = require("../src/poseidon.js");
const assert = chai.assert; const assert = chai.assert;
describe("Blake2b version test", function() {
it("Should give the expected output for blake2b version", async () => {
var output = new Uint8Array(32);
var input = Buffer.from('poseidon_constants');
h = blake2b(output.length).update(input).digest('hex')
assert.equal('e57ba154fb2c47811dc1a2369b27e25a44915b4e4ece4eb8ec74850cb78e01b1', h);
});
});
describe("Poseidon Circuit test", function () { describe("Poseidon Circuit test", function () {
let circuit; let circuit2;
let circuit4;
this.timeout(100000); this.timeout(100000);
before( async () => { before(async () => {
const cirDef = await compiler(path.join(__dirname, "circuits", "poseidon_test.circom")); circuit2 = await tester(path.join(__dirname, "circuits", "poseidon2_test.circom"));
circuit4 = await tester(path.join(__dirname, "circuits", "poseidon4_test.circom"));
circuit = new snarkjs.Circuit(cirDef);
console.log("Poseidon constraints: " + circuit.nConstraints);
}); });
it("Should check constrain of hash([1, 2])", async () => { it("Should check constrain of hash([1, 2])", async () => {
const w = circuit.calculateWitness({inputs: [1, 2]}); const hash = poseidon([1, 2]);
assert.equal("17117985411748610629288516079940078114952304104811071254131751175361957805920", hash.toString());
const res = w[circuit.getSignalIdx("main.out")]; const w = await circuit2.calculateWitness({inputs: [1, 2]}, true);
await circuit2.assertOut(w, {out : hash});
const hash = poseidon.createHash(6, 8, 57); await circuit2.checkConstraints(w);
const res2 = hash([1,2]);
assert.equal('12242166908188651009877250812424843524687801523336557272219921456462821518061', res2.toString());
assert.equal(res.toString(), res2.toString());
assert(circuit.checkWitness(w));
}); });
it("Should check constrain of hash([3, 4])", async () => { it("Should check constrain of hash([3, 4])", async () => {
const w = circuit.calculateWitness({inputs: [3, 4]}); const hash = poseidon([3, 4]);
assert.equal("21867347236198497199818917118739170715216974132230970409806500217655788551452", hash.toString());
const w = await circuit2.calculateWitness({inputs: [3, 4]});
await circuit2.assertOut(w, {out : hash});
await circuit2.checkConstraints(w);
});
const res = w[circuit.getSignalIdx("main.out")];
const hash = poseidon.createHash(6, 8, 57); it("Should check constrain of hash([1, 2, 3, 4])", async () => {
const hash = poseidon([1, 2, 3, 4]);
assert.equal("10501812514110530158422365608831771203648472822841727510887411206067265790462", hash.toString());
const w = await circuit4.calculateWitness({inputs: [1, 2, 3, 4]});
await circuit4.assertOut(w, {out : hash});
await circuit4.checkConstraints(w);
});
const res2 = hash([3, 4]); it("Should check constrain of hash([5, 6, 7, 8])", async () => {
assert.equal('17185195740979599334254027721507328033796809509313949281114643312710535000993', res2.toString()); const hash = poseidon([5, 6, 7, 8]);
assert.equal("20761996991478317428195238015626872345373101531750069996451149877836620406299", hash.toString());
assert.equal(res.toString(), res2.toString()); const w = await circuit4.calculateWitness({inputs: [5, 6, 7, 8]});
await circuit4.assertOut(w, {out : hash});
assert(circuit.checkWitness(w)); await circuit4.checkConstraints(w);
}); });
}); });