Giter VIP home page Giter VIP logo

circomlibjs's People

Contributors

alrubio avatar dan1kov avatar jbaylina avatar judiciouscoder avatar kolezhniuk avatar obrezhniev avatar xavi-pinsach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

circomlibjs's Issues

Something weird with F.toString

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?

Official way to transform the UInt8Array output of Poseidon into a BigInt?

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 */]))

Poseidon hash has changed

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])

Question about prune in eddsa

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.

  1. First, EdDSA (as in RFC 8032) is using the base point of the entire curve with co-factor 8, which generates 8*order points. This is why they set the three LSB bits to 0. I may be confused here, but my understanding is that in this code you are using the base point "Base8", which does not generate all points on the curve -- instead it only generates a subgroup of prime order. So setting the three LSB bits to zero in this code does not immediately make sense to me.
  2. You are setting the second-highest MSB bit to "1" in the private key scalar. I realize this is also done in RFC 8032, but I believe this is to protect against a specific old implementation flaw in a Montgomery implementations, and is not relevant or useful to this curve.
  3. On top of this, I am concerned that there may be a minor bias introduced. Imagining that we skip the steps mentioned in (1) and (2), you are sampling a scalar s between {0, ..., 2^255 - 1} and then computing s * B, where B generates a group of prime order L. Here L is a prime with log2(L) = 250.59669135500214387862176939450353061902897749239721141062673528. Since s * B can be viewed as equivalent to (s mod L) * B, then the reduction modulo L causes some values (s mod L) to occur with slightly higher probability.

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

Error when attempting to use buildPoseidon

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:

image

Issue running in React.js (browser)

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?

Error on eddsa.sign()

Would keep getting this error when trying to use the sign function: TypeError: "list" argument must be an Array of Buffers

Screenshot from 2021-10-15 13-40-23

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.

Failed generate Poseidon smart contract with inputs > 6

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.

Question about eddsa + poseidon

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

Discrepancy between Poseidon hashes in circom vs circomlibjs

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!

Error on import inside worker module

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 hash inputs?

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

LICENSE missing

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.

Correct method for calculating Pedersen hash

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?

Modify ethers imports

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'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.