Skip to content

Commit

Permalink
feat: Support multiple verida networks [DEV-3801] (#511)
Browse files Browse the repository at this point in the history
* feat: Support multiple verida networks

* Use VeridaDidValidator

* Fix verida DID validator

* Add missing catch block
  • Loading branch information
DaevMithran authored Apr 4, 2024
1 parent c89411c commit 5dd4665
Show file tree
Hide file tree
Showing 12 changed files with 46 additions and 41 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ jobs:
TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
TESTNET_RPC_URL: ${{ vars.TESTNET_RPC_URL }}
VERIDA_NETWORK: ${{ vars.VERIDA_NETWORK }}
VERIDA_PRIVATE_KEY: ${{ secrets.VERIDA_PRIVATE_KEY }}
CREDS_DECRYPTION_SECRET: ${{ secrets.CREDS_DECRYPTION_SECRET }}

Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,8 @@ Verida Wallet.
By default, `ENABLE_VERIDA_CONNECTOR` is set to off/`false`. To enable external Veramo KMS database, set
`ENABLE_VERIDA_CONNECTOR` to `true`, then define below environment variables in `.env` file:

1. `VERIDA_NETWORK`: Verida Network type to connect to. (Default: `testnet`)
2. `VERIDA_PRIVATE_KEY`: Secret key for Verida Network API.
3. `POLYGON_PRIVATE_KEY`: Secret key for Polygon Network.
1. `VERIDA_PRIVATE_KEY`: Secret key for Verida Network API.
2. `POLYGON_PRIVATE_KEY`: Secret key for Polygon Network.

## 🧑‍💻🛠 Developer Guide

Expand Down
2 changes: 0 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ ARG LOG_LEVEL=info

# Verida connector: build-time
ARG ENABLE_VERIDA_CONNECTOR=false
ARG VERIDA_NETWORK=testnet
ARG POLYGON_RPC_URL=https://rpc.ankr.com/polygon_mumbai
ARG VERIDA_PRIVATE_KEY
ARG POLYGON_PRIVATE_KEY
Expand Down Expand Up @@ -120,7 +119,6 @@ ENV TESTNET_MINIMUM_BALANCE ${TESTNET_MINIMUM_BALANCE}

# Environment variables: Verida connector
ENV ENABLE_VERIDA_CONNECTOR ${ENABLE_VERIDA_CONNECTOR}
ENV VERIDA_NETWORK ${VERIDA_NETWORK}
ENV POLYGON_RPC_URL ${POLYGON_RPC_URL}
ENV VERIDA_PRIVATE_KEY ${VERIDA_PRIVATE_KEY}
ENV POLYGON_PRIVATE_KEY ${POLYGON_PRIVATE_KEY}
Expand Down
1 change: 0 additions & 1 deletion docker/no-external-db/no-db.env
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ ENABLE_AUTHENTICATION="false"
ENABLE_VERIDA_CONNECTOR="false"
VERIDA_PRIVATE_KEY="akjvncanv....avoa"
POLYGON_PRIVATE_KEY="alnvca...dvncioa"
VERIDA_NETWORK="testnet"

# Standalone install (no external DB for KMS)
ISSUER_PUBLIC_KEY_HEX="alnvca...dvncioa"
Expand Down
1 change: 0 additions & 1 deletion docker/with-external-db/with-db.env
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ COOKIE_SECRET="sdf...sdf"
ENABLE_VERIDA_CONNECTOR="false"
VERIDA_PRIVATE_KEY="akjvncanv....avoa"
POLYGON_PRIVATE_KEY="alnvca...dvncioa"
VERIDA_NETWORK="testnet"
1 change: 0 additions & 1 deletion example.env
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ TESTNET_MINIMUM_BALANCE="1000"
ENABLE_VERIDA_CONNECTOR="false"
VERIDA_PRIVATE_KEY="akjvncanv....avoa"
POLYGON_PRIVATE_KEY="alnvca...dvncioa"
VERIDA_NETWORK="testnet"

# Without Database
ISSUER_PRIVATE_KEY_HEX="akjvncanv....avoa"
Expand Down
18 changes: 10 additions & 8 deletions src/controllers/validator/did.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CheqdNetwork } from '@cheqd/sdk';
import type { IValidationResult, IValidator, Validatable } from './validator.js';
import { CheqdIdentifierValidator, KeyIdentifierValidator, VeridaIdentifierValidator } from './identifier.js';
import { EnvironmentType } from '@verida/types';

export class BaseDidValidator implements IValidator {
validate(did: Validatable): IValidationResult {
Expand Down Expand Up @@ -157,7 +158,7 @@ export class VeridaDIDValidator extends BaseDidValidator implements IValidator {
return this.subject;
}

validate(did: Validatable): IValidationResult {
validate(did: Validatable): IValidationResult & { namespace?: EnvironmentType; identifier?: string } {
// Call base validation
let _v = super.validate(did);
if (!_v.valid) {
Expand All @@ -174,38 +175,39 @@ export class VeridaDIDValidator extends BaseDidValidator implements IValidator {
}

// Check namepsace
const namespace = did.split(':')[2];
const namespace = did.split(':')[2] as EnvironmentType | undefined;
if (!namespace) {
return {
valid: false,
error: 'Verida DID namespace is required ("did:vda:mainnet:..." or "did:vda:testnet:...")',
};
}

// Check if namespace is valid
if (namespace !== CheqdNetwork.Testnet && namespace !== CheqdNetwork.Mainnet) {
if (!(namespace in EnvironmentType)) {
return {
valid: false,
error: `Verida DID namespace must be ${CheqdNetwork.Testnet} or ${CheqdNetwork.Mainnet}`,
error: `Verida DID namespace must be ${EnvironmentType.MAINNET} or ${EnvironmentType.TESTNET}`,
};
}

// Check identifier
const id = did.split(':')[3];
if (!id) {
const identifier = did.split(':')[3];
if (!identifier) {
return {
valid: false,
error: 'Identifier is required after "did:vda:<namespace>:" prefix',
};
}
// Check that identifier is valid
_v = this.identifierValidator.validate(id);
_v = this.identifierValidator.validate(identifier);
if (!_v.valid) {
return {
valid: false,
error: _v.error,
};
}
return { valid: true };
return { valid: true, namespace, identifier };
}
}

Expand Down
1 change: 1 addition & 0 deletions src/controllers/validator/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export interface IValidator {
export interface IValidationResult {
valid: boolean;
error?: string;
[x: string]: any;
}
2 changes: 1 addition & 1 deletion src/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function isValidService(didDocument: DIDDocument): boolean {
return didDocument.service
? didDocument?.service?.every((s) => {
return s?.serviceEndpoint && s?.id && s?.type;
})
})
: true;
}

Expand Down
49 changes: 28 additions & 21 deletions src/services/connectors/verida.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import * as dotenv from 'dotenv';
import type { VerifiableCredential } from '@veramo/core';
dotenv.config();

const { VERIDA_NETWORK, VERIDA_PRIVATE_KEY, POLYGON_PRIVATE_KEY } = process.env;
const { VERIDA_PRIVATE_KEY, POLYGON_PRIVATE_KEY } = process.env;

/**
* Helper class for the Verida protocol.
*
* Run the init method before running any other method.
*/
export class VeridaService {
private context?: Context;
private account?: AutoAccount;
private context: Partial<Record<EnvironmentType, Context>> = {};
private account: Partial<Record<EnvironmentType, AutoAccount>> = {};

static instance = new VeridaService();

Expand All @@ -35,10 +35,11 @@ export class VeridaService {
accountPrivateKey: string,
polygonPrivateKey: string
) {
if (this.context && this.account) {
if (this.context[environment] && this.account[environment]) {
return;
}
this.account = new AutoAccount({

this.account[environment] = new AutoAccount({
privateKey: accountPrivateKey,
environment,
didClientConfig: {
Expand All @@ -49,15 +50,16 @@ export class VeridaService {
},
},
});

try {
this.context = await Network.connect({
this.context[environment] = await Network.connect({
client: {
environment,
},
context: {
name: contextName,
},
account: this.account,
account: this.account[environment]!,
});
} catch (error) {
throw new Error(`Error: ${error}`);
Expand All @@ -71,18 +73,18 @@ export class VeridaService {
* @param subject The subject of the message (similar to an email subject).
* @param data The data to be sent.
*/
async sendData(recipientDid: string, subject: string, data: DataRecord) {
async sendData(environment: EnvironmentType, recipientDid: string, subject: string, data: DataRecord) {
try {
if (!this.context) {
await VeridaService.instance.init(
VERIDA_NETWORK,
environment,
VERIDA_APP_NAME,
VERIDA_PRIVATE_KEY,
POLYGON_PRIVATE_KEY
);
}

const messagingClient = await this.context?.getMessaging();
const messagingClient = await this.context[environment]?.getMessaging();

const messageType = 'inbox/type/dataSend'; // There are different types of message, here we are sending some data.
const messageData = {
Expand All @@ -95,7 +97,7 @@ export class VeridaService {

await messagingClient?.send(recipientDid, messageType, messageData, subject, messageConfig);
} catch (error) {
throw new Error(`Error sending data ${error}`);
throw new Error(`${(error as Error).message || error}`);
}
}

Expand All @@ -109,22 +111,27 @@ export class VeridaService {
* @param credentialSummary A summary of the credential. For instance, will be displayed in the Verida Wallet UI.
*/
async sendCredential(
environment: EnvironmentType,
recipientDid: string,
messageSubject: string,
credential: VerifiableCredential,
credentialName: string,
credentialSchema: string,
credentialSummary?: string
) {
// The Credential record is how Verida wrap the credential to store it on the Network. Check the JSdoc of the type and each property. They are following the Verida Credential Record schema.
const credentialRecord: CredentialDataRecord = {
name: credentialName,
summary: credentialSummary,
schema: VERIDA_CREDENTIAL_RECORD_SCHEMA,
didJwtVc: credential.proof.jwt,
credentialSchema,
credentialData: credential,
};
await this.sendData(recipientDid, messageSubject, credentialRecord);
try {
// The Credential record is how Verida wrap the credential to store it on the Network. Check the JSdoc of the type and each property. They are following the Verida Credential Record schema.
const credentialRecord: CredentialDataRecord = {
name: credentialName,
summary: credentialSummary,
schema: VERIDA_CREDENTIAL_RECORD_SCHEMA,
didJwtVc: credential.proof.jwt,
credentialSchema,
credentialData: credential,
};
await this.sendData(environment, recipientDid, messageSubject, credentialRecord);
} catch (error) {
throw new Error(`Error sending data to verida wallet: ${(error as Error).message || error}`);
}
}
}
5 changes: 4 additions & 1 deletion src/services/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IdentityServiceStrategySetup } from './identity/index.js';
import { v4 } from 'uuid';
import * as dotenv from 'dotenv';
import type { CustomerEntity } from '../database/entities/customer.entity.js';
import { VeridaDIDValidator } from '../controllers/validator/did.js';
dotenv.config();

const { ENABLE_VERIDA_CONNECTOR } = process.env;
Expand Down Expand Up @@ -36,13 +37,15 @@ export class Credentials {
customer.customerId
).agent.createCredential(credential, request.format, statusOptions, customer);

if (ENABLE_VERIDA_CONNECTOR === 'true' && request.subjectDid.startsWith('did:vda')) {
const isVeridaDid = new VeridaDIDValidator().validate(request.subjectDid);
if (ENABLE_VERIDA_CONNECTOR === 'true' && isVeridaDid.valid && isVeridaDid.namespace) {
if (!request.credentialSchema) throw new Error('Credential schema is required');

// dynamic import to avoid circular dependency
const { VeridaService } = await import('./connectors/verida.js');

await VeridaService.instance.sendCredential(
isVeridaDid.namespace,
request.subjectDid,
'New Verifiable Credential',
verifiable_credential,
Expand Down
1 change: 0 additions & 1 deletion src/types/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ declare global {

// Verida
ENABLE_VERIDA_CONNECTOR: string | 'false';
VERIDA_NETWORK: EnvironmentType;
POLYGON_RPC_URL: string;
VERIDA_PRIVATE_KEY: string;
POLYGON_PRIVATE_KEY: string;
Expand Down

0 comments on commit 5dd4665

Please sign in to comment.