Browse Source

Poseidon

feature/pr-19
Jordi Baylina 4 years ago
parent
commit
590d62a07c
No known key found for this signature in database GPG Key ID: 7480C80C1BE43112
13 changed files with 12606 additions and 266 deletions
  1. +202
    -0
      circuits/poseidon.circom
  2. +11471
    -0
      log.txt
  3. +487
    -263
      package-lock.json
  4. +1
    -1
      package.json
  5. +6
    -2
      src/evmasm.js
  6. +115
    -0
      src/poseidon.js
  7. +180
    -0
      src/poseidon_gencontract.js
  8. +16
    -0
      src/poseidon_printconstants.js
  9. +5
    -0
      src/poseidon_printcontract.js
  10. +22
    -0
      src/poseidon_printmatrix.js
  11. +3
    -0
      test/circuits/poseidon_test.circom
  12. +39
    -0
      test/poseidoncircuit.js
  13. +59
    -0
      test/poseidoncontract.js

+ 202
- 0
circuits/poseidon.circom

@ -0,0 +1,202 @@
template Sigma() {
signal input in;
signal output out;
signal in2;
signal in4;
in2 <== in*in;
in4 <== in2*in2;
out <== in4*in;
}
template Ark(t, C) {
signal input in[t];
signal output out[t];
for (var i=0; i<t; i++) {
out[i] <== in[i] + C;
}
}
template Mix(t, M) {
signal input in[t];
signal output out[t];
var lc;
for (var i=0; i<t; i++) {
lc = 0;
for (var j=0; j<t; j++) {
lc = lc + M[i][j]*in[j];
}
out[i] <== lc;
}
}
// var nRoundsF = 8;
// var nRoundsP = 57;
// var t = 6;
template Poseidon(nInputs, t, nRoundsF, nRoundsP) {
var C = [
1354025385229961744769229343273868518314335569873439558835166239576773343708,
14378844218625331047742339561309620904043097700980163591187076404481144585564,
19370813333785034372560258124535206525278124675105547990205946542330734467847,
19339919977375282793227987561727366424934767905100680591050582380667086628202,
8422275364316831706903446151602338206948363474119622730702497535467809432414,
7433947928146794739292515659214608949964639549664630885486968546133071892830,
20753350951496713264979200428833003140825588922450200809553577914404143341040,
21437018888080559864996889038552602698883513894753499324572098534369350747880,
6815447461573214810645016226370803316854141900901895816072622961504507953749,
11746901014017411226283657362310634990885933032228701131899561585228216986615,
13428722985713722660261823704226749286675766756093697029778643398137361221114,
10894749142018158588533110743373157663019829222177356873395009880383491602356,
13720066349319517776784660327982201297420012713976744204318378121767771862484,
4665971664531891307640113589671067488692829783046247264698659233443166028412,
5364541878673041196937884015923978656299860601766012213141306792903990539664,
1747411112078838632103476037026417344408680910500491241306123941341644055714,
1835035066071759522152578007305026433897204459772540871869487151042238374039,
21055637025200882290106914253232617255445210101773537879323245961051751183138,
19215142378199333594641095299020637309879189505235993967243075316794127016728,
5264915734761449905856538116909638235466879053604660991987579054616980096262,
10045917614023673044141331581538797960977704806582630277050701175565262856382,
7789533631025313780520004400945166898631872921910178131342071973345076732672,
19972498130123740615813398456152565371201412297339860820234719562997192378694,
14216077977931104808041453382683965630500368477063603536695472115075869912121,
2555981910568472028679439033700033287358333564454149464965229699417319912931,
11735111941739477522347883122573591136355121750109794306409399099068977659499,
16957890207070910233505303498878270130436461648242792271152604513138986611609,
358520143235124107055243451716507147499373646513864848163778795927281509793,
6389190271835493841487214043252929516390267871881261173336690351147926750010,
12939611907106878137030264379019786496625916664142697309441198804098015086205,
456696638239570875759108333278090964360604015255503302016190396687826618227,
20569764931577491771523222445135914903999292206008007769649255123342890660989,
3341317247281280728261320226215152668194921091152837358683334635401182164637,
11821224368797169892747612081573458772211068320533296007836648033607696236615,
2185176387193659864764608304330502180888068591409305411859725028056771843945,
17161758741114551704796078639849789476380746803787101178795737276111464575519,
11889888355183092530573415929875444766831739308191993115663429638848547668798,
19503084309842095729857399025417533485089330284851492085544757107759925099089,
20280152151768521735424910115623976684589382048161526796204268413029416266256,
17607351334578006856860845064970248988416315904757246948622629528628773644114,
20769468546336672951384440602623018830493119282829911435465737079851020547355,
1546429204704408801701093430086546742691081844046642451323347737810191940866,
11200569249561318620053458114013886527484999499632128671282997438827107108229,
20198213289062010915914579158889968647687156198219713083074315178786791524034,
12911209060630719799682705729835581341676468319742562559036843993017369855977,
18925188847138989248835676768641716482476141553833902070405850166412140133887,
20376466983584239909415025821007315251422803039634139484663713769887712746966,
5250536177612235431141386983455571232984126980327432803966957255414592397060,
344847263093423485167610155050688875010578621416939967999896518937961396680,
19870333728541576497318505013425518731999037041271340888970559590767369935385,
20604512495859902382577228682521142736989261702707317046150335692410020156250,
18293964088694556689667514948280720605277532123251572448395882397578643211622,
21211253784206079547762943120791531326787601501123474236198383850531142559075,
17247914781304154940895909185506123510423535554247372833321968000513811053190,
16432189025237084563810000088202867449333673799705533820685782829379758927490,
7204783063529242442830743239980168374343946353418800547956909848044774363774,
2108268032972510691814120801033692084861951866306906253604497017452204030001,
13483247063999686990721729188827693318708500027762095503921527692630827328070,
18194665574704709729868916970580318177272365766028499528691737819467051567534,
9275639092314761435364537152118796424056864859607619908791935597976913759411,
12700165678727983685032332532042155014245610964583614362270316386368455898604,
19127554193875137546212792134421555344854604853165878912238751527694270097310,
16817455471920423226570476063669349525676437756352144502692679769377074040335,
17331185397945904160678487303485263376032543419657328137924881433628614769955,
11605569208043267134285833752214422326246425655661043119023566733685555142420
];
var M = [
[
12037235000515189726428793905458162231976011685774991261061889278958802340039,
15885254491685089152627111704751054604879027605518712546805347341940526552948,
12220682901774309471330657931458552366575439534877271150866614914170639536810,
11195096069395318710866553226959674457987772316145705151894647995087349332576,
598000700605575401177173118514166571064818339748567530524299773749024042768,
1430722853270801285936618367966028727146977818314178365893636191280860605092
],[
2478807512283787086755520696277192042388812450777005645736937700857950416751,
2233078558675693495109307103888903813217767160310116996267068037131890781382,
18876120310576321787684235034151243856925703159922712959767164734820803570163,
16850806847813838898693870637445284830832219430572133120914449039176718036761,
1333036876405396107763619427367607232034082504897380383847654332653606615427,
8024813862822108072341428765418554797098705487914663416123759888639403831315
],[
6500095533434910955474026763873910167129305339747639394388586826716085593160,
8370208895752773828195461153114215021863221486187756770286440396339432061839,
9303960191058819387902927958335344077259478983903385066838134730814638010892,
12233235273183315334651707749232602313616943260130930740801007863528547113209,
9140649423847813564210562288927015604065254723368331178903734405333716165919,
1715897011776785698388835993891828784112980527691698420841641796168725014542
],[
19092640119254952117901733765506685707486610088248205883870061221436629780176,
3099417239661426161523333446616746096617346084970490427773391639810420659748,
4498351799178816410443394768364090919370619407529999860500200773846156676605,
17766546613911504681964882079687098358108049762069064124348111606619240189874,
19735199036142202254810745273503942799136628866093225336283966579447096889407,
18618071352177331467649099054615438672487187348128402045695624605978242597928
],[
13282641901514824677915134398195165361567825236648422290753355584311135817423,
842963276112770184096507757019973399127531993386626262873928307889423235104,
5724939764407712239242783736588389538981693110806985476155198757555171230777,
3015568820237516884743892014063659856564948150197038824747891972506031149123,
8926202997251578932273751544826990276848999132609774201265891812369804099098,
13523975980414815337318802343464374889255493007711857898760066111901192774093
],[
13163705634211158833717167498904899472888572514300041636511944020589976373689,
6705743158328622712684686369234272301477431173971663001146157949982901448493,
5341285785102635724684809992160578025785996331334612975598752577556138916495,
2365134732811643517126128253974711531515283135672252986518431351700239265326,
11812366860142870031000585328436207067560705932658624313008187741187899702321,
11192751017231920972897699839932659451476356148115786649719660565307185769796
]
];
signal input inputs[nInputs];
signal output out;
component ark[nRoundsF + nRoundsP];
component sigmaF[nRoundsF][t];
component sigmaP[nRoundsP];
component mix[nRoundsF + nRoundsP];
var k;
for (var i=0; i<(nRoundsF + nRoundsP); i++) {
ark[i] = Ark(t, C[i]);
mix[i] = Mix(t, M);
for (var j=0; j<t; j++) {
if (i==0) {
if (j<nInputs) {
ark[i].in[j] <== inputs[j];
} else {
ark[i].in[j] <== 0;
}
} else {
ark[i].in[j] <== mix[i-1].out[j];
}
}
if ((i<(nRoundsF/2)) || (i>= (nRoundsP + nRoundsF/2))) {
k= i<nRoundsF/2 ? i : (i-nRoundsP);
for (var j=0; j<t; j++) {
sigmaF[k][j] = Sigma();
sigmaF[k][j].in <== ark[i].out[j];
mix[i].in[j] <== sigmaF[k][j].out;
}
} else {
k= i-nRoundsF/2;
sigmaP[k] = Sigma();
sigmaP[k].in <== ark[i].out[0];
mix[i].in[0] <== sigmaP[k].out;
for (var j=1; j<t; j++) {
mix[i].in[j] <== ark[i].out[j];
}
}
}
out <== mix[nRoundsF + nRoundsP -1].out[0];
}

+ 11471
- 0
log.txt
File diff suppressed because it is too large
View File


+ 487
- 263
package-lock.json
File diff suppressed because it is too large
View File


+ 1
- 1
package.json

@ -29,7 +29,7 @@
"web3": "^1.0.0-beta.36"
},
"devDependencies": {
"circom": "0.0.24",
"circom": "0.0.28",
"eslint-plugin-mocha": "^5.2.0",
"ganache-cli": "^6.2.3",
"mocha": "^5.2.0"

+ 6
- 2
src/evmasm.js

@ -127,12 +127,16 @@ class Contract {
jmp(label) {
this._pushLabel(label);
if (typeof label !== "undefined") {
this._pushLabel(label);
}
this.code.push(0x56);
}
jmpi(label) {
this._pushLabel(label);
if (typeof label !== "undefined") {
this._pushLabel(label);
}
this.code.push(0x57);
}

+ 115
- 0
src/poseidon.js

@ -0,0 +1,115 @@
const bn128 = require("snarkjs").bn128;
const bigInt = require("snarkjs").bigInt;
const createBlakeHash = require("blake-hash");
const assert = require("assert");
const F = bn128.Fr;
const SEED = "poseidon";
const NROUNDSF = 8;
const NROUNDSP = 57;
const T = 6;
function getPseudoRandom(seed, n) {
const res = [];
let h = createBlakeHash("blake256").update(seed).digest();
while (res.length<n) {
const n = F.affine(bigInt.leBuff2int(h));
res.push(n);
h = createBlakeHash("blake256").update(h).digest();
}
return res;
}
function allDifferent(v) {
for (let i=0; i<v.length; i++) {
if (v[i].isZero()) return false;
for (let j=i+1; j<v.length; j++) {
if (v[i].equals(v[j])) return false;
}
}
return true;
}
exports.getMatrix = (t, seed, nRounds) => {
if (typeof seed === "undefined") seed = SEED;
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]);
};
};

+ 180
- 0
src/poseidon_gencontract.js

@ -0,0 +1,180 @@
// Copyright (c) 2018 Jordi Baylina
// License: LGPL-3.0+
//
const Poseidon = require("./poseidon.js");
const Contract = require("./evmasm");
const SEED = "poseidon";
const NROUNDSF = 8;
const NROUNDSP = 57;
const T = 6;
function toHex256(a) {
let S = a.toString(16);
while (S.length < 64) S="0"+S;
return "0x" + S;
}
function createCode(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;
const K = Poseidon.getConstants(t, seed, nRoundsP + nRoundsF);
const M = Poseidon.getMatrix(t, seed, nRoundsP + nRoundsF);
const C = new Contract();
function saveM() {
for (let i=0; i<t; i++) {
for (let j=0; j<t; j++) {
C.push(toHex256(M[i][j]));
C.push((1+i*t+j)*32);
C.mstore();
}
}
}
function ark(r) {
C.push(toHex256(K[r])); // K, st, q
for (let i=0; i<t; i++) {
C.dup(1+t); // q, K, st, q
C.dup(1); // K, q, K, st, q
C.dup(3+i); // st[i], K, q, K, st, q
C.addmod(); // newSt[i], K, st, q
C.swap(2 + i); // xx, K, st, q
C.pop();
}
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.push("0xc4420fb4"); // poseidon(uint256[])
C.eq();
C.jmpi("start");
C.invalid();
C.label("start");
saveM();
C.push("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); // q
// Load 6 values from the call data.
// The function has a single array param param
// [Selector (4)] [Pointer (32)][Length (32)] [data1 (32)] ....
// We ignore the pointer and the length and just load 6 values to the state
// (Stack positions 0-5) If the array is shorter, we just set zeros.
for (let i=0; i<t; i++) {
C.push(0x44+(0x20*(5-i)));
C.calldataload();
}
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();
}
module.exports.abi = [
{
"constant": true,
"inputs": [
{
"name": "input",
"type": "uint256[]"
}
],
"name": "poseidon",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
}
];
module.exports.createCode = createCode;

+ 16
- 0
src/poseidon_printconstants.js

@ -0,0 +1,16 @@
const Poseidon = require("./poseidon.js");
const C = Poseidon.getConstants();
let S = "[\n";
for (let i=0; i<C.length; i++) {
S = S + " " + C[i].toString();
if (i<C.length-1) S = S + ",";
S = S + "\n";
}
S=S+ "]\n";
console.log(S);

+ 5
- 0
src/poseidon_printcontract.js

@ -0,0 +1,5 @@
const poseidonGenContract = require("./poseidon_gencontract");
console.log(poseidonGenContract.createCode(6, 8, 57));

+ 22
- 0
src/poseidon_printmatrix.js

@ -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);

+ 3
- 0
test/circuits/poseidon_test.circom

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

+ 39
- 0
test/poseidoncircuit.js

@ -0,0 +1,39 @@
const chai = require("chai");
const path = require("path");
const snarkjs = require("snarkjs");
const compiler = require("circom");
const poseidon = require("../src/poseidon.js");
const assert = chai.assert;
describe("Poseidon Circuit test", function () {
let circuit;
this.timeout(100000);
before( async () => {
const cirDef = await compiler(path.join(__dirname, "circuits", "poseidon_test.circom"));
circuit = new snarkjs.Circuit(cirDef);
console.log("Poseidon constraints: " + circuit.nConstraints);
});
it("Should check constrain", async () => {
const w = circuit.calculateWitness({inputs: [1, 2]});
const res = w[circuit.getSignalIdx("main.out")];
const hash = poseidon.createHash(6, 8, 57);
const res2 = hash([1,2]);
console.log(res.toString());
assert.equal(res.toString(), res2.toString());
assert(circuit.checkWitness(w));
});
});

+ 59
- 0
test/poseidoncontract.js

@ -0,0 +1,59 @@
const TestRPC = 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 bigInt = require("snarkjs").bigInt;
const assert = chai.assert;
const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); };
const SEED = "mimc";
describe("Poseidon Smart contract test", () => {
let testrpc;
let web3;
let mimc;
let accounts;
before(async () => {
testrpc = TestRPC.server({
ws: true,
gasLimit: 5800000,
total_accounts: 10,
});
testrpc.listen(8546, "127.0.0.1");
web3 = new Web3("ws://127.0.0.1:8546");
accounts = await web3.eth.getAccounts();
});
after(async () => testrpc.close());
it("Should deploy the contract", async () => {
const C = new web3.eth.Contract(poseidonGenContract.abi);
mimc = await C.deploy({
data: poseidonGenContract.createCode()
}).send({
gas: 2500000,
from: accounts[0]
});
});
it("Shold calculate the mimic correctly", async () => {
const res = await mimc.methods.poseidon([1,2]).call();
console.log("Cir: " + bigInt(res.toString(16)).toString(16));
const hash = Poseidon.createHash(6, 8, 57);
const res2 = hash([1,2]);
console.log("Ref: " + bigInt(res2).toString(16));
assert.equal(res.toString(), res2.toString());
});
});

Loading…
Cancel
Save