iden3 / circomlibjs Goto Github PK
View Code? Open in Web Editor NEWJavascript library to work with circomlib circuits
Javascript library to work with circomlib circuits
This is my code:
const transactionHash = poseidon([1,2,3,4])
console.log(poseidon.F.toString(transactionHash, 16))
poseidon.F.toRprLE(transactionHash)
console.log(transactionHash)
The result is:
Uint8Array(32) [
101, 4, 37, 101, 223, 37, 165, 186,
61, 102, 224, 28, 187, 14, 230, 55,
152, 11, 81, 228, 64, 250, 206, 157,
215, 253, 193, 182, 125, 134, 156, 41
]
If I comment out the 2. line (F.toString with the console.log), the result changes to:
Uint8Array(32) [
55, 101, 196, 198, 129, 138, 140, 45,
142, 65, 142, 10, 95, 238, 7, 252,
22, 29, 96, 57, 220, 241, 71, 104,
73, 180, 227, 41, 211, 150, 75, 39
]
toString should be stateless. Or not?
In version 0.0.8, the output of poseidon is a BigInt which matches the output of a circuit. In the current version 0.1.7, it's returning a UInt8Array. I found a way to do it (see the snippet below), but is there a way to do it without directly using the ffjavascript
library?
// bn128
const F = new ffjavascript.ZqField(
ffjavascript.Scalar.fromString(
'21888242871839275222246405745257275088548364400416034343698204186575808495617'
)
)
const expectedBigInt = F.fromRprLEM(poseidon([/* some input */]))
Similar to issue #13
Old and new versions of circomlibjs give different Poseidon functions with different outputs for the same inputs. Perhaps this is intentional, as I see it passes test/poseidon.js
. However, the old version seems to be more compatible with other Poseidon implementations, whereas the new version does not pass tests when comparing to other Poseidon implementations.
To reproduce:
npm i circomlibjs-old@npm:[email protected]
npm i circomlibjs-new@npm:[email protected]
const { buildPoseidon } = require("circomlibjs-new");
const { poseidon } = require("circomlibjs-old");
const { ethers } = require("ethers");
const oldIsNew = async () => {
const oldPoseidon = await buildPoseidon();
const newPoseidon = poseidon;
console.log(
ethers.BigNumber.from(oldPoseidon([1,2,3,4,5])),
"is",
ethers.BigNumber.from(newPoseidon([1,2,3,4,5]))
)
}
oldIsNew()
Output:
> BigNumber {
_hex: '0x6e2f2d34107fa746e4ac7bfdf5cceafa076c606db07e7391806811e85434bb01',
_isBigNumber: true
} is BigNumber {
_hex: '0x0dab9449e4a1398a15224c0b15a49d598b2174d305a316c918125f8feeb123c0',
_isBigNumber: true
}
As you can see, it gives a different value for poseidon([1,2,3,4,5])
Hi all,
I was looking at the implementation of EdDSA in BabyJubJub, specifically the generation of private/public keypairs. I notice you are using a "pruneBuffer" function to set the 3 LSB bits to "0" and to set the second-most-significant bit to "1". The use of this function seems to be inspired by processing from RFC 8032.
However, this approach does not seem to make sense for BabyJubJub. I notice three things in this code that confuse me.
I don't think that any of this is practically exploitable, since these keys are not used for generating signature nonces. Nonetheless it seems like bad practice, if I am understanding the code correctly.
A proposed resolution would be to use a similar approach as you currently use for generating nonces in signature generation. This code generates a much larger bitstring and then computes a modular reduction mod L, with no "pruning" applied. That approach should thoroughly eliminate the bias mentioned above:
const sBuff = this.pruneBuffer(createBlakeHash("blake512").update(Buffer.from(prv)).digest());
let r = Scalar.mod(Scalar.fromRprLE(sBuff, 0, 64), this.babyJub.subOrder);
const R8 = this.babyJub.mulPointEscalar(this.babyJub.Base8, r);
I'm currently encountering an error when building for the browser. The issue is present in 0.1.0
and 0.1.1
, but downgrading to version 0.0.8
resolves the issue. Since I only need to generate a poseidon hash for the moment, I won't debug further but will log the issue in case others encounter something similar.
I am building circomlibjs for web using create-react-app
. Unfortunately I do not know what underlying system bundles the JS for web.
I am using circomlibjs as follows:
const circomlibjs = require('circomlibjs')
circomlibjs.buildPoseidon()
The result is the following error:
Hi all,
I'm experiencing an issue with both circomlibjs and circomlib on browser:
Uncaught TypeError: Cannot read properties of undefined (reading 'fromString') at Object../node_modules/circomlib/src/babyjub.js (babyjub.js:20:1)
It seems to occur when circomlibjs loads ffjavascript. It cannot find Scalar
(or any other exported submodules).
Has anyone had success running circomlibjs on browser?
Would keep getting this error when trying to use the sign
function: TypeError: "list" argument must be an Array of Buffers
Quick fix for the error by modifying lines 57 & 58 of eddsa.js
function now looks like:
function sign(prv, msg) {
const h1 = createBlakeHash("blake512").update(prv).digest();
const sBuff = pruneBuffer(h1.slice(0,32));
const s = utils.leBuff2int(sBuff);
const A = babyJub.mulPointEscalar(babyJub.Base8, Scalar.shr(s, 3));
const rBuff = createBlakeHash("blake512").update(Buffer.concat([h1.slice(32,64), msg])).digest();
let r = utils.leBuff2int(rBuff);
const Fr = new F1Field(babyJub.subOrder);
r = Fr.e(r);
const R8 = babyJub.mulPointEscalar(babyJub.Base8, r);
const R8p = Buffer.from(babyJub.packPoint(R8)); //CHANGED from: const R8p = babyJub.packPoint(R8);
const Ap = Buffer.from(babyJub.packPoint(A)); //CHANGED from: const Ap = babyJub.packPoint(A);
const hmBuff = pedersenHash(Buffer.concat([R8p, Ap, msg]));
const hm = utils.leBuff2int(hmBuff);
const S = Fr.add(r , Fr.mul(hm, s));
return {
R8: R8,
S: S
};
}
Not sure if this is a suitable fix to merge but it has worked for my needs so far.
This test had passed. It is mean that we can't create a smart contract where the Poseidon hash function will accept more than 6 inputs.
import {expect} from "chai";
const {ethers} = require("hardhat");
const {poseidonContract: poseidonGenContract} = require("circomlibjs");
describe("success case", async () => {
it("generate Poseidon SC with 6 inputs", async () => {
expect(()=>{poseidonGenContract.createCode(6)}).to.not.throw("Assertion failed")
});
})
describe("error case", async () => {
it("failed generate Poseidon SC with 7 inputs", async () => {
expect(()=>{poseidonGenContract.createCode(7)}).to.throw("Assertion failed")
});
})
package.json
"dependencies": {
...
"circomlibjs": "^0.1.6",
...
}
But the latest implementation of circomlibjs
has validation if (( nInputs<1) || (nInputs>8))
instead of if (( nInputs<1) || (nInputs>6))
.
https://github.com/iden3/circomlibjs/blob/main/src/poseidon_gencontract.js#L25
When I pass 7
as a param for createCode
I get an error on this line. Because the code asks to create a DUP17
opcode, this opcode is not supported by EVM.
The createCode
function should return the error earlier.
Hello!
My circuit calculates the poseidon hash of 3 values and checks its signature. It looks like this:
include "../node_modules/circomlib/circuits/eddsa.circom";
include "../node_modules/circomlib/circuits/poseidon.circom";
include "../node_modules/circomlib/circuits/bitify.circom";
template VerifyTransferRequest() {
signal input targetAddress;
signal input nftID;
signal input transactionID;
signal input A[256];
signal input R8[256];
signal input S[256];
component eddsa = EdDSAVerifier(254);
component poseidon = Poseidon(3);
component bitify = Num2Bits_strict();
poseidon.inputs[0] <== targetAddress;
poseidon.inputs[1] <== nftID;
poseidon.inputs[2] <== transactionID;
bitify.in <== poseidon.out;
for (var i=0; i<254; i++) {
eddsa.msg[i] <== bitify.out[i];
}
for (var i=0; i<256; i++) {
eddsa.A[i] <== A[i];
}
for (var i=0; i<256; i++) {
eddsa.R8[i] <== R8[i];
}
for (var i=0; i<256; i++) {
eddsa.S[i] <== S[i];
}
}
This is the code that generates the signature and checks it with the circuit:
const buffer2hex = (buff) => {
return ethers.BigNumber.from(buff).toHexString()
}
const transactionID = randomBytes(32);
const transactionHash = poseidon([buffer2hex(targetAddress), nftID, buffer2hex(transactionID)])
const signature = eddsa.signPedersen(prvKey, transactionHash);
const pPubKey = babyJub.packPoint(pubKey);
const pSignature = eddsa.packSignature(signature);
const r8Bits = buffer2bits(pSignature.slice(0, 32));
const sBits = buffer2bits(pSignature.slice(32, 64));
const aBits = buffer2bits(pPubKey);
const w = await circuit.calculateWitness({
targetAddress: buffer2hex(targetAddress),
nftID: nftID,
transactionID: buffer2hex(transactionID),
A: aBits, R8: r8Bits, S: sBits
}, true);
The problem is that the signature check always fails, but I do exactly the same things with circomlibjs. Generate the poseidon hash, and sign it. Should I somehow convert the transactionHash? Or what should I do in the JS part to get the right eddsa signature?
Thx
I'm running into some issues with getting the poseidon
hash from circomlibjs
to match up to what is being generated from my circom circuit.
Simply running a basic test, the following circom code outputs 3726866466909573295204180778017195060429212542252399592723041604622325297091
:
component hash = Poseidon(2);
hash.inputs[0] <== 4;
hash.inputs[1] <== 9;
log("hash", hash.out);
However, in JS, the output is 76775920680290703591279339458061373370655801884570234877842980728484382496803
:
BigInt("0x" + Buffer.from(poseidon([4, 9])).toString("hex"))
Could someone help me point out what I'm missing here? I'm using the default poseidon = await buildPoseidon();
and I'm on [email protected]
. Thanks!
I have a CPU intensive operation over JubJub, which I want to split into parallel tasks and offload to different worker threads. Each worker needs a eddsa
instance to work with. My context does not allow for shared memory among workers and transferring eddsa
to the worker threads is also not possible in any feasible way. I would like each worker to create its own eddsa
instance:
const circomlibjs = require('circomlibjs');
...
const eddsa = await circomlibjs.buildEddsa();
However, importing circomlibjs
inside the worker module raises an error when the worker starts running:
TypeError: Cannot destructure property 'mod' of 'threads.workerData' as it is undefined.
at workerThread (node_modules/web-worker/cjs/node.js:151:5)
at Object.<anonymous> (node_modules/web-worker/cjs/node.js:79:56)
at Object.<anonymous> (node_modules/circomlibjs/node_modules/ffjavascript/build/main.cjs:8:14)
The exact error depends on the multithreading library in use. The above one is raised with workerpool
. If I use the native node:worker_threads
or web workers for the browser, I get something like
TypeError [ERR_INVALID_ARG_TYPE]: The "id" argument must be of type string. Received undefined
at new NodeError (node:internal/errors:399:5)
at validateString (node:internal/validators:163:11)
at Module.require (node:internal/modules/cjs/loader:1120:3)
at require (node:internal/modules/helpers:112:18)
...
at Module.load (node:internal/modules/cjs/loader:1103:32) {
code: 'ERR_INVALID_ARG_TYPE'
I do not face import issues with other libraries. I wonder if this is my fault (that is, if circomlibjs
should be imported in a different way inside js threads) or a bug.
Any idea or hint is welcome. Thanks in advance.
Why there is no Poseidon contract generator for arbitrary number of has inputs?
It can be only fixed and in 1..8 range.
export function createCode(nInputs) {
if (( nInputs<1) || (nInputs>8)) throw new Error("Invalid number of inputs. Must be 1<=nInputs<=8");
...
As far as I remember, there some old contract generator version had this functionality. Although, the code was located in the iden3/circomlib repo at that time
Hi, could you pls indicate the license for this project?
We would really appreciate if this repo was licensed under Apache 2.0 so we could integrate it into TLSNotary's code.
Thanks.
I tried to calculate Pedersen hash with circom and circomjs, but the results are different.
This is my circuit:
template PedersenHasher() {
signal input source;
signal output hash;
signal output hash2;
component hasher = Pedersen(248);
component sourceBits = Num2Bits(248);
sourceBits.in <== source;
for (var i = 0; i < 248; i++) {
hasher.in[i] <== sourceBits.out[i];
}
hash <== hasher.out[0];
hash2 <== hasher.out[1];
}
component main = PedersenHasher();
And this is my circomjs code:
const pedersen = await buildPedersenHash();
const source = crypto.randomBytes(31);
const pedersenHash = pedersen.hash(source)
const points = pedersen.babyJub.unpackPoint(pedersenHash)
const { proof, publicSignals } = await groth16.fullProve({ source: ethers.BigNumber.from(source).toString() },
"./build/pedersenHasherTest_js/pedersenHasherTest.wasm", "./build/pedersenHasher_0001.zkey")
If am I right publicSignals[0] should be equal to points[0] and publicSignals[1] should be equal to points[1], but the hashes are different.
Am I doing something wrong, or is it a bug in circomjs?
There are a number of ethersjs imports in the library as follows:
import ethers from 'ethers'
In my build process, this causes an error as ethersjs does not have a default export. The correct way of importing seems to be import { ethers } from 'ethers'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.