UNPKG

circomlibjs-hinkal-fork

Version:

Javascript library to work with circomlib

301 lines (253 loc) 8.16 kB
import fs from "fs"; import path from "path"; import {Scalar, ZqField, utils} from "ffjavascript"; const { unstringifyBigInts } = 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(JSON.parse(fs.readFileSync(path.join("src", "poseidon_constants.json"), "utf8"))); const N_ROUNDS_F = 8; const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; 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( "src", "poseidon_constants_opt.js"), "export default " + JSON.stringify(opt, null, 1), "utf8"); } run().then(()=> { process.exit(0); }, (err) => { // console.log(err); console.log(err.stack); process.exit(1); });