Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add FSCircuitStorage #110

Merged
merged 2 commits into from
Jul 31, 2023
Merged
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
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export * from './schema-processor';
export * from './proof';
export * from './iden3comm';
export * from './circuits';
export * from './loaders';
export * from './iden3comm/handlers';
export * from './utils';
import * as core from '@iden3/js-iden3-core';
Expand Down
1 change: 0 additions & 1 deletion src/loaders/index.ts

This file was deleted.

36 changes: 0 additions & 36 deletions src/loaders/key.ts

This file was deleted.

24 changes: 15 additions & 9 deletions src/proof/prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,16 @@ export class NativeProver implements IZKProver {
* @param {string} circuitId - circuit id for proof verification
* @returns `Promise<ZKProof>`
*/
async verify(zkp: ZKProof, circuitName: CircuitId): Promise<boolean> {
async verify(zkp: ZKProof, circuitId: CircuitId): Promise<boolean> {
try {
const verKey: Uint8Array = (await this._circuitStorage.loadCircuitData(circuitName))
.verificationKey;
const circuitData = await this._circuitStorage.loadCircuitData(circuitId);

if (!circuitData.verificationKey) {
throw new Error(`verification file doesn't exist for circuit ${circuitId}`);
}

await snarkjs.groth16.verify(
JSON.parse(byteDecoder.decode(verKey)),
JSON.parse(byteDecoder.decode(circuitData.verificationKey)),
zkp.pub_signals,
zkp.proof
);
Expand All @@ -79,17 +82,20 @@ export class NativeProver implements IZKProver {
*/
async generate(inputs: Uint8Array, circuitId: CircuitId): Promise<ZKProof> {
const circuitData = await this._circuitStorage.loadCircuitData(circuitId);
const wasm: Uint8Array = circuitData.wasm;
if (!circuitData.wasm) {
throw new Error(`wasm file doesn't exist for circuit ${circuitId}`);
}

const witnessCalculator = await witnessBuilder(wasm);
const witnessCalculator = await witnessBuilder(circuitData.wasm);

const parsedData = JSON.parse(byteDecoder.decode(inputs));

const wtnsBytes: Uint8Array = await witnessCalculator.calculateWTNSBin(parsedData, 0);

const provingKey = circuitData.provingKey;

const { proof, publicSignals } = await snarkjs.groth16.prove(provingKey, wtnsBytes);
if (!circuitData.provingKey) {
throw new Error(`proving file doesn't exist for circuit ${circuitId}`);
}
const { proof, publicSignals } = await snarkjs.groth16.prove(circuitData.provingKey, wtnsBytes);

// we need to terminate curve manually
await this.terminateCurve();
Expand Down
6 changes: 3 additions & 3 deletions src/storage/entities/circuitData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** Circuit data that includes id, wasm file, verification key and proving key */
export type CircuitData = {
circuitId: string;
wasm: Uint8Array;
verificationKey: Uint8Array;
provingKey: Uint8Array;
wasm: Uint8Array | null;
verificationKey: Uint8Array | null;
provingKey: Uint8Array | null;
};
116 changes: 116 additions & 0 deletions src/storage/fs/circuits-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { CircuitId } from '../../circuits';
import { CircuitData } from '../entities/circuitData';
import { ICircuitStorage } from '../interfaces/circuits';
import fs from 'fs';

/**
* Options for FSCircuitStorage,
* Path to the circuit file is constructed from `${this._dirname}/${circuitId}/${filename}`,
* by default values for keys are:
* - verification key : verification_key.json
* - proving key : circuit_final.zkey
* - wasm file : circuit.wasm
* you can customize filename by passing the corresponding option.
* dirname is mandatory.
* hierarchical structure for files is mandatory
* e.g. --circuits
* -----circuitId
* ---------file
* @public
* @interface FSCircuitStorageOptions
*/
export interface FSCircuitStorageOptions {
dirname: string;
verificationFileName?: string;
provingFileName?: string;
wasmFileName?: string;
}
/**
* Implementation of ICircuitStorage to store keys data in file system
*
* @public
* @class FSCircuitStorage
* @implements implements ICircuitStorage interface
*/
export class FSCircuitStorage implements ICircuitStorage {
private readonly _verificationKeyPath: string = 'verification_key.json';
private readonly _provingKeyPath: string = 'circuit_final.zkey';
private readonly _wasmFilePath: string = 'circuit.wasm';

/**
* Creates an instance of FSCircuitStorage.
* @param {string} opts - options to read / save files
*/
constructor(private readonly opts: FSCircuitStorageOptions) {
this._verificationKeyPath = this.opts.verificationFileName ?? this._verificationKeyPath;
this._provingKeyPath = this.opts.provingFileName ?? this._provingKeyPath;
this._wasmFilePath = this.opts.wasmFileName ?? this._wasmFilePath;
}

/**
* loads circuit data by id from file storage
* {@inheritdoc ICircuitStorage.loadCircuitData}
* @param {CircuitId} circuitId - id of the circuit
* @returns `Promise<CircuitData>`
*/
async loadCircuitData(circuitId: CircuitId): Promise<CircuitData> {
const verificationKey = await this.loadCircuitFile(circuitId, this._verificationKeyPath);
const provingKey = await this.loadCircuitFile(circuitId, this._provingKeyPath);
const wasm = await this.loadCircuitFile(circuitId, this._wasmFilePath);

return {
circuitId,
wasm,
provingKey,
verificationKey
};
}

private async loadCircuitFile(
circuitId: CircuitId,
filename: string
): Promise<Uint8Array | null> {
const keyPath = `${this.opts.dirname}/${circuitId}/${filename}`;
if (fs.existsSync(keyPath)) {
const keyData = fs.readFileSync(keyPath);
return new Uint8Array(keyData);
}
return null;
}
private async writeCircuitFile(
circuitId: CircuitId,
filename: string,
file: Uint8Array,
encoding?: BufferEncoding
): Promise<void> {
const dirPath = `${this.opts.dirname}/${circuitId}`;
const keyPath = `${dirPath}/${filename}`;
fs.mkdirSync(dirPath, { recursive: true });
fs.writeFileSync(keyPath, file, encoding);
}

/**
* {@inheritdoc ICircuitStorage.loadCircuitData}
* saves circuit data for circuit id to the file storage
* @param {CircuitId} circuitId - id of the circuit
* @param {CircuitData} circuitData - circuit keys
* @returns `Promise<void>`
*/
async saveCircuitData(circuitId: CircuitId, circuitData: CircuitData): Promise<void> {
if (circuitData.verificationKey) {
await this.writeCircuitFile(
circuitId,
this._verificationKeyPath,
circuitData.verificationKey,
'utf-8'
);
}

if (circuitData.provingKey) {
await this.writeCircuitFile(circuitId, this._provingKeyPath, circuitData.provingKey);
}
if (circuitData.wasm) {
await this.writeCircuitFile(circuitId, this._wasmFilePath, circuitData.wasm);
}
}
}
1 change: 1 addition & 0 deletions src/storage/fs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './circuits-storage';
1 change: 1 addition & 0 deletions src/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './memory';
export * from './local-storage';
export * from './indexed-db';
export * from './shared';
export * from './fs';
43 changes: 13 additions & 30 deletions tests/handlers/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { IdentityStorage } from '../../src/storage/shared/identity-storage';
import { PlainPacker } from '../../src/iden3comm/packers/plain';
import {
AuthHandler,
CircuitStorage,
CredentialStorage,
FSCircuitStorage,
IAuthHandler,
IdentityWallet,
byteEncoder
Expand All @@ -17,7 +17,6 @@ import { InMemoryDataSource, InMemoryMerkleTreeStorage } from '../../src/storage
import { CredentialRequest, CredentialWallet } from '../../src/credentials';
import { ProofService } from '../../src/proof';
import { CircuitId } from '../../src/circuits';
import { FSKeyLoader } from '../../src/loaders';
import { CredentialStatusType, VerifiableConstants, W3CCredential } from '../../src/verifiable';
import { RootInfo, StateProof } from '../../src/storage/entities/state';
import path from 'path';
Expand Down Expand Up @@ -99,6 +98,10 @@ describe('auth', () => {

const verificationFn = new VerificationHandlerFunc(stateVerificationFn);
const mapKey = proving.provingMethodGroth16AuthV2Instance.methodAlg.toString();

if (!circuitData.verificationKey) {
throw new Error(`verification key doesn't exist for ${circuitData.circuitId}`);
}
const verificationParamMap: Map<string, VerificationParams> = new Map([
[
mapKey,
Expand All @@ -109,6 +112,12 @@ describe('auth', () => {
]
]);

if (!circuitData.provingKey) {
throw new Error(`proving doesn't exist for ${circuitData.circuitId}`);
}
if (!circuitData.wasm) {
throw new Error(`wasm file doesn't exist for ${circuitData.circuitId}`);
}
const provingParamMap: Map<string, ProvingParams> = new Map();
provingParamMap.set(mapKey, {
dataPreparer: authInputsHandler,
Expand Down Expand Up @@ -138,34 +147,8 @@ describe('auth', () => {
mt: new InMemoryMerkleTreeStorage(40),
states: mockStateStorage // new EthStateStorage(defaultEthConnectionConfig)
};

const circuitStorage = new CircuitStorage(new InMemoryDataSource<CircuitData>());

const loader = new FSKeyLoader(path.join(__dirname, '../proofs/testdata'));

await circuitStorage.saveCircuitData(CircuitId.AuthV2, {
circuitId: CircuitId.AuthV2.toString(),
wasm: await loader.load(`${CircuitId.AuthV2.toString()}/circuit.wasm`),
provingKey: await loader.load(`${CircuitId.AuthV2.toString()}/circuit_final.zkey`),
verificationKey: await loader.load(`${CircuitId.AuthV2.toString()}/verification_key.json`)
});

await circuitStorage.saveCircuitData(CircuitId.AtomicQuerySigV2, {
circuitId: CircuitId.AtomicQuerySigV2.toString(),
wasm: await loader.load(`${CircuitId.AtomicQuerySigV2.toString()}/circuit.wasm`),
provingKey: await loader.load(`${CircuitId.AtomicQuerySigV2.toString()}/circuit_final.zkey`),
verificationKey: await loader.load(
`${CircuitId.AtomicQuerySigV2.toString()}/verification_key.json`
)
});

await circuitStorage.saveCircuitData(CircuitId.StateTransition, {
circuitId: CircuitId.StateTransition.toString(),
wasm: await loader.load(`${CircuitId.StateTransition.toString()}/circuit.wasm`),
provingKey: await loader.load(`${CircuitId.StateTransition.toString()}/circuit_final.zkey`),
verificationKey: await loader.load(
`${CircuitId.AtomicQueryMTPV2.toString()}/verification_key.json`
)
const circuitStorage = new FSCircuitStorage({
dirname: path.join(__dirname, '../proofs/testdata')
});

const resolvers = new CredentialStatusResolverRegistry();
Expand Down
30 changes: 4 additions & 26 deletions tests/proofs/mtp-onchain.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import {
CircuitStorage,
CredentialStorage,
FSCircuitStorage,
Identity,
IdentityStorage,
IdentityWallet,
Expand All @@ -20,14 +20,12 @@ import {
} from '../../src/credentials';
import { ProofService } from '../../src/proof';
import { CircuitId } from '../../src/circuits';
import { FSKeyLoader } from '../../src/loaders';
import { ethers } from 'ethers';
import { EthStateStorage } from '../../src/storage/blockchain/state';
import { RootInfo, StateProof } from '../../src/storage/entities/state';
import path from 'path';
import { CredentialStatusType, W3CCredential } from '../../src/verifiable';
import { ZeroKnowledgeProofRequest } from '../../src/iden3comm';
import { CircuitData } from '../../src/storage/entities/circuitData';
import { Blockchain, DidMethod, NetworkId } from '@iden3/js-iden3-core';
import { expect } from 'chai';

Expand Down Expand Up @@ -95,29 +93,8 @@ describe('mtp onchain proofs', () => {
states: mockStateStorage
};

const circuitStorage = new CircuitStorage(new InMemoryDataSource<CircuitData>());

// todo: change this loader
const loader = new FSKeyLoader(path.join(__dirname, './testdata'));

await circuitStorage.saveCircuitData(CircuitId.AtomicQueryMTPV2OnChain, {
circuitId: CircuitId.AtomicQueryMTPV2OnChain,
wasm: await loader.load(`${CircuitId.AtomicQueryMTPV2OnChain.toString()}/circuit.wasm`),
provingKey: await loader.load(
`${CircuitId.AtomicQueryMTPV2OnChain.toString()}/circuit_final.zkey`
),
verificationKey: await loader.load(
`${CircuitId.AtomicQueryMTPV2OnChain.toString()}/verification_key.json`
)
});

await circuitStorage.saveCircuitData(CircuitId.StateTransition, {
circuitId: CircuitId.StateTransition,
wasm: await loader.load(`${CircuitId.StateTransition.toString()}/circuit.wasm`),
provingKey: await loader.load(`${CircuitId.StateTransition.toString()}/circuit_final.zkey`),
verificationKey: await loader.load(
`${CircuitId.AtomicQueryMTPV2OnChain.toString()}/verification_key.json`
)
const circuitStorage = new FSCircuitStorage({
dirname: path.join(__dirname, './testdata')
});

/*
Expand All @@ -130,6 +107,7 @@ describe('mtp onchain proofs', () => {
dataStorage.states = ethStorage;

*/

const resolvers = new CredentialStatusResolverRegistry();
resolvers.register(
CredentialStatusType.Iden3ReverseSparseMerkleTreeProof,
Expand Down
Loading