Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/poseidon_opt.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ export default async function buildPoseidon() {
assert(inputs.length > 0);
assert(inputs.length <= N_ROUNDS_P.length);

if (inputs.some((i) => i < 0 || i >= F.p)) {
throw new Error(`One or more inputs are not in the field: ${F.p}`);
}

if (initState) {
initState = F.e(initState);
} else {
Expand Down
4 changes: 4 additions & 0 deletions src/poseidon_reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export default async function buildPoseidon() {
assert(inputs.length > 0);
assert(inputs.length <= N_ROUNDS_P.length);

if (inputs.some((i) => i < 0 || i >= F.p)) {
throw new Error(`One or more inputs are not in the field: ${F.p}`);
}

const t = inputs.length + 1;
const nRoundsF = N_ROUNDS_F;
const nRoundsP = N_ROUNDS_P[t - 2];
Expand Down
21 changes: 19 additions & 2 deletions src/poseidon_wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,31 @@ export async function buildPoseidon() {
let buff;
let n;
if (Array.isArray(arr)) {
if (arr.some((i) => i < 0 || i >= F.p)) {
throw new Error(`One or more inputs are not in the field: ${F.p}`);
}
n = arr.length;
buff = new Uint8Array(n*32);
for (let i=0; i<n; i++) buff.set(F.e(arr[i]), i*32);
} else {
buff = arr;
n = buff.byteLength / 32;
if (n*32 != buff.byteLength) throw new Error("Invalid iput buff size");
if (buff.byteLength % 32 !== 0) throw new Error("Invalid input buff size");

const chunkSize = 32;
const chunks = [];
for (let i = 0; i < buff.length; i += chunkSize) {
chunks.push(buff.slice(i, i + chunkSize));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess conversion to big int and check could be implemented in the same loop

}

chunks.forEach((chunk) => {
const bi = chunk.reduceRight((acc, n) => (acc << 8n) | BigInt(n), 0n);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndriianChestnykh better to use bigint to bytes conversions from library iden3/ffjavascript/src/utils.js
@OBrezhniev better to clarify with someone if little endian is valid here

if (bi < 0 || bi >= F.p) {
throw new Error(`One or more inputs are not in the field: ${F.p}`);
}
});
}

bn128.tm.setBuff(pIn, buff);

if ((n<1)||(n>16)) throw new Error("Invalid poseidon size");
Expand Down Expand Up @@ -437,4 +454,4 @@ export function buildPoseidonWasm(module) {

buildPoseidon();
module.exportFunction("poseidon");
}
}
54 changes: 54 additions & 0 deletions test/poseidon.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import chai from "chai";
const assert = chai.assert;
const expect = chai.expect;

import buildPoseidonOpt from "../src/poseidon_opt.js";
import {buildPoseidon as buildPoseidonWasm } from "../src/poseidon_wasm.js";
Expand Down Expand Up @@ -209,4 +210,57 @@ describe("Poseidon test", function () {
}
}
});

it("Should check if inputs are in field", async () => {

function arrayToReversedUint8Array(arr) {
const byteArray = [];
arr.forEach((num) => {
assert(num < 2n ** 256n)
const hex = num.toString(16);
const paddedHex = hex.padStart(64, '0');

for (let i = 0; i < paddedHex.length; i += 2) {
byteArray.push(parseInt(paddedHex.substring(i, i + 2), 16));
}
});
return new Uint8Array(byteArray.reverse());
}

const funcFactory = (func, input) => {
return () => func(input);
}

// TODO fix this assertion
assert(
poseidonWasm.F.eq(funcFactory(poseidonWasm, [1n])(),
funcFactory(poseidonWasm, arrayToReversedUint8Array([1n]))()),
);

expect(funcFactory(poseidonWasm, [21888242871839275222246405745257275088548364400416034343698204186575808495616n]))
.not.to.throw();
expect(funcFactory(poseidonWasm, [21888242871839275222246405745257275088548364400416034343698204186575808495617n]))
.to.throw("One or more inputs are not in the field");
expect(funcFactory(poseidonWasm, ([-1n])))
.to.throw("One or more inputs are not in the field");

expect(funcFactory(poseidonWasm, arrayToReversedUint8Array([21888242871839275222246405745257275088548364400416034343698204186575808495616n])))
.not.to.throw();
expect(funcFactory(poseidonWasm, arrayToReversedUint8Array([21888242871839275222246405745257275088548364400416034343698204186575808495617n])))
.to.throw("One or more inputs are not in the field");

expect(funcFactory(poseidonOpt, [21888242871839275222246405745257275088548364400416034343698204186575808495616n]))
.not.to.throw();
expect(funcFactory(poseidonOpt, [21888242871839275222246405745257275088548364400416034343698204186575808495617n]))
.to.throw("One or more inputs are not in the field");
expect(funcFactory(poseidonOpt, ([-1n]), false))
.to.throw("One or more inputs are not in the field");

expect(funcFactory(poseidonReference, [21888242871839275222246405745257275088548364400416034343698204186575808495616n]))
.not.to.throw();
expect(funcFactory(poseidonReference, [21888242871839275222246405745257275088548364400416034343698204186575808495617n]))
.to.throw("One or more inputs are not in the field");
expect(funcFactory(poseidonReference, ([-1n]), false))
.to.throw("One or more inputs are not in the field");
});
});