Skip to content

Commit

Permalink
changes proposed from review
Browse files Browse the repository at this point in the history
  • Loading branch information
daveroga committed Apr 2, 2024
1 parent b31c69b commit a68a6dd
Show file tree
Hide file tree
Showing 16 changed files with 177 additions and 99 deletions.
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
publishState: async () => {
return '0xc837f95c984892dbcc3ac41812ecb145fedc26d7003202c50e1b87e226a9b33c';
},
publishStateGeneric: async () => {
return '0xc837f95c984892dbcc3ac41812ecb145fedc26d7003202c50e1b87e226a9b33c';
},
getGISTProof: () => {
return Promise.resolve({
root: 0n,
Expand Down
18 changes: 8 additions & 10 deletions src/credentials/status/reverse-sparse-merkle-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { IStateStorage } from '../../storage';
import { CredentialStatusResolver, CredentialStatusResolveOptions } from './resolver';
import { CredentialStatus, RevocationStatus, State } from '../../verifiable';
import { VerifiableConstants, CredentialStatusType } from '../../verifiable/constants';
import { isGenesisState } from '../../utils';
import { isEthereumIdentity, isGenesisState } from '../../utils';
import { IssuerResolver } from './sparse-merkle-tree';

/**
Expand Down Expand Up @@ -174,20 +174,18 @@ export class RHSResolver implements CredentialStatusResolver {
}
const currentStateBigInt = Hash.fromHex(stateHex).bigInt();

const issuerId = DID.idFromDID(issuerDID);
let isBjjIdentity = false; // don't generate proof for ethereum identities
try {
Id.ethAddressFromId(issuerId);
} catch {
// not an ethereum identity
isBjjIdentity = true;
}
const isEthIdentity = isEthereumIdentity(issuerDID);

if (isBjjIdentity && !isGenesisState(issuerDID, currentStateBigInt)) {
if (!isEthIdentity && !isGenesisState(issuerDID, currentStateBigInt)) {
throw new Error(
`latest state not found and state parameter ${stateHex} is not genesis state`
);
}

if (isEthIdentity) {
throw new Error(`State must be published for Ethereum based identity`);
}

latestState = currentStateBigInt;
}

Expand Down
16 changes: 10 additions & 6 deletions src/identity/identity-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ export class IdentityWallet implements IIdentityWallet {
}
}

private async createAuthClaim(
private async createAuthCoreClaim(
revNonce: number,
seed: Uint8Array
): Promise<{ authClaim: Claim; pubKey: PublicKey }> {
Expand All @@ -472,7 +472,7 @@ export class IdentityWallet implements IIdentityWallet {
return { authClaim, pubKey };
}

private async createAuthCredential(
private async createAuthBJJCredential(
did: DID,
pubKey: PublicKey,
authClaim: Claim,
Expand Down Expand Up @@ -558,7 +558,7 @@ export class IdentityWallet implements IIdentityWallet {

const revNonce = opts.revocationOpts.nonce ?? 0;

const { authClaim, pubKey } = await this.createAuthClaim(revNonce, opts.seed);
const { authClaim, pubKey } = await this.createAuthCoreClaim(revNonce, opts.seed);

await this._storage.mt.addToMerkleTree(
tmpIdentifier,
Expand All @@ -584,7 +584,7 @@ export class IdentityWallet implements IIdentityWallet {

await this._storage.mt.bindMerkleTreeToNewIdentifier(tmpIdentifier, did.string());

const credential = await this.createAuthCredential(
const credential = await this.createAuthBJJCredential(
did,
pubKey,
authClaim,
Expand Down Expand Up @@ -657,7 +657,7 @@ export class IdentityWallet implements IIdentityWallet {
});

// Add Auth BJJ credential after saving identity for Ethereum identities
const { authClaim, pubKey } = await this.createAuthClaim(
const { authClaim, pubKey } = await this.createAuthCoreClaim(
opts.revocationOpts.nonce ?? 0,
opts.seed
);
Expand All @@ -680,7 +680,7 @@ export class IdentityWallet implements IIdentityWallet {
ZERO_HASH.bigInt()
]);

const credential = await this.createAuthCredential(
const credential = await this.createAuthBJJCredential(
did,
pubKey,
authClaim,
Expand All @@ -696,6 +696,10 @@ export class IdentityWallet implements IIdentityWallet {
rootOfRoots: ZERO_HASH
};

if (!proofService) {
throw new Error('Proof service is required to create Ethereum identities');
}

// Mandatory transit state after adding auth credential in Ethereum identities
await proofService?.transitState(did, oldTreeState, true, this._storage.states, ethSigner);

Expand Down
30 changes: 16 additions & 14 deletions src/proof/proof-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { ZKProof } from '@iden3/js-jwz';
import { Signer } from 'ethers';
import { JSONObject, ZeroKnowledgeProofRequest, ZeroKnowledgeProofResponse } from '../iden3comm';
import { cacheLoader } from '../schema-processor';
import { ICircuitStorage, IStateStorage, UserStateTransition } from '../storage';
import { ICircuitStorage, IStateStorage, UserStateTransitionInfo } from '../storage';
import { byteDecoder, byteEncoder } from '../utils/encoding';
import {
InputGenerator,
Expand All @@ -42,6 +42,7 @@ import {
} from './provers/inputs-generator';
import { PubSignalsVerifier, VerifyContext } from './verifiers/pub-signals-verifier';
import { VerifyOpts } from './verifiers';
import { isEthereumIdentity } from '../utils';

export interface QueryWithFieldName {
query: Query;
Expand Down Expand Up @@ -375,15 +376,9 @@ export class ProofService implements IProofService {
const userId = DID.idFromDID(did);

let proof;
let generateProof = false; // don't generate proof for ethereum identities
try {
Id.ethAddressFromId(userId);
} catch {
// not an ethereum identity
generateProof = true;
}
const isEthIdentity = isEthereumIdentity(did); // don't generate proof for ethereum identities

if (generateProof) {
if (!isEthIdentity) {
// generate the proof
const authInfo = await this._inputsGenerator.prepareAuthBJJCredential(did, oldTreeState);
const challenge = Poseidon.hash([oldTreeState.state.bigInt(), newTreeState.state.bigInt()]);
Expand Down Expand Up @@ -419,14 +414,21 @@ export class ProofService implements IProofService {

const oldUserState = oldTreeState.state;
const newUserState = newTreeState.state;
const userStateTransition: UserStateTransition = {
const userStateTransitionInfo: UserStateTransitionInfo = {
userId,
oldUserState,
newUserState,
isOldStateGenesis
} as UserStateTransition;

const txId = await stateStorage.publishState(proof, ethSigner, userStateTransition);
isOldStateGenesis,
methodId: BigInt(1),
methodParams: '0x'
} as UserStateTransitionInfo;

let txId;
if (!isEthIdentity) {
txId = await stateStorage.publishState(proof, ethSigner);
} else {
txId = await stateStorage.publishStateGeneric(ethSigner, userStateTransitionInfo);
}
await this._identityWallet.updateIdentityState(did, true, newTreeState);

return txId;
Expand Down
8 changes: 2 additions & 6 deletions src/proof/provers/inputs-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
ICredentialWallet,
getUserDIDFromCredential
} from '../../credentials';
import { isEthereumIdentity } from '../../utils';

export type DIDProfileMetadata = {
authProfileNonce: number;
Expand Down Expand Up @@ -537,12 +538,7 @@ export class InputGenerator {
? BigInt(proofReq.params?.nullifierSessionId?.toString())
: BigInt(0);

let isEthIdentity = true;
try {
Id.ethAddressFromId(circuitInputs.id);
} catch {
isEthIdentity = false;
}
const isEthIdentity = isEthereumIdentity(identifier);
circuitInputs.isBJJAuthEnabled = isEthIdentity ? 0 : 1;

circuitInputs.challenge = BigInt(params.challenge ?? 0);
Expand Down
128 changes: 73 additions & 55 deletions src/storage/blockchain/state.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RootInfo, StateProof } from './../entities/state';
import { ZKProof } from '@iden3/js-jwz';
import { IStateStorage, UserStateTransition } from '../interfaces/state';
import { IStateStorage, UserStateTransitionInfo } from '../interfaces/state';
import { Contract, ContractTransaction, JsonRpcProvider, Signer, TransactionRequest } from 'ethers';
import { StateInfo } from '../entities/state';
import { StateTransitionPubSignals } from '../../circuits';
Expand Down Expand Up @@ -102,14 +102,29 @@ export class EthStateStorage implements IStateStorage {
}

/** {@inheritdoc IStateStorage.publishState} */
async publishState(
proof: ZKProof,
signer: Signer,
userStateTranstion: UserStateTransition
): Promise<string> {
const { userId, oldUserState, newUserState, isOldStateGenesis } = userStateTranstion;
async publishState(proof: ZKProof, signer: Signer): Promise<string> {
const stateTransitionPubSig = new StateTransitionPubSignals();
stateTransitionPubSig.pubSignalsUnmarshal(
byteEncoder.encode(JSON.stringify(proof.pub_signals))
);
const { userId, oldUserState, newUserState, isOldStateGenesis } = stateTransitionPubSig;

const { stateContract, provider } = this.getStateContractAndProviderForId(userId.bigInt());
const contract = stateContract.connect(signer) as Contract;

const payload = [
userId.bigInt().toString(),
oldUserState.bigInt().toString(),
newUserState.bigInt().toString(),
isOldStateGenesis,
proof.proof.pi_a.slice(0, 2),
[
[proof.proof.pi_b[0][1], proof.proof.pi_b[0][0]],
[proof.proof.pi_b[1][1], proof.proof.pi_b[1][0]]
],
proof.proof.pi_c.slice(0, 2)
];

const feeData = await provider.getFeeData();

const maxFeePerGas = defaultEthConnectionConfig.maxFeePerGas
Expand All @@ -119,57 +134,60 @@ export class EthStateStorage implements IStateStorage {
? BigInt(defaultEthConnectionConfig.maxPriorityFeePerGas)
: feeData.maxPriorityFeePerGas;

let gasLimit: bigint;
let txData: ContractTransaction;

if (proof) {
const stateTransitionPubSig = new StateTransitionPubSignals();
stateTransitionPubSig.pubSignalsUnmarshal(
byteEncoder.encode(JSON.stringify(proof.pub_signals))
);
const {
userId: userIdPub,
oldUserState: oldUserStatePub,
newUserState: newUserStatePub,
isOldStateGenesis: isOldStateGenesisPub
} = stateTransitionPubSig;

if (
userIdPub.bigInt() !== userId.bigInt() ||
oldUserStatePub.bigInt() !== oldUserState.bigInt() ||
newUserStatePub.bigInt() !== newUserState.bigInt() ||
isOldStateGenesisPub !== isOldStateGenesis
) {
throw new Error(`public inputs do not match with user state transition`);
}
const gasLimit = await contract.transitState.estimateGas(...payload);
const txData = await contract.transitState.populateTransaction(...payload);

const request: TransactionRequest = {
to: txData.to,
data: txData.data,
gasLimit,
maxFeePerGas,
maxPriorityFeePerGas
};
const tx = await signer.sendTransaction(request);

const txnReceipt = await tx.wait();
if (!txnReceipt) {
throw new Error(`transaction: ${tx.hash} failed to mined`);
}
const status: number | null = txnReceipt.status;
const txnHash: string = txnReceipt.hash;

const payload = [
userId.bigInt().toString(),
oldUserState.bigInt().toString(),
newUserState.bigInt().toString(),
isOldStateGenesis,
proof.proof.pi_a.slice(0, 2),
[
[proof.proof.pi_b[0][1], proof.proof.pi_b[0][0]],
[proof.proof.pi_b[1][1], proof.proof.pi_b[1][0]]
],
proof.proof.pi_c.slice(0, 2)
];
gasLimit = await contract.transitState.estimateGas(...payload);
txData = await contract.transitState.populateTransaction(...payload);
} else {
const payload = [
userId.bigInt().toString(),
oldUserState.bigInt().toString(),
newUserState.bigInt().toString(),
isOldStateGenesis,
BigInt(1),
'0x'
];
gasLimit = await contract.transitStateGeneric.estimateGas(...payload);
txData = await contract.transitStateGeneric.populateTransaction(...payload);
if (!status) {
throw new Error(`transaction: ${txnHash} failed to mined`);
}

return txnHash;
}

/** {@inheritdoc IStateStorage.publishState} */
async publishStateGeneric(
signer: Signer,
userStateTranstionInfo: UserStateTransitionInfo
): Promise<string> {
const { userId, oldUserState, newUserState, isOldStateGenesis, methodId, methodParams } = userStateTranstionInfo;
const { stateContract, provider } = this.getStateContractAndProviderForId(userId.bigInt());
const contract = stateContract.connect(signer) as Contract;
const feeData = await provider.getFeeData();

const maxFeePerGas = defaultEthConnectionConfig.maxFeePerGas
? BigInt(defaultEthConnectionConfig.maxFeePerGas)
: feeData.maxFeePerGas;
const maxPriorityFeePerGas = defaultEthConnectionConfig.maxPriorityFeePerGas
? BigInt(defaultEthConnectionConfig.maxPriorityFeePerGas)
: feeData.maxPriorityFeePerGas;

const payload = [
userId.bigInt().toString(),
oldUserState.bigInt().toString(),
newUserState.bigInt().toString(),
isOldStateGenesis,
methodId, //BigInt(1),
methodParams //'0x'
];
const gasLimit = await contract.transitStateGeneric.estimateGas(...payload);
const txData = await contract.transitStateGeneric.populateTransaction(...payload);

const request: TransactionRequest = {
to: txData.to,
data: txData.data,
Expand Down
25 changes: 17 additions & 8 deletions src/storage/interfaces/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { RootInfo, StateInfo, StateProof } from '../entities/state';
import { Id } from '@iden3/js-iden3-core';
import { Hash } from '@iden3/js-merkletree';

export interface UserStateTransition {
userId: Id;
oldUserState: Hash;
newUserState: Hash;
isOldStateGenesis: boolean;
export interface UserStateTransitionInfo {
userId: Id; // Identity id
oldUserState: Hash; // Previous identity state
newUserState: Hash; // New identity state
isOldStateGenesis: boolean; // Is the previous state genesis?
methodId: bigint; // State transition method id
methodParams: string; // State transition method-specific params
}

/**
Expand Down Expand Up @@ -41,10 +43,17 @@ export interface IStateStorage {
* @param {Signer} signer - signer of transaction
* @returns `Promise<string>` - transaction identifier
*/
publishState(
proof: ZKProof | undefined,
publishState(proof: ZKProof | undefined, signer: Signer): Promise<string>;
/**
* method to publish state onchain
*
* @param {Signer} signer - signer of transaction
* @param {UserStateTransitionInfo} userStateTransitionInfo - user state transition information
* @returns `Promise<string>` - transaction identifier
*/
publishStateGeneric(
signer: Signer,
userStateTransition?: UserStateTransition
userStateTransitionInfo?: UserStateTransitionInfo
): Promise<string>;
/**
* generates proof of inclusion / non-inclusion to global identity state for given identity
Expand Down
Loading

0 comments on commit a68a6dd

Please sign in to comment.