Skip to content

Commit

Permalink
improvments
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-korotya committed Dec 2, 2024
1 parent 30c4882 commit c7c5bb6
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 92 deletions.
8 changes: 4 additions & 4 deletions src/iden3comm/handlers/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,17 @@ export class FetchHandler
if (!this.opts?.onchainIssuer) {
throw new Error('onchain issuer is not provided');
}

const credentials: W3CCredential[] = [];
for (const credentialInfo of offerMessage.body.credentials) {
const userId = DID.idFromDID(DID.parse(offerMessage.from));
const issuerDID = DID.parse(offerMessage.from);
const userDID = DID.parse(offerMessage.to);
const credential = await this.opts.onchainIssuer.getCredential(
userId,
issuerDID,
userDID,
BigInt(credentialInfo.id)
);
credentials.push(credential);
}

return credentials;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { JsonDocumentObject } from '../../../../../../iden3comm';
import { ethers } from 'ethers';
import { getDateFromUnixTimestamp } from '@iden3/js-iden3-core';
import { Options } from '@iden3/js-jsonld-merklization';
import { EthConnectionConfig } from '../../../../state';

enum NonMerklizedIssuerInterfaces {
InterfaceDetection = '0x01ffc9a7',
Expand Down Expand Up @@ -48,25 +49,30 @@ export class OnchainNonMerklizedIssuerAdapter {
/**
* Initializes an instance of `OnchainNonMerklizedIssuerAdapter`.
*
* @param address The contract address of the non-merklized issuer.
* @param rpcUrl The URL of the blockchain RPC provider.
* @param chainId The chain ID of the blockchain network.
* @param ethConnectionConfig The configuration for the Ethereum connection.
* @param issuerDid The decentralized identifier (DID) of the issuer.
* @param merklizationOptions Optional settings for merklization.
*/
constructor(
address: string,
ethConnectionConfig: EthConnectionConfig,
options: {
rpcUrl: string;
chainId: number;
issuerDid: DID;
merklizationOptions?: Options;
}
) {
const rpcProvider = new ethers.JsonRpcProvider(options.rpcUrl);
this._contract = NonMerklizedIssuerBase__factory.connect(address, rpcProvider);
this._contractAddress = address;
this._chainId = options.chainId;
if (!ethConnectionConfig.chainId) {
throw new Error('Chain ID is required');
}
this._chainId = ethConnectionConfig.chainId;

this._contractAddress = ethers.getAddress(
ethers.hexlify(Id.ethAddressFromId(DID.idFromDID(options.issuerDid)))
);
this._contract = NonMerklizedIssuerBase__factory.connect(
this._contractAddress,
new ethers.JsonRpcProvider(ethConnectionConfig.url)
);

this._issuerDid = options.issuerDid;
this._merklizationOptions = options.merklizationOptions;
}
Expand Down
83 changes: 40 additions & 43 deletions src/storage/blockchain/onchain-issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { OnchainNonMerklizedIssuerAdapter } from './onchain-issuer-adapter/non-m
import { EthConnectionConfig } from './state';
import { IOnchainIssuer } from '../interfaces/onchain-issuer';

enum OnchainIssuerVersion {
'v0.0.1' = '0.0.1'
}

/**
* Represents an adapter for interacting with on-chain issuers.
*
Expand All @@ -19,57 +15,41 @@ enum OnchainIssuerVersion {
* @class OnchainIssuer
*/
export class OnchainIssuer implements IOnchainIssuer {
private readonly _url: string;
private readonly _chainId: number;
private readonly _contractAddress: string;
private readonly _contract: Contract;

private readonly _issuerDid: DID;

private readonly _ethConnectionConfig: EthConnectionConfig[];
private readonly _merklizationOptions?: Options;

/**
* Initializes an instance of `Adapter`.
* @param config The configuration for the Ethereum connection.
* @param did The decentralized identifier (DID) of the issuer. The DID provides the blockchain and network information.
* @param merklizationOptions Optional settings for merklization.
*/
constructor(config: EthConnectionConfig[], did: DID, options?: Options) {
const issuerId = DID.idFromDID(did);
this._contractAddress = ethers.getAddress(ethers.hexlify(Id.ethAddressFromId(issuerId)));
this._chainId = chainIDfromDID(did);
const url = config.find((c) => c.chainId === this._chainId)?.url;
if (!url) {
throw new Error(`No URL found for chain ID ${this._chainId}`);
}
this._url = url;
constructor(config: EthConnectionConfig[], options?: Options) {
this._ethConnectionConfig = config;
this._merklizationOptions = options;
this._contract = new Contract(
this._contractAddress,
abi,
new ethers.JsonRpcProvider(this._url)
);
this._issuerDid = did;
}

/**
* Retrieves a credential from the on-chain issuer.
* @param issuerDID The issuer's core.DID.
* @param userId The user's core.Id.
* @param credentialId The unique identifier of the credential.
*/
public async getCredential(userId: Id, credentialId: bigint): Promise<W3CCredential> {
const response = await this._contract.getCredentialAdapterVersion();
public async getCredential(
issuerDID: DID,
userDID: DID,
credentialId: bigint
): Promise<W3CCredential> {
const { contract, connection } = this.getContractConnection(issuerDID);
const response = await contract.getCredentialAdapterVersion();
switch (response) {
case OnchainIssuerVersion['v0.0.1']: {
const adapter = new OnchainNonMerklizedIssuerAdapter(this._contractAddress, {
rpcUrl: this._url,
chainId: this._chainId,
issuerDid: this._issuerDid,
case '0.0.1': {
const adapter = new OnchainNonMerklizedIssuerAdapter(connection, {
issuerDid: issuerDID,
merklizationOptions: this._merklizationOptions
});
await adapter.isSupportsInterface();
const { credentialData, coreClaimBigInts, credentialSubjectFields } =
await adapter.getCredential(userId, credentialId);
await adapter.getCredential(DID.idFromDID(userDID), credentialId);
return await adapter.convertOnChainInfoToW3CCredential(
credentialData,
coreClaimBigInts,
Expand All @@ -83,23 +63,40 @@ export class OnchainIssuer implements IOnchainIssuer {

/**
* Retrieves the credential identifiers for a user from the on-chain issuer.
* @param issuerDID The issuer's core.DID.
* @param userId The user's core.Id.
*/
public async getUserCredentialIds(userId: Id): Promise<bigint[]> {
const response = await this._contract.getCredentialAdapterVersion();
public async getUserCredentialIds(issuerDID: DID, userDID: DID): Promise<bigint[]> {
const { contract, connection } = this.getContractConnection(issuerDID);
const response = await contract.getCredentialAdapterVersion();
switch (response) {
case OnchainIssuerVersion['v0.0.1']: {
const adapter = new OnchainNonMerklizedIssuerAdapter(this._contractAddress, {
rpcUrl: this._url,
chainId: this._chainId,
issuerDid: this._issuerDid,
case '0.0.1': {
const adapter = new OnchainNonMerklizedIssuerAdapter(connection, {
issuerDid: issuerDID,
merklizationOptions: this._merklizationOptions
});
await adapter.isSupportsInterface();
return await adapter.getUserCredentialsIds(userId);
return await adapter.getUserCredentialsIds(DID.idFromDID(userDID));
}
default:
throw new Error(`Unsupported adapter version ${response}`);
}
}

private getContractConnection(did: DID): { contract: Contract; connection: EthConnectionConfig } {
const issuerId = DID.idFromDID(did);
const chainId = chainIDfromDID(did);
const contractAddress = ethers.getAddress(ethers.hexlify(Id.ethAddressFromId(issuerId)));
const connection = this._ethConnectionConfig.find((c) => c.chainId === chainId);
if (!connection) {
throw new Error(`No connection found for chain ID ${chainId}`);
}
if (!connection.url) {
throw new Error(`No URL found for chain ID ${chainId}`);
}

const contract = new Contract(contractAddress, abi, new ethers.JsonRpcProvider(connection.url));

return { contract, connection };
}
}
6 changes: 3 additions & 3 deletions src/storage/interfaces/onchain-issuer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Id } from '@iden3/js-iden3-core';
import { Id, DID } from '@iden3/js-iden3-core';

Check warning on line 1 in src/storage/interfaces/onchain-issuer.ts

View workflow job for this annotation

GitHub Actions / build

'Id' is defined but never used
import { W3CCredential } from '../../verifiable';

/**
Expand All @@ -8,6 +8,6 @@ import { W3CCredential } from '../../verifiable';
* @interface IOnchainIssuer
*/
export interface IOnchainIssuer {
getCredential(userId: Id, credentialId: bigint): Promise<W3CCredential>;
getUserCredentialIds(userId: Id): Promise<bigint[]>;
getCredential(issuerDID: DID, userDID: DID, credentialId: bigint): Promise<W3CCredential>;
getUserCredentialIds(issuerDID: DID, userDID: DID): Promise<bigint[]>;
}
17 changes: 8 additions & 9 deletions tests/adapter/onchain-issuer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,25 @@ describe('OnchainIssuer', () => {
const issuerDid = DID.parse(
'did:polygonid:polygon:amoy:2qQ68JkRcf3xyDFsGSWU5QqxbKpzM75quxS628JgvJ'
);
const userId = DID.idFromDID(
DID.parse('did:polygonid:polygon:amoy:2qQ68JkRcf3xyDFsGSWU5QqxbKpzM75quxS628JgvJ')
const userDid = DID.parse(
'did:polygonid:polygon:amoy:2qQ68JkRcf3xyDFsGSWU5QqxbKpzM75quxS628JgvJ'
);
const adapter = new OnchainIssuer([copyDefaultEthConnectionConfig], issuerDid);
const cred = await adapter.getCredential(userId, BigInt(6));
console.log(JSON.stringify(cred.toJSON(), null, 2));
const adapter = new OnchainIssuer([copyDefaultEthConnectionConfig]);
const cred = await adapter.getCredential(issuerDid, userDid, BigInt(6));
expect(W3CCredential.fromJSON(balanceCredentialHttpSchema)).to.deep.equal(cred);
});

it('Test adapter for v0.0.1 IPFS schema', async () => {
const issuerDid = DID.parse(
'did:polygonid:polygon:amoy:2qQ68JkRcf3z3923i5rrszrsJ4kdu4GKWARQ5eftsB'
);
const userId = DID.idFromDID(
DID.parse('did:polygonid:polygon:amoy:2qZYiH9CFMoo6oTjSEot3qzkHFHhjLRLKp8yfwCYng')
const userId = DID.parse(
'did:polygonid:polygon:amoy:2qZYiH9CFMoo6oTjSEot3qzkHFHhjLRLKp8yfwCYng'
);
const adapter = new OnchainIssuer([copyDefaultEthConnectionConfig], issuerDid, {
const adapter = new OnchainIssuer([copyDefaultEthConnectionConfig], {
ipfsNodeURL: IPFS_URL
});
const cred = await adapter.getCredential(userId, BigInt(0));
const cred = await adapter.getCredential(issuerDid, userId, BigInt(0));
expect(W3CCredential.fromJSON(balanceCredentialIpfsSchema)).to.deep.equal(cred);
});
});
46 changes: 24 additions & 22 deletions tests/adapter/onchain-non-merklized-issuer-adapter.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { OnchainNonMerklizedIssuerAdapter } from '../../src/storage/blockchain/onchain-issuer-adapter/non-merklized/version/v0.0.1/onchain-non-merklized-issuer-adapter';
import { ethers } from 'ethers';
import nock from 'nock';
import { DID, Id, ChainIds } from '@iden3/js-iden3-core';
import { DID } from '@iden3/js-iden3-core';
import {
NonMerklizedIssuerBaseABI as abi,
INonMerklizedIssuer
} from '@iden3/onchain-non-merklized-issuer-base-abi';
import { expect } from 'chai';
import { W3CCredential } from '../../src/verifiable';
import { IPFS_URL } from '../helpers';
import { defaultEthConnectionConfig } from '../../src';

// prettier-ignore
const w3cHttpSchemaExpect =
Expand All @@ -20,13 +21,6 @@ const w3cIpfsSchemaExpect =
const mockedState =
'0x00000000000000000000000000000000000000000000000000000000000000a026ab96fbcfb7a137e7acd773909a14ce77de394252318b5cca4c00131fd95d6f2ec78fb0a9f5898469243a85cc4a2fe97a0a95c00a95c4b50edad7d4ea01a73c00000000000000000000000000000000000000000000000000000000000000000ec7af27591b23ea49b849ddd31c1073bde30753b6cfbb727611a2cffe33a6192ec78fb0a9f5898469243a85cc4a2fe97a0a95c00a95c4b50edad7d4ea01a73c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010015bbb043d40cadbd377aeb19ef410cd8adb55a41c63707a628fefa0fac2c3ba126280d1f3ca6d144c0fedf0519644786c96d821dcc947576ecbbb4a175279ab700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280a6002414282733c9a4a2b7ccf5ad7cd2d9eea2c6cf0c03d7dbac53da6a60577000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';

const chainInfo = (did: DID): { contractAddress: string; chainId: number } => {
const issuerId = DID.idFromDID(did);
const contractAddress = ethers.getAddress(ethers.hexlify(Id.ethAddressFromId(issuerId)));
const chainId = ChainIds[`${DID.blockchainFromId(issuerId)}:${DID.networkIdFromId(issuerId)}`];
return { contractAddress, chainId };
};

describe('Convertor v0.0.1', () => {
it('Test adapter with https schema', async () => {
nock('http://localhost:8545')
Expand Down Expand Up @@ -85,12 +79,16 @@ describe('Convertor v0.0.1', () => {
const iface = new ethers.Interface(abi);
const res = iface.decodeFunctionResult('getCredential', hexResponse);

const { chainId, contractAddress } = chainInfo(issuerDid);
const adapter = new OnchainNonMerklizedIssuerAdapter(contractAddress, {
rpcUrl: 'http://localhost:8545',
chainId: chainId,
issuerDid: issuerDid
});
const adapter = new OnchainNonMerklizedIssuerAdapter(
{
...defaultEthConnectionConfig,
url: 'http://localhost:8545',
chainId: 80001
},
{
issuerDid: issuerDid
}
);
const w3cCredential = await adapter.convertOnChainInfoToW3CCredential(
res[0] as INonMerklizedIssuer.CredentialDataStructOutput,
res[1] as bigint[],
Expand Down Expand Up @@ -155,15 +153,19 @@ describe('Convertor v0.0.1', () => {
const iface = new ethers.Interface(abi);
const res = iface.decodeFunctionResult('getCredential', hexResponse);

const { chainId, contractAddress } = chainInfo(issuerDid);
const adapter = new OnchainNonMerklizedIssuerAdapter(contractAddress, {
rpcUrl: 'http://localhost:8545',
chainId: chainId,
issuerDid: issuerDid,
merklizationOptions: {
ipfsNodeURL: IPFS_URL
const adapter = new OnchainNonMerklizedIssuerAdapter(
{
...defaultEthConnectionConfig,
url: 'http://localhost:8545',
chainId: 80002
},
{
issuerDid: issuerDid,
merklizationOptions: {
ipfsNodeURL: IPFS_URL
}
}
});
);
const w3cCredential = await adapter.convertOnChainInfoToW3CCredential(
res[0] as INonMerklizedIssuer.CredentialDataStructOutput,
res[1] as bigint[],
Expand Down
Loading

0 comments on commit c7c5bb6

Please sign in to comment.