Skip to content

Commit

Permalink
refactoring: optimize r1cs info extraction
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjei committed Apr 26, 2024
1 parent 1fe25b3 commit e5816c3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 20 deletions.
4 changes: 2 additions & 2 deletions src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ async function cli(): Promise<number> {
titleLog('Circuit information');
const info = await circomkit.info(process.argv[3]);
circomkit.log(`Prime Field: ${info.primeName}`);
circomkit.log(`Number of Wires: ${info.variables}`);
circomkit.log(`Number of Wires: ${info.wires}`);
circomkit.log(`Number of Constraints: ${info.constraints}`);
circomkit.log(`Number of Private Inputs: ${info.privateInputs}`);
circomkit.log(`Number of Public Inputs: ${info.publicInputs}`);
circomkit.log(`Number of Public Outputs: ${info.publicOutputs}`);
circomkit.log(`Number of Labels: ${info.labels}`);
circomkit.log(`Number of Outputs: ${info.outputs}`);
break;
}

Expand Down
89 changes: 74 additions & 15 deletions src/circomkit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as snarkjs from 'snarkjs';
const wasm_tester = require('circom_tester').wasm;
import {writeFileSync, readFileSync, existsSync, mkdirSync, rmSync, renameSync} from 'fs';
import {writeFileSync, readFileSync, existsSync, mkdirSync, rmSync, renameSync, openSync} from 'fs';
import {readFile, rm, writeFile} from 'fs/promises';
import {randomBytes} from 'crypto';
import {Logger, getLogger} from 'loglevel';
Expand All @@ -21,6 +21,7 @@ import {WitnessTester, ProofTester} from './testers/';
import {prettyStringify, primeToName} from './utils';
import {defaultConfig, colors, PRIMES, PROTOCOLS} from './utils/config';
import {getCalldata} from './utils/calldata';
import {readBytesFromFile} from './utils/r1cs';

/**
* Circomkit is an opinionated wrapper around many SnarkJS functions.
Expand Down Expand Up @@ -183,22 +184,80 @@ export class Circomkit {
return vkeyPath;
}

/** Information about circuit. */
/** Read the information about the circuit by extracting it from the R1CS file.
* This implementation follows the specs at
* https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md.
*/
async info(circuit: string): Promise<R1CSInfoType> {
// we do not pass `this.snarkjsLogger` here on purpose
const r1csinfo = await snarkjs.r1cs.info(this.path(circuit, 'r1cs'), undefined);

return {
variables: r1csinfo.nVars,
constraints: r1csinfo.nConstraints,
privateInputs: r1csinfo.nPrvInputs,
publicInputs: r1csinfo.nPubInputs,
useCustomGates: r1csinfo.useCustomGates,
labels: r1csinfo.nLabels,
outputs: r1csinfo.nOutputs,
prime: r1csinfo.prime,
primeName: primeToName[r1csinfo.prime.toString(10) as `${bigint}`],
let pointer = 0;

const r1csInfoType: R1CSInfoType = {
wires: 0,
constraints: 0,
privateInputs: 0,
publicInputs: 0,
publicOutputs: 0,
useCustomGates: false,
labels: 0,
prime: BigInt(0),
primeName: '',
};

// Open the file (read mode).
const fd = openSync(this.path(circuit, 'r1cs'), 'r');

// Get 'number of section' (jump magic r1cs and version1 data).
const numberOfSections = readBytesFromFile(fd, 0, 4, 8);
pointer = 12;

for (let i = Number(numberOfSections); i >= 0; i--) {
const sectionType = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;

const sectionSize = Number(readBytesFromFile(fd, 0, 8, pointer));
pointer += 8;

switch (sectionType) {
// Header section.
case 1:
// Field size (skip).
pointer += 4;

r1csInfoType.prime = readBytesFromFile(fd, 0, 32, pointer).toString() as unknown as bigint;
pointer += 32;

r1csInfoType.wires = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;

r1csInfoType.publicOutputs = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;

r1csInfoType.publicInputs = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;

r1csInfoType.privateInputs = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;

r1csInfoType.labels = Number(readBytesFromFile(fd, 0, 8, pointer));
pointer += 8;

r1csInfoType.constraints = Number(readBytesFromFile(fd, 0, 4, pointer));
pointer += 4;
break;
// Custom gates list section (plonk only).
case 4:
r1csInfoType.useCustomGates = Number(readBytesFromFile(fd, 0, 4, pointer)) > 0 ? true : false;

pointer += Number(sectionSize);
break;
default:
pointer += Number(sectionSize);
break;
}
}

r1csInfoType.primeName = primeToName[r1csInfoType.prime.toString() as `${bigint}`];
return r1csInfoType;
}

/** Downloads the phase-1 setup PTAU file for a circuit based on it's number of constraints.
Expand Down
6 changes: 3 additions & 3 deletions src/types/circuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@ export type CircuitConfig = {
params?: (number | bigint)[];
};

/** Some fields for the R1CS information returned by SnarkJS.
/** Some fields for the R1CS information.
* Many other fields are omitted in this type.
*/
export type R1CSInfoType = {
variables: number;
wires: number;
constraints: number;
privateInputs: number;
publicInputs: number;
publicOutputs: number;
useCustomGates: boolean;
labels: number;
outputs: number;
prime: bigint;
primeName: string;
};
11 changes: 11 additions & 0 deletions src/utils/r1cs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {readSync, ReadPosition} from 'fs';

/** Reads a specified number of bytes from a file and converts them to a BigInt.
*/
export function readBytesFromFile(fd: number, offset: number, length: number, position: ReadPosition): BigInt {
const buffer = Buffer.alloc(length);

readSync(fd, buffer, offset, length, position);

return BigInt(`0x${buffer.reverse().toString('hex')}`);
}

0 comments on commit e5816c3

Please sign in to comment.