Skip to content

Commit 31c1442

Browse files
authored
chore: improve testing (#19)
1 parent 4bc50d8 commit 31c1442

File tree

10 files changed

+236
-69
lines changed

10 files changed

+236
-69
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
env:
1010
CARGO_TERM_COLOR: always
11-
MINIMUM_NOIR_VERSION: v0.36.0
11+
MINIMUM_NOIR_VERSION: v1.0.0
1212

1313
jobs:
1414
noir-version-list:
@@ -61,43 +61,6 @@ jobs:
6161
- name: Run all tests (including oracle tests)
6262
working-directory: ./scripts
6363
run: ./run.sh
64-
rust-equivalence-tests:
65-
name: Test for equivalence against Rust impl
66-
runs-on: ubuntu-latest
67-
steps:
68-
- name: Checkout sources
69-
uses: actions/checkout@v4
70-
71-
- name: Install Nargo
72-
uses: noir-lang/[email protected]
73-
with:
74-
toolchain: v0.36.0
75-
76-
- name: Install Cargo
77-
uses: dtolnay/[email protected]
78-
with:
79-
targets: x86_64-unknown-linux-gnu
80-
81-
- name: Cache Cargo dependencies
82-
uses: Swatinem/rust-cache@v2
83-
with:
84-
key: x86_64-unknown-linux-gnu
85-
cache-on-failure: true
86-
87-
- name: Setup Node.js and install dependencies
88-
uses: actions/setup-node@v4
89-
with:
90-
node-version: 22.15.0
91-
cache: 'yarn'
92-
cache-dependency-path: 'yarn.lock'
93-
94-
- name: Install dependencies
95-
run: |
96-
yarn --immutable
97-
98-
- name: Export and Test Noir Functions
99-
working-directory: ./scripts
100-
run: ./run.sh
10164

10265
format:
10366
runs-on: ubuntu-latest

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ node_modules
33
target
44
export
55
gates_report.json
6+
package-lock.json

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This package contains the Poseidon hashing interface, formerly in the Noir stand
44

55
## Noir version compatibility
66

7-
This library is tested to work as of Noir version 0.36.0.
7+
This library is tested to work as of Noir version 1.0.0.
88

99
## Benchmarks
1010

oracle_server.ts

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { BarretenbergSync, Fr } from "@aztec/bb.js";
44
import express, { Request, Response } from "express";
55
import bodyParser from "body-parser";
66
import cors from "cors";
7+
import { poseidon1, poseidon2, poseidon3, poseidon4, poseidon5, poseidon6, poseidon7, poseidon8, poseidon9, poseidon10, poseidon11, poseidon12, poseidon13, poseidon14, poseidon15, poseidon16 } from "poseidon-lite";
78

89
// Type definitions
910
interface JsonRpcRequest {
@@ -98,10 +99,12 @@ app.post("/", async (req: Request<{}, JsonRpcResponse, JsonRpcRequest>, res: Res
9899
async function handleForeignCall(params: any[]): Promise<ForeignCallResult> {
99100
const [callInfo] = params as [ForeignCallInfo];
100101
const { function: functionName, inputs } = callInfo;
101-
102-
102+
103+
103104
if (functionName === "getPoseidon2Hash") {
104105
return await handlePoseidon2Hash(inputs);
106+
} else if (functionName === "getPoseidonHash") {
107+
return await handlePoseidonHash(inputs);
105108
} else {
106109
throw new Error(`Unknown oracle function: ${functionName}`);
107110
}
@@ -121,25 +124,77 @@ function hexToBigInt(hex: string): bigint {
121124

122125
async function handlePoseidon2Hash(inputs: string[][]): Promise<ForeignCallResult> {
123126
try {
124-
// Extract the input array from the first parameter
125-
const inputArray = inputs[0] || [];
126-
127-
const fieldInputs: Fr[] = inputArray.map((input: string) => new Fr(hexToBigInt(input)))
128-
129-
// Perform Poseidon2 hash using bb.js
130-
let result: Fr;
131-
if (fieldInputs.length === 0) {
132-
// Empty input case
133-
result = await bb.poseidon2Hash([]);
134-
} else {
135-
result = await bb.poseidon2Hash(fieldInputs);
127+
// For dynamic slices [Field], Noir sends:
128+
// inputs[0] = array length (metadata)
129+
// inputs[1] = array contents
130+
// inputs[2] = message_size
131+
132+
const inputArray = inputs[1] || [];
133+
134+
// For u32, Noir sends as strings, not arrays
135+
const messageSizeHex = inputs[2] as unknown as string;
136+
if (!messageSizeHex) {
137+
throw new Error("message_size parameter is missing");
136138
}
137-
138-
139+
const messageSize = Number(hexToBigInt(messageSizeHex));
140+
141+
// Convert input array to Field elements
142+
const fieldInputs: Fr[] = inputArray.map((input: string) => new Fr(hexToBigInt(input)));
143+
144+
// Slice to only use the first message_size elements
145+
const slicedInputs = fieldInputs.slice(0, messageSize);
146+
147+
// Get Poseidon2 hash using bb.js
148+
const result = bb.poseidon2Hash(slicedInputs);
149+
139150
return {
140151
values: [result.toString()]
141152
};
142-
153+
154+
} catch (error) {
155+
throw error;
156+
}
157+
}
158+
159+
async function handlePoseidonHash(inputs: string[][]): Promise<ForeignCallResult> {
160+
try {
161+
162+
// Extract the input array from the second parameter (inputs[1])
163+
const inputArray = inputs[1] || [];
164+
165+
// Extract message_size from the third parameter (inputs[2])
166+
const messageSizeHex = inputs[2] as unknown as string;
167+
if (!messageSizeHex) {
168+
throw new Error("message_size parameter is missing");
169+
}
170+
const messageSize = Number(hexToBigInt(messageSizeHex));
171+
172+
if (messageSize < 1 || messageSize > 16) {
173+
throw new Error(`message_size must be between 1 and 16, got ${messageSize}`);
174+
}
175+
176+
// Convert input array to BigInt array, slicing to message_size
177+
const bigIntInputs: bigint[] = inputArray
178+
.slice(0, messageSize)
179+
.map((input: string) => hexToBigInt(input));
180+
181+
// Map to appropriate poseidon function
182+
const poseidonFunctions = [
183+
poseidon1, poseidon2, poseidon3, poseidon4, poseidon5, poseidon6,
184+
poseidon7, poseidon8, poseidon9, poseidon10, poseidon11, poseidon12,
185+
poseidon13, poseidon14, poseidon15, poseidon16
186+
];
187+
188+
const poseidonFunc = poseidonFunctions[messageSize - 1];
189+
if (!poseidonFunc) {
190+
throw new Error(`No poseidon function available for message_size ${messageSize}`);
191+
}
192+
const result = poseidonFunc(bigIntInputs);
193+
194+
return {
195+
values: ["0x" + result.toString(16)]
196+
};
197+
143198
} catch (error) {
144199
throw error;
145200
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"@aztec/bb.js": "^1.2.1",
1717
"body-parser": "^1.20.2",
1818
"cors": "^2.8.5",
19-
"express": "^4.18.2"
19+
"express": "^4.18.2",
20+
"poseidon-lite": "^0.3.0"
2021
},
2122
"devDependencies": {
2223
"@types/body-parser": "^1.19.2",

scripts/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ yarn build
1111
echo "Starting TypeScript RPC server..."
1212
yarn start &
1313
TS_SERVER_PID=$!
14-
trap 'kill $TS_SERVER_PID' EXIT
14+
trap 'kill $TS_SERVER_PID 2>/dev/null || true' EXIT
1515

1616
# Wait for server to start
1717
sleep 2

src/oracle_tests.nr

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,130 @@
1-
use super::poseidon2;
1+
use super::{poseidon, poseidon2};
22

33
/// This calls the bb.js Poseidon2 implementation
44
#[oracle(getPoseidon2Hash)]
5-
unconstrained fn poseidon2_hash_oracle(input: [Field; 10]) -> Field {}
5+
unconstrained fn get_poseidon2_hash(input: [Field], message_size: u32) -> Field {}
66

7-
unconstrained fn get_poseidon2_hash(input: [Field; 10]) -> Field {
8-
poseidon2_hash_oracle(input)
7+
/// This calls the poseidon-lite Poseidon implementation
8+
#[oracle(getPoseidonHash)]
9+
unconstrained fn get_poseidon_hash(input: [Field], message_size: u32) -> Field {}
10+
11+
#[test]
12+
unconstrained fn test_poseidon2(input: [Field; 20]) {
13+
for i in 0..=20 {
14+
// get hash from Noir implementation
15+
let temp_size = i % (input.len() + 1);
16+
let noir_hash = poseidon2::Poseidon2::hash(input, temp_size);
17+
18+
// get hash from bb.js via oracle
19+
let bb_hash = get_poseidon2_hash(input, temp_size);
20+
21+
assert_eq(noir_hash, bb_hash);
22+
}
23+
}
24+
25+
// Poseidon (original) oracle tests
26+
27+
#[test]
28+
unconstrained fn test_poseidon_hash_1(input: [Field; 1]) {
29+
let noir_hash = poseidon::bn254::hash_1(input);
30+
let oracle_hash = get_poseidon_hash(input, 1);
31+
assert_eq(noir_hash, oracle_hash);
32+
}
33+
34+
#[test]
35+
unconstrained fn test_poseidon_hash_2(input: [Field; 2]) {
36+
let noir_hash = poseidon::bn254::hash_2(input);
37+
let oracle_hash = get_poseidon_hash(input, 2);
38+
assert_eq(noir_hash, oracle_hash);
939
}
1040

1141
#[test]
12-
unconstrained fn test_poseidon2(input: [Field; 10]) {
13-
// get hash from Noir implementation
14-
let noir_hash = poseidon2::Poseidon2::hash(input, input.len());
42+
unconstrained fn test_poseidon_hash_3(input: [Field; 3]) {
43+
let noir_hash = poseidon::bn254::hash_3(input);
44+
let oracle_hash = get_poseidon_hash(input, 3);
45+
assert_eq(noir_hash, oracle_hash);
46+
}
1547

16-
// get hash from bb.js via oracle
17-
let bb_hash = get_poseidon2_hash(input);
48+
#[test]
49+
unconstrained fn test_poseidon_hash_4(input: [Field; 4]) {
50+
let noir_hash = poseidon::bn254::hash_4(input);
51+
let oracle_hash = get_poseidon_hash(input, 4);
52+
assert_eq(noir_hash, oracle_hash);
53+
}
54+
55+
#[test]
56+
unconstrained fn test_poseidon_hash_5(input: [Field; 5]) {
57+
let noir_hash = poseidon::bn254::hash_5(input);
58+
let oracle_hash = get_poseidon_hash(input, 5);
59+
assert_eq(noir_hash, oracle_hash);
60+
}
61+
62+
#[test]
63+
unconstrained fn test_poseidon_hash_6(input: [Field; 6]) {
64+
let noir_hash = poseidon::bn254::hash_6(input);
65+
let oracle_hash = get_poseidon_hash(input, 6);
66+
assert_eq(noir_hash, oracle_hash);
67+
}
68+
69+
#[test]
70+
unconstrained fn test_poseidon_hash_7(input: [Field; 7]) {
71+
let noir_hash = poseidon::bn254::hash_7(input);
72+
let oracle_hash = get_poseidon_hash(input, 7);
73+
assert_eq(noir_hash, oracle_hash);
74+
}
1875

19-
assert_eq(noir_hash, bb_hash);
76+
#[test]
77+
unconstrained fn test_poseidon_hash_8(input: [Field; 8]) {
78+
let noir_hash = poseidon::bn254::hash_8(input);
79+
let oracle_hash = get_poseidon_hash(input, 8);
80+
assert_eq(noir_hash, oracle_hash);
81+
}
82+
83+
#[test]
84+
unconstrained fn test_poseidon_hash_9(input: [Field; 9]) {
85+
let noir_hash = poseidon::bn254::hash_9(input);
86+
let oracle_hash = get_poseidon_hash(input, 9);
87+
assert_eq(noir_hash, oracle_hash);
88+
}
89+
90+
#[test]
91+
unconstrained fn test_poseidon_hash_10(input: [Field; 10]) {
92+
let noir_hash = poseidon::bn254::hash_10(input);
93+
let oracle_hash = get_poseidon_hash(input, 10);
94+
assert_eq(noir_hash, oracle_hash);
95+
}
96+
97+
#[test]
98+
unconstrained fn test_poseidon_hash_11(input: [Field; 11]) {
99+
let noir_hash = poseidon::bn254::hash_11(input);
100+
let oracle_hash = get_poseidon_hash(input, 11);
101+
assert_eq(noir_hash, oracle_hash);
102+
}
103+
104+
#[test]
105+
unconstrained fn test_poseidon_hash_12(input: [Field; 12]) {
106+
let noir_hash = poseidon::bn254::hash_12(input);
107+
let oracle_hash = get_poseidon_hash(input, 12);
108+
assert_eq(noir_hash, oracle_hash);
109+
}
110+
111+
#[test]
112+
unconstrained fn test_poseidon_hash_13(input: [Field; 13]) {
113+
let noir_hash = poseidon::bn254::hash_13(input);
114+
let oracle_hash = get_poseidon_hash(input, 13);
115+
assert_eq(noir_hash, oracle_hash);
116+
}
117+
118+
#[test]
119+
unconstrained fn test_poseidon_hash_14(input: [Field; 14]) {
120+
let noir_hash = poseidon::bn254::hash_14(input);
121+
let oracle_hash = get_poseidon_hash(input, 14);
122+
assert_eq(noir_hash, oracle_hash);
123+
}
124+
125+
#[test]
126+
unconstrained fn test_poseidon_hash_15(input: [Field; 15]) {
127+
let noir_hash = poseidon::bn254::hash_15(input);
128+
let oracle_hash = get_poseidon_hash(input, 15);
129+
assert_eq(noir_hash, oracle_hash);
20130
}

src/poseidon/mod.nr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ pub fn permute<let T: u32, let N: u32, let X: u32>(
5252
) -> [Field; T] {
5353
let PoseidonConfig { t, rf, rp, alpha, round_constants, mds, presparse_mds, sparse_mds } =
5454
pos_conf;
55-
5655
for i in 0..state.len() {
5756
state[i] += round_constants[i];
5857
}

src/tests.nr

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,36 @@ fn reference_impl_test_vectors() {
3232
);
3333
}
3434
}
35+
36+
#[test]
37+
fn test_poseidon_hash_1() {
38+
// Test hash_1 function
39+
let input = [42];
40+
let hash = poseidon::bn254::hash_1(input);
41+
let expected = 0x1b408dafebeddf0871388399b1e53bd065fd70f18580be5cdde15d7eb2c52743;
42+
assert_eq(hash, expected);
43+
}
44+
45+
#[test]
46+
fn test_poseidon_hash_2() {
47+
let input = [1, 2];
48+
let hash = poseidon::bn254::hash_2(input);
49+
let expected = 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a;
50+
assert_eq(hash, expected);
51+
}
52+
53+
#[test]
54+
fn test_poseidon_hash_4() {
55+
let input = [1, 2, 3, 4];
56+
let hash = poseidon::bn254::hash_4(input);
57+
let expected = 0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465;
58+
assert_eq(hash, expected);
59+
}
60+
61+
#[test]
62+
fn test_poseidon_hash_8() {
63+
let input = [1, 2, 3, 4, 5, 6, 7, 8];
64+
let hash = poseidon::bn254::hash_8(input);
65+
let expected = 0x2921ab9bd0140cbc98e40395c0fefb40337a4d54fbbecd9a4d43b3d8d0c4d8d1;
66+
assert_eq(hash, expected);
67+
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,11 @@ pino@^9.5.0:
821821
sonic-boom "^4.0.1"
822822
thread-stream "^3.0.0"
823823

824+
poseidon-lite@^0.3.0:
825+
version "0.3.0"
826+
resolved "https://registry.yarnpkg.com/poseidon-lite/-/poseidon-lite-0.3.0.tgz#93c42f6f9b870f154f2722dfd686b909c4285765"
827+
integrity sha512-ilJj4MIve4uBEG7SrtPqUUNkvpJ/pLVbndxa0WvebcQqeIhe+h72JR4g0EvwchUzm9sOQDlOjiDNmRAgxNZl4A==
828+
824829
process-warning@^5.0.0:
825830
version "5.0.0"
826831
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7"

0 commit comments

Comments
 (0)