-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/contract request handler (#136)
* add contract request handler * add onchain zkp verifier --------- Co-authored-by: vmidyllic <[email protected]>
- Loading branch information
1 parent
16de07e
commit 70c0d35
Showing
12 changed files
with
1,071 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { CircuitId } from '../../circuits/models'; | ||
import { IProofService } from '../../proof/proof-service'; | ||
import { PROTOCOL_MESSAGE_TYPE } from '../constants'; | ||
|
||
import { IPackageManager, ZeroKnowledgeProofRequest, ZeroKnowledgeProofResponse } from '../types'; | ||
|
||
import { ProofQuery } from '../../verifiable'; | ||
import { ContractInvokeRequest } from '../types/protocol/contract-request'; | ||
import { DID } from '@iden3/js-iden3-core'; | ||
import { IOnChainZKPVerifier } from '../../storage'; | ||
import { Signer } from 'ethers'; | ||
|
||
/** | ||
* Interface that allows the processing of the contract request | ||
* | ||
* @beta | ||
* @interface IContractRequestHandler | ||
*/ | ||
export interface IContractRequestHandler { | ||
/** | ||
* unpacks contract invoker request | ||
* @beta | ||
* @param {Uint8Array} request - raw byte message | ||
* @returns `Promise<ContractInvokeRequest>` | ||
*/ | ||
parseContractInvokeRequest(request: Uint8Array): Promise<ContractInvokeRequest>; | ||
|
||
/** | ||
* handle contract invoker request | ||
* @beta | ||
* @param {did} did - sender DID | ||
* @param {Uint8Array} request - raw byte message | ||
* @param {ContractInvokeHandlerOptions} opts - handler options | ||
* @returns {Map<string, ZeroKnowledgeProofResponse>}` - map of transaction hash - ZeroKnowledgeProofResponse | ||
*/ | ||
handleContractInvokeRequest( | ||
did: DID, | ||
request: Uint8Array, | ||
opts?: ContractInvokeHandlerOptions | ||
): Promise<Map<string, ZeroKnowledgeProofResponse>>; | ||
} | ||
|
||
/** ContractInvokeHandlerOptions represents contract invoke handler options */ | ||
export type ContractInvokeHandlerOptions = { | ||
ethSigner: Signer; | ||
challenge?: bigint; | ||
}; | ||
|
||
/** | ||
* | ||
* Allows to process ContractInvokeRequest protocol message | ||
* | ||
* @beta | ||
* @class ContractRequestHandler | ||
* @implements implements IContractRequestHandler interface | ||
*/ | ||
export class ContractRequestHandler implements IContractRequestHandler { | ||
private readonly _allowedCircuits = [ | ||
CircuitId.AtomicQueryMTPV2OnChain, | ||
CircuitId.AtomicQuerySigV2OnChain | ||
]; | ||
|
||
/** | ||
* 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 | ||
* | ||
*/ | ||
|
||
constructor( | ||
private readonly _packerMgr: IPackageManager, | ||
private readonly _proofService: IProofService, | ||
private readonly _zkpVerifier: IOnChainZKPVerifier | ||
) {} | ||
|
||
/** | ||
* unpacks contract-invoke request | ||
* @beta | ||
* @param {Uint8Array} request - raw byte message | ||
* @returns `Promise<ContractInvokeRequest>` | ||
*/ | ||
async parseContractInvokeRequest(request: Uint8Array): Promise<ContractInvokeRequest> { | ||
const { unpackedMessage: message } = await this._packerMgr.unpack(request); | ||
const ciRequest = message as unknown as ContractInvokeRequest; | ||
if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { | ||
throw new Error('Invalid media type'); | ||
} | ||
return ciRequest; | ||
} | ||
|
||
/** | ||
* handle contract invoker request | ||
* @beta | ||
* @param {did} did - sender DID | ||
* @param {ContractInvokeRequest} request - contract invoke request | ||
* @param {ContractInvokeHandlerOptions} opts - handler options | ||
* @returns {Map<string, ZeroKnowledgeProofResponse>}` - map of transaction hash - ZeroKnowledgeProofResponse | ||
*/ | ||
async handleContractInvokeRequest( | ||
did: DID, | ||
request: Uint8Array, | ||
opts: ContractInvokeHandlerOptions | ||
): Promise<Map<string, ZeroKnowledgeProofResponse>> { | ||
const ciRequest = await this.parseContractInvokeRequest(request); | ||
|
||
if (ciRequest.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { | ||
throw new Error('Invalid message type for contract invoke request'); | ||
} | ||
|
||
if (!opts.ethSigner) { | ||
throw new Error("Can't sign transaction. Provide Signer in options."); | ||
} | ||
|
||
const zkRequests = []; | ||
for (const proofReq of ciRequest.body.scope) { | ||
if (!this._allowedCircuits.includes(proofReq.circuitId as CircuitId)) { | ||
throw new Error( | ||
`Can't handle circuit ${proofReq.circuitId}. Only onchain circuits are allowed.` | ||
); | ||
} | ||
|
||
const zkpReq: ZeroKnowledgeProofRequest = { | ||
id: proofReq.id, | ||
circuitId: proofReq.circuitId as CircuitId, | ||
query: proofReq.query | ||
}; | ||
|
||
const query = proofReq.query as unknown as ProofQuery; | ||
|
||
const zkpRes: ZeroKnowledgeProofResponse = await this._proofService.generateProof( | ||
zkpReq, | ||
did, | ||
{ | ||
skipRevocation: query.skipClaimRevocationCheck ?? false, | ||
challenge: opts.challenge | ||
} | ||
); | ||
|
||
zkRequests.push(zkpRes); | ||
} | ||
|
||
const txData = ciRequest.body.transaction_data; | ||
|
||
return this._zkpVerifier.submitZKPResponse( | ||
txData.contract_address, | ||
opts.ethSigner, | ||
txData.chain_id, | ||
zkRequests | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './auth'; | ||
export * from './fetch'; | ||
export * from './contract-request'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ZeroKnowledgeProofRequest } from './auth'; | ||
|
||
/** ContractInvokeRequest represents structure of contract invoke request object */ | ||
export type ContractInvokeRequest = { | ||
id: string; | ||
typ: string; | ||
type: string; | ||
thid: string; | ||
body: ContractInvokeRequestBody; | ||
}; | ||
|
||
/** ContractInvokeRequestBody represents structure of contract invoke request body object */ | ||
export type ContractInvokeRequestBody = { | ||
reason: string; | ||
transaction_data: ContractInvokeTransactionData; | ||
scope: Array<ZeroKnowledgeProofRequest>; | ||
}; | ||
|
||
/** ContractInvokeTransactionData represents structure of contract invoke transaction data object */ | ||
export type ContractInvokeTransactionData = { | ||
contract_address: string; | ||
method_id: string; | ||
chain_id: number; | ||
network?: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './state'; | ||
export * from './chainid'; | ||
export * from './onchain-zkp-verifier'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import abi from './zkp-verifier-abi.json'; | ||
import { ethers, Signer } from 'ethers'; | ||
import { EthConnectionConfig } from './state'; | ||
import { IOnChainZKPVerifier } from '../interfaces/onchain-zkp-verifier'; | ||
import { ZeroKnowledgeProofResponse } from '../../iden3comm'; | ||
|
||
/** | ||
* OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract | ||
* and submitZKPResponse. | ||
* | ||
* @beta | ||
* @class OnChainZKPVerifier | ||
*/ | ||
export class OnChainZKPVerifier implements IOnChainZKPVerifier { | ||
/** | ||
* | ||
* Creates an instance of OnChainZKPVerifier. | ||
* @beta | ||
* @param {EthConnectionConfig[]} - array of ETH configs | ||
*/ | ||
|
||
constructor(private readonly _configs: EthConnectionConfig[]) {} | ||
|
||
/** | ||
* Submit ZKP Responses to OnChainZKPVerifier contract. | ||
* @beta | ||
* @param {string} address - OnChainZKPVerifier contract address | ||
* @param {Signer} ethSigner - tx signer | ||
* @param {number} chainId - chain Id | ||
* @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses | ||
* @returns {Promise<Map<string, ZeroKnowledgeProofResponse>>} - map of transaction hash - ZeroKnowledgeProofResponse | ||
*/ | ||
public async submitZKPResponse( | ||
address: string, | ||
ethSigner: Signer, | ||
chainId: number, | ||
zkProofResponses: ZeroKnowledgeProofResponse[] | ||
): Promise<Map<string, ZeroKnowledgeProofResponse>> { | ||
const chainConfig = this._configs.find((i) => i.chainId == chainId); | ||
if (!chainConfig) { | ||
throw new Error(`config for chain id ${chainId} was not found`); | ||
} | ||
const provider = new ethers.providers.JsonRpcProvider(chainConfig); | ||
const verifierContract: ethers.Contract = new ethers.Contract(address, abi, provider); | ||
ethSigner = ethSigner.connect(provider); | ||
const contract = verifierContract.connect(ethSigner); | ||
|
||
const response = new Map<string, ZeroKnowledgeProofResponse>(); | ||
for (const zkProof of zkProofResponses) { | ||
const requestID = zkProof.id; | ||
const inputs = zkProof.pub_signals; | ||
|
||
const payload = [ | ||
requestID, | ||
inputs, | ||
zkProof.proof.pi_a.slice(0, 2), | ||
[ | ||
[zkProof.proof.pi_b[0][1], zkProof.proof.pi_b[0][0]], | ||
[zkProof.proof.pi_b[1][1], zkProof.proof.pi_b[1][0]] | ||
], | ||
zkProof.proof.pi_c.slice(0, 2) | ||
]; | ||
|
||
const tx = await contract.submitZKPResponse(...payload); | ||
const txnReceipt = await tx.wait(); | ||
const status: number = txnReceipt.status; | ||
const txnHash: string = txnReceipt.transactionHash; | ||
|
||
if (status === 0) { | ||
throw new Error(`transaction: ${txnHash} failed to mined`); | ||
} | ||
response.set(txnHash, zkProof); | ||
} | ||
|
||
return response; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.