From 92b719be6516d862a745fd02f2d844521a77379d Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Thu, 15 Aug 2024 18:30:32 +0300 Subject: [PATCH 01/41] feat: add support of submitZKPResponseCrossChain --- package-lock.json | 4 +- package.json | 2 +- src/circuits/atomic-query-mtp-v2-on-chain.ts | 18 +- src/circuits/atomic-query-sig-v2-on-chain.ts | 18 +- src/circuits/atomic-query-v3-on-chain.ts | 18 +- src/circuits/common.ts | 31 + src/iden3comm/handlers/contract-request.ts | 26 +- src/storage/blockchain/abi/ZkpVerifier.json | 839 ++++++++++++------ .../blockchain/onchain-zkp-verifier.ts | 280 +++++- src/storage/entities/state.ts | 53 ++ .../interfaces/onchain-zkp-verifier.ts | 14 + src/utils/did-helper.ts | 58 ++ tests/handlers/contract-request.test.ts | 168 ++++ 13 files changed, 1238 insertions(+), 291 deletions(-) diff --git a/package-lock.json b/package-lock.json index a48e1b3f..cb7d5236 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.17.2", + "version": "1.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", - "version": "1.17.2", + "version": "1.18.0", "license": "MIT or Apache-2.0", "dependencies": { "@noble/curves": "^1.4.0", diff --git a/package.json b/package.json index 7e1c38b4..9781a872 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@0xpolygonid/js-sdk", - "version": "1.17.2", + "version": "1.18.0", "description": "SDK to work with Polygon ID", "main": "dist/node/cjs/index.js", "module": "dist/node/esm/index.js", diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index 8ff07df2..c898e38c 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -7,6 +7,8 @@ import { bigIntArrayToStringArray, existenceToInt, getNodeAuxValue, + IGistRootStatePubSignals, + OnChainStateInfo, prepareCircuitArrayValues, prepareSiblingsStr } from './common'; @@ -240,7 +242,10 @@ interface atomicQueryMTPV2OnChainCircuitInputs { * @class AtomicQueryMTPV2OnChainPubSignals * @extends {BaseConfig} */ -export class AtomicQueryMTPV2OnChainPubSignals extends BaseConfig { +export class AtomicQueryMTPV2OnChainPubSignals + extends BaseConfig + implements IGistRootStatePubSignals +{ requestID!: bigint; userID!: Id; issuerID!: Id; @@ -322,4 +327,15 @@ export class AtomicQueryMTPV2OnChainPubSignals extends BaseConfig { return this; } + + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ + getGistRootStatePugSignals(): OnChainStateInfo { + return { + issuerId: this.issuerID, + userId: this.userID, + gist: this.gistRoot, + issuerState: this.issuerClaimIdenState, + nonRevState: this.issuerClaimNonRevState + }; + } } diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index a3d901d6..fff6c23e 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -7,6 +7,8 @@ import { bigIntArrayToStringArray, existenceToInt, getNodeAuxValue, + IGistRootStatePubSignals, + OnChainStateInfo, prepareCircuitArrayValues, prepareSiblingsStr } from './common'; @@ -312,7 +314,10 @@ export class AtomicQuerySigV2OnChainCircuitInputs { * @class AtomicQuerySigV2OnChainPubSignals * @extends {BaseConfig} */ -export class AtomicQuerySigV2OnChainPubSignals extends BaseConfig { +export class AtomicQuerySigV2OnChainPubSignals + extends BaseConfig + implements IGistRootStatePubSignals +{ requestID!: bigint; userID!: Id; issuerID!: Id; @@ -397,4 +402,15 @@ export class AtomicQuerySigV2OnChainPubSignals extends BaseConfig { return this; } + + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ + getGistRootStatePugSignals(): OnChainStateInfo { + return { + issuerId: this.issuerID, + userId: this.userID, + gist: this.gistRoot, + issuerState: this.issuerAuthState, + nonRevState: this.issuerClaimNonRevState + }; + } } diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index 3898d35d..06ca0225 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -4,7 +4,9 @@ import { bigIntArrayToStringArray, prepareSiblingsStr, getNodeAuxValue, - prepareCircuitArrayValues + prepareCircuitArrayValues, + IGistRootStatePubSignals, + OnChainStateInfo } from './common'; import { BJJSignatureProof, CircuitError, GISTProof, Query, TreeState, ValueProof } from './models'; import { Hash, Proof, ZERO_HASH } from '@iden3/js-merkletree'; @@ -420,7 +422,7 @@ interface AtomicQueryV3OnChainCircuitInputs { * @beta * AtomicQueryV3OnChainPubSignals public inputs */ -export class AtomicQueryV3OnChainPubSignals extends BaseConfig { +export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistRootStatePubSignals { requestID!: bigint; userID!: Id; issuerID!: Id; @@ -515,4 +517,16 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig { return this; } + + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ + getGistRootStatePugSignals(): OnChainStateInfo { + return { + issuerId: this.issuerID, + userId: this.userID, + gist: this.gistRoot, + issuerState: this.issuerState, + nonRevState: this.issuerClaimNonRevState, + operatorOutput: this.operatorOutput + }; + } } diff --git a/src/circuits/common.ts b/src/circuits/common.ts index 57c10dd4..444bd8ae 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -1,6 +1,7 @@ import { Hex } from '@iden3/js-crypto'; import { Hash, ZERO_HASH, Proof, swapEndianness } from '@iden3/js-merkletree'; import { TreeState } from './models'; +import { Id } from '@iden3/js-iden3-core'; export const defaultMTLevels = 40; // max MT levels, default value for identity circuits export const defaultValueArraySize = 64; // max value array size, default value for identity circuits @@ -226,3 +227,33 @@ export function getProperties(obj: object): object { } return result; } + +/** + * onchain state info from pub signals + * + * @public + * @type OnChainStateInfo + */ +export type OnChainStateInfo = { + issuerId: Id; + userId: Id; + gist: Hash; + issuerState: Hash; + nonRevState: Hash; + operatorOutput?: bigint; +}; + +/** + * state pub signals + * + * @public + * @interface IStatePubSignals + */ +export interface IGistRootStatePubSignals { + /** + * return object with state params + * + * @returns {OnChainStateInfo} + */ + getGistRootStatePugSignals(): OnChainStateInfo; +} diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index d9959938..38cbfe6f 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -4,7 +4,7 @@ import { PROTOCOL_MESSAGE_TYPE } from '../constants'; import { BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types'; import { ContractInvokeRequest } from '../types/protocol/contract-request'; import { DID, ChainIds } from '@iden3/js-iden3-core'; -import { IOnChainZKPVerifier } from '../../storage'; +import { IOnChainZKPVerifier, OnChainZKPVerifier } from '../../storage'; import { Signer } from 'ethers'; import { processZeroKnowledgeProofRequests } from './common'; import { AbstractMessageHandler, IProtocolMessageHandler } from './message-handler'; @@ -127,11 +127,25 @@ export class ContractRequestHandler { ethSigner, challenge, supportedCircuits: this._supportedCircuits } ); - return this._zkpVerifier.submitZKPResponse( - ethSigner, - message.body.transaction_data, - zkpResponses - ); + const methodId = message.body.transaction_data.method_id.replace('0x', ''); + switch (methodId) { + case OnChainZKPVerifier.SupportedCrossChainMethodId: + return this._zkpVerifier.submitZKPResponseCrossChain( + ethSigner, + message.body.transaction_data, + zkpResponses + ); + case OnChainZKPVerifier.SupportedMethodId: + return this._zkpVerifier.submitZKPResponse( + ethSigner, + message.body.transaction_data, + zkpResponses + ); + default: + throw new Error( + `Not supported method id. Only '${OnChainZKPVerifier.SupportedCrossChainMethodId} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + ); + } } /** diff --git a/src/storage/blockchain/abi/ZkpVerifier.json b/src/storage/blockchain/abi/ZkpVerifier.json index b8559527..aad817b7 100644 --- a/src/storage/blockchain/abi/ZkpVerifier.json +++ b/src/storage/blockchain/abi/ZkpVerifier.json @@ -1,268 +1,573 @@ [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "REQUESTS_RETURN_LIMIT", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "requestId", - "type": "uint64" - } - ], - "name": "getZKPRequest", - "outputs": [ - { - "components": [ - { - "internalType": "string", - "name": "metadata", - "type": "string" - }, - { - "internalType": "contract ICircuitValidator", - "name": "validator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct IZKPVerifier.ZKPRequest", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "length", - "type": "uint256" - } - ], - "name": "getZKPRequests", - "outputs": [ - { - "components": [ - { - "internalType": "string", - "name": "metadata", - "type": "string" - }, - { - "internalType": "contract ICircuitValidator", - "name": "validator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct IZKPVerifier.ZKPRequest[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getZKPRequestsCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "name": "proofs", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "requestId", - "type": "uint64" - } - ], - "name": "requestIdExists", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "requestId", - "type": "uint64" - }, - { - "components": [ - { - "internalType": "string", - "name": "metadata", - "type": "string" - }, - { - "internalType": "contract ICircuitValidator", - "name": "validator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct IZKPVerifier.ZKPRequest", - "name": "request", - "type": "tuple" - } - ], - "name": "setZKPRequest", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "requestId", - "type": "uint64" - }, - { - "internalType": "uint256[]", - "name": "inputs", - "type": "uint256[]" - }, - { - "internalType": "uint256[2]", - "name": "a", - "type": "uint256[2]" - }, - { - "internalType": "uint256[2][2]", - "name": "b", - "type": "uint256[2][2]" - }, - { - "internalType": "uint256[2]", - "name": "c", - "type": "uint256[2]" - } - ], - "name": "submitZKPResponse", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + }, + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "linkID", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "requestIdToCompare", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "linkIdToCompare", + "type": "uint256" + } + ], + "name": "LinkedProofError", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "REQUESTS_RETURN_LIMIT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IStateCrossChain", + "name": "_stateCrossChain", + "type": "address" + } + ], + "name": "__ZKPVerifierBase_init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + } + ], + "name": "getProofStatus", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isVerified", + "type": "bool" + }, + { + "internalType": "string", + "name": "validatorVersion", + "type": "string" + }, + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "blockTimestamp", + "type": "uint256" + } + ], + "internalType": "struct IZKPVerifier.ProofStatus", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "string", + "name": "key", + "type": "string" + } + ], + "name": "getProofStorageField", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + } + ], + "name": "getZKPRequest", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IZKPVerifier.ZKPRequest", + "name": "zkpRequest", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "getZKPRequests", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IZKPVerifier.ZKPRequest[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getZKPRequestsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + } + ], + "name": "isProofVerified", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + } + ], + "name": "requestIdExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IZKPVerifier.ZKPRequest", + "name": "request", + "type": "tuple" + } + ], + "name": "setZKPRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "uint256[]", + "name": "inputs", + "type": "uint256[]" + }, + { + "internalType": "uint256[2]", + "name": "a", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "b", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "c", + "type": "uint256[2]" + } + ], + "name": "submitZKPResponse", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "zkProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "crossChainProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "submitZKPResponseCrossChain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "requestIds", + "type": "uint64[]" + } + ], + "name": "verifyLinkedProofs", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "uint256[]", + "name": "inputs", + "type": "uint256[]" + }, + { + "internalType": "uint256[2]", + "name": "a", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "b", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "c", + "type": "uint256[2]" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "verifyZKPResponse", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "key", + "type": "string" + }, + { + "internalType": "uint256", + "name": "inputIndex", + "type": "uint256" + } + ], + "internalType": "struct ICircuitValidator.KeyToInputIndex[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 0449c28b..924acb0d 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -1,9 +1,20 @@ -import { JsonRpcProvider, Signer, Contract, TransactionRequest } from 'ethers'; +import { JsonRpcProvider, Signer, Contract, TransactionRequest, ethers } from 'ethers'; import { EthConnectionConfig } from './state'; import { IOnChainZKPVerifier } from '../interfaces/onchain-zkp-verifier'; import { ContractInvokeTransactionData, ZeroKnowledgeProofResponse } from '../../iden3comm'; import abi from './abi/ZkpVerifier.json'; import { TransactionService } from '../../blockchain'; +import { DID } from '@iden3/js-iden3-core'; +import { + AtomicQueryMTPV2OnChainPubSignals, + AtomicQuerySigV2OnChainPubSignals, + AtomicQueryV3OnChainPubSignals, + CircuitId, + OnChainStateInfo +} from '../../circuits'; +import { byteEncoder, resolveDidDocumentEip712MessageAndSignature } from '../../utils'; +import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; +import { poseidon } from '@iden3/js-crypto'; /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract @@ -18,7 +29,32 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { * function submitZKPResponse(uint64 requestId, uint256[] calldata inputs, * uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c) public */ - private readonly _supportedMethodId = 'b68967e2'; + public static readonly SupportedMethodId = 'b68967e2'; + + /** + * solidity identifier for function signature: + * function submitZKPResponseCrossChain( + uint64 requestId, + bytes calldata zkProof, + bytes calldata crossChainProof, + bytes calldata data + ) public + */ + public static readonly SupportedCrossChainMethodId = '1c100d01'; + + /** + * supported circuits + */ + private readonly _supportedCircuits = [ + CircuitId.AtomicQueryMTPV2OnChain, + CircuitId.AtomicQuerySigV2OnChain, + CircuitId.AtomicQueryV3OnChain + ]; + + /** + * abi coder to encode/decode structures to solidity bytes + */ + private readonly _abiCoder = new ethers.AbiCoder(); /** * * Creates an instance of OnChainZKPVerifier. @@ -26,15 +62,13 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { * @param {EthConnectionConfig[]} - array of ETH configs */ - constructor(private readonly _configs: EthConnectionConfig[]) {} + constructor( + private readonly _configs: EthConnectionConfig[], + private readonly _didResolverUrl?: string + ) {} /** - * Submit ZKP Responses to OnChainZKPVerifier contract. - * @beta - * @param {Signer} ethSigner - tx signer - * @param {txData} ContractInvokeTransactionData - transaction data - * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses - * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponse} */ public async submitZKPResponse( ethSigner: Signer, @@ -45,9 +79,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); } - if (txData.method_id.replace('0x', '') !== this._supportedMethodId) { + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { throw new Error( - `submit doesn't implement requested method id. Only '0x${this._supportedMethodId}' is supported.` + `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` ); } const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); @@ -97,4 +131,228 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return response; } + + /** + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseCrossChain} + */ + public async submitZKPResponseCrossChain( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); + if (!chainConfig) { + throw new Error(`config for chain id ${txData.chain_id} was not found`); + } + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedCrossChainMethodId) { + throw new Error( + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedCrossChainMethodId}' is supported.` + ); + } + if (!this._didResolverUrl) { + throw new Error(`did resolver url required for crosschain verification`); + } + const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); + const verifierContract = new Contract(txData.contract_address, abi, provider); + ethSigner = ethSigner.connect(provider); + const contract = verifierContract.connect(ethSigner) as Contract; + + const response = new Map(); + for (const zkProof of zkProofResponses) { + const requestID = zkProof.id; + const inputs = zkProof.pub_signals; + + if (!this._supportedCircuits.includes(zkProof.circuitId as CircuitId)) { + throw new Error(`Circuit ${zkProof.circuitId} not supported by OnChainZKPVerifier`); + } + const { gist, issuerState, nonRevState, issuerId, userId, operatorOutput } = + this.getOnChainGistRootStatePubSignals( + zkProof.circuitId as + | CircuitId.AtomicQueryMTPV2OnChain + | CircuitId.AtomicQuerySigV2OnChain + | CircuitId.AtomicQueryV3OnChain, + zkProof.pub_signals + ); + + const userDid = DID.parseFromId(userId); + const issuerDid = DID.parseFromId(issuerId); + + const zkProofEncoded = this.packZkpProof( + 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 globalStateUpdate = (await resolveDidDocumentEip712MessageAndSignature( + userDid, + this._didResolverUrl, + { gist } + )) as GlobalStateUpdate; + + const issuerStateResolution = (await resolveDidDocumentEip712MessageAndSignature( + issuerDid, + this._didResolverUrl, + { state: issuerState } + )) as IdentityStateUpdate; + + const issuerNonRevResolution = (await resolveDidDocumentEip712MessageAndSignature( + issuerDid, + this._didResolverUrl, + { state: nonRevState } + )) as IdentityStateUpdate; + + const crossChainProofs = this.packCrossChainProofs( + globalStateUpdate, + issuerStateResolution, + issuerNonRevResolution + ); + + let metadataArr: { key: string; value: string }[] = []; + if (operatorOutput && operatorOutput !== 0n) { + const metadataValue = poseidon.hash([operatorOutput]); + metadataArr = [ + { + key: 'operator output', + value: `0x${metadataValue.toString(16)}` // TODO: fix + } + ]; + } + + const metadata = this.packMetadatas(metadataArr); + const payload = [requestID, zkProofEncoded, crossChainProofs, metadata]; + + const feeData = await provider.getFeeData(); + const maxFeePerGas = chainConfig.maxFeePerGas + ? BigInt(chainConfig.maxFeePerGas) + : feeData.maxFeePerGas; + const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas + ? BigInt(chainConfig.maxPriorityFeePerGas) + : feeData.maxPriorityFeePerGas; + + const gasLimit = await contract.submitZKPResponseCrossChain.estimateGas(...payload); + const txData = await contract.submitZKPResponseCrossChain.populateTransaction(...payload); + + const request: TransactionRequest = { + to: txData.to, + data: txData.data, + gasLimit, + maxFeePerGas, + maxPriorityFeePerGas + }; + + const transactionService = new TransactionService(provider); + const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); + response.set(txnHash, zkProof); + } + + return response; + } + + private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { + return this._abiCoder.encode( + ['uint256[] inputs', 'uint256[2]', 'uint256[2][2]', 'uint256[2]'], + [inputs, a, b, c] + ); + } + + private packCrossChainProofs( + globalStateUpdate: GlobalStateUpdate, + issuerStateResolution: IdentityStateUpdate, + issuerNonRevResolution: IdentityStateUpdate + ) { + const proofs = [ + { + proofType: 'globalStateProof', + proof: this.packGlobalStateMsg(globalStateUpdate) + }, + { + proofType: 'stateProof', + proof: this.packIdentityStateMsg(issuerStateResolution) + }, + { + proofType: 'stateProof', + proof: this.packIdentityStateMsg(issuerNonRevResolution) + } + ]; + return this._abiCoder.encode( + ['tuple(' + 'string proofType,' + 'bytes proof' + ')[]'], + [proofs] + ); + } + + private packGlobalStateMsg(msg: GlobalStateUpdate): string { + return this._abiCoder.encode( + [ + 'tuple(' + + 'tuple(' + + 'address from,' + + 'uint256 timestamp,' + + 'uint256 root,' + + 'uint256 replacedByRoot,' + + 'uint256 createdAtTimestamp,' + + 'uint256 replacedAtTimestamp' + + ') globalStateMsg,' + + 'bytes signature,' + + ')' + ], + [msg] + ); + } + + private packIdentityStateMsg(msg: IdentityStateUpdate): string { + return this._abiCoder.encode( + [ + 'tuple(' + + 'tuple(' + + 'address from,' + + 'uint256 timestamp,' + + 'uint256 identity,' + + 'uint256 state,' + + 'uint256 replacedByState,' + + 'uint256 createdAtTimestamp,' + + 'uint256 replacedAtTimestamp' + + ') idStateMsg,' + + 'bytes signature,' + + ')' + ], + [msg] + ); + } + + private packMetadatas( + metas: { + key: string; + value: string; + }[] + ): string { + return this._abiCoder.encode(['tuple(' + 'string key,' + 'bytes value' + ')[]'], [metas]); + } + + private getOnChainGistRootStatePubSignals( + onChainCircuitId: + | CircuitId.AtomicQueryMTPV2OnChain + | CircuitId.AtomicQuerySigV2OnChain + | CircuitId.AtomicQueryV3OnChain, + inputs: string[] + ): OnChainStateInfo { + let atomicQueryPubSignals; + switch (onChainCircuitId) { + case CircuitId.AtomicQueryMTPV2OnChain: + atomicQueryPubSignals = new AtomicQueryMTPV2OnChainPubSignals(); + break; + case CircuitId.AtomicQuerySigV2OnChain: + atomicQueryPubSignals = new AtomicQuerySigV2OnChainPubSignals(); + break; + case CircuitId.AtomicQueryV3OnChain: + atomicQueryPubSignals = new AtomicQueryV3OnChainPubSignals(); + break; + } + const encodedInputs = byteEncoder.encode(JSON.stringify(inputs)); + atomicQueryPubSignals.pubSignalsUnmarshal(encodedInputs); + return atomicQueryPubSignals.getGistRootStatePugSignals(); + } } diff --git a/src/storage/entities/state.ts b/src/storage/entities/state.ts index 0b743151..d7a8ee7b 100644 --- a/src/storage/entities/state.ts +++ b/src/storage/entities/state.ts @@ -45,3 +45,56 @@ export interface RootInfo { createdAtBlock: bigint; replacedAtBlock: bigint; } + +/** + * identity state message + * + * @public + * @interface IdentityStateMsg + */ +export interface IdentityStateMsg { + from: string; + timestamp: number; + identity: bigint; + state: bigint; + replacedByState: bigint; + createdAtTimestamp: number; + replacedAtTimestamp: number; +} + +/** + * global state message + * + * @public + * @interface GlobalStateMsg + */ +export interface GlobalStateMsg { + from: string; + timestamp: number; + root: bigint; + replacedByRoot: bigint; + createdAtTimestamp: number; + replacedAtTimestamp: number; +} + +/** + * identity state update + * + * @public + * @interface IdentityStateUpdate + */ +export interface IdentityStateUpdate { + idStateMsg: IdentityStateMsg; + signature: string; +} + +/** + * global state update + * + * @public + * @interface GlobalStateUpdate + */ +export interface GlobalStateUpdate { + globalStateMsg: GlobalStateMsg; + signature: string; +} diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index dba233c2..8d936362 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -21,4 +21,18 @@ export interface IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; + + /** + * Submit ZKP Response Cross Chain to OnChainZKPVerifier contract. + * @beta + * @param {Signer} ethSigner - tx signer + * @param {txData} ContractInvokeTransactionData - transaction data + * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses + * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse + */ + submitZKPResponseCrossChain( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; } diff --git a/src/utils/did-helper.ts b/src/utils/did-helper.ts index 2bb3eb9f..f573f695 100644 --- a/src/utils/did-helper.ts +++ b/src/utils/did-helper.ts @@ -4,6 +4,7 @@ import { Hash } from '@iden3/js-merkletree'; import { DIDResolutionResult, VerificationMethod } from 'did-resolver'; import { keccak256 } from 'js-sha3'; import { hexToBytes } from './encoding'; +import { GlobalStateUpdate, IdentityStateUpdate } from '../storage'; /** * Checks if state is genesis state @@ -86,6 +87,63 @@ export const resolveDIDDocumentAuth = async ( ); }; +export const resolveDidDocumentEip712MessageAndSignature = async ( + did: DID, + resolveURL: string, + opts?: { + state?: Hash; + gist?: Hash; + } +): Promise => { + let didString = did.string().replace(/:/g, '%3A'); + // for gist resolve we have to `hide` user did (look into resolver implementation) + const isGistRequest = opts?.gist && !opts.state; + if (isGistRequest) { + didString = did + .string() + .replace(did.idStrings[2], '000000000000000000000000000000000000000000'); + } + let url = `${resolveURL}/1.0/identifiers/${didString}?signature=EthereumEip712Signature2021`; + if (opts?.state) { + url += `&state=${opts.state.hex()}`; + } + + if (opts?.gist) { + url += `&gist=${opts.gist.hex()}`; + } + const resp = await fetch(url); + const data = await resp.json(); + const message = data.didResolutionMetadata.proof[0].eip712.message; + const signature = data.didResolutionMetadata.proof[0].proofValue; + + if (isGistRequest) { + return { + globalStateMsg: { + from: message.from, + timestamp: message.timestamp, + root: message.root, + replacedByRoot: message.replacedByRoot, + createdAtTimestamp: message.createdAtTimestamp, + replacedAtTimestamp: message.replacedAtTimestamp + }, + signature + }; + } + + return { + idStateMsg: { + from: message.from, + timestamp: message.timestamp, + identity: message.identity, + state: message.state, + replacedByState: message.replacedByState, + createdAtTimestamp: message.createdAtTimestamp, + replacedAtTimestamp: message.replacedAtTimestamp + }, + signature + }; +}; + export const buildDIDFromEthPubKey = (didType: Uint8Array, pubKeyEth: string): DID => { // Use Keccak-256 hash function to get public key hash const hashOfPublicKey = keccak256(hexToBytes(pubKeyEth)); diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 9fbe6d29..aed304a4 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -122,6 +122,16 @@ describe('contract-request', () => { const response = new Map(); response.set('txhash1', zkProofResponses[0]); return response; + }, + + submitZKPResponseCrossChain: async ( + signer: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ) => { + const response = new Map(); + response.set('txhash1', zkProofResponses[0]); + return response; } }; @@ -631,4 +641,162 @@ describe('contract-request', () => { proofReqs[0].id ); }); + + // cross chain integration test + it.skip('cross chain contract request flow - integration test', async () => { + const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test + const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; + const amoyVerifierRpcUrl = '<> '; // verifier RPC URL - amoy + const erc20Verifier = '0xf8a8d8389938261a7827eac9b4b6b2b68189bef2'; + + const issuerStateEthConfig = defaultEthConnectionConfig; + issuerStateEthConfig.url = privadoTestRpcUrl; + issuerStateEthConfig.contractAddress = privadoTestStateContract; // privado test state contract + + const memoryKeyStore = new InMemoryPrivateKeyStore(); + const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); + const kms = new KMS(); + kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + dataStorage = { + credential: new CredentialStorage(new InMemoryDataSource()), + identity: new IdentityStorage( + new InMemoryDataSource(), + new InMemoryDataSource() + ), + mt: new InMemoryMerkleTreeStorage(40), + states: new EthStateStorage(issuerStateEthConfig) + }; + const circuitStorage = new FSCircuitStorage({ + dirname: path.join(__dirname, '../proofs/testdata') + }); + + const resolvers = new CredentialStatusResolverRegistry(); + resolvers.register( + CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + new RHSResolver(dataStorage.states) + ); + credWallet = new CredentialWallet(dataStorage, resolvers); + idWallet = new IdentityWallet(kms, dataStorage, credWallet); + + proofService = new ProofService(idWallet, credWallet, circuitStorage, dataStorage.states, { + ipfsNodeURL + }); + packageMgr = await getPackageMgr( + await circuitStorage.loadCircuitData(CircuitId.AuthV2), + proofService.generateAuthV2Inputs.bind(proofService), + proofService.verifyState.bind(proofService) + ); + + const { did: userDID, credential: cred } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Privado, + networkId: NetworkId.Main, + seed: seedPhrase, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + + expect(cred).not.to.be.undefined; + + const { did: issuerDID, credential: issuerAuthCredential } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Privado, + networkId: NetworkId.Test, + seed: seedPhraseIssuer, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + expect(issuerAuthCredential).not.to.be.undefined; + + const claimReq: CredentialRequest = { + credentialSchema: + 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json/KYCAgeCredential-v3.json', + type: 'KYCAgeCredential', + credentialSubject: { + id: userDID.string(), + birthday: 19960424, + documentType: 99 + }, + expiration: 2793526400, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }; + const issuerCred = await idWallet.issueCredential(issuerDID, claimReq); + + await credWallet.save(issuerCred); + + const proofReqs: ZeroKnowledgeProofRequest[] = [ + { + id: 4, // 2 - mtp, 4 - sig + circuitId: CircuitId.AtomicQuerySigV2OnChain, + optional: false, + query: { + allowedIssuers: ['*'], + type: claimReq.type, + context: + 'https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld', + credentialSubject: { + birthday: { + $lt: 20020101 + } + } + } + } + ]; + + const conf = defaultEthConnectionConfig; + conf.contractAddress = erc20Verifier; + conf.url = amoyVerifierRpcUrl; + conf.chainId = 80002; // amoy chain id + + const zkpVerifier = new OnChainZKPVerifier([conf], 'https://resolver-dev.privado.id'); + contractRequestHandler = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); + + const transactionData: ContractInvokeTransactionData = { + contract_address: erc20Verifier, + method_id: '1c100d01', + chain_id: conf.chainId + }; + + const ciRequestBody: ContractInvokeRequestBody = { + reason: 'reason', + transaction_data: transactionData, + scope: [...proofReqs] + }; + + const id = uuid.v4(); + const ciRequest: ContractInvokeRequest = { + id, + typ: MediaType.PlainMessage, + type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE, + thid: id, + body: ciRequestBody + }; + + const ethSigner = new ethers.Wallet(walletKey); + + const challenge = BytesHelper.bytesToInt(hexToBytes(ethSigner.address)); + + const options: ContractInvokeHandlerOptions = { + ethSigner, + challenge + }; + const msgBytes = byteEncoder.encode(JSON.stringify(ciRequest)); + const ciResponse = await contractRequestHandler.handleContractInvokeRequest( + userDID, + msgBytes, + options + ); + + expect(ciResponse).not.be.undefined; + expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( + proofReqs[0].id + ); + }); }); From 14a40801deac2a0d58b4feb4274e634504c4ca8a Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Thu, 15 Aug 2024 18:44:23 +0300 Subject: [PATCH 02/41] fix unit test --- tests/handlers/contract-request.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index aed304a4..e9c8a6c7 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -280,7 +280,7 @@ describe('contract-request', () => { const transactionData: ContractInvokeTransactionData = { contract_address: '0x134b1be34911e39a8397ec6289782989729807a4', - method_id: '123', + method_id: 'b68967e2', chain_id: 80001 }; @@ -646,7 +646,7 @@ describe('contract-request', () => { it.skip('cross chain contract request flow - integration test', async () => { const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; - const amoyVerifierRpcUrl = '<> '; // verifier RPC URL - amoy + const amoyVerifierRpcUrl = '<>'; // verifier RPC URL - amoy const erc20Verifier = '0xf8a8d8389938261a7827eac9b4b6b2b68189bef2'; const issuerStateEthConfig = defaultEthConnectionConfig; From 977d0f21004893dcaef7f68096a82226bbbca6e7 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Fri, 16 Aug 2024 15:14:49 +0300 Subject: [PATCH 03/41] submitZKPResponseV2 new method abi --- src/circuits/atomic-query-v3-on-chain.ts | 3 +- src/circuits/common.ts | 1 - src/iden3comm/handlers/contract-request.ts | 6 +- src/iden3comm/types/protocol/auth.ts | 13 +- src/proof/proof-service.ts | 7 +- src/proof/verifiers/pub-signals-verifier.ts | 6 +- src/proof/verifiers/query.ts | 7 +- src/storage/blockchain/abi/ZkpVerifier.json | 177 +++--------------- .../blockchain/onchain-zkp-verifier.ts | 64 ++++--- .../interfaces/onchain-zkp-verifier.ts | 4 +- src/verifiable/presentation.ts | 4 +- tests/handlers/contract-request.test.ts | 6 +- 12 files changed, 104 insertions(+), 194 deletions(-) diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index 06ca0225..9a7b03db 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -525,8 +525,7 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistR userId: this.userID, gist: this.gistRoot, issuerState: this.issuerState, - nonRevState: this.issuerClaimNonRevState, - operatorOutput: this.operatorOutput + nonRevState: this.issuerClaimNonRevState }; } } diff --git a/src/circuits/common.ts b/src/circuits/common.ts index 444bd8ae..32b7296c 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -240,7 +240,6 @@ export type OnChainStateInfo = { gist: Hash; issuerState: Hash; nonRevState: Hash; - operatorOutput?: bigint; }; /** diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 38cbfe6f..1c0ca76d 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -129,8 +129,8 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { - case OnChainZKPVerifier.SupportedCrossChainMethodId: - return this._zkpVerifier.submitZKPResponseCrossChain( + case OnChainZKPVerifier.SupportedMethodIdV2: + return this._zkpVerifier.submitZKPResponseV2( ethSigner, message.body.transaction_data, zkpResponses @@ -143,7 +143,7 @@ export class ContractRequestHandler ); default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedCrossChainMethodId} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` ); } } diff --git a/src/iden3comm/types/protocol/auth.ts b/src/iden3comm/types/protocol/auth.ts index 42d17def..b36ae3ec 100644 --- a/src/iden3comm/types/protocol/auth.ts +++ b/src/iden3comm/types/protocol/auth.ts @@ -45,5 +45,16 @@ export type ZeroKnowledgeProofRequest = { export type ZeroKnowledgeProofResponse = { id: number; circuitId: string; - vp?: object; + vp?: VerifiablePresentation; } & ZKProof; + +/** VerifiablePresentation represents structure of Verifiable Presentation */ +export type VerifiablePresentation = { + '@context': string[]; + '@type': string; + verifiableCredential: { + '@context': string[]; + '@type': string[]; + credentialSubject: JSONObject; + }; +}; diff --git a/src/proof/proof-service.ts b/src/proof/proof-service.ts index d75e56c6..b8dbff63 100644 --- a/src/proof/proof-service.ts +++ b/src/proof/proof-service.ts @@ -39,7 +39,8 @@ import { JSONObject, ZeroKnowledgeProofRequest, ZeroKnowledgeProofResponse, - PROTOCOL_CONSTANTS + PROTOCOL_CONSTANTS, + VerifiablePresentation } from '../iden3comm'; import { cacheLoader } from '../schema-processor'; import { ICircuitStorage, IStateStorage } from '../storage'; @@ -219,7 +220,7 @@ export class ProofService implements IProofService { const verifyContext: VerifyContext = { pubSignals: proofResp.pub_signals, query: opts.query, - verifiablePresentation: proofResp.vp as JSON, + verifiablePresentation: proofResp.vp, sender: opts.sender, challenge: BigInt(proofResp.id), opts: opts.opts, @@ -339,7 +340,7 @@ export class ProofService implements IProofService { ); const sdQueries = queriesMetadata.filter((q) => q.operator === Operators.SD); - let vp: object | undefined; + let vp: VerifiablePresentation | undefined; if (sdQueries.length) { vp = createVerifiablePresentation( context, diff --git a/src/proof/verifiers/pub-signals-verifier.ts b/src/proof/verifiers/pub-signals-verifier.ts index 81a06622..7b106d44 100644 --- a/src/proof/verifiers/pub-signals-verifier.ts +++ b/src/proof/verifiers/pub-signals-verifier.ts @@ -32,7 +32,7 @@ import { parseQueriesMetadata, QueryMetadata } from '../common'; import { Operators } from '../../circuits'; import { calculateQueryHashV3 } from './query-hash'; import { JsonLd } from 'jsonld/jsonld-spec'; -import { PROTOCOL_CONSTANTS, JSONObject } from '../../iden3comm'; +import { PROTOCOL_CONSTANTS, JSONObject, VerifiablePresentation } from '../../iden3comm'; /** * Verify Context - params for pub signal verification @@ -41,7 +41,7 @@ import { PROTOCOL_CONSTANTS, JSONObject } from '../../iden3comm'; export type VerifyContext = { pubSignals: string[]; query: ProofQuery; - verifiablePresentation?: JSON; + verifiablePresentation?: VerifiablePresentation; sender: string; challenge: bigint; opts?: VerifyOpts; @@ -521,7 +521,7 @@ export class PubSignalsVerifier { query: ProofQuery, outs: ClaimOutputs, opts: VerifyOpts | undefined, - verifiablePresentation: JSON | undefined + verifiablePresentation: VerifiablePresentation | undefined ) { if (!query.type) { throw new Error(`proof query type is undefined`); diff --git a/src/proof/verifiers/query.ts b/src/proof/verifiers/query.ts index 5afb9cb9..4fb2751f 100644 --- a/src/proof/verifiers/query.ts +++ b/src/proof/verifiers/query.ts @@ -8,6 +8,7 @@ import { calculateCoreSchemaHash, ProofQuery, VerifiableConstants } from '../../ import { QueryMetadata } from '../common'; import { circuitValidator } from '../provers'; import { JsonLd } from 'jsonld/jsonld-spec'; +import { VerifiablePresentation } from '../../iden3comm'; /** * Options to verify state @@ -189,7 +190,7 @@ export async function validateOperators(cq: QueryMetadata, outputs: ClaimOutputs export async function validateDisclosureV2Circuit( cq: QueryMetadata, outputs: ClaimOutputs, - verifiablePresentation?: JSON, + verifiablePresentation?: VerifiablePresentation, ldLoader?: DocumentLoader ) { const bi = await fieldValueFromVerifiablePresentation( @@ -215,7 +216,7 @@ export async function validateDisclosureV2Circuit( export async function validateDisclosureNativeSDSupport( cq: QueryMetadata, outputs: ClaimOutputs, - verifiablePresentation?: JSON, + verifiablePresentation?: VerifiablePresentation, ldLoader?: DocumentLoader ) { const bi = await fieldValueFromVerifiablePresentation( @@ -250,7 +251,7 @@ export async function validateEmptyCredentialSubjectNoopNativeSupport(outputs: C export const fieldValueFromVerifiablePresentation = async ( fieldName: string, - verifiablePresentation?: JSON, + verifiablePresentation?: VerifiablePresentation, ldLoader?: DocumentLoader ): Promise => { if (!verifiablePresentation) { diff --git a/src/storage/blockchain/abi/ZkpVerifier.json b/src/storage/blockchain/abi/ZkpVerifier.json index aad817b7..3134341a 100644 --- a/src/storage/blockchain/abi/ZkpVerifier.json +++ b/src/storage/blockchain/abi/ZkpVerifier.json @@ -40,28 +40,6 @@ "name": "NotInitializing", "type": "error" }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, { "anonymous": false, "inputs": [ @@ -75,44 +53,6 @@ "name": "Initialized", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, { "inputs": [], "name": "REQUESTS_RETURN_LIMIT", @@ -126,26 +66,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract IStateCrossChain", - "name": "_stateCrossChain", - "type": "address" - } - ], - "name": "__ZKPVerifierBase_init", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -335,39 +255,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -458,40 +345,34 @@ { "inputs": [ { - "internalType": "uint64", - "name": "requestId", - "type": "uint64" - }, - { - "internalType": "bytes", - "name": "zkProof", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "crossChainProof", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "submitZKPResponseCrossChain", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" + "components": [ + { + "internalType": "uint64", + "name": "requestId", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "zkProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "crossChainProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct ZKPResponse[]", + "name": "responses", + "type": "tuple[]" } ], - "name": "transferOwnership", + "name": "submitZKPResponseV2", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -558,16 +439,16 @@ }, { "internalType": "uint256", - "name": "inputIndex", + "name": "inputValue", "type": "uint256" } ], - "internalType": "struct ICircuitValidator.KeyToInputIndex[]", + "internalType": "struct ICircuitValidator.KeyToInputValue[]", "name": "", "type": "tuple[]" } ], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" } ] \ No newline at end of file diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 924acb0d..a31d0060 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -33,14 +33,17 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { /** * solidity identifier for function signature: - * function submitZKPResponseCrossChain( - uint64 requestId, - bytes calldata zkProof, - bytes calldata crossChainProof, - bytes calldata data + * struct ZKPResponse { + uint64 requestId; + bytes zkProof; + bytes crossChainProof; + bytes data; + } + * function submitZKPResponseV2( + ZKPResponse[] memory responses ) public */ - public static readonly SupportedCrossChainMethodId = '1c100d01'; + public static readonly SupportedMethodIdV2 = 'fd41d8d4'; /** * supported circuits @@ -133,9 +136,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } /** - * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseCrossChain} + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} */ - public async submitZKPResponseCrossChain( + public async submitZKPResponseV2( ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] @@ -144,9 +147,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); } - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedCrossChainMethodId) { + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedCrossChainMethodId}' is supported.` + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` ); } if (!this._didResolverUrl) { @@ -165,7 +168,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!this._supportedCircuits.includes(zkProof.circuitId as CircuitId)) { throw new Error(`Circuit ${zkProof.circuitId} not supported by OnChainZKPVerifier`); } - const { gist, issuerState, nonRevState, issuerId, userId, operatorOutput } = + const { gist, issuerState, nonRevState, issuerId, userId } = this.getOnChainGistRootStatePubSignals( zkProof.circuitId as | CircuitId.AtomicQueryMTPV2OnChain @@ -211,19 +214,34 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { issuerNonRevResolution ); - let metadataArr: { key: string; value: string }[] = []; - if (operatorOutput && operatorOutput !== 0n) { - const metadataValue = poseidon.hash([operatorOutput]); - metadataArr = [ - { - key: 'operator output', - value: `0x${metadataValue.toString(16)}` // TODO: fix + const metadataArr: { key: string; value: Uint8Array }[] = []; + if (zkProof.vp) { + for (const key in zkProof.vp.verifiableCredential.credentialSubject) { + if (key === '@type') { + continue; } - ]; + const metadataValue = poseidon.hashBytes( + byteEncoder.encode( + JSON.stringify(zkProof.vp.verifiableCredential.credentialSubject[key]) + ) + ); + const bytesValue = byteEncoder.encode(metadataValue.toString()); + metadataArr.push({ + key, + value: bytesValue + }); + } } const metadata = this.packMetadatas(metadataArr); - const payload = [requestID, zkProofEncoded, crossChainProofs, metadata]; + const payload = [ + { + requestId: requestID, + zkProof: zkProofEncoded, + crossChainProof: crossChainProofs, + data: metadata + } + ]; const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas @@ -233,8 +251,8 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ? BigInt(chainConfig.maxPriorityFeePerGas) : feeData.maxPriorityFeePerGas; - const gasLimit = await contract.submitZKPResponseCrossChain.estimateGas(...payload); - const txData = await contract.submitZKPResponseCrossChain.populateTransaction(...payload); + const gasLimit = await contract.submitZKPResponseV2.estimateGas(payload); + const txData = await contract.submitZKPResponseV2.populateTransaction(payload); const request: TransactionRequest = { to: txData.to, @@ -326,7 +344,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { private packMetadatas( metas: { key: string; - value: string; + value: Uint8Array; }[] ): string { return this._abiCoder.encode(['tuple(' + 'string key,' + 'bytes value' + ')[]'], [metas]); diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 8d936362..76c996d7 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -23,14 +23,14 @@ export interface IOnChainZKPVerifier { ): Promise>; /** - * Submit ZKP Response Cross Chain to OnChainZKPVerifier contract. + * Submit ZKP Response V2 to OnChainZKPVerifier contract. * @beta * @param {Signer} ethSigner - tx signer * @param {txData} ContractInvokeTransactionData - transaction data * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse */ - submitZKPResponseCrossChain( + submitZKPResponseV2( ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] diff --git a/src/verifiable/presentation.ts b/src/verifiable/presentation.ts index 7d3448bb..ee9a0c4f 100644 --- a/src/verifiable/presentation.ts +++ b/src/verifiable/presentation.ts @@ -2,7 +2,7 @@ import { VerifiableConstants } from './constants'; import { Options, Path } from '@iden3/js-jsonld-merklization'; import { W3CCredential } from './credential'; import { QueryMetadata } from '../proof'; -import { JSONObject } from '../iden3comm'; +import { JSONObject, VerifiablePresentation } from '../iden3comm'; export const stringByPath = (obj: { [key: string]: unknown }, path: string): string => { const parts = path.split('.'); @@ -50,7 +50,7 @@ export const createVerifiablePresentation = ( tp: string, credential: W3CCredential, queries: QueryMetadata[] -): object => { +): VerifiablePresentation => { const baseContext = [VerifiableConstants.JSONLD_SCHEMA.W3C_CREDENTIAL_2018]; const ldContext = baseContext[0] === context ? baseContext : [...baseContext, context]; diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index e9c8a6c7..e62b7a7b 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -124,7 +124,7 @@ describe('contract-request', () => { return response; }, - submitZKPResponseCrossChain: async ( + submitZKPResponseV2: async ( signer: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] @@ -647,7 +647,7 @@ describe('contract-request', () => { const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; const amoyVerifierRpcUrl = '<>'; // verifier RPC URL - amoy - const erc20Verifier = '0xf8a8d8389938261a7827eac9b4b6b2b68189bef2'; + const erc20Verifier = '0x7F98857f2EF85407495cceD35Ff0aa0681128f03'; const issuerStateEthConfig = defaultEthConnectionConfig; issuerStateEthConfig.url = privadoTestRpcUrl; @@ -760,7 +760,7 @@ describe('contract-request', () => { const transactionData: ContractInvokeTransactionData = { contract_address: erc20Verifier, - method_id: '1c100d01', + method_id: 'fd41d8d4', chain_id: conf.chainId }; From bf4379e61024ee18ad542d54b92afa05b3cdeb0f Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 19 Aug 2024 12:13:10 +0300 Subject: [PATCH 04/41] add email-verified integration test --- tests/handlers/contract-request.test.ts | 176 +++++++++++++++++++++++- 1 file changed, 175 insertions(+), 1 deletion(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index e62b7a7b..57545f07 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -647,7 +647,7 @@ describe('contract-request', () => { const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; const amoyVerifierRpcUrl = '<>'; // verifier RPC URL - amoy - const erc20Verifier = '0x7F98857f2EF85407495cceD35Ff0aa0681128f03'; + const erc20Verifier = '0x74030e4c5d53ef381A889C01f0bBd3B8336F4a4a'; const issuerStateEthConfig = defaultEthConnectionConfig; issuerStateEthConfig.url = privadoTestRpcUrl; @@ -799,4 +799,178 @@ describe('contract-request', () => { proofReqs[0].id ); }); + + it.skip('contract request flow V3 sig `email-verified` transak req - integration test', async () => { + const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test + const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; + const amoyVerifierRpcUrl = '<>'; // verifier RPC URL - amoy + const verifierAddress = '0x74030e4c5d53ef381A889C01f0bBd3B8336F4a4a'; + + const issuerStateEthConfig = defaultEthConnectionConfig; + issuerStateEthConfig.url = privadoTestRpcUrl; + issuerStateEthConfig.contractAddress = privadoTestStateContract; // privado test state contract + + const memoryKeyStore = new InMemoryPrivateKeyStore(); + const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); + const kms = new KMS(); + kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + dataStorage = { + credential: new CredentialStorage(new InMemoryDataSource()), + identity: new IdentityStorage( + new InMemoryDataSource(), + new InMemoryDataSource() + ), + mt: new InMemoryMerkleTreeStorage(40), + states: new EthStateStorage(issuerStateEthConfig) + }; + const circuitStorage = new FSCircuitStorage({ + dirname: path.join(__dirname, '../proofs/testdata') + }); + + const resolvers = new CredentialStatusResolverRegistry(); + resolvers.register( + CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + new RHSResolver(dataStorage.states) + ); + credWallet = new CredentialWallet(dataStorage, resolvers); + idWallet = new IdentityWallet(kms, dataStorage, credWallet); + + proofService = new ProofService(idWallet, credWallet, circuitStorage, dataStorage.states, { + ipfsGatewayURL: 'https://ipfs.io' + }); + packageMgr = await getPackageMgr( + await circuitStorage.loadCircuitData(CircuitId.AuthV2), + proofService.generateAuthV2Inputs.bind(proofService), + proofService.verifyState.bind(proofService) + ); + + const { did: userDID, credential: cred } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Privado, + networkId: NetworkId.Main, + seed: seedPhrase, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + + expect(cred).not.to.be.undefined; + + const { did: issuerDID, credential: issuerAuthCredential } = await idWallet.createIdentity({ + method: DidMethod.Iden3, + blockchain: Blockchain.Privado, + networkId: NetworkId.Test, + seed: seedPhraseIssuer, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }); + expect(issuerAuthCredential).not.to.be.undefined; + + const profileDID = await idWallet.createProfile(userDID, 777, issuerDID.string()); + + const claimReq: CredentialRequest = { + credentialSchema: 'ipfs://QmYgooZFeXYi1QQm6iUpiEteMJ822pUSuxonXUpqNgFVnQ', + type: 'IndividualKYC', + credentialSubject: { + id: profileDID.string(), + 'full-name': 'full-name', + country: 'USA', + state: 'Florida', + city: 'homeland', + street: 'groove', + 'zip-code': '123', + phone: '333', + email: 'me-eme-e@gmail.com', + 'email-verified': true + }, + expiration: 2793526400, + revocationOpts: { + type: CredentialStatusType.Iden3ReverseSparseMerkleTreeProof, + id: rhsUrl + } + }; + const issuerCred = await idWallet.issueCredential(issuerDID, claimReq, { + ipfsGatewayURL: 'https://ipfs.io' + }); + + await credWallet.save(issuerCred); + + const proofReqs: ZeroKnowledgeProofRequest[] = [ + { + id: 1, + circuitId: CircuitId.AtomicQueryV3OnChain, + optional: false, + query: { + allowedIssuers: ['*'], + type: claimReq.type, + proofType: ProofType.BJJSignature, + context: 'ipfs://Qmdhuf9fhqzweDa1TgoajDEj7Te7p28eeeZVfiioAjUC15', + credentialSubject: { + 'email-verified': { + $eq: true + } + } + }, + params: { + nullifierSessionId: '8372131' + } + } + ]; + + const conf = defaultEthConnectionConfig; + conf.contractAddress = verifierAddress; + conf.url = amoyVerifierRpcUrl; + conf.chainId = 80002; + + const zkpVerifier = new OnChainZKPVerifier([conf], 'https://resolver-dev.privado.id'); + contractRequestHandler = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); + + const transactionData: ContractInvokeTransactionData = { + contract_address: verifierAddress, + method_id: 'fd41d8d4', + chain_id: conf.chainId + }; + + const verifierDid = 'did:iden3:polygon:amoy:x6x5sor7zpy1YGS4yjcmnzQSC7FZC7q7DPgNMT79q'; + + const ciRequestBody: ContractInvokeRequestBody = { + reason: 'reason', + transaction_data: transactionData, + scope: proofReqs + }; + + const id = uuid.v4(); + const ciRequest: ContractInvokeRequest = { + id, + typ: MediaType.PlainMessage, + type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE, + thid: id, + body: ciRequestBody, + from: verifierDid + }; + + console.log(JSON.stringify(ciRequest)); + const ethSigner = new ethers.Wallet(walletKey); + + const challenge = BytesHelper.bytesToInt(hexToBytes(ethSigner.address)); + + const options: ContractInvokeHandlerOptions = { + ethSigner, + challenge + }; + const msgBytes = byteEncoder.encode(JSON.stringify(ciRequest)); + const ciResponse = await contractRequestHandler.handleContractInvokeRequest( + userDID, + msgBytes, + options + ); + + expect(ciResponse).not.be.undefined; + expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( + proofReqs[0].id + ); + }); }); From 330c60f1ddb6516ad14014ce4242a8348872dec0 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 19 Aug 2024 15:45:23 +0300 Subject: [PATCH 05/41] add prepareContractInvokeRequestTxData --- src/iden3comm/handlers/contract-request.ts | 55 +++++++ .../blockchain/onchain-zkp-verifier.ts | 147 ++++++++++++------ .../interfaces/onchain-zkp-verifier.ts | 20 +++ tests/handlers/contract-request.test.ts | 8 + 4 files changed, 183 insertions(+), 47 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 1c0ca76d..0f5044e8 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -185,4 +185,59 @@ export class ContractRequestHandler challenge: opts.challenge }); } + + /** + * prepare contract invoker request transaction data + * @beta + * @param {did} did - sender DID + * @param {ContractInvokeRequest} request - contract invoke request + * @param {ContractInvokeHandlerOptions} opts - handler options + * @returns {Map}` - map of transaction hash - ZeroKnowledgeProofResponse + */ + async prepareContractInvokeRequestTxData( + did: DID, + request: Uint8Array, + opts?: { + challenge?: bigint; + } + ): Promise> { + const message = await this.parseContractInvokeRequest(request); + + if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { + throw new Error('Invalid message type for contract invoke request'); + } + + const { chain_id } = message.body.transaction_data; + const networkFlag = Object.keys(ChainIds).find((key) => ChainIds[key] === chain_id); + + if (!networkFlag) { + throw new Error(`Invalid chain id ${chain_id}`); + } + const verifierDid = message.from ? DID.parse(message.from) : undefined; + const zkpResponses = await processZeroKnowledgeProofRequests( + did, + message?.body?.scope, + verifierDid, + this._proofService, + { challenge: opts?.challenge, supportedCircuits: this._supportedCircuits } + ); + + const methodId = message.body.transaction_data.method_id.replace('0x', ''); + switch (methodId) { + case OnChainZKPVerifier.SupportedMethodIdV2: + return this._zkpVerifier.prepareZKPResponseV2TxData( + message.body.transaction_data, + zkpResponses + ); + case OnChainZKPVerifier.SupportedMethodId: + return this._zkpVerifier.prepareZKPResponseTxData( + message.body.transaction_data, + zkpResponses + ); + default: + throw new Error( + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + ); + } + } } diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index a31d0060..34c30514 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -71,13 +71,12 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ) {} /** - * {@inheritDoc IOnChainZKPVerifier.submitZKPResponse} + * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseTxData} */ - public async submitZKPResponse( - ethSigner: Signer, + public async prepareZKPResponseTxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + ): Promise> { const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); @@ -87,12 +86,8 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` ); } - const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); - const verifierContract = new Contract(txData.contract_address, abi, provider); - ethSigner = ethSigner.connect(provider); - const contract = verifierContract.connect(ethSigner) as Contract; - - const response = new Map(); + const verifierContract = new Contract(txData.contract_address, abi); + const response = new Map(); for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -108,25 +103,57 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.proof.pi_c.slice(0, 2) ]; - const feeData = await provider.getFeeData(); - const maxFeePerGas = chainConfig.maxFeePerGas - ? BigInt(chainConfig.maxFeePerGas) - : feeData.maxFeePerGas; - const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas - ? BigInt(chainConfig.maxPriorityFeePerGas) - : feeData.maxPriorityFeePerGas; + const txData = await verifierContract.submitZKPResponse.populateTransaction(...payload); + response.set(requestID, txData.data); + } + + return response; + } + + /** + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponse} + */ + public async submitZKPResponse( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); + if (!chainConfig) { + throw new Error(`config for chain id ${txData.chain_id} was not found`); + } + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { + throw new Error( + `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` + ); + } + const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); + ethSigner = ethSigner.connect(provider); + + const txDataMap = await this.prepareZKPResponseTxData(txData, zkProofResponses); + const response = new Map(); + + const feeData = await provider.getFeeData(); + const maxFeePerGas = chainConfig.maxFeePerGas + ? BigInt(chainConfig.maxFeePerGas) + : feeData.maxFeePerGas; + const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas + ? BigInt(chainConfig.maxPriorityFeePerGas) + : feeData.maxPriorityFeePerGas; - const gasLimit = await contract.submitZKPResponse.estimateGas(...payload); - const txData = await contract.submitZKPResponse.populateTransaction(...payload); + for (const zkProof of zkProofResponses) { + const payload = txDataMap.get(zkProof.id); const request: TransactionRequest = { - to: txData.to, - data: txData.data, - gasLimit, + to: txData.contract_address, + data: payload, maxFeePerGas, maxPriorityFeePerGas }; + const gasLimit = await ethSigner.estimateGas(request); + request.gasLimit = gasLimit; + const transactionService = new TransactionService(provider); const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); response.set(txnHash, zkProof); @@ -136,17 +163,12 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } /** - * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} + * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseV2TxData} */ - public async submitZKPResponseV2( - ethSigner: Signer, + public async prepareZKPResponseV2TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { - const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); - if (!chainConfig) { - throw new Error(`config for chain id ${txData.chain_id} was not found`); - } + ): Promise> { if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { throw new Error( `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` @@ -155,12 +177,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!this._didResolverUrl) { throw new Error(`did resolver url required for crosschain verification`); } - const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); - const verifierContract = new Contract(txData.contract_address, abi, provider); - ethSigner = ethSigner.connect(provider); - const contract = verifierContract.connect(ethSigner) as Contract; + const verifierContract = new Contract(txData.contract_address, abi); - const response = new Map(); + const response = new Map(); for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -243,25 +262,59 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } ]; - const feeData = await provider.getFeeData(); - const maxFeePerGas = chainConfig.maxFeePerGas - ? BigInt(chainConfig.maxFeePerGas) - : feeData.maxFeePerGas; - const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas - ? BigInt(chainConfig.maxPriorityFeePerGas) - : feeData.maxPriorityFeePerGas; + const txData = await verifierContract.submitZKPResponseV2.populateTransaction(payload); + + response.set(requestID, txData.data); + } - const gasLimit = await contract.submitZKPResponseV2.estimateGas(payload); - const txData = await contract.submitZKPResponseV2.populateTransaction(payload); + return response; + } + /** + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} + */ + public async submitZKPResponseV2( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); + if (!chainConfig) { + throw new Error(`config for chain id ${txData.chain_id} was not found`); + } + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { + throw new Error( + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` + ); + } + if (!this._didResolverUrl) { + throw new Error(`did resolver url required for crosschain verification`); + } + const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); + ethSigner = ethSigner.connect(provider); + + const txDataMap = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); + const feeData = await provider.getFeeData(); + const maxFeePerGas = chainConfig.maxFeePerGas + ? BigInt(chainConfig.maxFeePerGas) + : feeData.maxFeePerGas; + const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas + ? BigInt(chainConfig.maxPriorityFeePerGas) + : feeData.maxPriorityFeePerGas; + + const response = new Map(); + for (const zkProof of zkProofResponses) { + const payload = txDataMap.get(zkProof.id); const request: TransactionRequest = { - to: txData.to, - data: txData.data, - gasLimit, + to: txData.contract_address, + data: payload, maxFeePerGas, maxPriorityFeePerGas }; + const gasLimit = await ethSigner.estimateGas(request); + request.gasLimit = gasLimit; + const transactionService = new TransactionService(provider); const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); response.set(txnHash, zkProof); diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 76c996d7..20bdb6fb 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -35,4 +35,24 @@ export interface IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; + + /** + * Returns the Map of request id to transaction data for the ZKP verifier contract submission. + * @param txData + * @param zkProofResponses + */ + prepareZKPResponseTxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; + + /** + * Returns the Map of request id to transaction data for the ZKP verifier contract submission V2. + * @param txData + * @param zkProofResponses + */ + prepareZKPResponseV2TxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; } diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 57545f07..2b5f3f59 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -132,6 +132,14 @@ describe('contract-request', () => { const response = new Map(); response.set('txhash1', zkProofResponses[0]); return response; + }, + + prepareZKPResponseTxData: async () => { + return new Map(); + }, + + prepareZKPResponseV2TxData: async () => { + return new Map(); } }; From a49cd9f42b8bb1b3aae4e915ea58d814104f05f4 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 19 Aug 2024 15:50:32 +0300 Subject: [PATCH 06/41] fix typo --- src/circuits/atomic-query-mtp-v2-on-chain.ts | 4 ++-- src/circuits/atomic-query-sig-v2-on-chain.ts | 4 ++-- src/circuits/atomic-query-v3-on-chain.ts | 4 ++-- src/circuits/common.ts | 2 +- src/storage/blockchain/onchain-zkp-verifier.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index c898e38c..5aa0b2bf 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -328,8 +328,8 @@ export class AtomicQueryMTPV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ - getGistRootStatePugSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ + getGistRootStatePubSignals(): OnChainStateInfo { return { issuerId: this.issuerID, userId: this.userID, diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index fff6c23e..fce9d20e 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -403,8 +403,8 @@ export class AtomicQuerySigV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ - getGistRootStatePugSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ + getGistRootStatePubSignals(): OnChainStateInfo { return { issuerId: this.issuerID, userId: this.userID, diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index 9a7b03db..7f8462b7 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -518,8 +518,8 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistR return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePugSignals} */ - getGistRootStatePugSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ + getGistRootStatePubSignals(): OnChainStateInfo { return { issuerId: this.issuerID, userId: this.userID, diff --git a/src/circuits/common.ts b/src/circuits/common.ts index 32b7296c..e7ba3ee8 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -254,5 +254,5 @@ export interface IGistRootStatePubSignals { * * @returns {OnChainStateInfo} */ - getGistRootStatePugSignals(): OnChainStateInfo; + getGistRootStatePubSignals(): OnChainStateInfo; } diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 34c30514..a46dccd1 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -424,6 +424,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const encodedInputs = byteEncoder.encode(JSON.stringify(inputs)); atomicQueryPubSignals.pubSignalsUnmarshal(encodedInputs); - return atomicQueryPubSignals.getGistRootStatePugSignals(); + return atomicQueryPubSignals.getGistRootStatePubSignals(); } } From 75e4f13fec23202e9ef2820a2e55cc17340fdfa6 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 19 Aug 2024 16:35:51 +0300 Subject: [PATCH 07/41] refactor OnChainStateInfo --- src/circuits/atomic-query-mtp-v2-on-chain.ts | 10 +- src/circuits/atomic-query-sig-v2-on-chain.ts | 10 +- src/circuits/atomic-query-v3-on-chain.ts | 10 +- src/circuits/common.ts | 13 ++- .../blockchain/onchain-zkp-verifier.ts | 93 +++++++++---------- 5 files changed, 69 insertions(+), 67 deletions(-) diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index 5aa0b2bf..be6afc7c 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -331,11 +331,11 @@ export class AtomicQueryMTPV2OnChainPubSignals /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ getGistRootStatePubSignals(): OnChainStateInfo { return { - issuerId: this.issuerID, - userId: this.userID, - gist: this.gistRoot, - issuerState: this.issuerClaimIdenState, - nonRevState: this.issuerClaimNonRevState + states: [ + { id: this.issuerID, state: this.issuerClaimIdenState }, + { id: this.issuerID, state: this.issuerClaimNonRevState } + ], + gists: [{ id: this.userID, root: this.gistRoot }] }; } } diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index fce9d20e..918a930b 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -406,11 +406,11 @@ export class AtomicQuerySigV2OnChainPubSignals /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ getGistRootStatePubSignals(): OnChainStateInfo { return { - issuerId: this.issuerID, - userId: this.userID, - gist: this.gistRoot, - issuerState: this.issuerAuthState, - nonRevState: this.issuerClaimNonRevState + states: [ + { id: this.issuerID, state: this.issuerAuthState }, + { id: this.issuerID, state: this.issuerClaimNonRevState } + ], + gists: [{ id: this.userID, root: this.gistRoot }] }; } } diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index 7f8462b7..f95cc365 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -521,11 +521,11 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistR /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ getGistRootStatePubSignals(): OnChainStateInfo { return { - issuerId: this.issuerID, - userId: this.userID, - gist: this.gistRoot, - issuerState: this.issuerState, - nonRevState: this.issuerClaimNonRevState + states: [ + { id: this.issuerID, state: this.issuerState }, + { id: this.issuerID, state: this.issuerClaimNonRevState } + ], + gists: [{ id: this.userID, root: this.gistRoot }] }; } } diff --git a/src/circuits/common.ts b/src/circuits/common.ts index e7ba3ee8..69c68d0a 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -235,11 +235,14 @@ export function getProperties(obj: object): object { * @type OnChainStateInfo */ export type OnChainStateInfo = { - issuerId: Id; - userId: Id; - gist: Hash; - issuerState: Hash; - nonRevState: Hash; + states: { + id: Id; + state: Hash; + }[]; + gists: { + id: Id; + root: Hash; + }[]; }; /** diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index a46dccd1..e4ba3148 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -187,17 +187,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!this._supportedCircuits.includes(zkProof.circuitId as CircuitId)) { throw new Error(`Circuit ${zkProof.circuitId} not supported by OnChainZKPVerifier`); } - const { gist, issuerState, nonRevState, issuerId, userId } = - this.getOnChainGistRootStatePubSignals( - zkProof.circuitId as - | CircuitId.AtomicQueryMTPV2OnChain - | CircuitId.AtomicQuerySigV2OnChain - | CircuitId.AtomicQueryV3OnChain, - zkProof.pub_signals - ); - - const userDid = DID.parseFromId(userId); - const issuerDid = DID.parseFromId(issuerId); const zkProofEncoded = this.packZkpProof( inputs, @@ -209,30 +198,41 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.proof.pi_c.slice(0, 2) ); - const globalStateUpdate = (await resolveDidDocumentEip712MessageAndSignature( - userDid, - this._didResolverUrl, - { gist } - )) as GlobalStateUpdate; - - const issuerStateResolution = (await resolveDidDocumentEip712MessageAndSignature( - issuerDid, - this._didResolverUrl, - { state: issuerState } - )) as IdentityStateUpdate; - - const issuerNonRevResolution = (await resolveDidDocumentEip712MessageAndSignature( - issuerDid, - this._didResolverUrl, - { state: nonRevState } - )) as IdentityStateUpdate; - - const crossChainProofs = this.packCrossChainProofs( - globalStateUpdate, - issuerStateResolution, - issuerNonRevResolution + const stateInfo = this.getOnChainGistRootStatePubSignals( + zkProof.circuitId as + | CircuitId.AtomicQueryMTPV2OnChain + | CircuitId.AtomicQuerySigV2OnChain + | CircuitId.AtomicQueryV3OnChain, + zkProof.pub_signals ); + const gistUpdateResolutions = []; + for (const gist of stateInfo.gists) { + gistUpdateResolutions.push( + resolveDidDocumentEip712MessageAndSignature( + DID.parseFromId(gist.id), + this._didResolverUrl, + { gist: gist.root } + ) + ); + } + + const stateUpdateResolutions = []; + for (const state of stateInfo.states) { + stateUpdateResolutions.push( + resolveDidDocumentEip712MessageAndSignature( + DID.parseFromId(state.id), + this._didResolverUrl, + { state: state.state } + ) + ); + } + + const gistUpdateArr = (await Promise.all(gistUpdateResolutions)) as GlobalStateUpdate[]; + const stateUpdateArr = (await Promise.all(stateUpdateResolutions)) as IdentityStateUpdate[]; + + const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); + const metadataArr: { key: string; value: Uint8Array }[] = []; if (zkProof.vp) { for (const key in zkProof.vp.verifiableCredential.credentialSubject) { @@ -331,24 +331,23 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } private packCrossChainProofs( - globalStateUpdate: GlobalStateUpdate, - issuerStateResolution: IdentityStateUpdate, - issuerNonRevResolution: IdentityStateUpdate + gistUpdateArr: GlobalStateUpdate[], + stateUpdateArr: IdentityStateUpdate[] ) { - const proofs = [ - { + const proofs = []; + + for (const globalStateUpdate of gistUpdateArr) { + proofs.push({ proofType: 'globalStateProof', proof: this.packGlobalStateMsg(globalStateUpdate) - }, - { - proofType: 'stateProof', - proof: this.packIdentityStateMsg(issuerStateResolution) - }, - { + }); + } + for (const stateUpdate of stateUpdateArr) { + proofs.push({ proofType: 'stateProof', - proof: this.packIdentityStateMsg(issuerNonRevResolution) - } - ]; + proof: this.packIdentityStateMsg(stateUpdate) + }); + } return this._abiCoder.encode( ['tuple(' + 'string proofType,' + 'bytes proof' + ')[]'], [proofs] From 1547dc694219b4e6446d6f868f0e669ebf4b904c Mon Sep 17 00:00:00 2001 From: volodymyr-basiuk <31999965+volodymyr-basiuk@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:02:30 +0300 Subject: [PATCH 08/41] fix: getStateContractAndProviderForId - throw error if no config for chainId found (#256) * fix: getStateContractAndProviderForId - throw error if no config for chainId found * provide chainId in config --- src/storage/blockchain/state.ts | 20 +++++--------------- tests/handlers/auth.test.ts | 1 + 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/storage/blockchain/state.ts b/src/storage/blockchain/state.ts index 60a52eda..bd4a2a57 100644 --- a/src/storage/blockchain/state.ts +++ b/src/storage/blockchain/state.ts @@ -240,13 +240,6 @@ export class EthStateStorage implements IStateStorage { provider: JsonRpcProvider; } { const idTyped = Id.fromBigInt(id as bigint); - if (!Array.isArray(this.ethConfig)) { - return { - stateContract: this.stateContract, - provider: this.provider - }; - } - const chainId = getChainId(DID.blockchainFromId(idTyped), DID.networkIdFromId(idTyped)); const config = this.networkByChainId(chainId); @@ -257,14 +250,11 @@ export class EthStateStorage implements IStateStorage { } private networkByChainId(chainId: number): EthConnectionConfig { - if (Array.isArray(this.ethConfig)) { - const network = this.ethConfig.find((c) => c.chainId === chainId); - if (!network) { - throw new Error(`chainId "${chainId}" not supported`); - } - return network; + const config = Array.isArray(this.ethConfig) ? this.ethConfig : [this.ethConfig]; + const network = config.find((c) => c.chainId === chainId); + if (!network) { + throw new Error(`chainId "${chainId}" not supported`); } - - return this.ethConfig as EthConnectionConfig; + return network; } } diff --git a/tests/handlers/auth.test.ts b/tests/handlers/auth.test.ts index ba50c918..47122508 100644 --- a/tests/handlers/auth.test.ts +++ b/tests/handlers/auth.test.ts @@ -1556,6 +1556,7 @@ describe('auth', () => { const stateEthConfig = defaultEthConnectionConfig; stateEthConfig.url = RPC_URL; stateEthConfig.contractAddress = STATE_CONTRACT; + stateEthConfig.chainId = 80002; const eth = new EthStateStorage(stateEthConfig); const kms = registerKeyProvidersInMemoryKMS(); From 106945ffee898d2640d5894d7853654c9240f2f6 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 20 Aug 2024 12:09:43 +0300 Subject: [PATCH 09/41] merge(2) --- package-lock.json | 8 -------- package.json | 4 ---- 2 files changed, 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55550c6b..cb7d5236 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,12 @@ { "name": "@0xpolygonid/js-sdk", -<<<<<<< HEAD "version": "1.18.0", -======= - "version": "1.17.4", ->>>>>>> main "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@0xpolygonid/js-sdk", -<<<<<<< HEAD "version": "1.18.0", -======= - "version": "1.17.4", ->>>>>>> main "license": "MIT or Apache-2.0", "dependencies": { "@noble/curves": "^1.4.0", diff --git a/package.json b/package.json index c1bb3f1e..9781a872 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,6 @@ { "name": "@0xpolygonid/js-sdk", -<<<<<<< HEAD "version": "1.18.0", -======= - "version": "1.17.4", ->>>>>>> main "description": "SDK to work with Polygon ID", "main": "dist/node/cjs/index.js", "module": "dist/node/esm/index.js", From c18284e6d28d602071abbe429f9d9d1f8d3bdde2 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 21 Aug 2024 18:19:51 +0200 Subject: [PATCH 10/41] changes messages and empty did --- .../blockchain/onchain-zkp-verifier.ts | 9 ++----- src/storage/entities/state.ts | 9 ++----- src/utils/did-helper.ts | 26 ++++++++++++------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index e4ba3148..aee5ef53 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -359,11 +359,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { [ 'tuple(' + 'tuple(' + - 'address from,' + 'uint256 timestamp,' + + 'uint256 userID,' + 'uint256 root,' + - 'uint256 replacedByRoot,' + - 'uint256 createdAtTimestamp,' + 'uint256 replacedAtTimestamp' + ') globalStateMsg,' + 'bytes signature,' + @@ -378,12 +376,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { [ 'tuple(' + 'tuple(' + - 'address from,' + 'uint256 timestamp,' + - 'uint256 identity,' + + 'uint256 userID,' + 'uint256 state,' + - 'uint256 replacedByState,' + - 'uint256 createdAtTimestamp,' + 'uint256 replacedAtTimestamp' + ') idStateMsg,' + 'bytes signature,' + diff --git a/src/storage/entities/state.ts b/src/storage/entities/state.ts index d7a8ee7b..18c45190 100644 --- a/src/storage/entities/state.ts +++ b/src/storage/entities/state.ts @@ -53,12 +53,9 @@ export interface RootInfo { * @interface IdentityStateMsg */ export interface IdentityStateMsg { - from: string; timestamp: number; - identity: bigint; + userID: bigint; state: bigint; - replacedByState: bigint; - createdAtTimestamp: number; replacedAtTimestamp: number; } @@ -69,11 +66,9 @@ export interface IdentityStateMsg { * @interface GlobalStateMsg */ export interface GlobalStateMsg { - from: string; timestamp: number; + userID: bigint; root: bigint; - replacedByRoot: bigint; - createdAtTimestamp: number; replacedAtTimestamp: number; } diff --git a/src/utils/did-helper.ts b/src/utils/did-helper.ts index f573f695..1bca0f38 100644 --- a/src/utils/did-helper.ts +++ b/src/utils/did-helper.ts @@ -87,6 +87,19 @@ export const resolveDIDDocumentAuth = async ( ); }; +function emptyStateDID(did: DID) { + const id = DID.idFromDID(did); + const didType = buildDIDType( + DID.methodFromId(id), + DID.blockchainFromId(id), + DID.networkIdFromId(id) + ); + const identifier = Id.idGenesisFromIdenState(didType, 0n); + const emptyDID = DID.parseFromId(identifier); + + return emptyDID; +} + export const resolveDidDocumentEip712MessageAndSignature = async ( did: DID, resolveURL: string, @@ -99,9 +112,7 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( // for gist resolve we have to `hide` user did (look into resolver implementation) const isGistRequest = opts?.gist && !opts.state; if (isGistRequest) { - didString = did - .string() - .replace(did.idStrings[2], '000000000000000000000000000000000000000000'); + didString = emptyStateDID(did).string(); } let url = `${resolveURL}/1.0/identifiers/${didString}?signature=EthereumEip712Signature2021`; if (opts?.state) { @@ -119,11 +130,9 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( if (isGistRequest) { return { globalStateMsg: { - from: message.from, timestamp: message.timestamp, + userID: message.userID, root: message.root, - replacedByRoot: message.replacedByRoot, - createdAtTimestamp: message.createdAtTimestamp, replacedAtTimestamp: message.replacedAtTimestamp }, signature @@ -132,12 +141,9 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( return { idStateMsg: { - from: message.from, timestamp: message.timestamp, - identity: message.identity, + userID: message.userID, state: message.state, - replacedByState: message.replacedByState, - createdAtTimestamp: message.createdAtTimestamp, replacedAtTimestamp: message.replacedAtTimestamp }, signature From 403e3cac00f162ee3c60466e35f779e70eed07ce Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 21 Aug 2024 21:24:26 +0300 Subject: [PATCH 11/41] add all required eth configs --- tests/handlers/contract-request.test.ts | 41 +++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 2b5f3f59..7246ab33 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -810,13 +810,32 @@ describe('contract-request', () => { it.skip('contract request flow V3 sig `email-verified` transak req - integration test', async () => { const privadoTestRpcUrl = '<>'; // issuer RPC URL - privato test + const privadoMainRpcUrl = '<>'; const privadoTestStateContract = '0x975556428F077dB5877Ea2474D783D6C69233742'; const amoyVerifierRpcUrl = '<>'; // verifier RPC URL - amoy - const verifierAddress = '0x74030e4c5d53ef381A889C01f0bBd3B8336F4a4a'; + const verifierAddress = '0xE31725a735dd00eB0cc8aaf6b6eAB898f1BA9A69'; + const amoyStateAddress = '0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124'; - const issuerStateEthConfig = defaultEthConnectionConfig; - issuerStateEthConfig.url = privadoTestRpcUrl; - issuerStateEthConfig.contractAddress = privadoTestStateContract; // privado test state contract + const issuerStateEthConfig = { + ...defaultEthConnectionConfig, + url: privadoTestRpcUrl, + contractAddress: privadoTestStateContract, + chainId: 21001 + }; + + const userStateEthConfig = { + ...defaultEthConnectionConfig, + url: privadoMainRpcUrl, + contractAddress: privadoTestStateContract, + chainId: 21000 + }; + + const amoyStateEthConfig = { + ...defaultEthConnectionConfig, + url: amoyVerifierRpcUrl, + contractAddress: amoyStateAddress, + chainId: 21001 + }; const memoryKeyStore = new InMemoryPrivateKeyStore(); const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); @@ -829,7 +848,7 @@ describe('contract-request', () => { new InMemoryDataSource() ), mt: new InMemoryMerkleTreeStorage(40), - states: new EthStateStorage(issuerStateEthConfig) + states: new EthStateStorage([issuerStateEthConfig, userStateEthConfig, amoyStateEthConfig]) }; const circuitStorage = new FSCircuitStorage({ dirname: path.join(__dirname, '../proofs/testdata') @@ -928,18 +947,16 @@ describe('contract-request', () => { } ]; - const conf = defaultEthConnectionConfig; - conf.contractAddress = verifierAddress; - conf.url = amoyVerifierRpcUrl; - conf.chainId = 80002; - - const zkpVerifier = new OnChainZKPVerifier([conf], 'https://resolver-dev.privado.id'); + const zkpVerifier = new OnChainZKPVerifier( + [amoyStateEthConfig], + 'https://resolver-dev.privado.id' + ); contractRequestHandler = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); const transactionData: ContractInvokeTransactionData = { contract_address: verifierAddress, method_id: 'fd41d8d4', - chain_id: conf.chainId + chain_id: amoyStateEthConfig.chainId }; const verifierDid = 'did:iden3:polygon:amoy:x6x5sor7zpy1YGS4yjcmnzQSC7FZC7q7DPgNMT79q'; From 324d9efcc4bfdab05f157e9d7041df5f294a9b6e Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 21 Aug 2024 21:28:00 +0300 Subject: [PATCH 12/41] fix chain id --- tests/handlers/contract-request.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 7246ab33..bb8688a4 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -834,7 +834,7 @@ describe('contract-request', () => { ...defaultEthConnectionConfig, url: amoyVerifierRpcUrl, contractAddress: amoyStateAddress, - chainId: 21001 + chainId: 80002 }; const memoryKeyStore = new InMemoryPrivateKeyStore(); From 1e174e4f589d1f7c003526d49e81fbc5762cc366 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Thu, 22 Aug 2024 12:44:52 +0300 Subject: [PATCH 13/41] rename getGistRootStatePubSignals -> getStatesInfo --- src/circuits/atomic-query-mtp-v2-on-chain.ts | 6 +++--- src/circuits/atomic-query-sig-v2-on-chain.ts | 8 ++++---- src/circuits/atomic-query-v3-on-chain.ts | 6 +++--- src/circuits/common.ts | 8 ++++---- src/storage/blockchain/onchain-zkp-verifier.ts | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index be6afc7c..5997363a 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -8,7 +8,7 @@ import { existenceToInt, getNodeAuxValue, IGistRootStatePubSignals, - OnChainStateInfo, + StatesInfo, prepareCircuitArrayValues, prepareSiblingsStr } from './common'; @@ -328,8 +328,8 @@ export class AtomicQueryMTPV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ - getGistRootStatePubSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + getStatesInfo(): StatesInfo { return { states: [ { id: this.issuerID, state: this.issuerClaimIdenState }, diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index 918a930b..48db6ed0 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -8,9 +8,9 @@ import { existenceToInt, getNodeAuxValue, IGistRootStatePubSignals, - OnChainStateInfo, prepareCircuitArrayValues, - prepareSiblingsStr + prepareSiblingsStr, + StatesInfo } from './common'; import { byteDecoder, byteEncoder } from '../utils'; @@ -403,8 +403,8 @@ export class AtomicQuerySigV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ - getGistRootStatePubSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + getStatesInfo(): StatesInfo { return { states: [ { id: this.issuerID, state: this.issuerAuthState }, diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index f95cc365..ce8489e7 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -6,7 +6,7 @@ import { getNodeAuxValue, prepareCircuitArrayValues, IGistRootStatePubSignals, - OnChainStateInfo + StatesInfo } from './common'; import { BJJSignatureProof, CircuitError, GISTProof, Query, TreeState, ValueProof } from './models'; import { Hash, Proof, ZERO_HASH } from '@iden3/js-merkletree'; @@ -518,8 +518,8 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistR return this; } - /** {@inheritDoc IGistRootStatePubSignals.getGistRootStatePubSignals} */ - getGistRootStatePubSignals(): OnChainStateInfo { + /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + getStatesInfo(): StatesInfo { return { states: [ { id: this.issuerID, state: this.issuerState }, diff --git a/src/circuits/common.ts b/src/circuits/common.ts index 69c68d0a..4ebf5574 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -229,12 +229,12 @@ export function getProperties(obj: object): object { } /** - * onchain state info from pub signals + * states info from pub signals * * @public - * @type OnChainStateInfo + * @type StatesInfo */ -export type OnChainStateInfo = { +export type StatesInfo = { states: { id: Id; state: Hash; @@ -257,5 +257,5 @@ export interface IGistRootStatePubSignals { * * @returns {OnChainStateInfo} */ - getGistRootStatePubSignals(): OnChainStateInfo; + getStatesInfo(): StatesInfo; } diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index aee5ef53..d793dd8a 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -10,7 +10,7 @@ import { AtomicQuerySigV2OnChainPubSignals, AtomicQueryV3OnChainPubSignals, CircuitId, - OnChainStateInfo + StatesInfo } from '../../circuits'; import { byteEncoder, resolveDidDocumentEip712MessageAndSignature } from '../../utils'; import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; @@ -403,7 +403,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { | CircuitId.AtomicQuerySigV2OnChain | CircuitId.AtomicQueryV3OnChain, inputs: string[] - ): OnChainStateInfo { + ): StatesInfo { let atomicQueryPubSignals; switch (onChainCircuitId) { case CircuitId.AtomicQueryMTPV2OnChain: @@ -418,6 +418,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const encodedInputs = byteEncoder.encode(JSON.stringify(inputs)); atomicQueryPubSignals.pubSignalsUnmarshal(encodedInputs); - return atomicQueryPubSignals.getGistRootStatePubSignals(); + return atomicQueryPubSignals.getStatesInfo(); } } From 0157850a91e5fc1b240ef88b93fc8fda40ea5ce3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 22 Aug 2024 18:13:23 +0200 Subject: [PATCH 14/41] update id and idType for identity and global messages --- src/storage/blockchain/onchain-zkp-verifier.ts | 4 ++-- src/storage/entities/state.ts | 4 ++-- src/utils/did-helper.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index d793dd8a..3b224b6d 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -360,7 +360,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { 'tuple(' + 'tuple(' + 'uint256 timestamp,' + - 'uint256 userID,' + + 'bytes2 idType,' + 'uint256 root,' + 'uint256 replacedAtTimestamp' + ') globalStateMsg,' + @@ -377,7 +377,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { 'tuple(' + 'tuple(' + 'uint256 timestamp,' + - 'uint256 userID,' + + 'uint256 id,' + 'uint256 state,' + 'uint256 replacedAtTimestamp' + ') idStateMsg,' + diff --git a/src/storage/entities/state.ts b/src/storage/entities/state.ts index 18c45190..a6661d25 100644 --- a/src/storage/entities/state.ts +++ b/src/storage/entities/state.ts @@ -54,7 +54,7 @@ export interface RootInfo { */ export interface IdentityStateMsg { timestamp: number; - userID: bigint; + id: bigint; state: bigint; replacedAtTimestamp: number; } @@ -67,7 +67,7 @@ export interface IdentityStateMsg { */ export interface GlobalStateMsg { timestamp: number; - userID: bigint; + idType: string; root: bigint; replacedAtTimestamp: number; } diff --git a/src/utils/did-helper.ts b/src/utils/did-helper.ts index 1bca0f38..03d0b0c7 100644 --- a/src/utils/did-helper.ts +++ b/src/utils/did-helper.ts @@ -131,7 +131,7 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( return { globalStateMsg: { timestamp: message.timestamp, - userID: message.userID, + idType: message.idType, root: message.root, replacedAtTimestamp: message.replacedAtTimestamp }, @@ -142,7 +142,7 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( return { idStateMsg: { timestamp: message.timestamp, - userID: message.userID, + id: message.id, state: message.state, replacedAtTimestamp: message.replacedAtTimestamp }, From f66639ae88ea3066227805256d654cb311ec4e95 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Sun, 25 Aug 2024 22:50:28 +0300 Subject: [PATCH 15/41] fix build --- src/iden3comm/types/protocol/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iden3comm/types/protocol/auth.ts b/src/iden3comm/types/protocol/auth.ts index 29f87a0c..d3b9c705 100644 --- a/src/iden3comm/types/protocol/auth.ts +++ b/src/iden3comm/types/protocol/auth.ts @@ -58,6 +58,6 @@ export type VerifiablePresentation = { verifiableCredential: { '@context': string[]; '@type': string[]; - credentialSubject: JSONObject; + credentialSubject: JsonDocumentObject; }; }; From 9085fcbbf4fde218dac0d425706f0cff0573af28 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 26 Aug 2024 15:51:40 +0200 Subject: [PATCH 16/41] add resolve gist and state cache --- src/iden3comm/handlers/contract-request.ts | 15 +- src/storage/blockchain/abi/ZkpVerifier.json | 35 +++ .../blockchain/onchain-zkp-verifier.ts | 200 ++++++++++++++++++ .../interfaces/onchain-zkp-verifier.ts | 24 +++ tests/handlers/contract-request.test.ts | 14 ++ 5 files changed, 286 insertions(+), 2 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 0f5044e8..6b2ac6f6 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -129,6 +129,12 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { + case OnChainZKPVerifier.SupportedMethodIdV3: + return this._zkpVerifier.submitZKPResponseV3( + ethSigner, + message.body.transaction_data, + zkpResponses + ); case OnChainZKPVerifier.SupportedMethodIdV2: return this._zkpVerifier.submitZKPResponseV2( ethSigner, @@ -143,7 +149,7 @@ export class ContractRequestHandler ); default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV3}, ${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` ); } } @@ -224,6 +230,11 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { + case OnChainZKPVerifier.SupportedMethodIdV3: + return this._zkpVerifier.prepareZKPResponseV3TxData( + message.body.transaction_data, + zkpResponses + ); case OnChainZKPVerifier.SupportedMethodIdV2: return this._zkpVerifier.prepareZKPResponseV2TxData( message.body.transaction_data, @@ -236,7 +247,7 @@ export class ContractRequestHandler ); default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV3}, ${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` ); } } diff --git a/src/storage/blockchain/abi/ZkpVerifier.json b/src/storage/blockchain/abi/ZkpVerifier.json index 3134341a..4e6befc9 100644 --- a/src/storage/blockchain/abi/ZkpVerifier.json +++ b/src/storage/blockchain/abi/ZkpVerifier.json @@ -395,6 +395,41 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "zkProof", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct ZKPResponseV3[]", + "name": "responses", + "type": "tuple[]" + }, + { + "internalType": "bytes", + "name": "crossChainProof", + "type": "bytes" + } + ], + "name": "submitZKPResponseV3", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 3b224b6d..4bc5d5e7 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -16,6 +16,10 @@ import { byteEncoder, resolveDidDocumentEip712MessageAndSignature } from '../../ import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; import { poseidon } from '@iden3/js-crypto'; +// Cache for resolved gists and states +const gistCache = new Map(); +const stateCache = new Map(); + /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract * and submitZKPResponse. @@ -43,8 +47,24 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ZKPResponse[] memory responses ) public */ + //function submitZKPResponseV2(tuple[](uint64 requestId,bytes zkProof,bytes crossChainProof,bytes data)) public static readonly SupportedMethodIdV2 = 'fd41d8d4'; + /** + * solidity identifier for function signature: + * struct ZKPResponseV3 { + uint256 requestId; + bytes zkProof; + bytes data; + } + * function submitZKPResponseV3( + ZKPResponseV3[] memory responses, + bytes memory crossChainProof + ) public + */ + //function submitZKPResponseV3(tuple[](uint256 requestId,bytes zkProof,bytes data),bytes crossChainProof) + public static readonly SupportedMethodIdV3 = '3fba5e46'; + /** * supported circuits */ @@ -323,6 +343,186 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return response; } + /** + * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseV3TxData} + */ + public async prepareZKPResponseV3TxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV3) { + throw new Error( + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV3}' is supported.` + ); + } + if (!this._didResolverUrl) { + throw new Error(`did resolver url required for crosschain verification`); + } + const verifierContract = new Contract(txData.contract_address, abi); + + const response = new Map(); + for (const zkProof of zkProofResponses) { + const requestID = zkProof.id; + const inputs = zkProof.pub_signals; + + if (!this._supportedCircuits.includes(zkProof.circuitId as CircuitId)) { + throw new Error(`Circuit ${zkProof.circuitId} not supported by OnChainZKPVerifier`); + } + + const zkProofEncoded = this.packZkpProof( + 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 stateInfo = this.getOnChainGistRootStatePubSignals( + zkProof.circuitId as + | CircuitId.AtomicQueryMTPV2OnChain + | CircuitId.AtomicQuerySigV2OnChain + | CircuitId.AtomicQueryV3OnChain, + zkProof.pub_signals + ); + + const gistUpdateArr: GlobalStateUpdate[] = []; + for (const gist of stateInfo.gists) { + let gistResolved; + const key = JSON.stringify({ + DID: DID.parseFromId(gist.id), + resolverUrl: this._didResolverUrl, + gist: gist.root + }); + if (!gistCache.has(key)) { + gistResolved = await resolveDidDocumentEip712MessageAndSignature( + DID.parseFromId(gist.id), + this._didResolverUrl, + { gist: gist.root } + ); + gistCache.set(key, gistResolved as GlobalStateUpdate); + } else { + gistResolved = gistCache.get(key); + } + gistUpdateArr.push(gistResolved as GlobalStateUpdate); + } + + const stateUpdateArr: IdentityStateUpdate[] = []; + for (const state of stateInfo.states) { + let stateResolved; + const key = JSON.stringify({ + DID: DID.parseFromId(state.id), + resolverUrl: this._didResolverUrl, + state: state.state + }); + if (!stateCache.has(key)) { + stateResolved = await resolveDidDocumentEip712MessageAndSignature( + DID.parseFromId(state.id), + this._didResolverUrl, + { state: state.state } + ); + stateCache.set(key, stateResolved as IdentityStateUpdate); + } else { + stateResolved = stateCache.get(key); + } + stateUpdateArr.push(stateResolved as IdentityStateUpdate); + } + + const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); + + const metadataArr: { key: string; value: Uint8Array }[] = []; + if (zkProof.vp) { + for (const key in zkProof.vp.verifiableCredential.credentialSubject) { + if (key === '@type') { + continue; + } + const metadataValue = poseidon.hashBytes( + byteEncoder.encode( + JSON.stringify(zkProof.vp.verifiableCredential.credentialSubject[key]) + ) + ); + const bytesValue = byteEncoder.encode(metadataValue.toString()); + metadataArr.push({ + key, + value: bytesValue + }); + } + } + + const metadata = this.packMetadatas(metadataArr); + const payload = [ + { + requestId: requestID, + zkProof: zkProofEncoded, + data: metadata + } + ]; + + const txData = await verifierContract.submitZKPResponseV3.populateTransaction( + payload, + crossChainProofs + ); + + response.set(requestID, txData.data); + } + + return response; + } + + /** + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV3} + */ + public async submitZKPResponseV3( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); + if (!chainConfig) { + throw new Error(`config for chain id ${txData.chain_id} was not found`); + } + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV3) { + throw new Error( + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV3}' is supported.` + ); + } + if (!this._didResolverUrl) { + throw new Error(`did resolver url required for crosschain verification`); + } + const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); + ethSigner = ethSigner.connect(provider); + + const txDataMap = await this.prepareZKPResponseV3TxData(txData, zkProofResponses); + const feeData = await provider.getFeeData(); + const maxFeePerGas = chainConfig.maxFeePerGas + ? BigInt(chainConfig.maxFeePerGas) + : feeData.maxFeePerGas; + const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas + ? BigInt(chainConfig.maxPriorityFeePerGas) + : feeData.maxPriorityFeePerGas; + + const response = new Map(); + for (const zkProof of zkProofResponses) { + const payload = txDataMap.get(zkProof.id); + const request: TransactionRequest = { + to: txData.contract_address, + data: payload, + maxFeePerGas, + maxPriorityFeePerGas + }; + + const gasLimit = await ethSigner.estimateGas(request); + request.gasLimit = gasLimit; + + const transactionService = new TransactionService(provider); + const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); + response.set(txnHash, zkProof); + } + + return response; + } + private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { return this._abiCoder.encode( ['uint256[] inputs', 'uint256[2]', 'uint256[2][2]', 'uint256[2]'], diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 20bdb6fb..fc75d29d 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -36,6 +36,20 @@ export interface IOnChainZKPVerifier { zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; + /** + * Submit ZKP Response V3 to OnChainZKPVerifier contract. + * @beta + * @param {Signer} ethSigner - tx signer + * @param {txData} ContractInvokeTransactionData - transaction data + * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses + * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse + */ + submitZKPResponseV3( + ethSigner: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; + /** * Returns the Map of request id to transaction data for the ZKP verifier contract submission. * @param txData @@ -55,4 +69,14 @@ export interface IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; + + /** + * Returns the Map of request id to transaction data for the ZKP verifier contract submission V3. + * @param txData + * @param zkProofResponses + */ + prepareZKPResponseV3TxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; } diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index bb8688a4..5f436c7a 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -134,12 +134,26 @@ describe('contract-request', () => { return response; }, + submitZKPResponseV3: async ( + signer: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ) => { + const response = new Map(); + response.set('txhash1', zkProofResponses[0]); + return response; + }, + prepareZKPResponseTxData: async () => { return new Map(); }, prepareZKPResponseV2TxData: async () => { return new Map(); + }, + + prepareZKPResponseV3TxData: async () => { + return new Map(); } }; From 881d0cb7d5fa5a2052c197e3e35487ce0fc4d893 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 27 Aug 2024 14:54:41 +0300 Subject: [PATCH 17/41] resolve VP type comments --- src/circuits/atomic-query-mtp-v2-on-chain.ts | 6 +++--- src/circuits/atomic-query-sig-v2-on-chain.ts | 6 +++--- src/circuits/atomic-query-v3-on-chain.ts | 6 +++--- src/circuits/common.ts | 2 +- src/iden3comm/types/packer.ts | 10 ++++++++++ src/iden3comm/types/protocol/auth.ts | 8 ++++---- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index 5997363a..051a73f0 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -7,7 +7,7 @@ import { bigIntArrayToStringArray, existenceToInt, getNodeAuxValue, - IGistRootStatePubSignals, + IStateInfoPubSignals, StatesInfo, prepareCircuitArrayValues, prepareSiblingsStr @@ -244,7 +244,7 @@ interface atomicQueryMTPV2OnChainCircuitInputs { */ export class AtomicQueryMTPV2OnChainPubSignals extends BaseConfig - implements IGistRootStatePubSignals + implements IStateInfoPubSignals { requestID!: bigint; userID!: Id; @@ -328,7 +328,7 @@ export class AtomicQueryMTPV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + /** {@inheritDoc IStateInfoPubSignals.getStatesInfo} */ getStatesInfo(): StatesInfo { return { states: [ diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index 48db6ed0..a85df80c 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -7,7 +7,7 @@ import { bigIntArrayToStringArray, existenceToInt, getNodeAuxValue, - IGistRootStatePubSignals, + IStateInfoPubSignals, prepareCircuitArrayValues, prepareSiblingsStr, StatesInfo @@ -316,7 +316,7 @@ export class AtomicQuerySigV2OnChainCircuitInputs { */ export class AtomicQuerySigV2OnChainPubSignals extends BaseConfig - implements IGistRootStatePubSignals + implements IStateInfoPubSignals { requestID!: bigint; userID!: Id; @@ -403,7 +403,7 @@ export class AtomicQuerySigV2OnChainPubSignals return this; } - /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + /** {@inheritDoc IStateInfoPubSignals.getStatesInfo} */ getStatesInfo(): StatesInfo { return { states: [ diff --git a/src/circuits/atomic-query-v3-on-chain.ts b/src/circuits/atomic-query-v3-on-chain.ts index ce8489e7..9a6ba1ad 100644 --- a/src/circuits/atomic-query-v3-on-chain.ts +++ b/src/circuits/atomic-query-v3-on-chain.ts @@ -5,7 +5,7 @@ import { prepareSiblingsStr, getNodeAuxValue, prepareCircuitArrayValues, - IGistRootStatePubSignals, + IStateInfoPubSignals, StatesInfo } from './common'; import { BJJSignatureProof, CircuitError, GISTProof, Query, TreeState, ValueProof } from './models'; @@ -422,7 +422,7 @@ interface AtomicQueryV3OnChainCircuitInputs { * @beta * AtomicQueryV3OnChainPubSignals public inputs */ -export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistRootStatePubSignals { +export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IStateInfoPubSignals { requestID!: bigint; userID!: Id; issuerID!: Id; @@ -518,7 +518,7 @@ export class AtomicQueryV3OnChainPubSignals extends BaseConfig implements IGistR return this; } - /** {@inheritDoc IGistRootStatePubSignals.getStatesInfo} */ + /** {@inheritDoc IStateInfoPubSignals.getStatesInfo} */ getStatesInfo(): StatesInfo { return { states: [ diff --git a/src/circuits/common.ts b/src/circuits/common.ts index 4ebf5574..7b7eab5c 100644 --- a/src/circuits/common.ts +++ b/src/circuits/common.ts @@ -251,7 +251,7 @@ export type StatesInfo = { * @public * @interface IStatePubSignals */ -export interface IGistRootStatePubSignals { +export interface IStateInfoPubSignals { /** * return object with state params * diff --git a/src/iden3comm/types/packer.ts b/src/iden3comm/types/packer.ts index ab1dccf7..7a50d933 100644 --- a/src/iden3comm/types/packer.ts +++ b/src/iden3comm/types/packer.ts @@ -38,6 +38,16 @@ export type JsonDocumentObjectValue = | JsonDocumentObject | JsonDocumentObjectValue[]; +/** + * Context object type (for VP and VC) + */ +export type ContextObject = { [key: string]: ContextObjectValue }; + +/** + * Context object allowed values + */ +export type ContextObjectValue = string | JsonDocumentObject | JsonDocumentObjectValue[]; + export type BasicMessage = { id: string; typ?: MediaType; diff --git a/src/iden3comm/types/protocol/auth.ts b/src/iden3comm/types/protocol/auth.ts index d3b9c705..4b063b26 100644 --- a/src/iden3comm/types/protocol/auth.ts +++ b/src/iden3comm/types/protocol/auth.ts @@ -1,5 +1,5 @@ import { ZKProof } from '@iden3/js-jwz'; -import { BasicMessage, JsonDocumentObject } from '../packer'; +import { BasicMessage, JsonDocumentObject, ContextObject } from '../packer'; import { PROTOCOL_MESSAGE_TYPE } from '../../constants'; /** AuthorizationResponseMessage is struct the represents iden3message authorization response */ @@ -53,11 +53,11 @@ export type ZeroKnowledgeProofResponse = { /** VerifiablePresentation represents structure of Verifiable Presentation */ export type VerifiablePresentation = { - '@context': string[]; + '@context': string | (string | object)[]; '@type': string; verifiableCredential: { - '@context': string[]; - '@type': string[]; + '@context': string | string[]; + '@type': string | string[]; credentialSubject: JsonDocumentObject; }; }; From 20d15f7a50c7fd8862be3c9c198c9dd4fe18222c Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 27 Aug 2024 14:56:25 +0300 Subject: [PATCH 18/41] format --- src/circuits/atomic-query-mtp-v2-on-chain.ts | 5 +---- src/circuits/atomic-query-sig-v2-on-chain.ts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/circuits/atomic-query-mtp-v2-on-chain.ts b/src/circuits/atomic-query-mtp-v2-on-chain.ts index 051a73f0..4d0216c5 100644 --- a/src/circuits/atomic-query-mtp-v2-on-chain.ts +++ b/src/circuits/atomic-query-mtp-v2-on-chain.ts @@ -242,10 +242,7 @@ interface atomicQueryMTPV2OnChainCircuitInputs { * @class AtomicQueryMTPV2OnChainPubSignals * @extends {BaseConfig} */ -export class AtomicQueryMTPV2OnChainPubSignals - extends BaseConfig - implements IStateInfoPubSignals -{ +export class AtomicQueryMTPV2OnChainPubSignals extends BaseConfig implements IStateInfoPubSignals { requestID!: bigint; userID!: Id; issuerID!: Id; diff --git a/src/circuits/atomic-query-sig-v2-on-chain.ts b/src/circuits/atomic-query-sig-v2-on-chain.ts index a85df80c..c91c17a3 100644 --- a/src/circuits/atomic-query-sig-v2-on-chain.ts +++ b/src/circuits/atomic-query-sig-v2-on-chain.ts @@ -314,10 +314,7 @@ export class AtomicQuerySigV2OnChainCircuitInputs { * @class AtomicQuerySigV2OnChainPubSignals * @extends {BaseConfig} */ -export class AtomicQuerySigV2OnChainPubSignals - extends BaseConfig - implements IStateInfoPubSignals -{ +export class AtomicQuerySigV2OnChainPubSignals extends BaseConfig implements IStateInfoPubSignals { requestID!: bigint; userID!: Id; issuerID!: Id; From ee7af79575e9f9ba570c1141b9e00530b9612a34 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 27 Aug 2024 15:04:11 +0300 Subject: [PATCH 19/41] rm unused import --- src/iden3comm/types/protocol/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iden3comm/types/protocol/auth.ts b/src/iden3comm/types/protocol/auth.ts index 4b063b26..6f787cdb 100644 --- a/src/iden3comm/types/protocol/auth.ts +++ b/src/iden3comm/types/protocol/auth.ts @@ -1,5 +1,5 @@ import { ZKProof } from '@iden3/js-jwz'; -import { BasicMessage, JsonDocumentObject, ContextObject } from '../packer'; +import { BasicMessage, JsonDocumentObject } from '../packer'; import { PROTOCOL_MESSAGE_TYPE } from '../../constants'; /** AuthorizationResponseMessage is struct the represents iden3message authorization response */ From 8e545c75bf4eb58652b45496825ffc76445146a4 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 27 Aug 2024 17:19:58 +0200 Subject: [PATCH 20/41] update submitZKPResponseV2 final version --- src/iden3comm/handlers/contract-request.ts | 15 +- src/storage/blockchain/abi/ZkpVerifier.json | 420 +++++++++++++++--- .../blockchain/onchain-zkp-verifier.ts | 209 +-------- .../interfaces/onchain-zkp-verifier.ts | 24 - tests/handlers/contract-request.test.ts | 14 - 5 files changed, 370 insertions(+), 312 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 6b2ac6f6..0f5044e8 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -129,12 +129,6 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { - case OnChainZKPVerifier.SupportedMethodIdV3: - return this._zkpVerifier.submitZKPResponseV3( - ethSigner, - message.body.transaction_data, - zkpResponses - ); case OnChainZKPVerifier.SupportedMethodIdV2: return this._zkpVerifier.submitZKPResponseV2( ethSigner, @@ -149,7 +143,7 @@ export class ContractRequestHandler ); default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV3}, ${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` ); } } @@ -230,11 +224,6 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { - case OnChainZKPVerifier.SupportedMethodIdV3: - return this._zkpVerifier.prepareZKPResponseV3TxData( - message.body.transaction_data, - zkpResponses - ); case OnChainZKPVerifier.SupportedMethodIdV2: return this._zkpVerifier.prepareZKPResponseV2TxData( message.body.transaction_data, @@ -247,7 +236,7 @@ export class ContractRequestHandler ); default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV3}, ${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` ); } } diff --git a/src/storage/blockchain/abi/ZkpVerifier.json b/src/storage/blockchain/abi/ZkpVerifier.json index 4e6befc9..36b1c1b8 100644 --- a/src/storage/blockchain/abi/ZkpVerifier.json +++ b/src/storage/blockchain/abi/ZkpVerifier.json @@ -12,9 +12,9 @@ "type": "string" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" }, { "internalType": "uint256", @@ -22,9 +22,9 @@ "type": "uint256" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "requestIdToCompare", - "type": "uint64" + "type": "uint256" }, { "internalType": "uint256", @@ -40,6 +40,28 @@ "name": "NotInitializing", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, { "anonymous": false, "inputs": [ @@ -53,6 +75,100 @@ "name": "Initialized", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "requestOwner", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "ZKPRequestSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "ZKPResponseSubmitted", + "type": "event" + }, { "inputs": [], "name": "REQUESTS_RETURN_LIMIT", @@ -66,6 +182,65 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + } + ], + "name": "addValidatorToWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "name": "disableZKPRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" + } + ], + "name": "enableZKPRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -74,9 +249,9 @@ "type": "address" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" } ], "name": "getProofStatus", @@ -120,9 +295,9 @@ "type": "address" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" }, { "internalType": "string", @@ -144,9 +319,28 @@ { "inputs": [ { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" + } + ], + "name": "getRequestOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" } ], "name": "getZKPRequest", @@ -231,6 +425,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IStateCrossChain", + "name": "stateCrossChain", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -239,9 +446,9 @@ "type": "address" }, { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" } ], "name": "isProofVerified", @@ -258,9 +465,93 @@ { "inputs": [ { - "internalType": "uint64", + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + } + ], + "name": "isWhitelistedValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" + } + ], + "name": "isZKPRequestEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ICircuitValidator", + "name": "validator", + "type": "address" + } + ], + "name": "removeValidatorFromWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" } ], "name": "requestIdExists", @@ -277,9 +568,27 @@ { "inputs": [ { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" + }, + { + "internalType": "address", + "name": "requestOwner", + "type": "address" + } + ], + "name": "setRequestOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requestId", + "type": "uint256" }, { "components": [ @@ -312,9 +621,9 @@ { "inputs": [ { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" }, { "internalType": "uint256[]", @@ -347,20 +656,15 @@ { "components": [ { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" }, { "internalType": "bytes", "name": "zkProof", "type": "bytes" }, - { - "internalType": "bytes", - "name": "crossChainProof", - "type": "bytes" - }, { "internalType": "bytes", "name": "data", @@ -370,6 +674,11 @@ "internalType": "struct ZKPResponse[]", "name": "responses", "type": "tuple[]" + }, + { + "internalType": "bytes", + "name": "crossChainProof", + "type": "bytes" } ], "name": "submitZKPResponseV2", @@ -381,61 +690,39 @@ "inputs": [ { "internalType": "address", - "name": "sender", + "name": "newOwner", "type": "address" - }, - { - "internalType": "uint64[]", - "name": "requestIds", - "type": "uint64[]" } ], - "name": "verifyLinkedProofs", + "name": "transferOwnership", "outputs": [], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { - "components": [ - { - "internalType": "uint256", - "name": "requestId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "zkProof", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct ZKPResponseV3[]", - "name": "responses", - "type": "tuple[]" + "internalType": "address", + "name": "sender", + "type": "address" }, { - "internalType": "bytes", - "name": "crossChainProof", - "type": "bytes" + "internalType": "uint256[]", + "name": "requestIds", + "type": "uint256[]" } ], - "name": "submitZKPResponseV3", + "name": "verifyLinkedProofs", "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { "inputs": [ { - "internalType": "uint64", + "internalType": "uint256", "name": "requestId", - "type": "uint64" + "type": "uint256" }, { "internalType": "uint256[]", @@ -485,5 +772,18 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" } ] \ No newline at end of file diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 4bc5d5e7..c790106c 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -38,32 +38,17 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { /** * solidity identifier for function signature: * struct ZKPResponse { - uint64 requestId; - bytes zkProof; - bytes crossChainProof; - bytes data; - } - * function submitZKPResponseV2( - ZKPResponse[] memory responses - ) public - */ - //function submitZKPResponseV2(tuple[](uint64 requestId,bytes zkProof,bytes crossChainProof,bytes data)) - public static readonly SupportedMethodIdV2 = 'fd41d8d4'; - - /** - * solidity identifier for function signature: - * struct ZKPResponseV3 { uint256 requestId; bytes zkProof; bytes data; } - * function submitZKPResponseV3( - ZKPResponseV3[] memory responses, + * function submitZKPResponseV2( + ZKPResponse[] memory responses, bytes memory crossChainProof ) public */ - //function submitZKPResponseV3(tuple[](uint256 requestId,bytes zkProof,bytes data),bytes crossChainProof) - public static readonly SupportedMethodIdV3 = '3fba5e46'; + //function submitZKPResponseV2(tuple[](uint256 requestId,bytes zkProof,bytes data),bytes crossChainProof) + public static readonly SupportedMethodIdV2 = '4c3d60fa'; /** * supported circuits @@ -277,12 +262,14 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { { requestId: requestID, zkProof: zkProofEncoded, - crossChainProof: crossChainProofs, data: metadata } ]; - const txData = await verifierContract.submitZKPResponseV2.populateTransaction(payload); + const txData = await verifierContract.submitZKPResponseV2.populateTransaction( + payload, + crossChainProofs + ); response.set(requestID, txData.data); } @@ -343,186 +330,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return response; } - /** - * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseV3TxData} - */ - public async prepareZKPResponseV3TxData( - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV3) { - throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV3}' is supported.` - ); - } - if (!this._didResolverUrl) { - throw new Error(`did resolver url required for crosschain verification`); - } - const verifierContract = new Contract(txData.contract_address, abi); - - const response = new Map(); - for (const zkProof of zkProofResponses) { - const requestID = zkProof.id; - const inputs = zkProof.pub_signals; - - if (!this._supportedCircuits.includes(zkProof.circuitId as CircuitId)) { - throw new Error(`Circuit ${zkProof.circuitId} not supported by OnChainZKPVerifier`); - } - - const zkProofEncoded = this.packZkpProof( - 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 stateInfo = this.getOnChainGistRootStatePubSignals( - zkProof.circuitId as - | CircuitId.AtomicQueryMTPV2OnChain - | CircuitId.AtomicQuerySigV2OnChain - | CircuitId.AtomicQueryV3OnChain, - zkProof.pub_signals - ); - - const gistUpdateArr: GlobalStateUpdate[] = []; - for (const gist of stateInfo.gists) { - let gistResolved; - const key = JSON.stringify({ - DID: DID.parseFromId(gist.id), - resolverUrl: this._didResolverUrl, - gist: gist.root - }); - if (!gistCache.has(key)) { - gistResolved = await resolveDidDocumentEip712MessageAndSignature( - DID.parseFromId(gist.id), - this._didResolverUrl, - { gist: gist.root } - ); - gistCache.set(key, gistResolved as GlobalStateUpdate); - } else { - gistResolved = gistCache.get(key); - } - gistUpdateArr.push(gistResolved as GlobalStateUpdate); - } - - const stateUpdateArr: IdentityStateUpdate[] = []; - for (const state of stateInfo.states) { - let stateResolved; - const key = JSON.stringify({ - DID: DID.parseFromId(state.id), - resolverUrl: this._didResolverUrl, - state: state.state - }); - if (!stateCache.has(key)) { - stateResolved = await resolveDidDocumentEip712MessageAndSignature( - DID.parseFromId(state.id), - this._didResolverUrl, - { state: state.state } - ); - stateCache.set(key, stateResolved as IdentityStateUpdate); - } else { - stateResolved = stateCache.get(key); - } - stateUpdateArr.push(stateResolved as IdentityStateUpdate); - } - - const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); - - const metadataArr: { key: string; value: Uint8Array }[] = []; - if (zkProof.vp) { - for (const key in zkProof.vp.verifiableCredential.credentialSubject) { - if (key === '@type') { - continue; - } - const metadataValue = poseidon.hashBytes( - byteEncoder.encode( - JSON.stringify(zkProof.vp.verifiableCredential.credentialSubject[key]) - ) - ); - const bytesValue = byteEncoder.encode(metadataValue.toString()); - metadataArr.push({ - key, - value: bytesValue - }); - } - } - - const metadata = this.packMetadatas(metadataArr); - const payload = [ - { - requestId: requestID, - zkProof: zkProofEncoded, - data: metadata - } - ]; - - const txData = await verifierContract.submitZKPResponseV3.populateTransaction( - payload, - crossChainProofs - ); - - response.set(requestID, txData.data); - } - - return response; - } - - /** - * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV3} - */ - public async submitZKPResponseV3( - ethSigner: Signer, - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { - const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); - if (!chainConfig) { - throw new Error(`config for chain id ${txData.chain_id} was not found`); - } - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV3) { - throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV3}' is supported.` - ); - } - if (!this._didResolverUrl) { - throw new Error(`did resolver url required for crosschain verification`); - } - const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); - ethSigner = ethSigner.connect(provider); - - const txDataMap = await this.prepareZKPResponseV3TxData(txData, zkProofResponses); - const feeData = await provider.getFeeData(); - const maxFeePerGas = chainConfig.maxFeePerGas - ? BigInt(chainConfig.maxFeePerGas) - : feeData.maxFeePerGas; - const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas - ? BigInt(chainConfig.maxPriorityFeePerGas) - : feeData.maxPriorityFeePerGas; - - const response = new Map(); - for (const zkProof of zkProofResponses) { - const payload = txDataMap.get(zkProof.id); - const request: TransactionRequest = { - to: txData.contract_address, - data: payload, - maxFeePerGas, - maxPriorityFeePerGas - }; - - const gasLimit = await ethSigner.estimateGas(request); - request.gasLimit = gasLimit; - - const transactionService = new TransactionService(provider); - const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); - response.set(txnHash, zkProof); - } - - return response; - } - private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { return this._abiCoder.encode( ['uint256[] inputs', 'uint256[2]', 'uint256[2][2]', 'uint256[2]'], diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index fc75d29d..20bdb6fb 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -36,20 +36,6 @@ export interface IOnChainZKPVerifier { zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; - /** - * Submit ZKP Response V3 to OnChainZKPVerifier contract. - * @beta - * @param {Signer} ethSigner - tx signer - * @param {txData} ContractInvokeTransactionData - transaction data - * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses - * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse - */ - submitZKPResponseV3( - ethSigner: Signer, - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; - /** * Returns the Map of request id to transaction data for the ZKP verifier contract submission. * @param txData @@ -69,14 +55,4 @@ export interface IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise>; - - /** - * Returns the Map of request id to transaction data for the ZKP verifier contract submission V3. - * @param txData - * @param zkProofResponses - */ - prepareZKPResponseV3TxData( - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; } diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 5f436c7a..bb8688a4 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -134,26 +134,12 @@ describe('contract-request', () => { return response; }, - submitZKPResponseV3: async ( - signer: Signer, - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ) => { - const response = new Map(); - response.set('txhash1', zkProofResponses[0]); - return response; - }, - prepareZKPResponseTxData: async () => { return new Map(); }, prepareZKPResponseV2TxData: async () => { return new Map(); - }, - - prepareZKPResponseV3TxData: async () => { - return new Map(); } }; From 5b3ac93767c55a4e699973a3c568d83611669de6 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 28 Aug 2024 11:33:59 +0200 Subject: [PATCH 21/41] back to uint64 requestId --- src/storage/blockchain/abi/ZkpVerifier.json | 76 +++++++++---------- .../blockchain/onchain-zkp-verifier.ts | 6 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/storage/blockchain/abi/ZkpVerifier.json b/src/storage/blockchain/abi/ZkpVerifier.json index 36b1c1b8..d16bee80 100644 --- a/src/storage/blockchain/abi/ZkpVerifier.json +++ b/src/storage/blockchain/abi/ZkpVerifier.json @@ -12,9 +12,9 @@ "type": "string" }, { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "uint256", @@ -22,9 +22,9 @@ "type": "uint256" }, { - "internalType": "uint256", + "internalType": "uint64", "name": "requestIdToCompare", - "type": "uint256" + "type": "uint64" }, { "internalType": "uint256", @@ -118,9 +118,9 @@ "inputs": [ { "indexed": true, - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "indexed": true, @@ -155,9 +155,9 @@ "inputs": [ { "indexed": true, - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "indexed": true, @@ -218,9 +218,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "disableZKPRequest", @@ -231,9 +231,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "enableZKPRequest", @@ -249,9 +249,9 @@ "type": "address" }, { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "getProofStatus", @@ -295,9 +295,9 @@ "type": "address" }, { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "string", @@ -319,9 +319,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "getRequestOwner", @@ -338,9 +338,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "getZKPRequest", @@ -446,9 +446,9 @@ "type": "address" }, { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "isProofVerified", @@ -484,9 +484,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "isZKPRequestEnabled", @@ -549,9 +549,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" } ], "name": "requestIdExists", @@ -568,9 +568,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "address", @@ -586,9 +586,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "components": [ @@ -621,9 +621,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "uint256[]", @@ -656,9 +656,9 @@ { "components": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "bytes", @@ -707,9 +707,9 @@ "type": "address" }, { - "internalType": "uint256[]", + "internalType": "uint64[]", "name": "requestIds", - "type": "uint256[]" + "type": "uint64[]" } ], "name": "verifyLinkedProofs", @@ -720,9 +720,9 @@ { "inputs": [ { - "internalType": "uint256", + "internalType": "uint64", "name": "requestId", - "type": "uint256" + "type": "uint64" }, { "internalType": "uint256[]", diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index c790106c..6e0e96c6 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -38,7 +38,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { /** * solidity identifier for function signature: * struct ZKPResponse { - uint256 requestId; + uint64 requestId; bytes zkProof; bytes data; } @@ -47,8 +47,8 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { bytes memory crossChainProof ) public */ - //function submitZKPResponseV2(tuple[](uint256 requestId,bytes zkProof,bytes data),bytes crossChainProof) - public static readonly SupportedMethodIdV2 = '4c3d60fa'; + //function submitZKPResponseV2(tuple[](uint64 requestId,bytes zkProof,bytes data),bytes crossChainProof) + public static readonly SupportedMethodIdV2 = 'ade09fcd'; /** * supported circuits From c34065f3f33a1a0557f2c23daeae0d51b1dae9b6 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Fri, 30 Aug 2024 18:00:38 +0300 Subject: [PATCH 22/41] rm chain config check for input preparation --- src/storage/blockchain/onchain-zkp-verifier.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 6e0e96c6..b28e7f61 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -82,10 +82,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise> { - const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); - if (!chainConfig) { - throw new Error(`config for chain id ${txData.chain_id} was not found`); - } if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { throw new Error( `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` From eb12d8483f58fbf0b3e4e0fda673dadb50ae8359 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Fri, 30 Aug 2024 18:34:38 +0300 Subject: [PATCH 23/41] send 1 tx in submit v2 --- src/iden3comm/handlers/contract-request.ts | 8 +-- .../blockchain/onchain-zkp-verifier.ts | 69 +++++++++---------- .../interfaces/onchain-zkp-verifier.ts | 6 +- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 0f5044e8..9118e07d 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -36,7 +36,7 @@ export interface IContractRequestHandler { did: DID, request: Uint8Array, opts?: ContractInvokeHandlerOptions - ): Promise>; + ): Promise | string>; } /** ContractInvokeHandlerOptions represents contract invoke handler options */ @@ -102,7 +102,7 @@ export class ContractRequestHandler private async handleContractInvoke( message: ContractInvokeRequest, ctx: ContractMessageHandlerOptions - ): Promise> { + ): Promise | string> { if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { throw new Error('Invalid message type for contract invoke request'); } @@ -176,7 +176,7 @@ export class ContractRequestHandler did: DID, request: Uint8Array, opts: ContractInvokeHandlerOptions - ): Promise> { + ): Promise | string> { const ciRequest = await this.parseContractInvokeRequest(request); return this.handleContractInvoke(ciRequest, { @@ -200,7 +200,7 @@ export class ContractRequestHandler opts?: { challenge?: bigint; } - ): Promise> { + ): Promise | string> { const message = await this.parseContractInvokeRequest(request); if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index b28e7f61..739eabc7 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -17,8 +17,8 @@ import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; import { poseidon } from '@iden3/js-crypto'; // Cache for resolved gists and states -const gistCache = new Map(); -const stateCache = new Map(); +// const gistCache = new Map(); +// const stateCache = new Map(); /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract @@ -169,7 +169,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { public async prepareZKPResponseV2TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + ): Promise { if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { throw new Error( `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` @@ -180,7 +180,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const verifierContract = new Contract(txData.contract_address, abi); - const response = new Map(); + const gistUpdateArr = []; + const stateUpdateArr = []; + const payload = []; for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -207,6 +209,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.pub_signals ); + // todo: check if we already have gist resolved for this gist const gistUpdateResolutions = []; for (const gist of stateInfo.gists) { gistUpdateResolutions.push( @@ -218,6 +221,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ); } + // todo: check if we already have state resolved for this gist const stateUpdateResolutions = []; for (const state of stateInfo.states) { stateUpdateResolutions.push( @@ -229,10 +233,10 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ); } - const gistUpdateArr = (await Promise.all(gistUpdateResolutions)) as GlobalStateUpdate[]; - const stateUpdateArr = (await Promise.all(stateUpdateResolutions)) as IdentityStateUpdate[]; - - const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); + gistUpdateArr.push(...((await Promise.all(gistUpdateResolutions)) as GlobalStateUpdate[])); + stateUpdateArr.push( + ...((await Promise.all(stateUpdateResolutions)) as IdentityStateUpdate[]) + ); const metadataArr: { key: string; value: Uint8Array }[] = []; if (zkProof.vp) { @@ -254,23 +258,20 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const metadata = this.packMetadatas(metadataArr); - const payload = [ + payload.push([ { requestId: requestID, zkProof: zkProofEncoded, data: metadata } - ]; - - const txData = await verifierContract.submitZKPResponseV2.populateTransaction( - payload, - crossChainProofs - ); - - response.set(requestID, txData.data); + ]); } - return response; + const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); + + return ( + await verifierContract.submitZKPResponseV2.populateTransaction(payload, crossChainProofs) + ).data; } /** @@ -280,7 +281,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + ): Promise { const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); @@ -296,7 +297,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txDataMap = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); + const txRequestData = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas ? BigInt(chainConfig.maxFeePerGas) @@ -305,25 +306,19 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ? BigInt(chainConfig.maxPriorityFeePerGas) : feeData.maxPriorityFeePerGas; - const response = new Map(); - for (const zkProof of zkProofResponses) { - const payload = txDataMap.get(zkProof.id); - const request: TransactionRequest = { - to: txData.contract_address, - data: payload, - maxFeePerGas, - maxPriorityFeePerGas - }; - - const gasLimit = await ethSigner.estimateGas(request); - request.gasLimit = gasLimit; + const request: TransactionRequest = { + to: txData.contract_address, + data: txRequestData, + maxFeePerGas, + maxPriorityFeePerGas + }; - const transactionService = new TransactionService(provider); - const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); - response.set(txnHash, zkProof); - } + const gasLimit = await ethSigner.estimateGas(request); + request.gasLimit = gasLimit; - return response; + const transactionService = new TransactionService(provider); + const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); + return txnHash; } private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 20bdb6fb..0b6a7495 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -34,7 +34,7 @@ export interface IOnChainZKPVerifier { ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; + ): Promise; /** * Returns the Map of request id to transaction data for the ZKP verifier contract submission. @@ -47,12 +47,12 @@ export interface IOnChainZKPVerifier { ): Promise>; /** - * Returns the Map of request id to transaction data for the ZKP verifier contract submission V2. + * Returns transaction data for the ZKP verifier contract submission V2. (one tx call for all responses) * @param txData * @param zkProofResponses */ prepareZKPResponseV2TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; + ): Promise; } From 10b8fa0379cf89444bd44df56de303d839edafc7 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 30 Aug 2024 18:01:37 +0200 Subject: [PATCH 24/41] fix payload --- src/storage/blockchain/onchain-zkp-verifier.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 739eabc7..e529afc9 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -258,13 +258,11 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const metadata = this.packMetadatas(metadataArr); - payload.push([ - { - requestId: requestID, - zkProof: zkProofEncoded, - data: metadata - } - ]); + payload.push({ + requestId: requestID, + zkProof: zkProofEncoded, + data: metadata + }); } const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); From c5210635b3985cfeff3a1523ad37267f0cf45f3e Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 08:03:25 +0200 Subject: [PATCH 25/41] control sometimes error in estimateGas --- .../blockchain/onchain-zkp-verifier.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index e529afc9..8d9a8569 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -16,10 +16,7 @@ import { byteEncoder, resolveDidDocumentEip712MessageAndSignature } from '../../ import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; import { poseidon } from '@iden3/js-crypto'; -// Cache for resolved gists and states -// const gistCache = new Map(); -// const stateCache = new Map(); - +const maxGasLimit = 10000000n; /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract * and submitZKPResponse. @@ -152,7 +149,12 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { maxPriorityFeePerGas }; - const gasLimit = await ethSigner.estimateGas(request); + let gasLimit; + try { + gasLimit = await ethSigner.estimateGas(request); + } catch (e) { + gasLimit = maxGasLimit; + } request.gasLimit = gasLimit; const transactionService = new TransactionService(provider); @@ -311,7 +313,12 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { maxPriorityFeePerGas }; - const gasLimit = await ethSigner.estimateGas(request); + let gasLimit; + try { + gasLimit = await ethSigner.estimateGas(request); + } catch (e) { + gasLimit = maxGasLimit; + } request.gasLimit = gasLimit; const transactionService = new TransactionService(provider); From 38392dfb2ec172730db2f27193423ace5df380f3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 08:30:14 +0200 Subject: [PATCH 26/41] cache gist and state resolution --- .../blockchain/onchain-zkp-verifier.ts | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 8d9a8569..d039d4a8 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -17,6 +17,11 @@ import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; import { poseidon } from '@iden3/js-crypto'; const maxGasLimit = 10000000n; + +// Cache for resolved gists and states +const gistCache = new Map(); +const stateCache = new Map(); + /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract * and submitZKPResponse. @@ -211,9 +216,13 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.pub_signals ); - // todo: check if we already have gist resolved for this gist const gistUpdateResolutions = []; for (const gist of stateInfo.gists) { + const gistCached = gistCache.get(JSON.stringify(gist)) as GlobalStateUpdate; + + if (gistCached) { + continue; + } gistUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(gist.id), @@ -223,9 +232,13 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ); } - // todo: check if we already have state resolved for this gist const stateUpdateResolutions = []; for (const state of stateInfo.states) { + const stateCached = stateCache.get(JSON.stringify(state)) as IdentityStateUpdate; + + if (stateCached) { + continue; + } stateUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(state.id), @@ -235,10 +248,14 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ); } - gistUpdateArr.push(...((await Promise.all(gistUpdateResolutions)) as GlobalStateUpdate[])); - stateUpdateArr.push( - ...((await Promise.all(stateUpdateResolutions)) as IdentityStateUpdate[]) - ); + if (gistUpdateResolutions.length > 0) { + gistUpdateArr.push(...((await Promise.all(gistUpdateResolutions)) as GlobalStateUpdate[])); + } + if (stateUpdateResolutions.length > 0) { + stateUpdateArr.push( + ...((await Promise.all(stateUpdateResolutions)) as IdentityStateUpdate[]) + ); + } const metadataArr: { key: string; value: Uint8Array }[] = []; if (zkProof.vp) { From cb564012957b493093b775447fe97376e3524df0 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Sep 2024 11:26:27 +0200 Subject: [PATCH 27/41] fix errors and local variables --- .../blockchain/onchain-zkp-verifier.ts | 24 +++++++---- tests/handlers/contract-request.test.ts | 43 ++++++++++++------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index d039d4a8..347f50e3 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -18,10 +18,6 @@ import { poseidon } from '@iden3/js-crypto'; const maxGasLimit = 10000000n; -// Cache for resolved gists and states -const gistCache = new Map(); -const stateCache = new Map(); - /** * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract * and submitZKPResponse. @@ -190,6 +186,10 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const gistUpdateArr = []; const stateUpdateArr = []; const payload = []; + // Resolved gists and states to avoid duplicate requests + const gistUpdateResolutionsPending: string[] = []; + const stateUpdateResolutionsPending: string[] = []; + for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -218,11 +218,15 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const gistUpdateResolutions = []; for (const gist of stateInfo.gists) { - const gistCached = gistCache.get(JSON.stringify(gist)) as GlobalStateUpdate; + const gistResolutionPending = gistUpdateResolutionsPending.find( + (g) => g == JSON.stringify(gist) + ); - if (gistCached) { + if (gistResolutionPending) { continue; } + gistUpdateResolutionsPending.push(JSON.stringify(gist)); + gistUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(gist.id), @@ -234,11 +238,15 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const stateUpdateResolutions = []; for (const state of stateInfo.states) { - const stateCached = stateCache.get(JSON.stringify(state)) as IdentityStateUpdate; + const stateResolutionPending = stateUpdateResolutionsPending.find( + (s) => s == JSON.stringify(state) + ); - if (stateCached) { + if (stateResolutionPending) { continue; } + stateUpdateResolutionsPending.push(JSON.stringify(state)); + stateUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(state.id), diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index bb8688a4..d573346c 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -129,8 +129,7 @@ describe('contract-request', () => { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ) => { - const response = new Map(); - response.set('txhash1', zkProofResponses[0]); + const response = 'txhash1'; return response; }, @@ -139,7 +138,7 @@ describe('contract-request', () => { }, prepareZKPResponseV2TxData: async () => { - return new Map(); + return ''; } }; @@ -320,7 +319,7 @@ describe('contract-request', () => { options ); - expect(ciResponse.has('txhash1')).to.be.true; + expect((ciResponse as Map).has('txhash1')).to.be.true; }); // SKIPPED : integration test @@ -471,9 +470,12 @@ describe('contract-request', () => { ); expect(ciResponse).not.be.undefined; - expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( - proofReq.id - ); + expect( + ( + (ciResponse as Map).values().next() + .value as ZeroKnowledgeProofResponse + ).id + ).to.be.equal(proofReq.id); }); // V3 integration test it.skip('contract request flow V3 - integration test', async () => { @@ -645,9 +647,12 @@ describe('contract-request', () => { ); expect(ciResponse).not.be.undefined; - expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( - proofReqs[0].id - ); + expect( + ( + (ciResponse as Map).values().next() + .value as ZeroKnowledgeProofResponse + ).id + ).to.be.equal(proofReqs[0].id); }); // cross chain integration test @@ -803,9 +808,12 @@ describe('contract-request', () => { ); expect(ciResponse).not.be.undefined; - expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( - proofReqs[0].id - ); + expect( + ( + (ciResponse as Map).values().next() + .value as ZeroKnowledgeProofResponse + ).id + ).to.be.equal(proofReqs[0].id); }); it.skip('contract request flow V3 sig `email-verified` transak req - integration test', async () => { @@ -994,8 +1002,11 @@ describe('contract-request', () => { ); expect(ciResponse).not.be.undefined; - expect((ciResponse.values().next().value as ZeroKnowledgeProofResponse).id).to.be.equal( - proofReqs[0].id - ); + expect( + ( + (ciResponse as Map).values().next() + .value as ZeroKnowledgeProofResponse + ).id + ).to.be.equal(proofReqs[0].id); }); }); From f361abf4dcb9c9db7105ab5f16a8479571cb763a Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 14:48:34 +0300 Subject: [PATCH 28/41] prepareZKPResponseTxData interface changes --- src/iden3comm/handlers/contract-request.ts | 27 +--- .../blockchain/onchain-zkp-verifier.ts | 142 ++++++++++-------- .../interfaces/onchain-zkp-verifier.ts | 20 +-- 3 files changed, 91 insertions(+), 98 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 9118e07d..87ac4fa0 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -1,7 +1,12 @@ import { CircuitId } from '../../circuits/models'; import { IProofService } from '../../proof/proof-service'; import { PROTOCOL_MESSAGE_TYPE } from '../constants'; -import { BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types'; +import { + BasicMessage, + IPackageManager, + JsonDocumentObjectValue, + ZeroKnowledgeProofResponse +} from '../types'; import { ContractInvokeRequest } from '../types/protocol/contract-request'; import { DID, ChainIds } from '@iden3/js-iden3-core'; import { IOnChainZKPVerifier, OnChainZKPVerifier } from '../../storage'; @@ -200,7 +205,7 @@ export class ContractRequestHandler opts?: { challenge?: bigint; } - ): Promise | string> { + ): Promise> { const message = await this.parseContractInvokeRequest(request); if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { @@ -222,22 +227,6 @@ export class ContractRequestHandler { challenge: opts?.challenge, supportedCircuits: this._supportedCircuits } ); - const methodId = message.body.transaction_data.method_id.replace('0x', ''); - switch (methodId) { - case OnChainZKPVerifier.SupportedMethodIdV2: - return this._zkpVerifier.prepareZKPResponseV2TxData( - message.body.transaction_data, - zkpResponses - ); - case OnChainZKPVerifier.SupportedMethodId: - return this._zkpVerifier.prepareZKPResponseTxData( - message.body.transaction_data, - zkpResponses - ); - default: - throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` - ); - } + return this._zkpVerifier.prepareZKPResponseTxData(message.body.transaction_data, zkpResponses); } } diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 347f50e3..2b2df5b8 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -1,7 +1,11 @@ import { JsonRpcProvider, Signer, Contract, TransactionRequest, ethers } from 'ethers'; import { EthConnectionConfig } from './state'; import { IOnChainZKPVerifier } from '../interfaces/onchain-zkp-verifier'; -import { ContractInvokeTransactionData, ZeroKnowledgeProofResponse } from '../../iden3comm'; +import { + ContractInvokeTransactionData, + JsonDocumentObjectValue, + ZeroKnowledgeProofResponse +} from '../../iden3comm'; import abi from './abi/ZkpVerifier.json'; import { TransactionService } from '../../blockchain'; import { DID } from '@iden3/js-iden3-core'; @@ -79,14 +83,16 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { public async prepareZKPResponseTxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + ): Promise> { + if (txData.method_id.replace('0x', '') === OnChainZKPVerifier.SupportedMethodIdV2) { + return this.prepareZKPResponseV2TxData(txData, zkProofResponses); + } if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { throw new Error( `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` ); } - const verifierContract = new Contract(txData.contract_address, abi); - const response = new Map(); + const response = new Map(); for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -102,8 +108,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.proof.pi_c.slice(0, 2) ]; - const txData = await verifierContract.submitZKPResponse.populateTransaction(...payload); - response.set(requestID, txData.data); + response.set([zkProof], payload); } return response; @@ -140,12 +145,13 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ? BigInt(chainConfig.maxPriorityFeePerGas) : feeData.maxPriorityFeePerGas; - for (const zkProof of zkProofResponses) { - const payload = txDataMap.get(zkProof.id); + const verifierContract = new Contract(txData.contract_address, abi); + for (const [[zkProof], value] of txDataMap) { + const payload = await verifierContract.submitZKPResponse.populateTransaction(...value); const request: TransactionRequest = { to: txData.contract_address, - data: payload, + data: payload.data, maxFeePerGas, maxPriorityFeePerGas }; @@ -166,13 +172,19 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return response; } + /** - * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseV2TxData} + * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} */ - public async prepareZKPResponseV2TxData( + public async submitZKPResponseV2( + ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise { + const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); + if (!chainConfig) { + throw new Error(`config for chain id ${txData.chain_id} was not found`); + } if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { throw new Error( `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` @@ -181,8 +193,59 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!this._didResolverUrl) { throw new Error(`did resolver url required for crosschain verification`); } + const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); + ethSigner = ethSigner.connect(provider); + + const txRequestMap = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); + const txRequestParams = txRequestMap.get(zkProofResponses); + if (!txRequestParams) { + throw new Error('no transaction args found for requests'); + } + const feeData = await provider.getFeeData(); + const maxFeePerGas = chainConfig.maxFeePerGas + ? BigInt(chainConfig.maxFeePerGas) + : feeData.maxFeePerGas; + const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas + ? BigInt(chainConfig.maxPriorityFeePerGas) + : feeData.maxPriorityFeePerGas; + const verifierContract = new Contract(txData.contract_address, abi); + const txRequestData = await verifierContract.submitZKPResponseV2.populateTransaction( + ...txRequestParams + ); + + const request: TransactionRequest = { + to: txData.contract_address, + data: txRequestData.data, + maxFeePerGas, + maxPriorityFeePerGas + }; + + let gasLimit; + try { + gasLimit = await ethSigner.estimateGas(request); + } catch (e) { + gasLimit = maxGasLimit; + } + request.gasLimit = gasLimit; + const transactionService = new TransactionService(provider); + const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); + return txnHash; + } + + private async prepareZKPResponseV2TxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise> { + if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { + throw new Error( + `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` + ); + } + if (!this._didResolverUrl) { + throw new Error(`did resolver url required for crosschain verification`); + } const gistUpdateArr = []; const stateUpdateArr = []; const payload = []; @@ -294,61 +357,8 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); - return ( - await verifierContract.submitZKPResponseV2.populateTransaction(payload, crossChainProofs) - ).data; - } - - /** - * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} - */ - public async submitZKPResponseV2( - ethSigner: Signer, - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise { - const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); - if (!chainConfig) { - throw new Error(`config for chain id ${txData.chain_id} was not found`); - } - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { - throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` - ); - } - if (!this._didResolverUrl) { - throw new Error(`did resolver url required for crosschain verification`); - } - const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); - ethSigner = ethSigner.connect(provider); - - const txRequestData = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); - const feeData = await provider.getFeeData(); - const maxFeePerGas = chainConfig.maxFeePerGas - ? BigInt(chainConfig.maxFeePerGas) - : feeData.maxFeePerGas; - const maxPriorityFeePerGas = chainConfig.maxPriorityFeePerGas - ? BigInt(chainConfig.maxPriorityFeePerGas) - : feeData.maxPriorityFeePerGas; - - const request: TransactionRequest = { - to: txData.contract_address, - data: txRequestData, - maxFeePerGas, - maxPriorityFeePerGas - }; - - let gasLimit; - try { - gasLimit = await ethSigner.estimateGas(request); - } catch (e) { - gasLimit = maxGasLimit; - } - request.gasLimit = gasLimit; - - const transactionService = new TransactionService(provider); - const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); - return txnHash; + const response = new Map(); + return response.set(zkProofResponses, [payload, crossChainProofs]); } private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 0b6a7495..9f382689 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -1,5 +1,9 @@ import { Signer } from 'ethers'; -import { ContractInvokeTransactionData, ZeroKnowledgeProofResponse } from '../../iden3comm'; +import { + ContractInvokeTransactionData, + JsonDocumentObjectValue, + ZeroKnowledgeProofResponse +} from '../../iden3comm'; /** * Interface that defines methods for ZKP verifier @@ -37,22 +41,12 @@ export interface IOnChainZKPVerifier { ): Promise; /** - * Returns the Map of request id to transaction data for the ZKP verifier contract submission. + * Returns the Map of ZKP Responses to transaction data args for the ZKP verifier contract submission. * @param txData * @param zkProofResponses */ prepareZKPResponseTxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; - - /** - * Returns transaction data for the ZKP verifier contract submission V2. (one tx call for all responses) - * @param txData - * @param zkProofResponses - */ - prepareZKPResponseV2TxData( - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise; + ): Promise>; } From 3b9fd92ccbaaea7712d608253daddf9ad587b379 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 14:50:23 +0300 Subject: [PATCH 29/41] format --- src/storage/blockchain/onchain-zkp-verifier.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 2b2df5b8..8a58207a 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -172,7 +172,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return response; } - /** * {@inheritDoc IOnChainZKPVerifier.submitZKPResponseV2} */ From 82aea54454a11553b804c170ff00eecb3861bc14 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 15:11:28 +0300 Subject: [PATCH 30/41] fix unit mock --- tests/handlers/contract-request.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index d573346c..39b0a47b 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -124,21 +124,13 @@ describe('contract-request', () => { return response; }, - submitZKPResponseV2: async ( - signer: Signer, - txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ) => { + submitZKPResponseV2: async () => { const response = 'txhash1'; return response; }, prepareZKPResponseTxData: async () => { return new Map(); - }, - - prepareZKPResponseV2TxData: async () => { - return ''; } }; From 755b7f74343dbd4d20763a45ca476145d57bfde5 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 18:08:22 +0300 Subject: [PATCH 31/41] add CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE --- src/iden3comm/constants.ts | 3 + src/iden3comm/handlers/contract-request.ts | 112 +++++++++++------- .../types/protocol/contract-request.ts | 20 +++- .../blockchain/onchain-zkp-verifier.ts | 41 +++---- .../interfaces/onchain-zkp-verifier.ts | 21 +++- 5 files changed, 125 insertions(+), 72 deletions(-) diff --git a/src/iden3comm/constants.ts b/src/iden3comm/constants.ts index e755af68..a2d4e1c2 100644 --- a/src/iden3comm/constants.ts +++ b/src/iden3comm/constants.ts @@ -34,6 +34,9 @@ export const PROTOCOL_MESSAGE_TYPE = Object.freeze({ // ContractInvokeRequestMessageType is type for request of contract invoke request CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE: `${IDEN3_PROTOCOL}proofs/1.0/contract-invoke-request` as const, + // ContractInvokeResponseMessageType is type for response of contract invoke request + CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE: + `${IDEN3_PROTOCOL}proofs/1.0/contract-invoke-response` as const, // CredentialOnchainOfferMessageType is type of message with credential onchain offering CREDENTIAL_ONCHAIN_OFFER_MESSAGE_TYPE: `${IDEN3_PROTOCOL}credentials/1.0/onchain-offer` as const, // ProposalRequestMessageType is type for proposal-request message diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 87ac4fa0..e1dd1a62 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -1,13 +1,8 @@ import { CircuitId } from '../../circuits/models'; import { IProofService } from '../../proof/proof-service'; import { PROTOCOL_MESSAGE_TYPE } from '../constants'; -import { - BasicMessage, - IPackageManager, - JsonDocumentObjectValue, - ZeroKnowledgeProofResponse -} from '../types'; -import { ContractInvokeRequest } from '../types/protocol/contract-request'; +import { BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types'; +import { ContractInvokeRequest, ContractInvokeResponse } from '../types/protocol/contract-request'; import { DID, ChainIds } from '@iden3/js-iden3-core'; import { IOnChainZKPVerifier, OnChainZKPVerifier } from '../../storage'; import { Signer } from 'ethers'; @@ -96,9 +91,11 @@ export class ContractRequestHandler ctx: ContractMessageHandlerOptions ): Promise { switch (message.type) { - case PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE: - await this.handleContractInvoke(message as ContractInvokeRequest, ctx); - return null; + case PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE: { + const ciMessage = message as ContractInvokeRequest; + const txHashResponsesMap = await this.handleContractInvoke(ciMessage, ctx); + return this.createContractInvokeResponse(ciMessage, txHashResponsesMap); + } default: return super.handle(message, ctx); } @@ -107,7 +104,7 @@ export class ContractRequestHandler private async handleContractInvoke( message: ContractInvokeRequest, ctx: ContractMessageHandlerOptions - ): Promise | string> { + ): Promise> { if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { throw new Error('Invalid message type for contract invoke request'); } @@ -140,12 +137,18 @@ export class ContractRequestHandler message.body.transaction_data, zkpResponses ); - case OnChainZKPVerifier.SupportedMethodId: - return this._zkpVerifier.submitZKPResponse( + case OnChainZKPVerifier.SupportedMethodId: { + const txHashZkpResponseMap = await this._zkpVerifier.submitZKPResponse( ethSigner, message.body.transaction_data, zkpResponses ); + const response = new Map(); + for (const [txHash, zkpResponse] of txHashZkpResponseMap) { + response.set(txHash, [zkpResponse]); + } + return response; + } default: throw new Error( `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` @@ -170,63 +173,86 @@ export class ContractRequestHandler } /** - * handle contract invoker request + * creates contract invoke response * @beta - * @param {did} did - sender DID - * @param {ContractInvokeRequest} request - contract invoke request - * @param {ContractInvokeHandlerOptions} opts - handler options - * @returns {Map}` - map of transaction hash - ZeroKnowledgeProofResponse + * @param {ContractInvokeRequest} request - ContractInvokeRequest + * @param { Map} responses - map tx hash to array of ZeroKnowledgeProofResponses + * @returns `Promise` */ - async handleContractInvokeRequest( - did: DID, - request: Uint8Array, - opts: ContractInvokeHandlerOptions - ): Promise | string> { - const ciRequest = await this.parseContractInvokeRequest(request); - - return this.handleContractInvoke(ciRequest, { - senderDid: did, - ethSigner: opts.ethSigner, - challenge: opts.challenge - }); + async createContractInvokeResponse( + request: ContractInvokeRequest, + responses: Map + ): Promise { + const response: ContractInvokeResponse = { + id: request.id, + type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE, + from: request.to, + to: request.from, + body: { + transaction_data: request.body.transaction_data, + reason: request.body.reason, + scope: [] + } + }; + for (const [txHash, zkpResponses] of responses) { + for (const zkpResponse of zkpResponses) { + response.body.scope.push({ + txHash, + ...zkpResponse + }); + } + } + return response; } /** - * prepare contract invoker request transaction data + * handle contract invoker request + * supports only 0xb68967e2 method id * @beta + * @deprecated * @param {did} did - sender DID * @param {ContractInvokeRequest} request - contract invoke request * @param {ContractInvokeHandlerOptions} opts - handler options * @returns {Map}` - map of transaction hash - ZeroKnowledgeProofResponse */ - async prepareContractInvokeRequestTxData( + async handleContractInvokeRequest( did: DID, request: Uint8Array, - opts?: { - challenge?: bigint; + opts: ContractInvokeHandlerOptions + ): Promise> { + const ciRequest = await this.parseContractInvokeRequest(request); + + if (ciRequest.body.transaction_data.method_id !== OnChainZKPVerifier.SupportedMethodId) { + throw new Error(`please use handle method to work with other method ids`); } - ): Promise> { - const message = await this.parseContractInvokeRequest(request); - if (message.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { + if (ciRequest.type !== PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_REQUEST_MESSAGE_TYPE) { throw new Error('Invalid message type for contract invoke request'); } - const { chain_id } = message.body.transaction_data; + const { ethSigner, challenge } = opts; + if (!ethSigner) { + throw new Error("Can't sign transaction. Provide Signer in options."); + } + + const { chain_id } = ciRequest.body.transaction_data; const networkFlag = Object.keys(ChainIds).find((key) => ChainIds[key] === chain_id); if (!networkFlag) { throw new Error(`Invalid chain id ${chain_id}`); } - const verifierDid = message.from ? DID.parse(message.from) : undefined; + const verifierDid = ciRequest.from ? DID.parse(ciRequest.from) : undefined; const zkpResponses = await processZeroKnowledgeProofRequests( did, - message?.body?.scope, + ciRequest?.body?.scope, verifierDid, this._proofService, - { challenge: opts?.challenge, supportedCircuits: this._supportedCircuits } + { ethSigner, challenge, supportedCircuits: this._supportedCircuits } + ); + return this._zkpVerifier.submitZKPResponse( + ethSigner, + ciRequest.body.transaction_data, + zkpResponses ); - - return this._zkpVerifier.prepareZKPResponseTxData(message.body.transaction_data, zkpResponses); } } diff --git a/src/iden3comm/types/protocol/contract-request.ts b/src/iden3comm/types/protocol/contract-request.ts index 5c1a230d..588c37d5 100644 --- a/src/iden3comm/types/protocol/contract-request.ts +++ b/src/iden3comm/types/protocol/contract-request.ts @@ -1,6 +1,6 @@ import { PROTOCOL_MESSAGE_TYPE } from '../../constants'; import { BasicMessage } from '../packer'; -import { ZeroKnowledgeProofRequest } from './auth'; +import { ZeroKnowledgeProofRequest, ZeroKnowledgeProofResponse } from './auth'; /** ContractInvokeRequest represents structure of contract invoke request object */ export type ContractInvokeRequest = BasicMessage & { @@ -15,6 +15,24 @@ export type ContractInvokeRequestBody = { scope: Array; }; +/** ContractInvokeResponse represents structure of contract invoke response object */ +export type ContractInvokeResponse = BasicMessage & { + body: ContractInvokeResponseBody; + type: typeof PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE; +}; + +/** ContractInvokeResponseBody represents structure of contract invoke response body object */ +export type ContractInvokeResponseBody = { + scope: Array; + transaction_data: ContractInvokeTransactionData; + reason: string; +}; + +/** OnChainZeroKnowledgeProofResponse represents structure of onchain zero knowledge proof response */ +export type OnChainZeroKnowledgeProofResponse = ZeroKnowledgeProofResponse & { + txHash: string; +}; + /** ContractInvokeTransactionData represents structure of contract invoke transaction data object */ export type ContractInvokeTransactionData = { contract_address: string; diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 8a58207a..037a1246 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -78,21 +78,18 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ) {} /** - * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseTxData} + * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseSubmitV1TxData} */ - public async prepareZKPResponseTxData( + public async prepareZKPResponseSubmitV1TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { - if (txData.method_id.replace('0x', '') === OnChainZKPVerifier.SupportedMethodIdV2) { - return this.prepareZKPResponseV2TxData(txData, zkProofResponses); - } + ): Promise> { if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { throw new Error( `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` ); } - const response = new Map(); + const response = new Map(); for (const zkProof of zkProofResponses) { const requestID = zkProof.id; const inputs = zkProof.pub_signals; @@ -108,7 +105,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { zkProof.proof.pi_c.slice(0, 2) ]; - response.set([zkProof], payload); + response.set(requestID, payload); } return response; @@ -134,7 +131,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txDataMap = await this.prepareZKPResponseTxData(txData, zkProofResponses); + const txDataMap = await this.prepareZKPResponseSubmitV1TxData(txData, zkProofResponses); const response = new Map(); const feeData = await provider.getFeeData(); @@ -147,7 +144,11 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const verifierContract = new Contract(txData.contract_address, abi); - for (const [[zkProof], value] of txDataMap) { + for (const [requestId, value] of txDataMap) { + const zkProof = zkProofResponses.find((i) => i.id == requestId); + if (!zkProof) { + throw new Error(`zkProof not found for request id ${requestId}`); + } const payload = await verifierContract.submitZKPResponse.populateTransaction(...value); const request: TransactionRequest = { to: txData.contract_address, @@ -179,7 +180,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise { + ): Promise> { const chainConfig = this._configs.find((i) => i.chainId == txData.chain_id); if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); @@ -195,11 +196,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txRequestMap = await this.prepareZKPResponseV2TxData(txData, zkProofResponses); - const txRequestParams = txRequestMap.get(zkProofResponses); - if (!txRequestParams) { - throw new Error('no transaction args found for requests'); - } + const txDataArgs = await this.prepareZKPResponseSubmitV1TxData(txData, zkProofResponses); const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas ? BigInt(chainConfig.maxFeePerGas) @@ -210,7 +207,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const verifierContract = new Contract(txData.contract_address, abi); const txRequestData = await verifierContract.submitZKPResponseV2.populateTransaction( - ...txRequestParams + ...txDataArgs ); const request: TransactionRequest = { @@ -230,13 +227,13 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const transactionService = new TransactionService(provider); const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); - return txnHash; + return new Map().set(txnHash, zkProofResponses); } - private async prepareZKPResponseV2TxData( + public async prepareZKPResponseSingleTxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + ): Promise { if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { throw new Error( `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` @@ -355,9 +352,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const crossChainProofs = this.packCrossChainProofs(gistUpdateArr, stateUpdateArr); - - const response = new Map(); - return response.set(zkProofResponses, [payload, crossChainProofs]); + return [payload, crossChainProofs]; } private packZkpProof(inputs: string[], a: string[], b: string[][], c: string[]): string { diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 9f382689..c48cc3cb 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -32,21 +32,32 @@ export interface IOnChainZKPVerifier { * @param {Signer} ethSigner - tx signer * @param {txData} ContractInvokeTransactionData - transaction data * @param {ZeroKnowledgeProofResponse[]} zkProofResponses - zkProofResponses - * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse + * @returns {Promise>} - map of transaction hash - ZeroKnowledgeProofResponse[] */ submitZKPResponseV2( ethSigner: Signer, txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise; + ): Promise>; + + /** + * Returns the Map of request id to transaction data args for the ZKP verifier contract submission. + * For each request id new transaction data is created. + * @param txData + * @param zkProofResponses + */ + prepareZKPResponseSubmitV1TxData( + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ): Promise>; /** - * Returns the Map of ZKP Responses to transaction data args for the ZKP verifier contract submission. + * Returns args for the ZKP verifier contract submission V2 (single tx). * @param txData * @param zkProofResponses */ - prepareZKPResponseTxData( + prepareZKPResponseSingleTxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; + ): Promise; } From cf7c4efc2e72ad129f7bde974a46a4cb09ce49f2 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 18:10:41 +0300 Subject: [PATCH 32/41] fix typo --- src/iden3comm/handlers/contract-request.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index e1dd1a62..31cfaa4e 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -17,7 +17,7 @@ import { AbstractMessageHandler, IProtocolMessageHandler } from './message-handl */ export interface IContractRequestHandler { /** - * unpacks contract invoker request + * unpacks contract invoke request * @beta * @param {Uint8Array} request - raw byte message * @returns `Promise` @@ -25,7 +25,7 @@ export interface IContractRequestHandler { parseContractInvokeRequest(request: Uint8Array): Promise; /** - * handle contract invoker request + * handle contract invoke request * @beta * @param {did} did - sender DID * @param {Uint8Array} request - raw byte message @@ -206,7 +206,7 @@ export class ContractRequestHandler } /** - * handle contract invoker request + * handle contract invoke request * supports only 0xb68967e2 method id * @beta * @deprecated From 376b8b9dafe1f6fff3387d5cbbe90f5a5cbe9810 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 18:15:43 +0300 Subject: [PATCH 33/41] fix mock --- tests/handlers/contract-request.test.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 39b0a47b..a1e70787 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -38,6 +38,7 @@ import { DataPrepareHandlerFunc, IContractRequestHandler, IPackageManager, + JsonDocumentObjectValue, PackageManager, ProvingParams, StateVerificationFunc, @@ -124,13 +125,22 @@ describe('contract-request', () => { return response; }, - submitZKPResponseV2: async () => { - const response = 'txhash1'; + submitZKPResponseV2: async ( + signer: Signer, + txData: ContractInvokeTransactionData, + zkProofResponses: ZeroKnowledgeProofResponse[] + ) => { + const response = new Map(); + response.set('txhash1', zkProofResponses); return response; }, - prepareZKPResponseTxData: async () => { - return new Map(); + prepareZKPResponseSubmitV1TxData: async () => { + return new Map(); + }, + + prepareZKPResponseSingleTxData: async () => { + return []; } }; From dff3bef7903c86cad54b5e45902eabf332de3371 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Mon, 2 Sep 2024 19:19:55 +0300 Subject: [PATCH 34/41] fix prepare v2 --- src/storage/blockchain/onchain-zkp-verifier.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 037a1246..d45a30b3 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -196,7 +196,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txDataArgs = await this.prepareZKPResponseSubmitV1TxData(txData, zkProofResponses); + const txDataArgs = await this.prepareZKPResponseSingleTxData(txData, zkProofResponses); const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas ? BigInt(chainConfig.maxFeePerGas) From d3759c814d891917d177f442f044ea83ca686f8b Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 3 Sep 2024 15:28:59 +0300 Subject: [PATCH 35/41] cleanup --- src/iden3comm/handlers/contract-request.ts | 2 +- src/iden3comm/types/packer.ts | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 31cfaa4e..b6294f1e 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -36,7 +36,7 @@ export interface IContractRequestHandler { did: DID, request: Uint8Array, opts?: ContractInvokeHandlerOptions - ): Promise | string>; + ): Promise>; } /** ContractInvokeHandlerOptions represents contract invoke handler options */ diff --git a/src/iden3comm/types/packer.ts b/src/iden3comm/types/packer.ts index 7a50d933..ab1dccf7 100644 --- a/src/iden3comm/types/packer.ts +++ b/src/iden3comm/types/packer.ts @@ -38,16 +38,6 @@ export type JsonDocumentObjectValue = | JsonDocumentObject | JsonDocumentObjectValue[]; -/** - * Context object type (for VP and VC) - */ -export type ContextObject = { [key: string]: ContextObjectValue }; - -/** - * Context object allowed values - */ -export type ContextObjectValue = string | JsonDocumentObject | JsonDocumentObjectValue[]; - export type BasicMessage = { id: string; typ?: MediaType; From 875d217a788524b62d98784be15402a71b3d01e4 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 3 Sep 2024 18:38:50 +0300 Subject: [PATCH 36/41] FunctionSignatures enum --- src/iden3comm/handlers/contract-request.ts | 14 ++-- .../blockchain/onchain-zkp-verifier.ts | 70 +++++++++---------- .../interfaces/onchain-zkp-verifier.ts | 2 +- src/utils/did-helper.ts | 4 +- tests/handlers/contract-request.test.ts | 2 +- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index b6294f1e..586580f7 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -4,7 +4,7 @@ import { PROTOCOL_MESSAGE_TYPE } from '../constants'; import { BasicMessage, IPackageManager, ZeroKnowledgeProofResponse } from '../types'; import { ContractInvokeRequest, ContractInvokeResponse } from '../types/protocol/contract-request'; import { DID, ChainIds } from '@iden3/js-iden3-core'; -import { IOnChainZKPVerifier, OnChainZKPVerifier } from '../../storage'; +import { FunctionSignatures, IOnChainZKPVerifier } from '../../storage'; import { Signer } from 'ethers'; import { processZeroKnowledgeProofRequests } from './common'; import { AbstractMessageHandler, IProtocolMessageHandler } from './message-handler'; @@ -131,13 +131,13 @@ export class ContractRequestHandler const methodId = message.body.transaction_data.method_id.replace('0x', ''); switch (methodId) { - case OnChainZKPVerifier.SupportedMethodIdV2: + case FunctionSignatures.SumbitZKPResponseV2: return this._zkpVerifier.submitZKPResponseV2( ethSigner, message.body.transaction_data, zkpResponses ); - case OnChainZKPVerifier.SupportedMethodId: { + case FunctionSignatures.SumbitZKPResponseV1: { const txHashZkpResponseMap = await this._zkpVerifier.submitZKPResponse( ethSigner, message.body.transaction_data, @@ -151,7 +151,7 @@ export class ContractRequestHandler } default: throw new Error( - `Not supported method id. Only '${OnChainZKPVerifier.SupportedMethodIdV2} and ${OnChainZKPVerifier.SupportedMethodId} are supported.'` + `Not supported method id. Only '${FunctionSignatures.SumbitZKPResponseV1} and ${FunctionSignatures.SumbitZKPResponseV2} are supported.'` ); } } @@ -174,17 +174,19 @@ export class ContractRequestHandler /** * creates contract invoke response + * @private * @beta * @param {ContractInvokeRequest} request - ContractInvokeRequest * @param { Map} responses - map tx hash to array of ZeroKnowledgeProofResponses * @returns `Promise` */ - async createContractInvokeResponse( + private async createContractInvokeResponse( request: ContractInvokeRequest, responses: Map ): Promise { const response: ContractInvokeResponse = { id: request.id, + thid: request.thid, type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE, from: request.to, to: request.from, @@ -222,7 +224,7 @@ export class ContractRequestHandler ): Promise> { const ciRequest = await this.parseContractInvokeRequest(request); - if (ciRequest.body.transaction_data.method_id !== OnChainZKPVerifier.SupportedMethodId) { + if (ciRequest.body.transaction_data.method_id !== FunctionSignatures.SumbitZKPResponseV1) { throw new Error(`please use handle method to work with other method ids`); } diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index d45a30b3..a7013078 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -23,35 +23,33 @@ import { poseidon } from '@iden3/js-crypto'; const maxGasLimit = 10000000n; /** - * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract - * and submitZKPResponse. - * - * @beta - * @class OnChainZKPVerifier + * Supported function signature for SubmitZKPResponse */ -export class OnChainZKPVerifier implements IOnChainZKPVerifier { +export enum FunctionSignatures { /** * solidity identifier for function signature: * function submitZKPResponse(uint64 requestId, uint256[] calldata inputs, * uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c) public */ - public static readonly SupportedMethodId = 'b68967e2'; - - /** - * solidity identifier for function signature: - * struct ZKPResponse { - uint64 requestId; - bytes zkProof; - bytes data; - } - * function submitZKPResponseV2( - ZKPResponse[] memory responses, - bytes memory crossChainProof - ) public - */ + SumbitZKPResponseV1 = 'b68967e2', //function submitZKPResponseV2(tuple[](uint64 requestId,bytes zkProof,bytes data),bytes crossChainProof) - public static readonly SupportedMethodIdV2 = 'ade09fcd'; + SumbitZKPResponseV2 = 'ade09fcd' +} +/** + * OnChainZKPVerifierOptions represents OnChainZKPVerifier options + */ +export type OnChainZKPVerifierOptions = { + didResolverUrl?: string; +}; +/** + * OnChainZKPVerifier is a class that allows to interact with the OnChainZKPVerifier contract + * and submitZKPResponse. + * + * @beta + * @class OnChainZKPVerifier + */ +export class OnChainZKPVerifier implements IOnChainZKPVerifier { /** * supported circuits */ @@ -74,7 +72,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { constructor( private readonly _configs: EthConnectionConfig[], - private readonly _didResolverUrl?: string + private readonly _opts?: OnChainZKPVerifierOptions ) {} /** @@ -84,9 +82,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise> { - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { + if (txData.method_id.replace('0x', '') !== FunctionSignatures.SumbitZKPResponseV1) { throw new Error( - `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` + `prepareZKPResponseSubmitV1TxData function doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV1}' is supported.` ); } const response = new Map(); @@ -123,9 +121,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); } - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodId) { + if (txData.method_id.replace('0x', '') !== FunctionSignatures.SumbitZKPResponseV1) { throw new Error( - `submit doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodId}' is supported.` + `submitZKPResponse function doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV1}' is supported.` ); } const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); @@ -185,18 +183,18 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { if (!chainConfig) { throw new Error(`config for chain id ${txData.chain_id} was not found`); } - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { + if (txData.method_id.replace('0x', '') !== FunctionSignatures.SumbitZKPResponseV2) { throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` + `submitZKPResponseV2 function doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV2}' is supported.` ); } - if (!this._didResolverUrl) { + if (!this._opts?.didResolverUrl) { throw new Error(`did resolver url required for crosschain verification`); } const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txDataArgs = await this.prepareZKPResponseSingleTxData(txData, zkProofResponses); + const txDataArgs = await this.prepareZKPResponseSubmitV2TxData(txData, zkProofResponses); const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas ? BigInt(chainConfig.maxFeePerGas) @@ -230,16 +228,16 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return new Map().set(txnHash, zkProofResponses); } - public async prepareZKPResponseSingleTxData( + public async prepareZKPResponseSubmitV2TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise { - if (txData.method_id.replace('0x', '') !== OnChainZKPVerifier.SupportedMethodIdV2) { + if (txData.method_id.replace('0x', '') !== FunctionSignatures.SumbitZKPResponseV2) { throw new Error( - `submit cross chain doesn't implement requested method id. Only '0x${OnChainZKPVerifier.SupportedMethodIdV2}' is supported.` + `submit cross chain doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV2}' is supported.` ); } - if (!this._didResolverUrl) { + if (!this._opts?.didResolverUrl) { throw new Error(`did resolver url required for crosschain verification`); } const gistUpdateArr = []; @@ -289,7 +287,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { gistUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(gist.id), - this._didResolverUrl, + this._opts.didResolverUrl, { gist: gist.root } ) ); @@ -309,7 +307,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { stateUpdateResolutions.push( resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(state.id), - this._didResolverUrl, + this._opts.didResolverUrl, { state: state.state } ) ); diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index c48cc3cb..031eb8af 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -56,7 +56,7 @@ export interface IOnChainZKPVerifier { * @param txData * @param zkProofResponses */ - prepareZKPResponseSingleTxData( + prepareZKPResponseSubmitV2TxData( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise; diff --git a/src/utils/did-helper.ts b/src/utils/did-helper.ts index 03d0b0c7..ff61c3fe 100644 --- a/src/utils/did-helper.ts +++ b/src/utils/did-helper.ts @@ -102,7 +102,7 @@ function emptyStateDID(did: DID) { export const resolveDidDocumentEip712MessageAndSignature = async ( did: DID, - resolveURL: string, + resolverUrl: string, opts?: { state?: Hash; gist?: Hash; @@ -114,7 +114,7 @@ export const resolveDidDocumentEip712MessageAndSignature = async ( if (isGistRequest) { didString = emptyStateDID(did).string(); } - let url = `${resolveURL}/1.0/identifiers/${didString}?signature=EthereumEip712Signature2021`; + let url = `${resolverUrl}/1.0/identifiers/${didString}?signature=EthereumEip712Signature2021`; if (opts?.state) { url += `&state=${opts.state.hex()}`; } diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index a1e70787..dfab44c3 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -139,7 +139,7 @@ describe('contract-request', () => { return new Map(); }, - prepareZKPResponseSingleTxData: async () => { + prepareZKPResponseSubmitV2TxData: async () => { return []; } }; From 08f5bfd79e6ea62d0d240691140b8bec99c0514a Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 3 Sep 2024 20:11:09 +0300 Subject: [PATCH 37/41] fix unit tests --- tests/handlers/contract-request.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index dfab44c3..7d5eda90 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -770,7 +770,9 @@ describe('contract-request', () => { conf.url = amoyVerifierRpcUrl; conf.chainId = 80002; // amoy chain id - const zkpVerifier = new OnChainZKPVerifier([conf], 'https://resolver-dev.privado.id'); + const zkpVerifier = new OnChainZKPVerifier([conf], { + didResolverUrl: 'https://resolver-dev.privado.id' + }); contractRequestHandler = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); const transactionData: ContractInvokeTransactionData = { @@ -957,10 +959,9 @@ describe('contract-request', () => { } ]; - const zkpVerifier = new OnChainZKPVerifier( - [amoyStateEthConfig], - 'https://resolver-dev.privado.id' - ); + const zkpVerifier = new OnChainZKPVerifier([amoyStateEthConfig], { + didResolverUrl: 'https://resolver-dev.privado.id' + }); contractRequestHandler = new ContractRequestHandler(packageMgr, proofService, zkpVerifier); const transactionData: ContractInvokeTransactionData = { From a006077bd27bd05801edbe133e8ab754767f1b47 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Tue, 3 Sep 2024 20:29:31 +0300 Subject: [PATCH 38/41] resolveDidDocument --- .../blockchain/onchain-zkp-verifier.ts | 52 +++++++++++++++++-- src/utils/did-helper.ts | 50 +++++++----------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index a7013078..1c38f059 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -16,9 +16,10 @@ import { CircuitId, StatesInfo } from '../../circuits'; -import { byteEncoder, resolveDidDocumentEip712MessageAndSignature } from '../../utils'; +import { byteEncoder, DIDDocumentSignature, resolveDidDocument } from '../../utils'; import { GlobalStateUpdate, IdentityStateUpdate } from '../entities/state'; import { poseidon } from '@iden3/js-crypto'; +import { Hash } from '@iden3/js-merkletree'; const maxGasLimit = 10000000n; @@ -285,7 +286,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { gistUpdateResolutionsPending.push(JSON.stringify(gist)); gistUpdateResolutions.push( - resolveDidDocumentEip712MessageAndSignature( + this.resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(gist.id), this._opts.didResolverUrl, { gist: gist.root } @@ -305,10 +306,12 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { stateUpdateResolutionsPending.push(JSON.stringify(state)); stateUpdateResolutions.push( - resolveDidDocumentEip712MessageAndSignature( + this.resolveDidDocumentEip712MessageAndSignature( DID.parseFromId(state.id), this._opts.didResolverUrl, - { state: state.state } + { + state: state.state + } ) ); } @@ -450,4 +453,45 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { atomicQueryPubSignals.pubSignalsUnmarshal(encodedInputs); return atomicQueryPubSignals.getStatesInfo(); } + + private async resolveDidDocumentEip712MessageAndSignature( + did: DID, + resolverUrl: string, + opts?: { + state?: Hash; + gist?: Hash; + } + ) { + const didDoc = await resolveDidDocument(did, resolverUrl, { + ...opts, + signature: DIDDocumentSignature.EthereumEip712Signature2021 + }); + if (!didDoc.didResolutionMetadata.proof?.length) { + throw new Error('No proof found in resolved DID document'); + } + const message = didDoc.didResolutionMetadata.proof[0].eip712.message; + const signature = didDoc.didResolutionMetadata.proof[0].proofValue; + const isGistRequest = opts?.gist && !opts.state; + if (isGistRequest) { + return { + globalStateMsg: { + timestamp: message.timestamp, + idType: message.idType, + root: message.root, + replacedAtTimestamp: message.replacedAtTimestamp + }, + signature + }; + } + + return { + idStateMsg: { + timestamp: message.timestamp, + id: message.id, + state: message.state, + replacedAtTimestamp: message.replacedAtTimestamp + }, + signature + }; + } } diff --git a/src/utils/did-helper.ts b/src/utils/did-helper.ts index ff61c3fe..058094ee 100644 --- a/src/utils/did-helper.ts +++ b/src/utils/did-helper.ts @@ -1,10 +1,16 @@ import { Hex } from '@iden3/js-crypto'; import { Id, buildDIDType, genesisFromEthAddress, DID } from '@iden3/js-iden3-core'; import { Hash } from '@iden3/js-merkletree'; -import { DIDResolutionResult, VerificationMethod } from 'did-resolver'; +import { DIDResolutionResult, VerificationMethod, DIDResolutionMetadata } from 'did-resolver'; import { keccak256 } from 'js-sha3'; import { hexToBytes } from './encoding'; -import { GlobalStateUpdate, IdentityStateUpdate } from '../storage'; + +/** + * Supported DID Document Signatures + */ +export enum DIDDocumentSignature { + EthereumEip712Signature2021 = 'EthereumEip712Signature2021' +} /** * Checks if state is genesis state @@ -100,54 +106,38 @@ function emptyStateDID(did: DID) { return emptyDID; } -export const resolveDidDocumentEip712MessageAndSignature = async ( +export const resolveDidDocument = async ( did: DID, resolverUrl: string, opts?: { state?: Hash; gist?: Hash; + signature?: DIDDocumentSignature; } -): Promise => { +): Promise => { let didString = did.string().replace(/:/g, '%3A'); // for gist resolve we have to `hide` user did (look into resolver implementation) const isGistRequest = opts?.gist && !opts.state; if (isGistRequest) { didString = emptyStateDID(did).string(); } - let url = `${resolverUrl}/1.0/identifiers/${didString}?signature=EthereumEip712Signature2021`; + let url = `${resolverUrl}/1.0/identifiers/${didString}`; + + if (opts?.signature) { + url += `?signature=${opts.signature}`; + } + if (opts?.state) { - url += `&state=${opts.state.hex()}`; + url += `${url.includes('?') ? '&' : '?'}state=${opts.state.hex()}`; } if (opts?.gist) { - url += `&gist=${opts.gist.hex()}`; + url += `${url.includes('?') ? '&' : '?'}gist=${opts.gist.hex()}`; } const resp = await fetch(url); const data = await resp.json(); - const message = data.didResolutionMetadata.proof[0].eip712.message; - const signature = data.didResolutionMetadata.proof[0].proofValue; - - if (isGistRequest) { - return { - globalStateMsg: { - timestamp: message.timestamp, - idType: message.idType, - root: message.root, - replacedAtTimestamp: message.replacedAtTimestamp - }, - signature - }; - } - return { - idStateMsg: { - timestamp: message.timestamp, - id: message.id, - state: message.state, - replacedAtTimestamp: message.replacedAtTimestamp - }, - signature - }; + return data; }; export const buildDIDFromEthPubKey = (didType: Uint8Array, pubKeyEth: string): DID => { From 17ffc65a781b73102d3002257748f4c5f2c2a4f6 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Sep 2024 14:09:29 +0300 Subject: [PATCH 39/41] prepareTxArgsSubmitV1/2 --- .../blockchain/onchain-zkp-verifier.ts | 58 ++++++++----------- .../interfaces/onchain-zkp-verifier.ts | 15 +++-- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/src/storage/blockchain/onchain-zkp-verifier.ts b/src/storage/blockchain/onchain-zkp-verifier.ts index 1c38f059..a873d646 100644 --- a/src/storage/blockchain/onchain-zkp-verifier.ts +++ b/src/storage/blockchain/onchain-zkp-verifier.ts @@ -77,37 +77,32 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { ) {} /** - * {@inheritDoc IOnChainZKPVerifier.prepareZKPResponseSubmitV1TxData} + * {@inheritDoc IOnChainZKPVerifier.prepareTxArgsSubmitV1} */ - public async prepareZKPResponseSubmitV1TxData( + public async prepareTxArgsSubmitV1( txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise> { + zkProofResponse: ZeroKnowledgeProofResponse + ): Promise { if (txData.method_id.replace('0x', '') !== FunctionSignatures.SumbitZKPResponseV1) { throw new Error( - `prepareZKPResponseSubmitV1TxData function doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV1}' is supported.` + `prepareTxArgsSubmitV1 function doesn't implement requested method id. Only '0x${FunctionSignatures.SumbitZKPResponseV1}' is supported.` ); } - const response = new Map(); - for (const zkProof of zkProofResponses) { - const requestID = zkProof.id; - const inputs = zkProof.pub_signals; + const requestID = zkProofResponse.id; + const inputs = zkProofResponse.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) - ]; - - response.set(requestID, payload); - } + const payload = [ + requestID, + inputs, + zkProofResponse.proof.pi_a.slice(0, 2), + [ + [zkProofResponse.proof.pi_b[0][1], zkProofResponse.proof.pi_b[0][0]], + [zkProofResponse.proof.pi_b[1][1], zkProofResponse.proof.pi_b[1][0]] + ], + zkProofResponse.proof.pi_c.slice(0, 2) + ]; - return response; + return payload; } /** @@ -129,8 +124,6 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { } const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - - const txDataMap = await this.prepareZKPResponseSubmitV1TxData(txData, zkProofResponses); const response = new Map(); const feeData = await provider.getFeeData(); @@ -143,12 +136,9 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const verifierContract = new Contract(txData.contract_address, abi); - for (const [requestId, value] of txDataMap) { - const zkProof = zkProofResponses.find((i) => i.id == requestId); - if (!zkProof) { - throw new Error(`zkProof not found for request id ${requestId}`); - } - const payload = await verifierContract.submitZKPResponse.populateTransaction(...value); + for (const zkProofResponse of zkProofResponses) { + const txArgs = await this.prepareTxArgsSubmitV1(txData, zkProofResponse); + const payload = await verifierContract.submitZKPResponse.populateTransaction(...txArgs); const request: TransactionRequest = { to: txData.contract_address, data: payload.data, @@ -166,7 +156,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const transactionService = new TransactionService(provider); const { txnHash } = await transactionService.sendTransactionRequest(ethSigner, request); - response.set(txnHash, zkProof); + response.set(txnHash, zkProofResponse); } return response; @@ -195,7 +185,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { const provider = new JsonRpcProvider(chainConfig.url, chainConfig.chainId); ethSigner = ethSigner.connect(provider); - const txDataArgs = await this.prepareZKPResponseSubmitV2TxData(txData, zkProofResponses); + const txDataArgs = await this.prepareTxArgsSubmitV2(txData, zkProofResponses); const feeData = await provider.getFeeData(); const maxFeePerGas = chainConfig.maxFeePerGas ? BigInt(chainConfig.maxFeePerGas) @@ -229,7 +219,7 @@ export class OnChainZKPVerifier implements IOnChainZKPVerifier { return new Map().set(txnHash, zkProofResponses); } - public async prepareZKPResponseSubmitV2TxData( + public async prepareTxArgsSubmitV2( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise { diff --git a/src/storage/interfaces/onchain-zkp-verifier.ts b/src/storage/interfaces/onchain-zkp-verifier.ts index 031eb8af..22769649 100644 --- a/src/storage/interfaces/onchain-zkp-verifier.ts +++ b/src/storage/interfaces/onchain-zkp-verifier.ts @@ -41,22 +41,21 @@ export interface IOnChainZKPVerifier { ): Promise>; /** - * Returns the Map of request id to transaction data args for the ZKP verifier contract submission. - * For each request id new transaction data is created. + * Returns tx args for the ZKP verifier contract submission (singe tx args for each response). * @param txData - * @param zkProofResponses + * @param zkProofResponse */ - prepareZKPResponseSubmitV1TxData( + prepareTxArgsSubmitV1( txData: ContractInvokeTransactionData, - zkProofResponses: ZeroKnowledgeProofResponse[] - ): Promise>; + zkProofResponse: ZeroKnowledgeProofResponse + ): Promise; /** - * Returns args for the ZKP verifier contract submission V2 (single tx). + * Returns args for the ZKP verifier contract submission V2 (single tx args for an array of responses). * @param txData * @param zkProofResponses */ - prepareZKPResponseSubmitV2TxData( + prepareTxArgsSubmitV2( txData: ContractInvokeTransactionData, zkProofResponses: ZeroKnowledgeProofResponse[] ): Promise; From 924b81872ae31d7f1bab6cc7af606ac47c981817 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Sep 2024 14:12:21 +0300 Subject: [PATCH 40/41] fix unit mock --- tests/handlers/contract-request.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index 7d5eda90..fbc730f7 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -135,11 +135,11 @@ describe('contract-request', () => { return response; }, - prepareZKPResponseSubmitV1TxData: async () => { - return new Map(); + prepareTxArgsSubmitV1: async () => { + return []; }, - prepareZKPResponseSubmitV2TxData: async () => { + prepareTxArgsSubmitV2: async () => { return []; } }; From cb1bd47835eae5974cfab7be01b5b03ea3aa2475 Mon Sep 17 00:00:00 2001 From: vbasiuk Date: Wed, 4 Sep 2024 15:47:08 +0300 Subject: [PATCH 41/41] import fixes --- src/iden3comm/handlers/contract-request.ts | 10 ++++---- src/verifiable/presentation.ts | 3 +-- tests/handlers/contract-request.test.ts | 30 +++++----------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/src/iden3comm/handlers/contract-request.ts b/src/iden3comm/handlers/contract-request.ts index 586580f7..a00afff3 100644 --- a/src/iden3comm/handlers/contract-request.ts +++ b/src/iden3comm/handlers/contract-request.ts @@ -182,9 +182,9 @@ export class ContractRequestHandler */ private async createContractInvokeResponse( request: ContractInvokeRequest, - responses: Map + txHashToZkpResponseMap: Map ): Promise { - const response: ContractInvokeResponse = { + const contractInvokeResponse: ContractInvokeResponse = { id: request.id, thid: request.thid, type: PROTOCOL_MESSAGE_TYPE.CONTRACT_INVOKE_RESPONSE_MESSAGE_TYPE, @@ -196,15 +196,15 @@ export class ContractRequestHandler scope: [] } }; - for (const [txHash, zkpResponses] of responses) { + for (const [txHash, zkpResponses] of txHashToZkpResponseMap) { for (const zkpResponse of zkpResponses) { - response.body.scope.push({ + contractInvokeResponse.body.scope.push({ txHash, ...zkpResponse }); } } - return response; + return contractInvokeResponse; } /** diff --git a/src/verifiable/presentation.ts b/src/verifiable/presentation.ts index fcd369e0..b207b0ce 100644 --- a/src/verifiable/presentation.ts +++ b/src/verifiable/presentation.ts @@ -2,8 +2,7 @@ import { VerifiableConstants } from './constants'; import { Options, Path } from '@iden3/js-jsonld-merklization'; import { W3CCredential } from './credential'; import { QueryMetadata } from '../proof'; -import { VerifiablePresentation } from '../iden3comm'; -import { JsonDocumentObject } from '../iden3comm'; +import { VerifiablePresentation, JsonDocumentObject } from '../iden3comm'; export const stringByPath = (obj: { [key: string]: unknown }, path: string): string => { const parts = path.split('.'); diff --git a/tests/handlers/contract-request.test.ts b/tests/handlers/contract-request.test.ts index fbc730f7..a45d4ca0 100644 --- a/tests/handlers/contract-request.test.ts +++ b/tests/handlers/contract-request.test.ts @@ -12,8 +12,6 @@ import { defaultEthConnectionConfig, hexToBytes } from '../../src'; -import { BjjProvider, KMS, KmsKeyType } from '../../src/kms'; -import { InMemoryPrivateKeyStore } from '../../src/kms/store'; import { IDataStorage, IStateStorage, IOnChainZKPVerifier } from '../../src/storage/interfaces'; import { InMemoryDataSource, InMemoryMerkleTreeStorage } from '../../src/storage/memory'; import { CredentialRequest, CredentialWallet } from '../../src/credentials'; @@ -38,7 +36,6 @@ import { DataPrepareHandlerFunc, IContractRequestHandler, IPackageManager, - JsonDocumentObjectValue, PackageManager, ProvingParams, StateVerificationFunc, @@ -56,7 +53,7 @@ import { expect } from 'chai'; import { CredentialStatusResolverRegistry } from '../../src/credentials'; import { RHSResolver } from '../../src/credentials'; import { ethers, JsonRpcProvider, Signer } from 'ethers'; -import { RPC_URL } from '../helpers'; +import { registerKeyProvidersInMemoryKMS, RPC_URL } from '../helpers'; describe('contract-request', () => { let idWallet: IdentityWallet; @@ -189,10 +186,7 @@ describe('contract-request', () => { }; beforeEach(async () => { - const memoryKeyStore = new InMemoryPrivateKeyStore(); - const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); - const kms = new KMS(); - kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + const kms = registerKeyProvidersInMemoryKMS(); dataStorage = { credential: new CredentialStorage(new InMemoryDataSource()), identity: new IdentityStorage( @@ -331,10 +325,7 @@ describe('contract-request', () => { stateEthConfig.contractAddress = '0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124'; stateEthConfig.chainId = 80002; - const memoryKeyStore = new InMemoryPrivateKeyStore(); - const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); - const kms = new KMS(); - kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + const kms = registerKeyProvidersInMemoryKMS(); dataStorage = { credential: new CredentialStorage(new InMemoryDataSource()), identity: new IdentityStorage( @@ -485,10 +476,7 @@ describe('contract-request', () => { stateEthConfig.url = rpcUrl; stateEthConfig.contractAddress = '0x1a4cC30f2aA0377b0c3bc9848766D90cb4404124'; - const memoryKeyStore = new InMemoryPrivateKeyStore(); - const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); - const kms = new KMS(); - kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + const kms = registerKeyProvidersInMemoryKMS(); dataStorage = { credential: new CredentialStorage(new InMemoryDataSource()), identity: new IdentityStorage( @@ -668,10 +656,7 @@ describe('contract-request', () => { issuerStateEthConfig.url = privadoTestRpcUrl; issuerStateEthConfig.contractAddress = privadoTestStateContract; // privado test state contract - const memoryKeyStore = new InMemoryPrivateKeyStore(); - const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); - const kms = new KMS(); - kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + const kms = registerKeyProvidersInMemoryKMS(); dataStorage = { credential: new CredentialStorage(new InMemoryDataSource()), identity: new IdentityStorage( @@ -849,10 +834,7 @@ describe('contract-request', () => { chainId: 80002 }; - const memoryKeyStore = new InMemoryPrivateKeyStore(); - const bjjProvider = new BjjProvider(KmsKeyType.BabyJubJub, memoryKeyStore); - const kms = new KMS(); - kms.registerKeyProvider(KmsKeyType.BabyJubJub, bjjProvider); + const kms = registerKeyProvidersInMemoryKMS(); dataStorage = { credential: new CredentialStorage(new InMemoryDataSource()), identity: new IdentityStorage(