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

Support for full multi-query on chain #301

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
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
64 changes: 63 additions & 1 deletion src/iden3comm/handlers/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { getRandomBytes } from '@iden3/js-crypto';
import {
AcceptProfile,
AuthProofResponse,
BasicMessage,
JsonDocumentObject,
JWSPackerParams,
ZeroKnowledgeProofAuth,
ZeroKnowledgeProofAuthResponse,
ZeroKnowledgeProofQuery,
ZeroKnowledgeProofRequest,
ZeroKnowledgeProofResponse
Expand All @@ -12,7 +16,7 @@
import { DID, getUnixTimestamp } from '@iden3/js-iden3-core';
import { IProofService } from '../../proof';
import { CircuitId } from '../../circuits';
import { MediaType } from '../constants';
import { defaultAcceptProfile, MediaType } from '../constants';
import { Signer } from 'ethers';

/**
Expand Down Expand Up @@ -136,6 +140,64 @@
return zkpResponses;
};

/**
* Processes zero knowledge proof requests.
*
* @param to - The identifier of the recipient.
* @param requests - An array of zero knowledge proof requests.
* @param from - The identifier of the sender.
* @param proofService - The proof service.
* @param opts - Additional options for processing the requests.
* @returns A promise that resolves to an array of zero knowledge proof responses.
*/
export const processProofAuth = async (
to: DID,
proofService: IProofService,
opts: {
supportedCircuits: CircuitId[];
acceptProfile?: AcceptProfile;
challenge?: bigint;
}
): Promise<AuthProofResponse> => {
if (!opts.acceptProfile) {
opts.acceptProfile = defaultAcceptProfile;
}

let authResponse: any;

Check warning on line 166 in src/iden3comm/handlers/common.ts

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
// First version we only generate proof for ZKPMessage
if (opts.acceptProfile.env === MediaType.ZKPMessage) {
if (!opts.acceptProfile.circuits) {
throw new Error('Circuit not specified');
}

for (const circuitId of opts.acceptProfile.circuits) {
if (!opts.supportedCircuits.includes(circuitId as unknown as CircuitId)) {
throw new Error(`Circuit ${circuitId} is not supported`);
}

const authProof: ZeroKnowledgeProofAuth = {
circuitId: circuitId as unknown as CircuitId
};

const zkpRes: ZeroKnowledgeProofAuthResponse = await proofService.generateAuthProof(
authProof,
to,
{ challenge: opts.challenge, skipRevocation: true }
);

authResponse = {
authMethod: ('zk-' + circuitId) as string,
circuitId: authProof.circuitId,
proof: zkpRes.proof,
pub_signals: zkpRes.pub_signals
};
break;
}
}

return authResponse;
};

/**
* Verifies that the expires_time field of a message is not in the past. Throws an error if it is.
*
Expand Down
83 changes: 77 additions & 6 deletions src/iden3comm/handlers/contract-request.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { CircuitId } from '../../circuits/models';
import { IProofService } from '../../proof/proof-service';
import { PROTOCOL_MESSAGE_TYPE } from '../constants';
import { BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types';
import { defaultAcceptProfile, PROTOCOL_MESSAGE_TYPE, ProtocolVersion } from '../constants';
import { AcceptProfile, BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types';
import { ContractInvokeRequest, ContractInvokeResponse } from '../types/protocol/contract-request';
import { DID, ChainIds, getUnixTimestamp } from '@iden3/js-iden3-core';
import { FunctionSignatures, IOnChainZKPVerifier } from '../../storage';
import {
FunctionSignatures,
FunctionSignaturesMultiQuery,
IOnChainZKPVerifier
} from '../../storage';
import { Signer } from 'ethers';
import { processZeroKnowledgeProofRequests, verifyExpiresTime } from './common';
import { processProofAuth, processZeroKnowledgeProofRequests, verifyExpiresTime } from './common';
import {
AbstractMessageHandler,
BasicHandlerOptions,
IProtocolMessageHandler
} from './message-handler';
import { IOnChainVerifierMultiQuery } from '../../storage/interfaces/onchain-verifier-multi-query';
import { parseAcceptProfile } from '../utils';

/**
* Interface that allows the processing of the contract request
*
Expand Down Expand Up @@ -71,21 +78,28 @@ export class ContractRequestHandler
CircuitId.AuthV2,
CircuitId.AtomicQueryMTPV2OnChain,
CircuitId.AtomicQuerySigV2OnChain,
CircuitId.AtomicQueryV3OnChain
CircuitId.AtomicQueryV3OnChain,
// Now we support off-chain circuits on-chain
// TODO: We need to create validators for them
CircuitId.AuthV2,
CircuitId.AtomicQueryV3,
CircuitId.LinkedMultiQuery10
];

/**
* Creates an instance of ContractRequestHandler.
* @param {IPackageManager} _packerMgr - package manager to unpack message envelope
* @param {IProofService} _proofService - proof service to verify zk proofs
* @param {IOnChainZKPVerifier} _zkpVerifier - zkp verifier to submit response
* @param {IOnChainVerifierMultiQuery} _verifierMultiQuery - verifier multi-query to submit response
*
*/

constructor(
private readonly _packerMgr: IPackageManager,
private readonly _proofService: IProofService,
private readonly _zkpVerifier: IOnChainZKPVerifier
private readonly _zkpVerifier: IOnChainZKPVerifier,
private readonly _verifierMultiQuery: IOnChainVerifierMultiQuery
) {
super();
}
Expand Down Expand Up @@ -160,13 +174,70 @@ export class ContractRequestHandler
}
return response;
}
case FunctionSignaturesMultiQuery.SubmitResponse: {
// We need to
// 1. Generate auth proof from message.body.accept -> authResponse
// 2. Generate proofs for each query in scope -> zkpResponses

// Build auth response from accept
if (!message.to) {
throw new Error(`failed message. empty 'to' field`);
}

// Get first supported accept profile and pass it to processProofAuth
const acceptProfile = this.getFirstSupportedProfile(
PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE,
message.body.accept
);

const identifier = DID.parse(message.to);
const authResponse = await processProofAuth(identifier, this._proofService, {
supportedCircuits: this._supportedCircuits,
acceptProfile,
challenge: challenge
});

return this._verifierMultiQuery.submitResponse(
ethSigner,
message.body.transaction_data,
authResponse,
zkpResponses
);
}
default:
throw new Error(
`Not supported method id. Only '${FunctionSignatures.SubmitZKPResponseV1} and ${FunctionSignatures.SubmitZKPResponseV2} are supported.'`
);
}
}

private getFirstSupportedProfile(
responseType: string,
profile?: string[] | undefined
): AcceptProfile {
if (profile?.length) {
for (const acceptProfileString of profile) {
// 1. check protocol version
const acceptProfile = parseAcceptProfile(acceptProfileString);
const responseTypeVersion = Number(responseType.split('/').at(-2));
if (
acceptProfile.protocolVersion !== ProtocolVersion.V1 ||
(acceptProfile.protocolVersion === ProtocolVersion.V1 &&
(responseTypeVersion < 1 || responseTypeVersion >= 2))
) {
continue;
}
// 2. check packer support
if (this._packerMgr.isProfileSupported(acceptProfile.env, acceptProfileString)) {
return acceptProfile;
}
}
}

// if we don't have supported profiles, we use default
return defaultAcceptProfile;
}

/**
* unpacks contract-invoke request
* @beta
Expand Down
10 changes: 10 additions & 0 deletions src/iden3comm/types/protocol/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ export type ZeroKnowledgeProofResponse = {
vp?: VerifiablePresentation;
} & ZKProof;

/** ZeroKnowledgeProofAuth represents structure of zkp auth object */
export type ZeroKnowledgeProofAuth = {
circuitId: CircuitId;
};

/** ZeroKnowledgeProofAuthResponse represents structure of zkp auth response */
export type ZeroKnowledgeProofAuthResponse = {
circuitId: string;
} & ZKProof;

/** VerifiablePresentation represents structure of Verifiable Presentation */
export type VerifiablePresentation = {
'@context': string | (string | object)[];
Expand Down
8 changes: 8 additions & 0 deletions src/iden3comm/types/protocol/contract-request.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ZKProof } from '@iden3/js-jwz';
import { PROTOCOL_MESSAGE_TYPE } from '../../constants';
import { BasicMessage } from '../packer';
import { DIDDocument, ZeroKnowledgeProofRequest, ZeroKnowledgeProofResponse } from './auth';
Expand All @@ -14,6 +15,7 @@ export type ContractInvokeRequestBody = {
transaction_data: ContractInvokeTransactionData;
scope: Array<ZeroKnowledgeProofRequest>;
did_doc?: DIDDocument;
accept?: string[];
};

/** ContractInvokeResponse represents structure of contract invoke response object */
Expand Down Expand Up @@ -41,3 +43,9 @@ export type ContractInvokeTransactionData = {
chain_id: number;
network?: string;
};

/** AuthProofResponse represents structure of zkp response */
export type AuthProofResponse = {
authMethod: string;
circuitId: string;
} & ZKProof;
56 changes: 54 additions & 2 deletions src/proof/proof-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import {
ZeroKnowledgeProofResponse,
PROTOCOL_CONSTANTS,
VerifiablePresentation,
JsonDocumentObject
JsonDocumentObject,
ZeroKnowledgeProofAuth,
ZeroKnowledgeProofAuthResponse
} from '../iden3comm';
import { cacheLoader } from '../schema-processor';
import { ICircuitStorage, IStateStorage } from '../storage';
Expand Down Expand Up @@ -132,14 +134,29 @@ export interface IProofService {
generateAuthV2Inputs(hash: Uint8Array, did: DID, circuitId: CircuitId): Promise<Uint8Array>;

/**
* generates auth inputs
* generates auth v2 proof from given identity
*
* @param {Uint8Array} hash - challenge that will be signed
* @param {DID} did - identity that will generate a proof
* @returns `Promise<ZKProof>`
*/
generateAuthV2Proof(hash: Uint8Array, did: DID): Promise<ZKProof>;

/**
* Generate auth proof from given identity with generic params
*
* @param {ZeroKnowledgeProofAuth} proofAuth - zkp generic auth params for the proof generation
* @param {DID} identifier - did that will generate proof
* @param {ProofGenerationOptions} opts - options that will be used for proof generation
*
* @returns `Promise<ZeroKnowledgeProofResponse>`
*/
generateAuthProof(
proofAuth: ZeroKnowledgeProofAuth,
identifier: DID,
opts?: ProofGenerationOptions
): Promise<ZeroKnowledgeProofAuthResponse>;

/**
* state verification function
*
Expand Down Expand Up @@ -370,6 +387,41 @@ export class ProofService implements IProofService {
};
}

/** {@inheritdoc IProofService.generateAuthProof} */
async generateAuthProof(
proofAuth: ZeroKnowledgeProofAuth,
identifier: DID,
opts?: ProofGenerationOptions
): Promise<ZeroKnowledgeProofAuthResponse> {
if (!opts) {
opts = {
skipRevocation: false,
challenge: 0n
};
}

let zkProof;

switch (proofAuth.circuitId) {
case CircuitId.AuthV2:
{
const challenge = opts.challenge
? BytesHelper.intToBytes(opts.challenge).reverse()
: new Uint8Array(32);
zkProof = await this.generateAuthV2Proof(challenge, identifier);
}
break;
default:
throw new Error(`CircuitId ${proofAuth.circuitId} is not supported`);
}

return {
circuitId: proofAuth.circuitId,
proof: zkProof.proof,
pub_signals: zkProof.pub_signals
};
}

/** {@inheritdoc IProofService.transitState} */
async transitState(
did: DID,
Expand Down
Loading