Skip to content

Commit 04c1b5f

Browse files
authored
Merge pull request #9 from luiz-lvj/npm_package
adding tests to getGroth16Calldata
2 parents 221803e + 0330a64 commit 04c1b5f

File tree

11 files changed

+1678
-304
lines changed

11 files changed

+1678
-304
lines changed

tools/npm/garaga_ts/babel.config.cjs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
presets: [
3+
'@babel/preset-env',
4+
'@babel/preset-typescript',
5+
],
6+
};

tools/npm/garaga_ts/jest.config.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
export default {
1+
module.exports= {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
44
testMatch: ['**/tests/**/*.test.ts'],
55
moduleFileExtensions: ['ts', 'js'],
6-
transform: {
7-
'^.+\\.ts$': 'ts-jest'
8-
},
6+
// transform: {
7+
// '^.+\\.ts$': 'ts-jest'
8+
// },
99
collectCoverage: true,
1010
collectCoverageFrom: ['src/**/*.ts'],
11+
transform: {
12+
'^.+\\.(ts|tsx|js|jsx)$': 'babel-jest',
13+
},
1114
};

tools/npm/garaga_ts/package-lock.json

+1,566-272
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/npm/garaga_ts/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
"test:watch": "jest --watch"
2020
},
2121
"devDependencies": {
22+
"@babel/preset-env": "^7.26.0",
23+
"@babel/preset-typescript": "^7.26.0",
2224
"@rollup/plugin-typescript": "^11.1.6",
2325
"@types/jest": "^29.5.13",
2426
"@types/node": "^22.7.4",

tools/npm/garaga_ts/src/node/api.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
// This files provides a ts-like interface for garaga_rs
22

33
import { msm_calldata_builder, mpc_calldata_builder, to_twistededwards, to_weirstrass, get_groth16_calldata } from '../wasm/pkg/garaga_rs';
4+
import { CurveId } from './definitions';
45
import { Groth16Proof, Groth16VerifyingKey } from './starknet/groth16ContractGenerator/parsingUtils';
56

6-
export enum CurveId {
7-
BN254 = 0,
8-
BLS12_381 = 1,
9-
SECP256K1 = 2,
10-
SECP256R1 = 3,
11-
X25519 = 4,
12-
}
13-
147
export type G1Point = [bigint, bigint];
158
export type G2Point = [[bigint, bigint], [bigint, bigint]];
169
export type G1G2Pair = [G1Point, G2Point];

tools/npm/garaga_ts/src/node/hints/io.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,13 @@ export const bitLength = (x: bigint): number => {
8080
}
8181

8282
export const split128 = (a: bigint): [bigint, bigint] => {
83-
const MAX_UINT256 = BigInt(2) ** BigInt(256);
84-
const MASK_128 = BigInt((1n << BigInt(128)) - 1n);
83+
try{
84+
85+
console.log("a bigint", a);
86+
87+
const MAX_UINT256 = 115792089237316195423570985008687907853269984665640564039457584007913129639936n;
88+
89+
const MASK_128 = BigInt((1n << 128n) - 1n);
8590

8691
if (a < 0n || a >= MAX_UINT256) {
8792
throw new Error(`Value ${a} is too large to fit in a u256`);
@@ -91,6 +96,10 @@ export const split128 = (a: bigint): [bigint, bigint] => {
9196
const high = a >> BigInt(128);
9297

9398
return [low, high];
99+
} catch(err){
100+
console.log("ERR split 128: ", err);
101+
throw new Error("ERROR split 128")
102+
}
94103
}
95104

96105
export const modInverse = (a: bigint, p: bigint): bigint => {

tools/npm/garaga_ts/src/node/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
export * from './api'; // exports "ts" interface
22
export * from '../wasm/pkg/garaga_rs'; // export "raw" interface
33

4+
export { CurveId } from './definitions';
5+
46
import pkg_init from '../wasm/pkg/garaga_rs';
57
import module_or_path from '../wasm/pkg/garaga_rs_bg.wasm';
68

9+
710
export function init(): ReturnType<typeof pkg_init> {
811
return pkg_init({ module_or_path });
912
}

tools/npm/garaga_ts/src/node/starknet/groth16ContractGenerator/parsingUtils.ts

+35-16
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ export const parseGroth16ProofFromObject = (data: any, publicInputsData?: bigint
203203
const imageIdBytes = hexStringToBytes(imageIdHex);
204204
const journalBytes = hexStringToBytes(journalHex);
205205

206+
206207
return createGroth16ProofFromRisc0(sealBytes, imageIdBytes, journalBytes)
207208

208209
} catch(err){
@@ -228,7 +229,7 @@ export const parseGroth16ProofFromObject = (data: any, publicInputsData?: bigint
228229
try {
229230
publicInputs = findItemFromKeyPatterns(data, ['public']);
230231
} catch(err){
231-
throw new Error(`Error: ${err}`);
232+
232233
}
233234
}
234235
const a = tryParseG1PointFromKey(proof, ['a'], curveId);
@@ -258,21 +259,26 @@ export const parseGroth16ProofFromObject = (data: any, publicInputsData?: bigint
258259
export const createGroth16ProofFromRisc0 = (seal: Uint8Array, imageId: Uint8Array, journalBytes: Uint8Array,
259260
controlRoot: bigint = RISC0_CONTROL_ROOT, bn254ControlId: bigint = RISC0_BN254_CONTROL_ID): Groth16Proof => {
260261

262+
261263
if(imageId.length > 32){
262264
throw new Error("imageId must be 32 bytes")
263265
}
264266

265-
const [constrolRoot0, controlRoot1] = splitDigest(controlRoot);
267+
const [controlRoot0, controlRoot1] = splitDigest(controlRoot);
266268

267269
const proof = seal.slice(4);
268270

269271

272+
270273
const journal = createHash("sha256").update(journalBytes).digest();
271274

272275
const claimDigest = digestReceiptClaim(ok(imageId, journal));
273276

277+
274278
const [claim0, claim1] = splitDigest(claimDigest);
275279

280+
281+
276282
const groth16Proof: Groth16Proof = {
277283
a: {
278284
x: toBigInt(proof.slice(0, 32)),
@@ -282,7 +288,7 @@ export const createGroth16ProofFromRisc0 = (seal: Uint8Array, imageId: Uint8Arra
282288
b: {
283289
x: [
284290
toBigInt(proof.slice(96, 128)),
285-
toBigInt(proof.slice(64, 196))
291+
toBigInt(proof.slice(64, 96))
286292
],
287293
y: [
288294
toBigInt(proof.slice(160, 192)),
@@ -296,7 +302,7 @@ export const createGroth16ProofFromRisc0 = (seal: Uint8Array, imageId: Uint8Arra
296302
curveId: CurveId.BN254
297303
},
298304
publicInputs: [
299-
constrolRoot0,
305+
controlRoot0,
300306
controlRoot1,
301307
claim0,
302308
claim1,
@@ -311,7 +317,6 @@ export const createGroth16ProofFromRisc0 = (seal: Uint8Array, imageId: Uint8Arra
311317

312318
throw new Error(`Invalid Groth16 proof: ${groth16Proof}`);
313319

314-
315320
}
316321

317322

@@ -344,19 +349,30 @@ export const checkGroth16VerifyingKey = (vk: Groth16VerifyingKey): boolean => {
344349

345350
const digestReceiptClaim = (receipt: ReceiptClaim): Uint8Array => {
346351
const { tagDigest, input, preStateDigest, postStateDigest, output, exitCode } = receipt;
352+
353+
const systemExitCodeBuffer = Buffer.alloc(4);
354+
systemExitCodeBuffer.writeUInt32BE(exitCode.system << 24);
355+
356+
const userExitCodeBuffer = Buffer.alloc(4);
357+
userExitCodeBuffer.writeUInt32BE(exitCode.user << 24);
358+
359+
// Create a 2-byte big-endian representation of 4 << 8
360+
const twoBytes = Buffer.alloc(2);
361+
twoBytes.writeUInt16BE(4 << 8);
362+
363+
347364
// Concatenating all parts into one Buffer
348365
const data = Buffer.concat([
349366
tagDigest!,
350367
input,
351368
preStateDigest,
352369
postStateDigest,
353370
output,
354-
Buffer.alloc(4, exitCode.system << 24), // Encode exitCode.system in 4 bytes, big-endian
355-
Buffer.alloc(4, exitCode.user << 24), // Encode exitCode.user in 4 bytes, big-endian
356-
Buffer.alloc(2, 4 << 8) // Add the 4 << 8 equivalent to (4 << 8).to_bytes(2, 'big')
371+
systemExitCodeBuffer,
372+
userExitCodeBuffer,
373+
twoBytes
357374
]);
358375

359-
// Return the sha256 digest of the combined data
360376
return createHash('sha256').update(data).digest();
361377
}
362378

@@ -390,19 +406,22 @@ function ok(imageId: Uint8Array, journalDigest: Uint8Array): ReceiptClaim {
390406
const digestOutput = (output: Output): Uint8Array =>{
391407
const { journalDigest, assumptionsDigest } = output;
392408

409+
393410
// Compute the internal tag digest equivalent to hashlib.sha256(b"risc0.Output").digest()
394411
const tagDigest = createHash('sha256').update(Buffer.from("risc0.Output")).digest();
395412

396-
// Concatenate all parts into a single Buffer
397-
const data = Buffer.concat([
398-
tagDigest,
399-
journalDigest,
400-
assumptionsDigest,
401-
Buffer.alloc(2, 2 << 8) // Add the 2 << 8 equivalent to (2 << 8).to_bytes(2, 'big')
413+
const twoBytes = Buffer.alloc(2);
414+
twoBytes.writeUInt16BE(512);
415+
416+
const combined = Buffer.concat([
417+
tagDigest,
418+
Buffer.from(journalDigest),
419+
Buffer.from(assumptionsDigest),
420+
twoBytes // Append 2 as a 2-byte big-endian integer
402421
]);
403422

404423
// Return the sha256 digest of the combined data
405-
return createHash('sha256').update(data).digest();
424+
return createHash('sha256').update(combined).digest();
406425
}
407426

408427
const reverseByteOrderUint256 = (value: bigint | Uint8Array): bigint => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Groth16Proof, parseGroth16ProofFromJson, parseGroth16VerifyingKeyFromJson } from "../../src/node/starknet/groth16ContractGenerator/parsingUtils";
2+
import * as garaga from "../../src/node/index";
3+
4+
5+
const PATH = '../../../hydra/garaga/starknet/groth16_contract_generator/examples';
6+
7+
describe('Groth16 Getting calldata', () => {
8+
9+
const proofAndVkWithPublicInputs = [
10+
[`${PATH}/proof_bn254.json`, `${PATH}/vk_bn254.json`, null],
11+
[`${PATH}/proof_bls.json`, `${PATH}/vk_bls.json`, null],
12+
13+
[`${PATH}/gnark_proof_bn254.json`, `${PATH}/gnark_vk_bn254.json`, `${PATH}/gnark_public_bn254.json`],
14+
[`${PATH}/snarkjs_proof_bn254.json`, `${PATH}/snarkjs_vk_bn254.json`, `${PATH}/snarkjs_public_bn254.json`],
15+
16+
[`${PATH}/proof_risc0.json`, `${PATH}/vk_risc0.json`, null],
17+
18+
]
19+
20+
test.each(proofAndVkWithPublicInputs)("should get groth16 calldata from proof %s, vk %s and pub inputs %s", async (proofPath, vkPath, pubInputsPath) => {
21+
22+
await garaga.init();
23+
24+
const vk = parseGroth16VerifyingKeyFromJson(vkPath as string);
25+
26+
let proof: Groth16Proof;
27+
if(pubInputsPath == null){
28+
proof = parseGroth16ProofFromJson(proofPath as string);
29+
} else{
30+
proof = parseGroth16ProofFromJson(proofPath as string, pubInputsPath);
31+
}
32+
33+
const curveId: garaga.CurveId = proof.a.curveId;
34+
35+
console.log("proof", proof);
36+
37+
console.log("vk", vk);
38+
console.log("curveId", curveId);
39+
const groth16Calldata = garaga.getGroth16CallData(proof, vk, curveId);
40+
41+
console.log("groth16Calldata", groth16Calldata);
42+
43+
});
44+
45+
});

tools/npm/garaga_ts/tests/starknet/groth16VkProofParsing.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ describe('Groth16 Parsing Tests', () => {
2121

2222
const proofPaths = [
2323
`${PATH}/proof_bn254.json`,
24-
`${PATH}/proof_bls.json`,
25-
`${PATH}/proof_risc0.json`
24+
`${PATH}/proof_bls.json`
2625
];
2726

2827
test.each(proofPaths)('should parse proof from %s', (proofPath) => {

tools/npm/garaga_ts/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"noPropertyAccessFromIndexSignature": true,
2525
"types": ["jest", "node"],
2626
"skipLibCheck": true, // Skipping type checks for external libraries
27+
"esModuleInterop": true
2728
},
2829
"include": ["src/**/*.ts", "tests/**/*.ts"]
2930
}

0 commit comments

Comments
 (0)