Skip to content

Commit

Permalink
Message bus POC
Browse files Browse the repository at this point in the history
  • Loading branch information
Kolezhniuk committed Feb 14, 2024
1 parent 21159ba commit e46cd47
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 56 deletions.
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@0xpolygonid/js-sdk",
"version": "1.7.5",
"version": "1.7.6",
"description": "SDK to work with Polygon ID",
"main": "dist/node/cjs/index.js",
"module": "dist/node/esm/index.js",
Expand Down Expand Up @@ -67,6 +67,7 @@
"@types/jsonld": "^1.5.11",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.9",
"@types/pubsub-js": "^1.8.6",
"@types/uuid": "^9.0.6",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"chai": "^4.3.10",
Expand All @@ -91,8 +92,8 @@
"@iden3/js-jwz": "1.2.1",
"@iden3/js-merkletree": "1.1.2",
"ffjavascript": "0.2.62",
"snarkjs": "0.7.2",
"rfc4648": "1.5.3"
"rfc4648": "1.5.3",
"snarkjs": "0.7.2"
},
"dependencies": {
"ajv": "8.12.0",
Expand All @@ -104,6 +105,7 @@
"idb-keyval": "6.2.0",
"js-sha3": "0.9.2",
"jsonld": "8.3.1",
"pubsub-js": "1.9.4",
"uuid": "9.0.1"
},
"browserslist": {
Expand Down
34 changes: 29 additions & 5 deletions src/credentials/status/credential-status-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { JSONObject } from '../../iden3comm';
import { OnChainRevocationStorage } from '../../storage';
import { CredentialStatusType } from '../../verifiable';
import { ProofNode } from './reverse-sparse-merkle-tree';
import { MessageBus, SDK_EVENTS } from '../../utils';

/**
* Represents a credential status publisher.
Expand Down Expand Up @@ -56,7 +57,10 @@ export class Iden3OnchainSmtCredentialStatusPublisher implements ICredentialStat
public async publish(params: {
nodes: ProofNode[];
credentialStatusType: CredentialStatusType;
onChain?: { txCallback?: (tx: TransactionReceipt) => Promise<void> };
onChain?: {
txCallback?: (tx: TransactionReceipt) => Promise<void>;
publishMode?: 'sync' | 'async' | 'callback';
};
}): Promise<void> {
if (
![CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023].includes(
Expand All @@ -71,13 +75,33 @@ export class Iden3OnchainSmtCredentialStatusPublisher implements ICredentialStat

const txPromise = this._storage.saveNodes(nodesBigInts);

let publishMode = params.onChain?.publishMode ?? 'sync';
if (params.onChain?.txCallback) {
const cb = params.onChain?.txCallback;
txPromise.then((receipt) => cb(receipt));
return;
publishMode = 'callback';
}

await txPromise;
switch (publishMode) {
case 'sync':
await txPromise;
break;
case 'callback': {
if (!params.onChain?.txCallback) {
throw new Error('txCallback is required for publishMode "callback"');
}
const cb = params.onChain?.txCallback;
txPromise.then((receipt) => cb(receipt));
break;
}
case 'async': {
const mb = MessageBus.getInstance();
txPromise.then((receipt) => mb.publish(SDK_EVENTS.TX_RECEIPT_ACCEPTED, receipt));
break;
}
default:
// sync by default
await txPromise;
break;
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/identity/identity-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ import {
Iden3SmtRhsCredentialStatusPublisher
} from '../credentials/status/credential-status-publisher';

/**
* Represents the publish mode for identity wallet.
* It can be one of the following values: 'sync', 'async', or 'callback'.
* 'sync' - publish the status synchronously
* 'async' - publish the status asynchronously via message bus
* 'callback' - publish the status with a txCallback
*/
export type PublishMode = 'sync' | 'async' | 'callback';

/**
* DID creation options
* seed - seed to generate BJJ key pair
Expand All @@ -72,6 +81,7 @@ export type IdentityCreationOptions = {
nonce?: number;
onChain?: {
txCallback?: (tx: TransactionReceipt) => Promise<void>;
publishMode?: PublishMode;
};
};
seed?: Uint8Array;
Expand All @@ -86,6 +96,7 @@ export type RevocationInfoOptions = {
rhsUrl?: string;
onChain?: {
txCallback?: (tx: TransactionReceipt) => Promise<void>;
publishMode?: PublishMode;
};
};

Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './encoding';
export * from './object';
export * from './did-helper';
export * from './message-bus';
87 changes: 87 additions & 0 deletions src/utils/message-bus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import PubSub from 'pubsub-js';

/**
* Represents an event in the SDK.
*/
type SdkEvent = string;

export const SDK_EVENTS: { [k: SdkEvent]: SdkEvent } = {
TX_RECEIPT_ACCEPTED: 'TX_RECEIPT_ACCEPTED'
};

/**
* Represents a topic in the SDK message bus.
*/
export type SdkTopic = keyof typeof SDK_EVENTS;

/**
* Represents a message bus that allows publishing and subscribing to topics.
*/
export class MessageBus {
/**
* The singleton instance of the MessageBus class.
*/
private static instance: MessageBus;

/**
* Private constructor for the MessageBus class.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {}

/**
* Returns the singleton instance of the MessageBus class.
* If the instance doesn't exist, it creates a new one.
* @returns The singleton instance of the MessageBus class.
*/
public static getInstance(): MessageBus {
// If the instance doesn't exist, create it
if (!MessageBus.instance) {
MessageBus.instance = new MessageBus();
}
// Return the instance
return MessageBus.instance;
}

/**
* Publishes a message to the specified topic.
*
* @template T - The type of data being published.
* @param {SdkTopic} topic - The topic to publish the message to.
* @param {T} data - The data to be published.
* @returns {boolean} - Returns true if the message was successfully published, false otherwise.
*/
public publish<T>(topic: SdkTopic, data: T): boolean {
return PubSub.publish(topic.toString(), data);
}

/**
* Subscribes to a specific topic and registers a callback function to be executed when a message is published.
*
* @param topic - The topic to subscribe to.
* @param callback - The callback function to be executed when a message is published.
*/
public subscribe<T>(topic: SdkTopic, callback: (data: T) => void): string {
return PubSub.subscribe(topic.toString(), (_, data) => callback(data));
}

/**
* Subscribes to a specific topic and registers a callback function to be executed when a message is published.
*
* @param topic - The topic to subscribe to.
* @param callback - The callback function to be executed when a message is published.
*/
public subscribeOnce<T>(topic: SdkTopic, callback: (data: T) => void): void {
PubSub.subscribeOnce(topic.toString(), (_, data) => callback(data));
}

/**
* Unsubscribes from a specific topic in the message bus.
*
* @param topic - The topic to unsubscribe from.
* @returns A string or boolean indicating the success of the unsubscribe operation.
*/
public unsubscribe(topic: SdkTopic): string | boolean {
return PubSub.unsubscribe(topic.toString());
}
}
98 changes: 52 additions & 46 deletions tests/onchain/on-chain-revocation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import {
ICircuitStorage,
byteEncoder,
CredentialStatusPublisherRegistry,
Iden3OnchainSmtCredentialStatusPublisher
Iden3OnchainSmtCredentialStatusPublisher,
SDK_EVENTS,
MessageBus
} from '../../src';

import {
Expand Down Expand Up @@ -324,56 +326,60 @@ describe('onchain revocation checks', () => {

it('issueCredential and generate proofs with onchain RHS status with tx callbacks', async () => {
const id = RHS_CONTRACT_ADDRESS;
return new Promise((resolve) => {
const msgBus = MessageBus.getInstance();
const msgBusUseCase = new Promise((resolve) => {
msgBus.subscribeOnce(SDK_EVENTS.TX_RECEIPT_ACCEPTED, (receipt) => {
expect(receipt).to.exist;
resolve(true);
});
});

const { did: issuerDID, credential: issuerAuthCredential } = await createIdentity(idWallet, {
seed: byteEncoder.encode('soedseedseedseedseedseedseedseed'),
revocationOpts: {
id,
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
onChain: {
publishMode: 'async'
}
}
});

await msgBusUseCase;

const callBackUseCase = new Promise((resolve) => {
(async () => {
const { did: issuerDID, credential: issuerAuthCredential } = await createIdentity(
idWallet,
{
seed: byteEncoder.encode('soedseedseedseedseedseedseedseed'),
revocationOpts: {
id,
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
onChain: {
txCallback: async () => {
const { did: userDID, credential: userAuthCredential } = await createIdentity(
idWallet,
{
seed: byteEncoder.encode('seedseedseedseedseedseedseedseex'),
revocationOpts: {
id,
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
onChain: {
txCallback: async () => {
const claimReq: CredentialRequest = createCredRequest(
userDID.string(),
{
revocationOpts: {
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
id
}
}
);

const issuerCred = await idWallet.issueCredential(issuerDID, claimReq);

expect(issuerCred.credentialSubject.id).to.equal(userDID.string());

await credWallet.save(issuerCred);
resolve();
}
}
}
}
);

expect(userAuthCredential.credentialStatus.id).to.contain(RHS_CONTRACT_ADDRESS);
}
const { did: userDID, credential: userAuthCredential } = await createIdentity(idWallet, {
seed: byteEncoder.encode('seedseedseedseedseedseedseedseex'),
revocationOpts: {
id,
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
onChain: {
txCallback: async () => {
const claimReq: CredentialRequest = createCredRequest(userDID.string(), {
revocationOpts: {
type: CredentialStatusType.Iden3OnchainSparseMerkleTreeProof2023,
id
}
});

const issuerCred = await idWallet.issueCredential(issuerDID, claimReq);

expect(issuerCred.credentialSubject.id).to.equal(userDID.string());

await credWallet.save(issuerCred);

expect(userAuthCredential.credentialStatus.id).to.contain(RHS_CONTRACT_ADDRESS);

expect(issuerAuthCredential.credentialStatus.id).to.contain(RHS_CONTRACT_ADDRESS);
resolve(true);
}
}
}
);
expect(issuerAuthCredential.credentialStatus.id).to.contain(RHS_CONTRACT_ADDRESS);
});
})();
});

await callBackUseCase;
});
});

0 comments on commit e46cd47

Please sign in to comment.