├── .github └── workflows │ ├── npm-publish.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .vscode └── launch.json ├── README.md ├── badmsges.md ├── build └── main.cjs ├── hardhat.config.cjs ├── main.js ├── package-lock.json ├── package.json ├── rollup.cjs.config.js ├── src ├── babyjub.js ├── eddsa.js ├── evmasm.js ├── mimc7.js ├── mimc7_gencontract.js ├── mimc7_print_iv.js ├── mimc7_printconstants.js ├── mimc7_printcontract.js ├── mimcsponge.js ├── mimcsponge_gencontract.js ├── mimcsponge_printconstants.js ├── mimcsponge_printcontract.js ├── pedersen_hash.js ├── pedersen_printbases.js ├── poseidon_constants.js ├── poseidon_constants.json ├── poseidon_constants_opt.js ├── poseidon_constants_opt.json ├── poseidon_gencontract.js ├── poseidon_opt.js ├── poseidon_printcontract.js ├── poseidon_printmatrix.js ├── poseidon_reference.js ├── poseidon_wasm.js ├── smt.js ├── smt_hashes_mimc.js ├── smt_hashes_poseidon.js ├── smt_memdb.js └── testblake.js ├── test ├── babyjub.js ├── eddsa.js ├── mimc7.js ├── mimc7contract.js ├── mimcsponge.js ├── mimcspongecontract.js ├── pedersenhash.js ├── poseidon.js ├── poseidoncontract.js └── smt.js └── tools └── poseidon_optimize_constants.js /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to NPM 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish-npm: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: '20.x' 15 | registry-url: https://registry.npmjs.org/ 16 | cache: 'npm' 17 | - run: npm ci 18 | - run: npm run build 19 | - run: npm publish 20 | env: 21 | NODE_AUTH_TOKEN: ${{secrets.IDEN3_CIRCOM_NPM_PUBLISH_TOKEN}} 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | name: Test 11 | runs-on: ${{ matrix.os }} 12 | timeout-minutes: 30 13 | 14 | strategy: 15 | fail-fast: true 16 | matrix: 17 | os: ["ubuntu-latest"] 18 | node-version: ["lts/*", "lts/-1"] 19 | 20 | steps: 21 | - name: Checkout project 22 | uses: actions/checkout@v4 23 | 24 | - name: Setup Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | check-latest: true 29 | cache: "npm" 30 | 31 | - name: Setup Circom 32 | run: wget https://github.com/iden3/circom/releases/latest/download/circom-linux-amd64 && sudo mv ./circom-linux-amd64 /usr/bin/circom && sudo chmod +x /usr/bin/circom 33 | 34 | - name: Install dependencies 35 | run: npm ci 36 | 37 | - name: Run tests 38 | run: npm test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | tmp 64 | 65 | .DS_Store 66 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hardhat.config.ts 2 | scripts 3 | test 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Test", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 15 | "args": ["test/mimc7.js"] 16 | }, 17 | { 18 | "type": "pwa-node", 19 | "request": "launch", 20 | "name": "Print Bases", 21 | "skipFiles": [ 22 | "/**" 23 | ], 24 | "program": "${workspaceFolder}/src/pedersen_printbases.js" 25 | }, 26 | { 27 | "type": "pwa-node", 28 | "request": "launch", 29 | "name": "Test Poseidon", 30 | "skipFiles": [ 31 | "/**" 32 | ], 33 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 34 | "args": [ 35 | "${workspaceFolder}/test/poseidon.js" 36 | ], 37 | } 38 | 39 | ] 40 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #circomlibjs 2 | 3 | `circomlibjs` is a Javascript library that provides programs to compute the witness of several circuits of `circomlib`. 4 | This library is used to make tests of circomlib circuits. 5 | 6 | In the `src` directory the package includes these programs. 7 | 8 | In the `test` directory includes its own tests and. 9 | 10 | In the `tools` directory includes programs to precompute some needed parameters. 11 | 12 | You can install `circomlibjs` with the following command: 13 | 14 | ```text 15 | npm install -g circomlibjs 16 | ``` -------------------------------------------------------------------------------- /badmsges.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/circomlibjs/48b3ab37013c5ed21e9ff8a80a5b010795c97094/badmsges.md -------------------------------------------------------------------------------- /hardhat.config.cjs: -------------------------------------------------------------------------------- 1 | require("@nomicfoundation/hardhat-ethers"); 2 | /** 3 | * @type import('hardhat/config').HardhatUserConfig 4 | */ 5 | module.exports = { 6 | solidity: "0.7.3" 7 | }; 8 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | export {default as buildBabyjub} from "./src/babyjub.js"; 2 | export {default as buildEddsa} from "./src/eddsa.js"; 3 | export {default as evmasm} from "./src/evmasm.js"; 4 | 5 | export {default as buildMimc7} from "./src/mimc7.js"; 6 | import * as _mimc7Contract from "./src/mimc7_gencontract.js"; 7 | export const mimc7Contract=_mimc7Contract; 8 | 9 | export {default as buildMimcSponge} from "./src/mimcsponge.js"; 10 | import * as _mimcSpongeContract from "./src/mimcsponge_gencontract.js"; 11 | export const mimcSpongecontract=_mimcSpongeContract; 12 | 13 | export {default as buildPedersenHash} from "./src/pedersen_hash.js"; 14 | 15 | export { buildPoseidon, buildPoseidonWasm } from "./src/poseidon_wasm.js"; 16 | import * as _poseidonContract from "./src/poseidon_gencontract.js"; 17 | export const poseidonContract=_poseidonContract; 18 | 19 | export {default as buildPoseidonReference} from "./src/poseidon_reference.js"; 20 | export {default as buildPoseidonOpt} from "./src/poseidon_opt.js"; 21 | 22 | export {SMT, buildSMT, newMemEmptyTrie} from "./src/smt.js"; 23 | 24 | export { default as SMTMemDb } from "./src/smt_memdb.js"; 25 | 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "circomlibjs", 3 | "type": "module", 4 | "main": "./build/main.cjs", 5 | "module": "./main.js", 6 | "exports": { 7 | "import": "./main.js", 8 | "require": "./build/main.cjs" 9 | }, 10 | "version": "0.1.8", 11 | "description": "Javascript library to work with circomlib", 12 | "scripts": { 13 | "test": "mocha", 14 | "poseidonOptimizeConstants": "node tools/poseidon_optimize_constants.js", 15 | "build": "rollup -c rollup.cjs.config.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/iden3/circomlibjs.git" 20 | }, 21 | "keywords": [ 22 | "circom", 23 | "circomlib", 24 | "iden3", 25 | "snarkjs", 26 | "snark", 27 | "zero", 28 | "knowledge" 29 | ], 30 | "author": "Jordi Baylina", 31 | "license": "GPL-3.0", 32 | "bugs": { 33 | "url": "https://github.com/iden3/circomlibjs/issues" 34 | }, 35 | "homepage": "https://github.com/iden3/circomlibjs#readme", 36 | "devDependencies": { 37 | "chai": "^4.3.4", 38 | "mocha": "^11.1.0", 39 | "hardhat": "^2.22.19", 40 | "@nomicfoundation/hardhat-ethers": "^3.0.8" 41 | }, 42 | "dependencies": { 43 | "@noble/hashes": "^1.7.1", 44 | "ffjavascript": "^0.3.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rollup.cjs.config.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { builtinModules as builtin } from "module"; 3 | 4 | const pkg = JSON.parse(fs.readFileSync("./package.json")); 5 | 6 | export default { 7 | input: "./main.js", 8 | output: { 9 | file: "build/main.cjs", 10 | format: "cjs", 11 | }, 12 | external: [ 13 | "@noble/hashes/blake1", 14 | "@noble/hashes/blake2b", 15 | "@noble/hashes/sha3", 16 | "@noble/hashes/utils", 17 | ...Object.keys(pkg.dependencies), 18 | ...builtin, 19 | ] 20 | }; -------------------------------------------------------------------------------- /src/babyjub.js: -------------------------------------------------------------------------------- 1 | import { getCurveFromName, Scalar } from "ffjavascript"; 2 | 3 | export default async function buildBabyJub() { 4 | const bn128 = await getCurveFromName("bn128", true); 5 | return new BabyJub(bn128.Fr); 6 | } 7 | 8 | class BabyJub { 9 | constructor(F) { 10 | this.F = F; 11 | this.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 12 | this.pm1d2 = Scalar.div(Scalar.sub(this.p, Scalar.e(1)), Scalar.e(2)); 13 | 14 | this.Generator = [ 15 | F.e("995203441582195749578291179787384436505546430278305826713579947235728471134"), 16 | F.e("5472060717959818805561601436314318772137091100104008585924551046643952123905") 17 | ]; 18 | this.Base8 = [ 19 | F.e("5299619240641551281634865583518297030282874472190772894086521144482721001553"), 20 | F.e("16950150798460657717958625567821834550301663161624707787222815936182638968203") 21 | ]; 22 | this.order = Scalar.fromString("21888242871839275222246405745257275088614511777268538073601725287587578984328"); 23 | this.subOrder = Scalar.shiftRight(this.order, 3); 24 | this.A = F.e("168700"); 25 | this.D = F.e("168696"); 26 | } 27 | 28 | 29 | addPoint(a,b) { 30 | const F = this.F; 31 | 32 | const res = []; 33 | 34 | /* does the equivalent of: 35 | 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); 36 | 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); 37 | */ 38 | 39 | const beta = F.mul(a[0],b[1]); 40 | const gamma = F.mul(a[1],b[0]); 41 | const delta = F.mul( 42 | F.sub(a[1], F.mul(this.A, a[0])), 43 | F.add(b[0], b[1]) 44 | ); 45 | const tau = F.mul(beta, gamma); 46 | const dtau = F.mul(this.D, tau); 47 | 48 | res[0] = F.div( 49 | F.add(beta, gamma), 50 | F.add(F.one, dtau) 51 | ); 52 | 53 | res[1] = F.div( 54 | F.add(delta, F.sub(F.mul(this.A,beta), gamma)), 55 | F.sub(F.one, dtau) 56 | ); 57 | 58 | return res; 59 | } 60 | 61 | mulPointEscalar(base, e) { 62 | const F = this.F; 63 | let res = [F.e("0"),F.e("1")]; 64 | let rem = e; 65 | let exp = base; 66 | 67 | while (! Scalar.isZero(rem)) { 68 | if (Scalar.isOdd(rem)) { 69 | res = this.addPoint(res, exp); 70 | } 71 | exp = this.addPoint(exp, exp); 72 | rem = Scalar.shiftRight(rem, 1); 73 | } 74 | 75 | return res; 76 | } 77 | 78 | inSubgroup(P) { 79 | const F = this.F; 80 | if (!this.inCurve(P)) return false; 81 | const res= this.mulPointEscalar(P, this.subOrder); 82 | return (F.isZero(res[0]) && F.eq(res[1], F.one)); 83 | } 84 | 85 | inCurve(P) { 86 | const F = this.F; 87 | const x2 = F.square(P[0]); 88 | const y2 = F.square(P[1]); 89 | 90 | if (!F.eq( 91 | F.add(F.mul(this.A, x2), y2), 92 | F.add(F.one, F.mul(F.mul(x2, y2), this.D)))) return false; 93 | 94 | return true; 95 | } 96 | 97 | packPoint(P) { 98 | const F = this.F; 99 | const buff = new Uint8Array(32); 100 | F.toRprLE(buff, 0, P[1]); 101 | const n = F.toObject(P[0]); 102 | if (Scalar.gt(n, this.pm1d2)) { 103 | buff[31] = buff[31] | 0x80; 104 | } 105 | return buff; 106 | } 107 | 108 | unpackPoint(buff) { 109 | const F = this.F; 110 | let sign = false; 111 | const P = new Array(2); 112 | if (buff[31] & 0x80) { 113 | sign = true; 114 | buff[31] = buff[31] & 0x7F; 115 | } 116 | P[1] = F.fromRprLE(buff, 0); 117 | if (Scalar.gt(F.toObject(P[1]), this.p)) return null; 118 | 119 | const y2 = F.square(P[1]); 120 | 121 | const x2 = F.div( 122 | F.sub(F.one, y2), 123 | F.sub(this.A, F.mul(this.D, y2)) 124 | ); 125 | 126 | const x2h = F.exp(x2, F.half); 127 | if (! F.eq(F.one, x2h)) return null; 128 | 129 | let x = F.sqrt(x2); 130 | 131 | if (x == null) return null; 132 | 133 | if (sign) x = F.neg(x); 134 | 135 | P[0] = x; 136 | 137 | return P; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/eddsa.js: -------------------------------------------------------------------------------- 1 | import { blake512 } from "@noble/hashes/blake1"; 2 | import { Scalar } from "ffjavascript"; 3 | import buildBabyJub from "./babyjub.js"; 4 | import buildMimc7 from "./mimc7.js"; 5 | import buildMimcSponge from "./mimcsponge.js"; 6 | import buildPedersenHash from "./pedersen_hash.js"; 7 | import { buildPoseidon } from "./poseidon_wasm.js"; 8 | 9 | export default async function buildEddsa() { 10 | const babyJub = await buildBabyJub("bn128"); 11 | const pedersenHash = await buildPedersenHash(); 12 | const mimc7 = await buildMimc7(); 13 | const poseidon = await buildPoseidon(); 14 | const mimcSponge = await buildMimcSponge(); 15 | return new Eddsa(babyJub, pedersenHash, mimc7, poseidon, mimcSponge); 16 | } 17 | 18 | class Eddsa { 19 | 20 | constructor(babyJub, pedersenHash, mimc7, poseidon, mimcSponge) { 21 | this.babyJub = babyJub; 22 | this.pedersenHash = pedersenHash; 23 | this.mimc7 = mimc7; 24 | this.poseidon = poseidon; 25 | this.mimcSponge = mimcSponge; 26 | this.F = babyJub.F; 27 | } 28 | 29 | pruneBuffer(buff) { 30 | buff[0] = buff[0] & 0xF8; 31 | buff[31] = buff[31] & 0x7F; 32 | buff[31] = buff[31] | 0x40; 33 | return buff; 34 | } 35 | 36 | prv2pub(prv) { 37 | const F = this.babyJub.F; 38 | const sBuff = this.pruneBuffer(blake512(Buffer.from(prv))); 39 | let s = Scalar.fromRprLE(sBuff, 0, 32); 40 | const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s,3)); 41 | return A; 42 | } 43 | 44 | signPedersen(prv, msg) { 45 | const F = this.babyJub.F; 46 | const sBuff = this.pruneBuffer(blake512(Buffer.from(prv))); 47 | const s = Scalar.fromRprLE(sBuff, 0, 32); 48 | const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3)); 49 | 50 | const composeBuff = new Uint8Array(32 + msg.length); 51 | composeBuff.set(sBuff.slice(32), 0); 52 | composeBuff.set(msg, 32); 53 | const rBuff = blake512(Buffer.from(composeBuff)); 54 | let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder); 55 | const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r); 56 | const R8p = this.babyJub.packPoint(R8); 57 | const Ap = this.babyJub.packPoint(A); 58 | 59 | const composeBuff2 = new Uint8Array(64 + msg.length); 60 | composeBuff2.set(R8p, 0); 61 | composeBuff2.set(Ap, 32); 62 | composeBuff2.set(msg, 64); 63 | 64 | const hmBuff = this.pedersenHash.hash(composeBuff2); 65 | const hm = Scalar.fromRprLE(hmBuff, 0, 32); 66 | 67 | const S = Scalar.mod( 68 | Scalar.add( 69 | r, 70 | Scalar.mul(hm, s) 71 | ), 72 | this.babyJub.subOrder 73 | ) 74 | return { 75 | R8: R8, 76 | S: S 77 | }; 78 | } 79 | 80 | signMiMC(prv, msg) { 81 | const F = this.babyJub.F; 82 | const sBuff = this.pruneBuffer(blake512(Buffer.from(prv))); 83 | const s = Scalar.fromRprLE(sBuff, 0, 32); 84 | const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3)); 85 | 86 | 87 | const composeBuff = new Uint8Array(32 + msg.length); 88 | composeBuff.set(sBuff.slice(32), 0); 89 | F.toRprLE(composeBuff, 32, msg); 90 | const rBuff = blake512(Buffer.from(composeBuff)); 91 | let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder); 92 | const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r); 93 | 94 | const hm = this.mimc7.multiHash([R8[0], R8[1], A[0], A[1], msg]); 95 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 96 | const S = Scalar.mod( 97 | Scalar.add( 98 | r, 99 | Scalar.mul(hms, s) 100 | ), 101 | this.babyJub.subOrder 102 | ) 103 | return { 104 | R8: R8, 105 | S: S 106 | }; 107 | } 108 | 109 | signMiMCSponge(prv, msg) { 110 | const F = this.babyJub.F; 111 | const sBuff = this.pruneBuffer(blake512(Buffer.from(prv))); 112 | const s = Scalar.fromRprLE(sBuff, 0, 32); 113 | const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3)); 114 | 115 | const composeBuff = new Uint8Array(32 + msg.length); 116 | composeBuff.set(sBuff.slice(32), 0); 117 | F.toRprLE(composeBuff, 32, msg); 118 | const rBuff = blake512(Buffer.from(composeBuff)); 119 | let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder); 120 | const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r); 121 | 122 | const hm = this.mimcSponge.multiHash([R8[0], R8[1], A[0], A[1], msg]); 123 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 124 | const S = Scalar.mod( 125 | Scalar.add( 126 | r, 127 | Scalar.mul(hms, s) 128 | ), 129 | this.babyJub.subOrder 130 | ) 131 | return { 132 | R8: R8, 133 | S: S 134 | }; 135 | } 136 | 137 | signPoseidon(prv, msg) { 138 | const F = this.babyJub.F; 139 | const sBuff = this.pruneBuffer(blake512(Buffer.from(prv))); 140 | const s = Scalar.fromRprLE(sBuff, 0, 32); 141 | const A = this.babyJub.mulPointEscalar(this.babyJub.Base8, Scalar.shr(s, 3)); 142 | 143 | const composeBuff = new Uint8Array(32 + msg.length); 144 | composeBuff.set(sBuff.slice(32), 0); 145 | F.toRprLE(composeBuff, 32, msg); 146 | const rBuff = blake512(Buffer.from(composeBuff)); 147 | let r = Scalar.mod(Scalar.fromRprLE(rBuff, 0, 64), this.babyJub.subOrder); 148 | const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r); 149 | 150 | const hm = this.poseidon([R8[0], R8[1], A[0], A[1], msg]); 151 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 152 | const S = Scalar.mod( 153 | Scalar.add( 154 | r, 155 | Scalar.mul(hms, s) 156 | ), 157 | this.babyJub.subOrder 158 | ) 159 | return { 160 | R8: R8, 161 | S: S 162 | }; 163 | } 164 | 165 | verifyPedersen(msg, sig, A) { 166 | // Check parameters 167 | if (typeof sig != "object") return false; 168 | if (!Array.isArray(sig.R8)) return false; 169 | if (sig.R8.length!= 2) return false; 170 | if (!this.babyJub.inCurve(sig.R8)) return false; 171 | if (!Array.isArray(A)) return false; 172 | if (A.length!= 2) return false; 173 | if (!this.babyJub.inCurve(A)) return false; 174 | if (Scalar.geq(sig.S, this.babyJub.subOrder)) return false; 175 | 176 | const R8p = this.babyJub.packPoint(sig.R8); 177 | const Ap = this.babyJub.packPoint(A); 178 | 179 | 180 | const composeBuff2 = new Uint8Array(64 + msg.length); 181 | composeBuff2.set(R8p, 0); 182 | composeBuff2.set(Ap, 32); 183 | composeBuff2.set(msg, 64); 184 | 185 | 186 | const hmBuff = this.pedersenHash.hash(composeBuff2); 187 | const hm = Scalar.fromRprLE(hmBuff, 0, 32); 188 | 189 | const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S); 190 | let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hm,8)); 191 | Pright = this.babyJub.addPoint(sig.R8, Pright); 192 | 193 | if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false; 194 | if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false; 195 | return true; 196 | } 197 | 198 | verifyMiMC(msg, sig, A) { 199 | // Check parameters 200 | if (typeof sig != "object") return false; 201 | if (!Array.isArray(sig.R8)) return false; 202 | if (sig.R8.length!= 2) return false; 203 | if (!this.babyJub.inCurve(sig.R8)) return false; 204 | if (!Array.isArray(A)) return false; 205 | if (A.length!= 2) return false; 206 | if (!this.babyJub.inCurve(A)) return false; 207 | if (sig.S>= this.babyJub.subOrder) return false; 208 | 209 | const hm = this.mimc7.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]); 210 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 211 | 212 | const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S); 213 | let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8)); 214 | Pright = this.babyJub.addPoint(sig.R8, Pright); 215 | 216 | if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false; 217 | if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false; 218 | return true; 219 | } 220 | 221 | verifyPoseidon(msg, sig, A) { 222 | 223 | // Check parameters 224 | if (typeof sig != "object") return false; 225 | if (!Array.isArray(sig.R8)) return false; 226 | if (sig.R8.length!= 2) return false; 227 | if (!this.babyJub.inCurve(sig.R8)) return false; 228 | if (!Array.isArray(A)) return false; 229 | if (A.length!= 2) return false; 230 | if (!this.babyJub.inCurve(A)) return false; 231 | if (sig.S>= this.babyJub.subOrder) return false; 232 | 233 | const hm = this.poseidon([sig.R8[0], sig.R8[1], A[0], A[1], msg]); 234 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 235 | 236 | const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S); 237 | let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8)); 238 | Pright = this.babyJub.addPoint(sig.R8, Pright); 239 | 240 | if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false; 241 | if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false; 242 | return true; 243 | } 244 | 245 | verifyMiMCSponge(msg, sig, A) { 246 | 247 | // Check parameters 248 | if (typeof sig != "object") return false; 249 | if (!Array.isArray(sig.R8)) return false; 250 | if (sig.R8.length!= 2) return false; 251 | if (!this.babyJub.inCurve(sig.R8)) return false; 252 | if (!Array.isArray(A)) return false; 253 | if (A.length!= 2) return false; 254 | if (!this.babyJub.inCurve(A)) return false; 255 | if (sig.S>= this.babyJub.subOrder) return false; 256 | 257 | const hm = this.mimcSponge.multiHash([sig.R8[0], sig.R8[1], A[0], A[1], msg]); 258 | const hms = Scalar.e(this.babyJub.F.toObject(hm)); 259 | 260 | const Pleft = this.babyJub.mulPointEscalar(this.babyJub.Base8, sig.S); 261 | let Pright = this.babyJub.mulPointEscalar(A, Scalar.mul(hms, 8)); 262 | Pright = this.babyJub.addPoint(sig.R8, Pright); 263 | 264 | if (!this.babyJub.F.eq(Pleft[0],Pright[0])) return false; 265 | if (!this.babyJub.F.eq(Pleft[1],Pright[1])) return false; 266 | return true; 267 | } 268 | 269 | packSignature(sig) { 270 | const buff = new Uint8Array(64); 271 | const R8p = this.babyJub.packPoint(sig.R8); 272 | buff.set(R8p, 0) 273 | const Sp = Scalar.toRprLE(buff, 32, sig.S, 32); 274 | return buff; 275 | } 276 | 277 | unpackSignature(sigBuff) { 278 | return { 279 | R8: this.babyJub.unpackPoint(sigBuff.slice(0,32)), 280 | S: Scalar.fromRprLE(sigBuff, 32, 32) 281 | }; 282 | } 283 | } 284 | 285 | 286 | -------------------------------------------------------------------------------- /src/evmasm.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jordi Baylina 2 | // License: LGPL-3.0+ 3 | // 4 | 5 | import { Scalar } from "ffjavascript"; 6 | import {bytesToHex, hexToBytes} from "@noble/hashes/utils"; 7 | 8 | export default class Contract { 9 | constructor() { 10 | this.code = []; 11 | this.labels = {}; 12 | this.pendingLabels = {}; 13 | } 14 | 15 | createTxData() { 16 | let C; 17 | 18 | // Check all labels are defined 19 | const pendingLabels = Object.keys(this.pendingLabels); 20 | if (pendingLabels.length>0) { 21 | throw new Error("Labels not defined: "+ pendingLabels.join(", ")); 22 | } 23 | 24 | let setLoaderLength = 0; 25 | let genLoadedLength = -1; 26 | 27 | while (genLoadedLength!=setLoaderLength) { 28 | setLoaderLength = genLoadedLength; 29 | C = new Contract(); 30 | C.codesize(); 31 | C.push(setLoaderLength); 32 | C.push(0); 33 | C.codecopy(); 34 | 35 | C.push(this.code.length); 36 | C.push(0); 37 | C.return(); 38 | genLoadedLength = C.code.length; 39 | } 40 | 41 | return "0x" + bytesToHex(new Uint8Array(C.code.concat(this.code))); 42 | } 43 | 44 | stop() { this.code.push(0x00); } 45 | add() { this.code.push(0x01); } 46 | mul() { this.code.push(0x02); } 47 | sub() { this.code.push(0x03); } 48 | div() { this.code.push(0x04); } 49 | sdiv() { this.code.push(0x05); } 50 | mod() { this.code.push(0x06); } 51 | smod() { this.code.push(0x07); } 52 | addmod() { this.code.push(0x08); } 53 | mulmod() { this.code.push(0x09); } 54 | exp() { this.code.push(0x0a); } 55 | signextend() { this.code.push(0x0b); } 56 | 57 | lt() { this.code.push(0x10); } 58 | gt() { this.code.push(0x11); } 59 | slt() { this.code.push(0x12); } 60 | sgt() { this.code.push(0x13); } 61 | eq() { this.code.push(0x14); } 62 | iszero() { this.code.push(0x15); } 63 | and() { this.code.push(0x16); } 64 | or() { this.code.push(0x17); } 65 | shor() { this.code.push(0x18); } 66 | not() { this.code.push(0x19); } 67 | byte() { this.code.push(0x1a); } 68 | 69 | keccak() { this.code.push(0x20); } 70 | sha3() { this.code.push(0x20); } // alias 71 | 72 | address() { this.code.push(0x30); } 73 | balance() { this.code.push(0x31); } 74 | origin() { this.code.push(0x32); } 75 | caller() { this.code.push(0x33); } 76 | callvalue() { this.code.push(0x34); } 77 | calldataload() { this.code.push(0x35); } 78 | calldatasize() { this.code.push(0x36); } 79 | calldatacopy() { this.code.push(0x37); } 80 | codesize() { this.code.push(0x38); } 81 | codecopy() { this.code.push(0x39); } 82 | gasprice() { this.code.push(0x3a); } 83 | extcodesize() { this.code.push(0x3b); } 84 | extcodecopy() { this.code.push(0x3c); } 85 | returndatasize() { this.code.push(0x3d); } 86 | returndatacopy() { this.code.push(0x3e); } 87 | 88 | blockhash() { this.code.push(0x40); } 89 | coinbase() { this.code.push(0x41); } 90 | timestamp() { this.code.push(0x42); } 91 | number() { this.code.push(0x43); } 92 | difficulty() { this.code.push(0x44); } 93 | gaslimit() { this.code.push(0x45); } 94 | 95 | pop() { this.code.push(0x50); } 96 | mload() { this.code.push(0x51); } 97 | mstore() { this.code.push(0x52); } 98 | mstore8() { this.code.push(0x53); } 99 | sload() { this.code.push(0x54); } 100 | sstore() { this.code.push(0x55); } 101 | 102 | _pushLabel(label) { 103 | if (typeof this.labels[label] != "undefined") { 104 | this.push(this.labels[label]); 105 | } else { 106 | this.pendingLabels[label] = this.pendingLabels[label] || []; 107 | this.pendingLabels[label].push(this.code.length); 108 | this.push("0x000000"); 109 | } 110 | } 111 | 112 | _fillLabel(label) { 113 | if (!this.pendingLabels[label]) return; 114 | 115 | let dst = this.labels[label]; 116 | 117 | const dst3 = [dst >> 16, (dst >> 8) & 0xFF, dst & 0xFF]; 118 | 119 | this.pendingLabels[label].forEach((p) => { 120 | for (let i=0; i<3; i++) { 121 | this.code[p+i+1] = dst3[i]; 122 | } 123 | }); 124 | 125 | delete this.pendingLabels[label]; 126 | } 127 | 128 | 129 | jmp(label) { 130 | if (typeof label !== "undefined") { 131 | this._pushLabel(label); 132 | } 133 | this.code.push(0x56); 134 | } 135 | 136 | jmpi(label) { 137 | if (typeof label !== "undefined") { 138 | this._pushLabel(label); 139 | } 140 | this.code.push(0x57); 141 | } 142 | 143 | pc() { this.code.push(0x58); } 144 | msize() { this.code.push(0x59); } 145 | gas() { this.code.push(0x5a); } 146 | label(name) { 147 | if (typeof this.labels[name] != "undefined") { 148 | throw new Error("Label already defined"); 149 | } 150 | this.labels[name] = this.code.length; 151 | this.code.push(0x5b); 152 | 153 | this._fillLabel(name); 154 | } 155 | 156 | push(data) { 157 | if ((typeof data !== "string") || (data.slice(0,2) != "0x")) { 158 | let v = Scalar.e(data); 159 | if (Scalar.isNegative(v)) { 160 | v = Scalar.add(Scalar.shl(Scalar.e(1), 256), v); 161 | } 162 | let S = Scalar.toString(v, 16); 163 | if (S.length % 2) S = "0"+S; 164 | S = "0x" +S; 165 | data = S; 166 | } 167 | const d = hexToBytes(data.substring(2)); // remove 0x and convert to bytes 168 | if (d.length == 0 || d.length > 32) { 169 | throw new Error("Assertion failed"); 170 | } 171 | const a = []; 172 | this.code.push(0x5F + d.length); 173 | for (let i=0; i= 16) { 180 | throw new Error("Assertion failed"); 181 | } 182 | this.code.push(0x80 + n); 183 | } 184 | 185 | swap(n) { 186 | if (n < 1 || n > 16) { 187 | throw new Error("Assertion failed"); 188 | } 189 | this.code.push(0x8f + n); 190 | } 191 | 192 | log0() { this.code.push(0xa0); } 193 | log1() { this.code.push(0xa1); } 194 | log2() { this.code.push(0xa2); } 195 | log3() { this.code.push(0xa3); } 196 | log4() { this.code.push(0xa4); } 197 | 198 | create() { this.code.push(0xf0); } 199 | call() { this.code.push(0xf1); } 200 | callcode() { this.code.push(0xf2); } 201 | return() { this.code.push(0xf3); } 202 | delegatecall() { this.code.push(0xf4); } 203 | 204 | staticcall() { this.code.push(0xfa); } 205 | revert() { this.code.push(0xfd); } 206 | invalid() { this.code.push(0xfe); } 207 | selfdestruct() { this.code.push(0xff); } 208 | } 209 | 210 | -------------------------------------------------------------------------------- /src/mimc7.js: -------------------------------------------------------------------------------- 1 | import {getCurveFromName, Scalar} from "ffjavascript"; 2 | 3 | import {keccak_256} from "@noble/hashes/sha3"; 4 | import {bytesToHex} from '@noble/hashes/utils'; 5 | 6 | 7 | const SEED = "mimc"; 8 | const NROUNDS = 91; 9 | 10 | export default async function buildMimc7() { 11 | const bn128 = await getCurveFromName("bn128", true); 12 | return new Mimc7(bn128.Fr); 13 | } 14 | 15 | 16 | class Mimc7 { 17 | constructor (F) { 18 | this.F = F; 19 | this.cts = this.getConstants(SEED, 91); 20 | } 21 | 22 | getIV(seed) { 23 | const F = this.F; 24 | if (typeof seed === "undefined") seed = SEED; 25 | const c = "0x" + bytesToHex(keccak_256(seed+"_iv")); 26 | const cn = Scalar.e(c); 27 | const iv = Scalar.mod(cn, F.p); 28 | return iv; 29 | }; 30 | 31 | getConstants(seed, nRounds) { 32 | const F = this.F; 33 | if (typeof seed === "undefined") seed = SEED; 34 | if (typeof nRounds === "undefined") nRounds = NROUNDS; 35 | const cts = new Array(nRounds); 36 | let c = keccak_256(SEED); 37 | for (let i=1; i { 10 | process.exit(0); 11 | }, (err) => { 12 | console.log(err.stack); 13 | console.log(err.message); 14 | process.exit(1); 15 | }); 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/mimc7_printconstants.js: -------------------------------------------------------------------------------- 1 | import buildMimc7 from "./mimc7.js"; 2 | 3 | async function run() { 4 | const mimc7 = await buildMimc7(); 5 | const nRounds = 91; 6 | let S = "[\n"; 7 | const cts = mimc7.getConstants(); 8 | for (let i=0; i { 19 | process.exit(0); 20 | }, (err) => { 21 | console.log(err.stack); 22 | console.log(err.message); 23 | process.exit(1); 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /src/mimc7_printcontract.js: -------------------------------------------------------------------------------- 1 | import {createCode} from "./mimc7_gencontract.js"; 2 | 3 | const SEED = "mimc"; 4 | 5 | let nRounds; 6 | if (typeof process.argv[2] != "undefined") { 7 | nRounds = parseInt(process.argv[2]); 8 | } else { 9 | nRounds = 91; 10 | } 11 | 12 | console.log(createCode(SEED, nRounds)); 13 | 14 | -------------------------------------------------------------------------------- /src/mimcsponge.js: -------------------------------------------------------------------------------- 1 | import { Scalar, getCurveFromName } from "ffjavascript"; 2 | import {bytesToHex} from "@noble/hashes/utils"; 3 | import {keccak_256} from "@noble/hashes/sha3"; 4 | 5 | const SEED = "mimcsponge"; 6 | const NROUNDS = 220; 7 | 8 | export default async function buildMimcSponge() { 9 | const bn128 = await getCurveFromName("bn128", true); 10 | return new MimcSponge(bn128.Fr); 11 | } 12 | 13 | class MimcSponge { 14 | constructor (F) { 15 | this.F = F; 16 | this.cts = this.getConstants(SEED, NROUNDS); 17 | } 18 | 19 | getIV (seed) { 20 | const F = this.F; 21 | if (typeof seed === "undefined") seed = SEED; 22 | const c = "0x" + bytesToHex(keccak_256(seed+"_iv")); 23 | const cn = Scalar.e(c); 24 | const iv = cn.mod(F.p); 25 | return iv; 26 | }; 27 | 28 | getConstants (seed, nRounds) { 29 | const F = this.F; 30 | if (typeof seed === "undefined") seed = SEED; 31 | if (typeof nRounds === "undefined") nRounds = NROUNDS; 32 | const cts = new Array(nRounds); 33 | let c = keccak_256(SEED); 34 | for (let i=1; i { 19 | process.exit(0); 20 | }, (err) => { 21 | console.log(err.stack); 22 | console.log(err.message); 23 | process.exit(1); 24 | }); -------------------------------------------------------------------------------- /src/mimcsponge_printcontract.js: -------------------------------------------------------------------------------- 1 | import {createCode} from "./mimcsponge_gencontract.js"; 2 | 3 | const SEED = "mimcsponge"; 4 | 5 | let nRounds; 6 | if (typeof process.argv[2] != "undefined") { 7 | nRounds = parseInt(process.argv[2]); 8 | } else { 9 | nRounds = 220; 10 | } 11 | 12 | console.log(createCode(SEED, nRounds)); 13 | 14 | -------------------------------------------------------------------------------- /src/pedersen_hash.js: -------------------------------------------------------------------------------- 1 | import { blake256 } from "@noble/hashes/blake1"; 2 | import { blake2b } from "@noble/hashes/blake2b"; 3 | import { Scalar } from "ffjavascript"; 4 | import buildBabyJub from "./babyjub.js"; 5 | 6 | const GENPOINT_PREFIX = "PedersenGenerator"; 7 | const windowSize = 4; 8 | const nWindowsPerSegment = 50; 9 | 10 | export default async function buildPedersenHash() { 11 | const babyJub = await buildBabyJub(); 12 | return new PedersenHash(babyJub); 13 | } 14 | 15 | class PedersenHash { 16 | 17 | constructor(babyJub) { 18 | this.babyJub = babyJub; 19 | this.bases = []; 20 | } 21 | 22 | baseHash(type, S) { 23 | if (type == "blake") { 24 | return Buffer.from(blake256(S)); 25 | } else if (type == "blake2b") { 26 | return Buffer.from(blake2b(Buffer.from(S))); 27 | } 28 | } 29 | 30 | hash(msg, options) { 31 | options = options || {}; 32 | options.baseHash = options.baseHash || "blake"; 33 | const babyJub = this.babyJub; 34 | const bitsPerSegment = windowSize*nWindowsPerSegment; 35 | const bits = this.buffer2bits(msg); 36 | 37 | const nSegments = Math.floor((bits.length - 1)/(windowSize*nWindowsPerSegment)) +1; 38 | 39 | let accP = [babyJub.F.zero,babyJub.F.one]; 40 | 41 | for (let s=0; s> 1; 118 | res[i*8+2] = (b & 0x04) >> 2; 119 | res[i*8+3] = (b & 0x08) >> 3; 120 | res[i*8+4] = (b & 0x10) >> 4; 121 | res[i*8+5] = (b & 0x20) >> 5; 122 | res[i*8+6] = (b & 0x40) >> 6; 123 | res[i*8+7] = (b & 0x80) >> 7; 124 | } 125 | return res; 126 | } 127 | } 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/pedersen_printbases.js: -------------------------------------------------------------------------------- 1 | import buildPedersenHash from "./pedersenhash.js"; 2 | 3 | async function run() { 4 | const pedersenHash = await buildPedersenHash(); 5 | 6 | let nBases; 7 | if (typeof process.argv[2] != "undefined") { 8 | nBases = parseInt(process.argv[2]); 9 | } else { 10 | nBases = 5; 11 | } 12 | 13 | let baseHash; 14 | if (typeof process.argv[3] != "undefined") { 15 | baseHash = process.argv[3]; 16 | } else { 17 | baseHash = "blake"; 18 | } 19 | 20 | for (let i=0; i < nBases; i++) { 21 | const p = pedersenHash.getBasePoint(i); 22 | console.log(`[${pedersenHash.babyJub.F.toString(p[0])},${pedersenHash.babyJub.F.toString(p[1])}]`); 23 | } 24 | 25 | 26 | } 27 | 28 | run().then(()=> { 29 | process.exit(0); 30 | }, (err) => { 31 | console.log(err.stack); 32 | console.log(err.message); 33 | process.exit(1); 34 | }); 35 | 36 | -------------------------------------------------------------------------------- /src/poseidon_gencontract.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jordi Baylina 2 | // License: LGPL-3.0+ 3 | // 4 | 5 | import Contract from "./evmasm.js"; 6 | import { utils } from "ffjavascript"; 7 | const { unstringifyBigInts } = utils; 8 | import {keccak_256} from "@noble/hashes/sha3"; 9 | import {bytesToHex} from '@noble/hashes/utils'; 10 | import poseidonConstants from "./poseidon_constants.js"; 11 | 12 | const { C:K, M } = unstringifyBigInts(poseidonConstants); 13 | 14 | const N_ROUNDS_F = 8; 15 | const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63]; 16 | 17 | function toHex256(a) { 18 | let S = a.toString(16); 19 | while (S.length < 64) S="0"+S; 20 | return "0x" + S; 21 | } 22 | 23 | export function createCode(nInputs) { 24 | 25 | if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8"); 26 | const t = nInputs + 1; 27 | const nRoundsF = N_ROUNDS_F; 28 | const nRoundsP = N_ROUNDS_P[t - 2]; 29 | 30 | const C = new Contract(); 31 | 32 | function saveM() { 33 | for (let i=0; i=nRoundsP+nRoundsF/2)) { 136 | for (let j=0; j { 27 | res[k] = unsringifyConstants(Fr, o[k]); 28 | }); 29 | return res; 30 | } else { 31 | return o; 32 | } 33 | } 34 | 35 | export default async function buildPoseidon() { 36 | const bn128 = await getCurveFromName("bn128", true); 37 | 38 | const F = bn128.Fr; 39 | 40 | const opt = unsringifyConstants(F, poseidonConstants); 41 | 42 | const N_ROUNDS_F = 8; 43 | const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; 44 | 45 | const pow5 = a => F.mul(a, F.square(F.square(a, a))); 46 | 47 | function poseidon(inputs, initState, nOut) { 48 | assert(inputs.length > 0); 49 | assert(inputs.length <= N_ROUNDS_P.length); 50 | 51 | if (initState) { 52 | initState = F.e(initState); 53 | } else { 54 | initState = F.zero; 55 | } 56 | nOut = nOut || 1; 57 | 58 | 59 | const t = inputs.length + 1; 60 | const nRoundsF = N_ROUNDS_F; 61 | const nRoundsP = N_ROUNDS_P[t - 2]; 62 | const C = opt.C[t-2]; 63 | const S = opt.S[t-2]; 64 | const M = opt.M[t-2]; 65 | const P = opt.P[t-2]; 66 | 67 | let state = [initState, ...inputs.map(a => F.e(a))]; 68 | 69 | state = state.map((a, i) => F.add(a, C[i])); 70 | 71 | for (let r = 0; r < nRoundsF/2-1; r++) { 72 | state = state.map(a => pow5(a)); 73 | state = state.map((a, i) => F.add(a, C[(r +1)* t +i])); 74 | state = state.map((_, i) => 75 | state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero) 76 | ); 77 | } 78 | state = state.map(a => pow5(a)); 79 | state = state.map((a, i) => F.add(a, C[(nRoundsF/2-1 +1)* t +i])); 80 | state = state.map((_, i) => 81 | state.reduce((acc, a, j) => F.add(acc, F.mul(P[j][i], a)), F.zero) 82 | ); 83 | for (let r = 0; r < nRoundsP; r++) { 84 | state[0] = pow5(state[0]); 85 | state[0] = F.add(state[0], C[(nRoundsF/2 +1)*t + r]); 86 | 87 | 88 | const s0 = state.reduce((acc, a, j) => { 89 | return F.add(acc, F.mul(S[(t*2-1)*r+j], a)); 90 | }, F.zero); 91 | for (let k=1; k pow5(a)); 98 | state = state.map((a, i) => F.add(a, C[ (nRoundsF/2 +1)*t + nRoundsP + r*t + i ])); 99 | state = state.map((_, i) => 100 | state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero) 101 | ); 102 | } 103 | state = state.map(a => pow5(a)); 104 | state = state.map((_, i) => 105 | state.reduce((acc, a, j) => F.add(acc, F.mul(M[j][i], a)), F.zero) 106 | ); 107 | 108 | if (nOut == 1) { 109 | return state[0] 110 | } else { 111 | return state.slice(0, nOut); 112 | } 113 | } 114 | 115 | poseidon.F = F; 116 | return poseidon; 117 | } 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/poseidon_printcontract.js: -------------------------------------------------------------------------------- 1 | import {createCode} from "./poseidon_gencontract.js"; 2 | 3 | if (process.argv.length != 3) { 4 | console.log("Usage: node poseidon_gencontract.js [numberOfInputs]"); 5 | process.exit(1); 6 | } 7 | 8 | const nInputs = Number(process.argv[2]); 9 | 10 | console.log(nInputs); 11 | 12 | console.log(createCode(nInputs)); 13 | 14 | -------------------------------------------------------------------------------- /src/poseidon_printmatrix.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const Poseidon = require("./poseidon.js"); 4 | 5 | const M = Poseidon.getMatrix(); 6 | 7 | let S = "[\n "; 8 | 9 | for (let i=0; i { 19 | res[k] = unsringifyConstants(Fr, o[k]); 20 | }); 21 | return res; 22 | } else { 23 | return o; 24 | } 25 | } 26 | 27 | export default async function buildPoseidon() { 28 | const bn128 = await getCurveFromName("bn128", true); 29 | 30 | const F = bn128.Fr; 31 | 32 | // Parameters are generated by a reference script https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage 33 | // Used like so: sage generate_parameters_grain.sage 1 0 254 2 8 56 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 34 | const {C, M} = unsringifyConstants(F, poseidonConstants); 35 | 36 | // Using recommended parameters from whitepaper https://eprint.iacr.org/2019/458.pdf (table 2, table 8) 37 | // Generated by https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/calc_round_numbers.py 38 | // And rounded up to nearest integer that divides by t 39 | const N_ROUNDS_F = 8; 40 | const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; 41 | 42 | const pow5 = a => F.mul(a, F.square(F.square(a, a))); 43 | 44 | function poseidon(inputs, initState, nOut) { 45 | assert(inputs.length > 0); 46 | assert(inputs.length <= N_ROUNDS_P.length); 47 | 48 | const t = inputs.length + 1; 49 | const nRoundsF = N_ROUNDS_F; 50 | const nRoundsP = N_ROUNDS_P[t - 2]; 51 | 52 | if (initState) { 53 | initState = F.e(initState); 54 | } else { 55 | initState = F.zero; 56 | } 57 | nOut = nOut || 1; 58 | 59 | let state = [initState, ...inputs.map(a => F.e(a))]; 60 | for (let r = 0; r < nRoundsF + nRoundsP; r++) { 61 | state = state.map((a, i) => F.add(a, C[t - 2][r * t + i])); 62 | 63 | if (r < nRoundsF / 2 || r >= nRoundsF / 2 + nRoundsP) { 64 | state = state.map(a => pow5(a)); 65 | } else { 66 | state[0] = pow5(state[0]); 67 | } 68 | 69 | state = state.map((_, i) => 70 | state.reduce((acc, a, j) => F.add(acc, F.mul(M[t - 2][i][j], a)), F.zero) 71 | ); 72 | } 73 | if (nOut == 1) { 74 | return state[0] 75 | } else { 76 | return state.slice(0, nOut); 77 | } 78 | } 79 | 80 | poseidon.F = F; 81 | return poseidon; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/poseidon_wasm.js: -------------------------------------------------------------------------------- 1 | 2 | import poseidonConstants from "./poseidon_constants_opt.js"; 3 | 4 | import { getCurveFromName, Scalar, F1Field } from "ffjavascript"; 5 | 6 | export async function buildPoseidon() { 7 | const bn128 = await getCurveFromName("bn128", true, buildPoseidonWasm); 8 | 9 | const F = bn128.Fr; 10 | 11 | const pState = bn128.tm.alloc(32); 12 | const pIn = bn128.tm.alloc(32*16); 13 | const pOut = bn128.tm.alloc(32*17); 14 | 15 | const poseidon = (arr, state, nOut) => { 16 | let buff; 17 | let n; 18 | if (Array.isArray(arr)) { 19 | n = arr.length; 20 | buff = new Uint8Array(n*32); 21 | for (let i=0; i16)) throw new Error("Invalid poseidon size"); 30 | 31 | if (typeof state == "undefined") { 32 | state = F.zero; 33 | } else { 34 | state = F.e(state); 35 | } 36 | bn128.tm.setBuff(pState, state); 37 | nOut = nOut || 1; 38 | 39 | bn128.tm.instance.exports.poseidon(pState, pIn, n, pOut, nOut); 40 | if (nOut == 1) { 41 | return bn128.tm.getBuff(pOut, 32); 42 | } else { 43 | const out = []; 44 | for (let i=0; i=0; level--) { 55 | let oldNode, newNode; 56 | const sibling = resFind.siblings[level]; 57 | if (keyBits[level]) { 58 | oldNode = [sibling, rtOld]; 59 | newNode = [sibling, rtNew]; 60 | } else { 61 | oldNode = [rtOld, sibling]; 62 | newNode = [rtNew, sibling]; 63 | } 64 | rtOld = this.hash0(oldNode[0], oldNode[1]); 65 | rtNew = this.hash0(newNode[0], newNode[1]); 66 | dels.push(rtOld); 67 | ins.push([rtNew, newNode]); 68 | } 69 | 70 | res.newRoot = rtNew; 71 | 72 | await this.db.multiDel(dels); 73 | await this.db.multiIns(ins); 74 | await this.db.setRoot(rtNew); 75 | this.root = rtNew; 76 | 77 | return res; 78 | } 79 | 80 | async delete(_key) { 81 | const F = this.F; 82 | const key = F.e(_key); 83 | 84 | const resFind = await this.find(key); 85 | if (!resFind.found) throw new Error("Key does not exists"); 86 | 87 | const res = { 88 | siblings: [], 89 | delKey: key, 90 | delValue: resFind.foundValue 91 | }; 92 | 93 | const dels = []; 94 | const ins = []; 95 | let rtOld = this.hash1(key, resFind.foundValue); 96 | let rtNew; 97 | dels.push(rtOld); 98 | 99 | let mixed; 100 | if (resFind.siblings.length > 0) { 101 | const record = await this.db.get(resFind.siblings[resFind.siblings.length - 1]); 102 | if ((record.length == 3)&&(F.eq(record[0], F.one))) { 103 | mixed = false; 104 | res.oldKey = record[1]; 105 | res.oldValue = record[2]; 106 | res.isOld0 = false; 107 | rtNew = resFind.siblings[resFind.siblings.length - 1]; 108 | } else if (record.length == 2) { 109 | mixed = true; 110 | res.oldKey = key; 111 | res.oldValue = F.zero; 112 | res.isOld0 = true; 113 | rtNew = F.zero; 114 | } else { 115 | throw new Error("Invalid node. Database corrupted"); 116 | } 117 | } else { 118 | rtNew = F.zero; 119 | res.oldKey = key; 120 | res.oldValue = F.zero; 121 | res.isOld0 = true; 122 | } 123 | 124 | const keyBits = this._splitBits(key); 125 | 126 | for (let level = resFind.siblings.length-1; level >=0; level--) { 127 | let newSibling = resFind.siblings[level]; 128 | if ((level == resFind.siblings.length-1)&&(!res.isOld0)) { 129 | newSibling = F.zero; 130 | } 131 | const oldSibling = resFind.siblings[level]; 132 | if (keyBits[level]) { 133 | rtOld = this.hash0(oldSibling, rtOld); 134 | } else { 135 | rtOld = this.hash0(rtOld, oldSibling); 136 | } 137 | dels.push(rtOld); 138 | if (!F.isZero(newSibling)) { 139 | mixed = true; 140 | } 141 | 142 | if (mixed) { 143 | res.siblings.unshift(resFind.siblings[level]); 144 | let newNode; 145 | if (keyBits[level]) { 146 | newNode = [newSibling, rtNew]; 147 | } else { 148 | newNode = [rtNew, newSibling]; 149 | } 150 | rtNew = this.hash0(newNode[0], newNode[1]); 151 | ins.push([rtNew, newNode]); 152 | } 153 | } 154 | 155 | await this.db.multiIns(ins); 156 | await this.db.setRoot(rtNew); 157 | this.root = rtNew; 158 | await this.db.multiDel(dels); 159 | 160 | res.newRoot = rtNew; 161 | res.oldRoot = rtOld; 162 | 163 | return res; 164 | } 165 | 166 | async insert(_key, _value) { 167 | const F = this.F; 168 | const key = F.e(_key); 169 | const value = F.e(_value); 170 | let addedOne = false; 171 | const res = {}; 172 | res.oldRoot = this.root; 173 | const newKeyBits = this._splitBits(key); 174 | 175 | let rtOld; 176 | 177 | const resFind = await this.find(key); 178 | 179 | if (resFind.found) throw new Error("Key already exists"); 180 | 181 | res.siblings = resFind.siblings; 182 | let mixed; 183 | 184 | if (!resFind.isOld0) { 185 | const oldKeyits = this._splitBits(resFind.notFoundKey); 186 | for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) { 187 | res.siblings.push(F.zero); 188 | } 189 | rtOld = this.hash1(resFind.notFoundKey, resFind.notFoundValue); 190 | res.siblings.push(rtOld); 191 | addedOne = true; 192 | mixed = false; 193 | } else if (res.siblings.length >0) { 194 | mixed = true; 195 | rtOld = F.zero; 196 | } 197 | 198 | const inserts = []; 199 | const dels = []; 200 | 201 | let rt = this.hash1(key, value); 202 | inserts.push([rt,[1, key, value]] ); 203 | 204 | for (let i=res.siblings.length-1; i>=0; i--) { 205 | if ((i0) && (F.isZero(res.siblings[res.siblings.length-1]))) { 232 | res.siblings.pop(); 233 | } 234 | res.oldKey = resFind.notFoundKey; 235 | res.oldValue = resFind.notFoundValue; 236 | res.newRoot = rt; 237 | res.isOld0 = resFind.isOld0; 238 | 239 | 240 | await this.db.multiIns(inserts); 241 | await this.db.setRoot(rt); 242 | this.root = rt; 243 | await this.db.multiDel(dels); 244 | 245 | return res; 246 | } 247 | 248 | async find(_key) { 249 | const key = this.F.e(_key); 250 | const keyBits = this._splitBits(key); 251 | return await this._find(key, keyBits, this.root, 0); 252 | } 253 | 254 | async _find(key, keyBits, root, level) { 255 | const F = this.F; 256 | if (typeof root === "undefined") root = this.root; 257 | 258 | let res; 259 | if (F.isZero(root)) { 260 | res = { 261 | found: false, 262 | siblings: [], 263 | notFoundKey: key, 264 | notFoundValue: F.zero, 265 | isOld0: true 266 | }; 267 | return res; 268 | } 269 | 270 | const record = await this.db.get(root); 271 | 272 | if ((record.length==3)&&(F.eq(record[0],F.one))) { 273 | if (F.eq(record[1],key)) { 274 | res = { 275 | found: true, 276 | siblings: [], 277 | foundValue: record[2], 278 | isOld0: false 279 | }; 280 | } else { 281 | res = { 282 | found: false, 283 | siblings: [], 284 | notFoundKey: record[1], 285 | notFoundValue: record[2], 286 | isOld0: false 287 | }; 288 | } 289 | } else { 290 | if (keyBits[level] == 0) { 291 | res = await this._find(key, keyBits, record[0], level+1); 292 | res.siblings.unshift(record[1]); 293 | } else { 294 | res = await this._find(key, keyBits, record[1], level+1); 295 | res.siblings.unshift(record[0]); 296 | } 297 | } 298 | return res; 299 | } 300 | } 301 | 302 | 303 | export async function newMemEmptyTrie() { 304 | const {hash0, hash1,F} = await getHashes(); 305 | const db = new SMTMemDB(F); 306 | const rt = await db.getRoot(); 307 | const smt = new SMT(db, rt, hash0, hash1, F); 308 | return smt; 309 | } 310 | -------------------------------------------------------------------------------- /src/smt_hashes_mimc.js: -------------------------------------------------------------------------------- 1 | 2 | import buildMimc7 from "./mimc7.js"; 3 | 4 | export default async function getHashes() { 5 | const mimc7 = await buildMimc7(); 6 | return { 7 | hash0: function (left, right) { 8 | return mimc7.hash(left, right); 9 | }, 10 | hash1: function(key, value) { 11 | return mimc7.multiHash([key, value], F.one); 12 | }, 13 | F: mimc7.F 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/smt_hashes_poseidon.js: -------------------------------------------------------------------------------- 1 | import { buildPoseidon } from "./poseidon_wasm.js"; 2 | import { getCurveFromName } from "ffjavascript"; 3 | 4 | 5 | export default async function getHashes() { 6 | const bn128 = await getCurveFromName("bn128", true); 7 | const poseidon = await buildPoseidon(); 8 | return { 9 | hash0: function (left, right) { 10 | return poseidon([left, right]); 11 | }, 12 | hash1: function(key, value) { 13 | return poseidon([key, value, bn128.Fr.one]); 14 | }, 15 | F: bn128.Fr 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/smt_memdb.js: -------------------------------------------------------------------------------- 1 | export default class SMTMemDb { 2 | constructor(F) { 3 | this.nodes = {}; 4 | this.root = F.zero; 5 | this.F = F; 6 | } 7 | 8 | async getRoot() { 9 | return this.root; 10 | } 11 | 12 | _key2str(k) { 13 | const F = this.F; 14 | const keyS = this.F.toString(k); 15 | return keyS; 16 | } 17 | 18 | _normalize(n) { 19 | const F = this.F; 20 | for (let i=0; i 5 | bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); 6 | 7 | console.log(toHexString(blake256(''))); 8 | console.log(toHexString(blake2b(''))); 9 | -------------------------------------------------------------------------------- /test/babyjub.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | import buildBabyjub from "../src/babyjub.js"; 3 | import { Scalar } from "ffjavascript"; 4 | 5 | const assert = chai.assert; 6 | 7 | // const bigInt = require("big-integer"); 8 | 9 | function buff2hex(buff) { 10 | function i2hex(i) { 11 | return ('0' + i.toString(16)).slice(-2); 12 | } 13 | return Array.from(buff).map(i2hex).join(''); 14 | } 15 | 16 | describe("Baby Jub js test", function () { 17 | let babyjub; 18 | this.timeout(100000); 19 | 20 | before(async () => { 21 | babyjub = await buildBabyjub(); 22 | }); 23 | 24 | it("Should add point (0,1) and (0,1)", () => { 25 | 26 | const p1 = [ 27 | babyjub.F.e(0), 28 | babyjub.F.e(1)]; 29 | const p2 = [ 30 | babyjub.F.e(0), 31 | babyjub.F.e(1) 32 | ]; 33 | 34 | const out = babyjub.addPoint(p1, p2); 35 | assert(babyjub.F.eq(out[0], babyjub.F.zero)); 36 | assert(babyjub.F.eq(out[1], babyjub.F.one)); 37 | }); 38 | 39 | it("Should base be 8*generator", () => { 40 | let res; 41 | res = babyjub.addPoint(babyjub.Generator, babyjub.Generator); 42 | res = babyjub.addPoint(res, res); 43 | res = babyjub.addPoint(res, res); 44 | 45 | assert(babyjub.F.eq(res[0], babyjub.Base8[0])); 46 | assert(babyjub.F.eq(res[1], babyjub.Base8[1])); 47 | }); 48 | 49 | it("Should add 2 same numbers", () => { 50 | 51 | const p1 = [ 52 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 53 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 54 | ]; 55 | const p2 = [ 56 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 57 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 58 | ]; 59 | 60 | const out = babyjub.addPoint(p1, p2); 61 | assert(babyjub.F.eq(out[0], babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"))); 62 | assert(babyjub.F.eq(out[1], babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"))); 63 | }); 64 | 65 | it("Should add 2 different numbers", () => { 66 | 67 | const p1 = [ 68 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 69 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 70 | ]; 71 | const p2 = [ 72 | babyjub.F.e("16540640123574156134436876038791482806971768689494387082833631921987005038935"), 73 | babyjub.F.e("20819045374670962167435360035096875258406992893633759881276124905556507972311"), 74 | ]; 75 | 76 | const out = babyjub.addPoint(p1, p2); 77 | assert(babyjub.F.eq(out[0], babyjub.F.e("7916061937171219682591368294088513039687205273691143098332585753343424131937"))); 78 | assert(babyjub.F.eq(out[1], babyjub.F.e("14035240266687799601661095864649209771790948434046947201833777492504781204499"))); 79 | 80 | }); 81 | 82 | it("should mulPointEscalar 0", () => { 83 | const p = [ 84 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 85 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 86 | ]; 87 | 88 | const r = babyjub.mulPointEscalar(p, 3); 89 | let r2 = babyjub.addPoint(p, p); 90 | r2 = babyjub.addPoint(r2, p); 91 | assert(babyjub.F.eq(r2[0], r[0])); 92 | assert(babyjub.F.eq(r2[1], r[1])); 93 | assert(babyjub.F.eq(r[0], babyjub.F.e("19372461775513343691590086534037741906533799473648040012278229434133483800898"))); 94 | assert(babyjub.F.eq(r[1], babyjub.F.e("9458658722007214007257525444427903161243386465067105737478306991484593958249"))); 95 | }); 96 | 97 | it("should mulPointEscalar 1", () => { 98 | const p = [ 99 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 100 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 101 | ]; 102 | 103 | const r = babyjub.mulPointEscalar(p, Scalar.fromString("14035240266687799601661095864649209771790948434046947201833777492504781204499")); 104 | assert(babyjub.F.eq(r[0], babyjub.F.e("17070357974431721403481313912716834497662307308519659060910483826664480189605"))); 105 | assert(babyjub.F.eq(r[1], babyjub.F.e("4014745322800118607127020275658861516666525056516280575712425373174125159339"))); 106 | }); 107 | 108 | it("should mulPointEscalar 2", () => { 109 | const p = [ 110 | babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"), 111 | babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"), 112 | ]; 113 | 114 | const r = babyjub.mulPointEscalar(p, Scalar.fromString("20819045374670962167435360035096875258406992893633759881276124905556507972311")); 115 | assert(babyjub.F.eq(r[0], babyjub.F.e("13563888653650925984868671744672725781658357821216877865297235725727006259983"))); 116 | assert(babyjub.F.eq(r[1], babyjub.F.e("8442587202676550862664528699803615547505326611544120184665036919364004251662"))); 117 | }); 118 | 119 | it("should inCurve 1", () => { 120 | const p = [ 121 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 122 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 123 | ]; 124 | assert(babyjub.inCurve(p)); 125 | }); 126 | 127 | it("should inCurve 2", () => { 128 | const p = [ 129 | babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"), 130 | babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"), 131 | ]; 132 | assert(babyjub.inCurve(p)); 133 | }); 134 | 135 | it("should inSubgroup 1", () => { 136 | const p = [ 137 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 138 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 139 | ]; 140 | assert(babyjub.inSubgroup(p)); 141 | }); 142 | 143 | it("should inSubgroup 2", () => { 144 | const p = [ 145 | babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"), 146 | babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"), 147 | ]; 148 | assert(babyjub.inSubgroup(p)); 149 | }); 150 | 151 | it("should packPoint - unpackPoint 1", () => { 152 | const p = [ 153 | babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"), 154 | babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"), 155 | ]; 156 | const buf = babyjub.packPoint(p); 157 | assert.equal(buff2hex(buf), "53b81ed5bffe9545b54016234682e7b2f699bd42a5e9eae27ff4051bc698ce85"); 158 | const p2 = babyjub.unpackPoint(buf); 159 | assert(babyjub.F.eq(p2[0], babyjub.F.e("17777552123799933955779906779655732241715742912184938656739573121738514868268"))); 160 | assert(babyjub.F.eq(p2[1], babyjub.F.e("2626589144620713026669568689430873010625803728049924121243784502389097019475"))); 161 | }); 162 | 163 | it("should packPoint - unpackPoint 2", () => { 164 | const p = [ 165 | babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"), 166 | babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"), 167 | ]; 168 | const buf = babyjub.packPoint(p); 169 | assert.equal(buff2hex(buf), "e114eb17eddf794f063a68fecac515e3620e131976108555735c8b0773929709"); 170 | const p2 = babyjub.unpackPoint(buf); 171 | assert(babyjub.F.eq(p2[0], babyjub.F.e("6890855772600357754907169075114257697580319025794532037257385534741338397365"))); 172 | assert(babyjub.F.eq(p2[1], babyjub.F.e("4338620300185947561074059802482547481416142213883829469920100239455078257889"))); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /test/eddsa.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | import { Scalar } from "ffjavascript"; 3 | 4 | const assert = chai.assert; 5 | 6 | import buildEddsa from "../src/eddsa.js"; 7 | 8 | const fromHexString = hexString => 9 | new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); 10 | 11 | const toHexString = bytes => 12 | bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); 13 | 14 | 15 | describe("EdDSA js test", function () { 16 | 17 | let eddsa; 18 | this.timeout(100000); 19 | 20 | before(async () => { 21 | eddsa = await buildEddsa(); 22 | }); 23 | 24 | 25 | it("Sign (using Pedersen) a single 10 bytes from 0 to 9", () => { 26 | const F = eddsa.babyJub.F; 27 | const msgBuf = fromHexString("00010203040506070809"); 28 | 29 | const prvKey = fromHexString("0001020304050607080900010203040506070809000102030405060708090001"); 30 | 31 | const pubKey = eddsa.prv2pub(prvKey); 32 | 33 | assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618"))); 34 | assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820"))); 35 | 36 | const pPubKey = eddsa.babyJub.packPoint(pubKey); 37 | 38 | const signature = eddsa.signPedersen(prvKey, msgBuf); 39 | // console.log(F.toString(signature.R8[0])); 40 | assert(F.eq(signature.R8[0], F.e("21253904451576600568378459528205653033385900307028841334532552830614710476912"))); 41 | // console.log(F.toString(signature.R8[1])); 42 | assert(F.eq(signature.R8[1], F.e("20125634407542493427571099944365246191501563803226486072348038614369379124499"))); 43 | // console.log(Scalar.toString(signature.S)); 44 | assert(Scalar.eq(signature.S, Scalar.e("2129243915978267980511515511350111723623685317644064470882297086073041379651"))); 45 | 46 | const pSignature = eddsa.packSignature(signature); 47 | 48 | // console.log(toHexString(pSignature)); 49 | assert.equal(toHexString(pSignature), ""+ 50 | "138501d9e734e73f485269bcdc29a9ef2da3fac2f5c9653761d0364f95b47eac"+ 51 | "43e1a02b56ff3dacfdac040f3e8c2023dc259ba3f6880ca8ad246b4bfe1bb504"); 52 | 53 | const uSignature = eddsa.unpackSignature(pSignature); 54 | assert(eddsa.verifyPedersen(msgBuf, uSignature, pubKey)); 55 | 56 | }); 57 | 58 | it("Sign (using Mimc7) a single 10 bytes from 0 to 9", () => { 59 | const F = eddsa.babyJub.F; 60 | const msgBuf = fromHexString("000102030405060708090000"); 61 | 62 | const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0)); 63 | 64 | // const prvKey = crypto.randomBytes(32); 65 | 66 | const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); 67 | 68 | const pubKey = eddsa.prv2pub(prvKey); 69 | 70 | assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618"))); 71 | assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820"))); 72 | 73 | const pPubKey = eddsa.babyJub.packPoint(pubKey); 74 | 75 | const signature = eddsa.signMiMC(prvKey, msg); 76 | // console.log(F.toString(signature.R8[0])); 77 | assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733"))); 78 | // console.log(F.toString(signature.R8[1])); 79 | assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959"))); 80 | // console.log(Scalar.toString(signature.S)); 81 | assert(Scalar.eq(signature.S, Scalar.e("2523202440825208709475937830811065542425109372212752003460238913256192595070"))); 82 | 83 | const pSignature = eddsa.packSignature(signature); 84 | 85 | // console.log(toHexString(pSignature)); 86 | assert.equal(toHexString(pSignature), ""+ 87 | "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ 88 | "7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405"); 89 | 90 | const uSignature = eddsa.unpackSignature(pSignature); 91 | assert(eddsa.verifyMiMC(msg, uSignature, pubKey)); 92 | 93 | }); 94 | 95 | it("Sign (using Poseidon) a single 10 bytes from 0 to 9", () => { 96 | const F = eddsa.babyJub.F; 97 | const msgBuf = fromHexString("000102030405060708090000"); 98 | 99 | const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0)); 100 | 101 | // const prvKey = crypto.randomBytes(32); 102 | 103 | const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); 104 | 105 | const pubKey = eddsa.prv2pub(prvKey); 106 | 107 | assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618"))); 108 | assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820"))); 109 | 110 | const pPubKey = eddsa.babyJub.packPoint(pubKey); 111 | 112 | const signature = eddsa.signPoseidon(prvKey, msg); 113 | // console.log(F.toString(signature.R8[0])); 114 | assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733"))); 115 | // console.log(F.toString(signature.R8[1])); 116 | assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959"))); 117 | // console.log(Scalar.toString(signature.S)); 118 | assert(Scalar.eq(signature.S, Scalar.e("1672775540645840396591609181675628451599263765380031905495115170613215233181"))); 119 | 120 | const pSignature = eddsa.packSignature(signature); 121 | 122 | // console.log(toHexString(pSignature)); 123 | assert.equal(toHexString(pSignature), ""+ 124 | "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ 125 | "9d043ece562a8f82bfc0adb640c0107a7d3a27c1c7c1a6179a0da73de5c1b203"); 126 | 127 | const uSignature = eddsa.unpackSignature(pSignature); 128 | assert(eddsa.verifyPoseidon(msg, uSignature, pubKey)); 129 | }); 130 | 131 | 132 | it("Sign (using mimcsponge) a single 10 bytes from 0 to 9", () => { 133 | const F = eddsa.babyJub.F; 134 | const msgBuf = fromHexString("000102030405060708090000"); 135 | 136 | const msg = eddsa.babyJub.F.e(Scalar.fromRprLE(msgBuf, 0)); 137 | 138 | // const prvKey = crypto.randomBytes(32); 139 | 140 | const prvKey = Buffer.from("0001020304050607080900010203040506070809000102030405060708090001", "hex"); 141 | 142 | const pubKey = eddsa.prv2pub(prvKey); 143 | 144 | assert(F.eq(pubKey[0], F.e("13277427435165878497778222415993513565335242147425444199013288855685581939618"))); 145 | assert(F.eq(pubKey[1], F.e("13622229784656158136036771217484571176836296686641868549125388198837476602820"))); 146 | 147 | const pPubKey = eddsa.babyJub.packPoint(pubKey); 148 | 149 | const signature = eddsa.signMiMCSponge(prvKey, msg); 150 | // console.log(F.toString(signature.R8[0])); 151 | assert(F.eq(signature.R8[0], F.e("11384336176656855268977457483345535180380036354188103142384839473266348197733"))); 152 | // console.log(F.toString(signature.R8[1])); 153 | assert(F.eq(signature.R8[1], F.e("15383486972088797283337779941324724402501462225528836549661220478783371668959"))); 154 | // console.log(Scalar.toString(signature.S)); 155 | assert(Scalar.eq(signature.S, Scalar.e("1868336918738674306327358602987493427631678603535639134028485964115448322340"))); 156 | 157 | const pSignature = eddsa.packSignature(signature); 158 | 159 | // console.log(toHexString(pSignature)); 160 | assert.equal(toHexString(pSignature), ""+ 161 | "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+ 162 | "24599218a1c2e5290bf58b2eec37bfec1395179ed5e817f10f86c9e7f3702104"); 163 | 164 | const uSignature = eddsa.unpackSignature(pSignature); 165 | assert(eddsa.verifyMiMCSponge(msg, uSignature, pubKey)); 166 | }); 167 | }); 168 | -------------------------------------------------------------------------------- /test/mimc7.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | const assert = chai.assert; 3 | 4 | import buildMimc7 from "../src/mimc7.js"; 5 | 6 | describe("Mimc7 test", function () { 7 | let mimc7; 8 | this.timeout(1000000); 9 | 10 | before(async () => { 11 | mimc7 = await buildMimc7(); 12 | }); 13 | 14 | it("Should check multihash reference 2", async () => { 15 | const res2 = mimc7.multiHash([1,2]); 16 | assert(mimc7.F.eq(mimc7.F.e("0xb91ebbd35d7448ecc13e75a7ceb1ce5bbe428090acfae0da2c3867a874ce6ea"), res2)); 17 | }); 18 | it("Should check multihash reference 4", async () => { 19 | const res2 = mimc7.multiHash([1,2,3,4]); 20 | assert(mimc7.F.eq(mimc7.F.e("0x19ce9298d9e8ada63b2fb30c938d25cc9116aca2795f8b90fd9530687b4ad075"), res2)); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/mimc7contract.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | import {createCode, abi} from "../src/mimc7_gencontract.js"; 3 | import pkg from 'hardhat'; 4 | const { ethers } = pkg; 5 | 6 | import buildMimc7 from "../src/mimc7.js"; 7 | 8 | 9 | const assert = chai.assert; 10 | const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); }; 11 | 12 | const SEED = "mimc"; 13 | 14 | describe("MiMC Smart contract test", function () { 15 | let mimc; 16 | let mimcJS; 17 | let account; 18 | this.timeout(100000); 19 | 20 | before(async () => { 21 | [account] = await ethers.getSigners(); 22 | mimcJS = await buildMimc7(); 23 | }); 24 | 25 | it("Should deploy the contract", async () => { 26 | 27 | const code = createCode(SEED, 91); 28 | const C = new ethers.ContractFactory( 29 | abi, 30 | code, 31 | account 32 | ); 33 | 34 | mimc = await C.deploy(); 35 | 36 | const codeHash = ethers.keccak256(code); 37 | assert.equal( 38 | codeHash, 39 | "0xd0b844e2fa96af5b59f8ec7c7a060936d7cfbe11ad3b2f1629333d164c2b3ab4" 40 | ); 41 | 42 | }); 43 | 44 | it("Should calculate the mimc correctly", async () => { 45 | 46 | const res = await mimc["MiMCpe7"](1,2); 47 | 48 | // console.log("Cir: " + bigInt(res.toString(16)).toString(16)); 49 | 50 | const res2 = mimcJS.hash(1,2); 51 | // console.log("Ref: " + bigInt(res2).toString(16)); 52 | 53 | assert.equal(res.toString(), mimcJS.F.toString(res2)); 54 | 55 | }); 56 | 57 | }); 58 | 59 | -------------------------------------------------------------------------------- /test/mimcsponge.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | const assert = chai.assert; 3 | 4 | import buildMimcSponge from "../src/mimcsponge.js"; 5 | 6 | describe("Mimc Sponge test", function () { 7 | let mimcSponge; 8 | 9 | before(async () => { 10 | mimcSponge = await buildMimcSponge(); 11 | }); 12 | 13 | it("Should check multihash reference 2", async () => { 14 | const res2 = mimcSponge.multiHash([1,2]); 15 | // console.log(mimcSponge.F.toString(res2,16)); 16 | assert(mimcSponge.F.eq(mimcSponge.F.e("0x2bcea035a1251603f1ceaf73cd4ae89427c47075bb8e3a944039ff1e3d6d2a6f"), res2)); 17 | }); 18 | it("Should check multihash reference 4", async () => { 19 | const res2 = mimcSponge.multiHash([1,2,3,4]); 20 | // console.log(mimcSponge.F.toString(res2,16)); 21 | assert(mimcSponge.F.eq(mimcSponge.F.e("0x3e86bdc4eac70bd601473c53d8233b145fe8fd8bf6ef25f0b217a1da305665c"), res2)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/mimcspongecontract.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | import {createCode, abi} from "../src/mimcsponge_gencontract.js"; 3 | import pkg from 'hardhat'; 4 | const { ethers } = pkg; 5 | 6 | import buildMimcSponge from "../src/mimcsponge.js"; 7 | 8 | 9 | const assert = chai.assert; 10 | const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); }; 11 | 12 | const SEED = "mimcsponge"; 13 | 14 | describe("MiMC Sponge Smart contract test", () => { 15 | let mimc; 16 | let mimcJS; 17 | let account; 18 | 19 | before(async () => { 20 | [account] = await ethers.getSigners(); 21 | mimcJS = await buildMimcSponge(); 22 | }); 23 | 24 | it("Should deploy the contract", async () => { 25 | 26 | const code = createCode(SEED, 220); 27 | const C = new ethers.ContractFactory( 28 | abi, 29 | code, 30 | account 31 | ); 32 | 33 | mimc = await C.deploy(); 34 | 35 | const codeHash = ethers.keccak256(code); 36 | assert.equal( 37 | codeHash, 38 | "0x08d93c30978b3338cd0c82d76edbda569d4dc71a56de48598bb8ba763669fe30" 39 | ); 40 | }); 41 | 42 | it("Should calculate the mimc correctly", async () => { 43 | 44 | const res = await mimc["MiMCSponge"](1,2, 3); 45 | 46 | // console.log("Cir: " + bigInt(res.toString(16)).toString(16)); 47 | 48 | const res2 = mimcJS.hash(1,2, 3); 49 | // console.log("Ref: " + bigInt(res2).toString(16)); 50 | 51 | assert.equal(res.xL.toString(), mimcJS.F.toString(res2.xL)); 52 | assert.equal(res.xR.toString(), mimcJS.F.toString(res2.xR)); 53 | 54 | }); 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /test/pedersenhash.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | const assert = chai.assert; 3 | 4 | import buildPedersenHash from "../src/pedersen_hash.js"; 5 | 6 | function buff2hex(buff) { 7 | function i2hex(i) { 8 | return ('0' + i.toString(16)).slice(-2); 9 | } 10 | return Array.from(buff).map(i2hex).join(''); 11 | } 12 | 13 | describe("Pedersen Hash test", function () { 14 | let pedersen; 15 | 16 | before(async () => { 17 | pedersen = await buildPedersenHash(); 18 | }); 19 | 20 | it("Should check multihash reference 2", async () => { 21 | const msg = (new TextEncoder()).encode("Hello"); 22 | 23 | const res2 = pedersen.hash(msg); 24 | // console.log(buff2hex(res2)); 25 | assert.equal(buff2hex(res2), "0e90d7d613ab8b5ea7f4f8bc537db6bb0fa2e5e97bbac1c1f609ef9e6a35fd8b"); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/poseidon.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | const assert = chai.assert; 3 | 4 | import buildPoseidonOpt from "../src/poseidon_opt.js"; 5 | import {buildPoseidon as buildPoseidonWasm } from "../src/poseidon_wasm.js"; 6 | import buildPoseidonReference from "../src/poseidon_reference.js"; 7 | 8 | describe("Poseidon test", function () { 9 | let poseidonOpt; 10 | let poseidonReference; 11 | let poseidonWasm; 12 | this.timeout(10000000); 13 | 14 | before(async () => { 15 | poseidonOpt = await buildPoseidonOpt(); 16 | poseidonReference = await buildPoseidonReference(); 17 | poseidonWasm = await buildPoseidonWasm(); 18 | }); 19 | 20 | it("Should check constrain reference implementation poseidonperm_x5_254_3", async () => { 21 | const res1 = poseidonReference([1, 2]); 22 | assert(poseidonReference.F.eq(poseidonReference.F.e("0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a"), res1)); 23 | 24 | const res2 = poseidonOpt([1,2]); 25 | assert(poseidonOpt.F.eq(poseidonOpt.F.e("0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a"), res2)); 26 | 27 | const res3 = poseidonWasm([1,2]); 28 | assert(poseidonWasm.F.eq(poseidonWasm.F.e("0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a"), res3)); 29 | }); 30 | it("Should check constrain reference implementation poseidonperm_x5_254_5", async () => { 31 | const res1 = poseidonReference([1,2,3,4]); 32 | assert(poseidonReference.F.eq(poseidonReference.F.e("0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465"), res1)); 33 | 34 | const res2 = poseidonOpt([1,2,3,4]); 35 | assert(poseidonOpt.F.eq(poseidonOpt.F.e("0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465"), res2)); 36 | 37 | const res3 = poseidonWasm([1,2,3,4]); 38 | assert(poseidonWasm.F.eq(poseidonWasm.F.e("0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465"), res3)); 39 | }); 40 | it("Should check state and nOuts", async () => { 41 | const F = poseidonWasm.F; 42 | const inp = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; 43 | const st = 0; 44 | const nOut = 17; 45 | const res1 = poseidonReference(inp, st, nOut); 46 | const res2 = poseidonOpt(inp, st, nOut); 47 | const res3 = poseidonWasm(inp, st, nOut); 48 | for (let i=0; i { 54 | const testvectors = [ 55 | { 56 | inputs: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], 57 | expected: "9989051620750914585850546081941653841776809718687451684622678807385399211877" 58 | }, 59 | { 60 | inputs: [1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0], 61 | expected: "11882816200654282475720830292386643970958445617880627439994635298904836126497" 62 | }, 63 | ]; 64 | 65 | for (let i=0; i { 78 | 79 | const testvectors = [ 80 | { 81 | inputs: [1,2,3,4,5,6], 82 | initState: 0, 83 | expected: "20400040500897583745843009878988256314335038853985262692600694741116813247201" 84 | }, 85 | { 86 | inputs: [1,2,3,4], 87 | initState: 7, 88 | expected: "1569211601569591254857354699102545060324851338714426496554851741114291465006" 89 | }, 90 | { 91 | inputs: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], 92 | initState: 17, 93 | expected: "7865037705064445207187340054656830232157001572238023180016026650118519857086" 94 | }, 95 | ]; 96 | 97 | for (let i=0; i { 110 | 111 | const testvectors = [ 112 | { 113 | inputs: [1], 114 | initState: 0, 115 | nOut: 1, 116 | expected: [ 117 | "18586133768512220936620570745912940619677854269274689475585506675881198879027" 118 | ] 119 | }, 120 | { 121 | inputs: [1, 2], 122 | initState: 0, 123 | nOut: 2, 124 | expected: [ 125 | "7853200120776062878684798364095072458815029376092732009249414926327459813530", 126 | "7142104613055408817911962100316808866448378443474503659992478482890339429929" 127 | ] 128 | }, 129 | { 130 | inputs: [1, 2, 0, 0, 0], 131 | initState: 0, 132 | nOut: 3, 133 | expected: [ 134 | "1018317224307729531995786483840663576608797660851238720571059489595066344487", 135 | "1268987460374965117190107941866588409937190018195924754936306024116268626868", 136 | "8783366202813713093021184624438037804022412226788318946130389248546914776762" 137 | ] 138 | }, 139 | { 140 | inputs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], 141 | initState: 0, 142 | nOut: 11, 143 | expected: [ 144 | "9989051620750914585850546081941653841776809718687451684622678807385399211877", 145 | "8319791455060392555425392842391403897548969645190976863995973180967774875286", 146 | "21636406227810893698117978732800647815305553312233448361627674958309476058692", 147 | "5858261170370825589990804751061473291946977191299454947182890419569833191564", 148 | "9379453522659079974536893534601645512603628658741037060370899250203068088821", 149 | "473570682425071423656832074606161521036781375454126861176650950315985887926", 150 | "6579803930273263668667567320853266118141819373699554146671374489258288008348", 151 | "19782381913414087710766737863494215505205430771941455097533197858199467016164", 152 | "16057750626779488870446366989248320873718232843994532204040561017822304578116", 153 | "18984357576272539606133217260692170661113104846539835604742079547853774113837", 154 | "6999414602732066348339779277600222355871064730107676749892229157577448591106" 155 | ] 156 | }, 157 | { 158 | inputs: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], 159 | initState: 17, 160 | nOut: 16, 161 | expected: [ 162 | "7865037705064445207187340054656830232157001572238023180016026650118519857086", 163 | "9292383997006336854008325030029058442489692927472584277596649832441082093099", 164 | "21700625464938935909463291795162623951575229166945244593449711331894544619498", 165 | "1749964961100464837642084889776091157070407086051097880220367435814831060919", 166 | "14926884742736943105557530036865339747160219875259470496706517357951967126770", 167 | "2039691552066237153485547245250552033884196017621501609319319339955236135906", 168 | "15632370980418377873678240526508190824831030254352022226082241110936555130543", 169 | "12415717486933552680955550946925876656737401305417786097937904386023163034597", 170 | "19518791782429957526810500613963817986723905805167983704284231822835104039583", 171 | "3946357499058599914103088366834769377007694643795968939540941315474973940815", 172 | "5618081863604788554613937982328324792980580854673130938690864738082655170455", 173 | "9119013501536010391475078939286676645280972023937320238963975266387024327421", 174 | "8377736769906336164136520530350338558030826788688113957410934156526990238336", 175 | "15295058061474937220002017533551270394267030149562824985607747654793981405060", 176 | "3767094797637425204201844274463024412131937665868967358407323347727519975724", 177 | "11046361685833871233801453306150294246339755171874771935347992312124050338976" 178 | ] 179 | }, 180 | 181 | ]; 182 | 183 | for (let i = 0; i < testvectors.length; i++) { 184 | const res1 = poseidonReference(testvectors[i].inputs, testvectors[i].initState, testvectors[i].nOut); 185 | if (testvectors[i].nOut === 1) { 186 | assert(poseidonReference.F.eq(poseidonReference.F.e(testvectors[i].expected[0]), res1)); 187 | } else { 188 | for (let j = 0; j < testvectors[i].nOut; j++) { 189 | assert(poseidonReference.F.eq(poseidonReference.F.e(testvectors[i].expected[j]), res1[j])); 190 | } 191 | } 192 | 193 | const res2 = poseidonOpt(testvectors[i].inputs, testvectors[i].initState, testvectors[i].nOut); 194 | if (testvectors[i].nOut === 1) { 195 | assert(poseidonOpt.F.eq(poseidonOpt.F.e(testvectors[i].expected[0]), res2)); 196 | } else { 197 | for (let j = 0; j < testvectors[i].nOut; j++) { 198 | assert(poseidonOpt.F.eq(poseidonOpt.F.e(testvectors[i].expected[j]), res2[j])); 199 | } 200 | } 201 | 202 | const res3 = poseidonWasm(testvectors[i].inputs, testvectors[i].initState, testvectors[i].nOut); 203 | if (testvectors[i].nOut === 1) { 204 | assert(poseidonWasm.F.eq(poseidonWasm.F.e(testvectors[i].expected[0]), res3)); 205 | } else { 206 | for (let j = 0; j < testvectors[i].nOut; j++) { 207 | assert(poseidonWasm.F.eq(poseidonWasm.F.e(testvectors[i].expected[j]), res3[j])); 208 | } 209 | } 210 | } 211 | }); 212 | }); 213 | -------------------------------------------------------------------------------- /test/poseidoncontract.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | import {createCode, generateABI} from "../src/poseidon_gencontract.js"; 3 | import { buildPoseidon } from "../src/poseidon_wasm.js"; 4 | import pkg from 'hardhat'; 5 | const { ethers } = pkg; 6 | 7 | const assert = chai.assert; 8 | const log = (msg) => { if (process.env.MOCHA_VERBOSE) console.log(msg); }; 9 | 10 | describe("Poseidon Smart contract test", function () { 11 | let testrpc; 12 | let web3; 13 | let poseidon1; 14 | let poseidon2; 15 | let poseidon3; 16 | let poseidon4; 17 | let poseidon5; 18 | let poseidon6; 19 | let poseidon; 20 | let account; 21 | this.timeout(100000); 22 | 23 | before(async () => { 24 | [account] = await ethers.getSigners(); 25 | poseidon = await buildPoseidon(); 26 | }); 27 | 28 | it("Should deploy the contract", async () => { 29 | const C1Code = createCode(1); 30 | const C1 = new ethers.ContractFactory( 31 | generateABI(1), 32 | C1Code, 33 | account 34 | ); 35 | const C2Code = createCode(2); 36 | const C2 = new ethers.ContractFactory( 37 | generateABI(2), 38 | C2Code, 39 | account 40 | ); 41 | const C3Code = createCode(3); 42 | const C3 = new ethers.ContractFactory( 43 | generateABI(3), 44 | C3Code, 45 | account 46 | ); 47 | const C4Code = createCode(4); 48 | const C4 = new ethers.ContractFactory( 49 | generateABI(4), 50 | C4Code, 51 | account 52 | ); 53 | const C5Code = createCode(5); 54 | const C5 = new ethers.ContractFactory( 55 | generateABI(5), 56 | C5Code, 57 | account 58 | ); 59 | const C6Code = createCode(6); 60 | const C6 = new ethers.ContractFactory( 61 | generateABI(6), 62 | C6Code, 63 | account 64 | ); 65 | 66 | poseidon1 = await C1.deploy(); 67 | poseidon2 = await C2.deploy(); 68 | poseidon3 = await C3.deploy(); 69 | poseidon4 = await C4.deploy(); 70 | poseidon5 = await C5.deploy(); 71 | poseidon6 = await C6.deploy(); 72 | 73 | // Check the code hashes match the expected values 74 | const C1CodeHash = ethers.keccak256(C1Code); 75 | const C2CodeHash = ethers.keccak256(C2Code); 76 | const C3CodeHash = ethers.keccak256(C3Code); 77 | const C4CodeHash = ethers.keccak256(C4Code); 78 | const C5CodeHash = ethers.keccak256(C5Code); 79 | const C6CodeHash = ethers.keccak256(C6Code); 80 | assert.equal( 81 | C1CodeHash, 82 | "0x6086de4ac21c89a98c65d2c8aa22a52018839944302ee65a57d17e6cd05962e3" 83 | ); 84 | assert.equal( 85 | C2CodeHash, 86 | "0x227d73043a7f0d27c6748154ca6eadfc134d3f775ba1500daf07a1e0c342eabc" 87 | ); 88 | assert.equal( 89 | C3CodeHash, 90 | "0x2e1debce261e8d21c6cd2ca10e56f03cc44c3ac0c68e86cfa38359de894fa78e" 91 | ); 92 | assert.equal( 93 | C4CodeHash, 94 | "0x5fc978a5e74c6c2f2585067c7280478d2d4d289e76ea6b721d3a6ffd17b95a0a" 95 | ); 96 | assert.equal( 97 | C5CodeHash, 98 | "0x429fad904244ef14d1018e233617173776442eeae30d3c35fba1d880689d2ade" 99 | ); 100 | assert.equal( 101 | C6CodeHash, 102 | "0x3e7a2a5573a6f467ddbbb010ea85cae64a5ce785a3f6732f8b0216be83b85bde" 103 | ); 104 | }); 105 | 106 | it("Should calculate the poseidon correctly t=2", async () => { 107 | const res = await poseidon1["poseidon(uint256[1])"]([1]); 108 | const res2 = poseidon([1]); 109 | assert.equal(res.toString(), poseidon.F.toString(res2)); 110 | }); 111 | 112 | it("Should calculate the poseidon correctly t=3", async () => { 113 | const res = await poseidon2["poseidon(uint256[2])"]([1, 2]); 114 | const res2 = poseidon([1, 2]); 115 | assert.equal(res.toString(), poseidon.F.toString(res2)); 116 | }); 117 | 118 | it("Should calculate the poseidon correctly t=4", async () => { 119 | const res = await poseidon3["poseidon(uint256[3])"]([1, 2, 0]); 120 | const res2 = poseidon([1, 2, 0]); 121 | assert.equal(res.toString(), poseidon.F.toString(res2)); 122 | }); 123 | 124 | it("Should calculate the poseidon correctly t=5", async () => { 125 | const res = await poseidon4["poseidon(uint256[4])"]([1, 2, 0, 0]); 126 | const res2 = poseidon([1, 2, 0, 0]); 127 | assert.equal(res.toString(), poseidon.F.toString(res2)); 128 | }); 129 | 130 | it("Should calculate the poseidon correctly t=6", async () => { 131 | const res = await poseidon5["poseidon(uint256[5])"]([1, 2, 0, 0, 0]); 132 | const res2 = poseidon([1, 2, 0, 0, 0]); 133 | assert.equal(res.toString(), poseidon.F.toString(res2)); 134 | }); 135 | 136 | it("Should calculate the poseidon correctly t=7", async () => { 137 | const res = await poseidon6["poseidon(uint256[6])"]([1, 2, 0, 0, 0, 0]); 138 | const res2 = poseidon([1, 2, 0, 0, 0, 0]); 139 | assert.equal(res.toString(), poseidon.F.toString(res2)); 140 | }); 141 | 142 | }); 143 | 144 | -------------------------------------------------------------------------------- /test/smt.js: -------------------------------------------------------------------------------- 1 | import chai from "chai"; 2 | 3 | import buildBabyjub from "../src/babyjub.js"; 4 | import {newMemEmptyTrie} from "../src/smt.js"; 5 | 6 | const assert = chai.assert; 7 | 8 | describe("SMT Javascript test", function () { 9 | let Fr; 10 | this.timeout(100000); 11 | before(async () => { 12 | const babyjub = await buildBabyjub(); 13 | Fr = babyjub.F; 14 | }); 15 | 16 | it("Should insert 2 elements and empty them", async () => { 17 | const tree = await newMemEmptyTrie(); 18 | const key1 = Fr.e(111); 19 | const value1 = Fr.e(222); 20 | const key2 = Fr.e(333); 21 | const value2 = Fr.e(444); 22 | 23 | await tree.insert(key1,value1); 24 | await tree.insert(key2,value2); 25 | await tree.delete(key2); 26 | await tree.delete(key1); 27 | 28 | assert(Fr.isZero(tree.root)); 29 | }); 30 | 31 | it("Should insert 3 elements in dferent order and should be the same", async () => { 32 | const keys = [Fr.e(8), Fr.e(9), Fr.e(32)]; 33 | const values = [Fr.e(88), Fr.e(99), Fr.e(3232)]; 34 | const tree1 = await newMemEmptyTrie(); 35 | const tree2 = await newMemEmptyTrie(); 36 | const tree3 = await newMemEmptyTrie(); 37 | const tree4 = await newMemEmptyTrie(); 38 | const tree5 = await newMemEmptyTrie(); 39 | const tree6 = await newMemEmptyTrie(); 40 | 41 | await tree1.insert(keys[0],values[0]); 42 | await tree1.insert(keys[1],values[1]); 43 | await tree1.insert(keys[2],values[2]); 44 | 45 | await tree2.insert(keys[0],values[0]); 46 | await tree2.insert(keys[2],values[2]); 47 | await tree2.insert(keys[1],values[1]); 48 | 49 | await tree3.insert(keys[1],values[1]); 50 | await tree3.insert(keys[0],values[0]); 51 | await tree3.insert(keys[2],values[2]); 52 | 53 | await tree4.insert(keys[1],values[1]); 54 | await tree4.insert(keys[2],values[2]); 55 | await tree4.insert(keys[0],values[0]); 56 | 57 | await tree5.insert(keys[2],values[2]); 58 | await tree5.insert(keys[0],values[0]); 59 | await tree5.insert(keys[1],values[1]); 60 | 61 | await tree6.insert(keys[2],values[2]); 62 | await tree6.insert(keys[1],values[1]); 63 | await tree6.insert(keys[0],values[0]); 64 | 65 | assert(Fr.eq(tree1.root, tree2.root)); 66 | assert(Fr.eq(tree2.root, tree3.root)); 67 | assert(Fr.eq(tree3.root, tree4.root)); 68 | assert(Fr.eq(tree4.root, tree5.root)); 69 | assert(Fr.eq(tree5.root, tree6.root)); 70 | 71 | assert.equal(Object.keys(tree1.db.nodes).length, Object.keys(tree2.db.nodes).length); 72 | assert.equal(Object.keys(tree2.db.nodes).length, Object.keys(tree3.db.nodes).length); 73 | assert.equal(Object.keys(tree3.db.nodes).length, Object.keys(tree4.db.nodes).length); 74 | assert.equal(Object.keys(tree4.db.nodes).length, Object.keys(tree5.db.nodes).length); 75 | assert.equal(Object.keys(tree5.db.nodes).length, Object.keys(tree6.db.nodes).length); 76 | 77 | await tree1.delete(keys[0]); 78 | await tree1.delete(keys[1]); 79 | await tree2.delete(keys[1]); 80 | await tree2.delete(keys[0]); 81 | assert(Fr.eq(tree1.root, tree2.root)); 82 | 83 | await tree3.delete(keys[0]); 84 | await tree3.delete(keys[2]); 85 | await tree4.delete(keys[2]); 86 | await tree4.delete(keys[0]); 87 | assert(Fr.eq(tree3.root, tree4.root)); 88 | 89 | await tree5.delete(keys[1]); 90 | await tree5.delete(keys[2]); 91 | await tree6.delete(keys[2]); 92 | await tree6.delete(keys[1]); 93 | assert(Fr.eq(tree5.root, tree6.root)); 94 | 95 | await tree1.delete(keys[2]); 96 | await tree2.delete(keys[2]); 97 | await tree3.delete(keys[1]); 98 | await tree4.delete(keys[1]); 99 | await tree5.delete(keys[0]); 100 | await tree6.delete(keys[0]); 101 | 102 | assert(Fr.isZero(tree1.root)); 103 | assert(Fr.isZero(tree2.root)); 104 | assert(Fr.isZero(tree3.root)); 105 | assert(Fr.isZero(tree4.root)); 106 | assert(Fr.isZero(tree5.root)); 107 | assert(Fr.isZero(tree6.root)); 108 | 109 | assert.equal(Object.keys(tree1.db.nodes).length, 0); 110 | assert.equal(Object.keys(tree2.db.nodes).length, 0); 111 | assert.equal(Object.keys(tree3.db.nodes).length, 0); 112 | assert.equal(Object.keys(tree4.db.nodes).length, 0); 113 | assert.equal(Object.keys(tree5.db.nodes).length, 0); 114 | assert.equal(Object.keys(tree6.db.nodes).length, 0); 115 | }); 116 | 117 | it("Insert and remove 100 numbers randomly", async () => { 118 | function perm(a) { 119 | const arr = a.slice(); 120 | const rArr = []; 121 | for (let i=0; i { 148 | const tree1 = await newMemEmptyTrie(); 149 | const tree2 = await newMemEmptyTrie(); 150 | 151 | await tree1.insert(8,88); 152 | await tree1.insert(9,99,); 153 | await tree1.insert(32,3232); 154 | 155 | await tree2.insert(8,888); 156 | await tree2.insert(9,999); 157 | await tree2.insert(32,323232); 158 | 159 | await tree1.update(8, 888); 160 | await tree1.update(9, 999); 161 | await tree1.update(32, 323232); 162 | 163 | assert(Fr.eq(tree1.root, tree2.root)); 164 | }); 165 | 166 | it("Should test update with same key-value", async () => { 167 | const tree1 = await newMemEmptyTrie(); 168 | 169 | await tree1.insert(8,88); 170 | await tree1.update(8,88); 171 | 172 | const res = await tree1.db.get(tree1.root); 173 | assert.notEqual(res, undefined); 174 | }); 175 | }); 176 | -------------------------------------------------------------------------------- /tools/poseidon_optimize_constants.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import {Scalar, ZqField, utils} from "ffjavascript"; 4 | const { unstringifyBigInts } = utils; 5 | 6 | 7 | // Version to write in hexadecimal 8 | function stringifyBigInts(o) { 9 | if ((typeof(o) == "bigint") || o.eq !== undefined) { 10 | return "0x"+o.toString(16); 11 | } else if (o instanceof Uint8Array) { 12 | return Scalar.fromRprLE(o, 0); 13 | } else if (Array.isArray(o)) { 14 | return o.map(stringifyBigInts); 15 | } else if (typeof o == "object") { 16 | const res = {}; 17 | const keys = Object.keys(o); 18 | keys.forEach( (k) => { 19 | res[k] = stringifyBigInts(o[k]); 20 | }); 21 | return res; 22 | } else { 23 | return o; 24 | } 25 | } 26 | 27 | const { C, M } = unstringifyBigInts(JSON.parse(fs.readFileSync(path.join("src", "poseidon_constants.json"), "utf8"))); 28 | 29 | const N_ROUNDS_F = 8; 30 | const N_ROUNDS_P = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; 31 | 32 | 33 | function matrix_inverse(Fr, A) { 34 | const m = A.length; 35 | const B=[]; 36 | for (let i=0; i=0; i--) { 70 | for (let i2=i+1; i20) S = S + ", "; 88 | S = S + Fr.toString(A[i][j]); 89 | } 90 | console.log(S); 91 | } 92 | } 93 | 94 | function vec_print(Fr, v) { 95 | for (let i=0; i=nRoundsF/2; r--) { 188 | const accp = vec_mul_matrix(Fr, acc, Minv); 189 | partialConst.push(accp[0]); 190 | accp[0] = Fr.zero; 191 | for (let k=0; k0) &&(j>0)) { 227 | m_hat[i-1][j-1] = m[i][j]; 228 | mp[i][j] = m[i][j]; 229 | } else { 230 | mp[i][j] = ((i==0)&&(j==0)) ? Fr.one : Fr.zero; 231 | } 232 | if ((i>0)&&(j==0)) w[i-1] = m[i][j]; 233 | } 234 | } 235 | const m_hat_inv = matrix_inverse(Fr, m_hat); 236 | const wp = matrix_mul_vec(Fr, m_hat_inv, w); 237 | 238 | const S = []; 239 | 240 | S.push(m[0][0]); 241 | for (let k=0; k { 296 | process.exit(0); 297 | }, (err) => { 298 | // console.log(err); 299 | console.log(err.stack); 300 | process.exit(1); 301 | }); --------------------------------------------------------------------------------