-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(indy-vdr): add indy-vdr package and indy vdr pool (#1160)
work funded by the Government of Ontario Signed-off-by: Victor Anene <victor@animo.id>
- Loading branch information
1 parent
b6ae948
commit e8d6ac3
Showing
19 changed files
with
952 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<p align="center"> | ||
<br /> | ||
<img | ||
alt="Hyperledger Aries logo" | ||
src="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/aa31131825e3331dc93694bc58414d955dcb1129/images/aries-logo.png" | ||
height="250px" | ||
/> | ||
</p> | ||
<h1 align="center"><b>Aries Framework JavaScript - Indy Verifiable Data Registry (Indy-Vdr)</b></h1> | ||
<p align="center"> | ||
<a | ||
href="https://raw.githubusercontent.com/hyperledger/aries-framework-javascript/main/LICENSE" | ||
><img | ||
alt="License" | ||
src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" | ||
/></a> | ||
<a href="https://www.typescriptlang.org/" | ||
><img | ||
alt="typescript" | ||
src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" | ||
/></a> | ||
<a href="https://www.npmjs.com/package/@aries-framework/indy-vdr" | ||
><img | ||
alt="@aries-framework/anoncreds version" | ||
src="https://img.shields.io/npm/v/@aries-framework/indy-vdr" | ||
/></a> | ||
|
||
</p> | ||
<br /> | ||
|
||
### Installation | ||
|
||
### Quick start | ||
|
||
### Example of usage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { Config } from '@jest/types' | ||
|
||
import base from '../../jest.config.base' | ||
|
||
import packageJson from './package.json' | ||
|
||
const config: Config.InitialOptions = { | ||
...base, | ||
name: packageJson.name, | ||
displayName: packageJson.name, | ||
setupFilesAfterEnv: ['./tests/setup.ts'], | ||
} | ||
|
||
export default config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "@aries-framework/indy-vdr", | ||
"main": "build/index", | ||
"types": "build/index", | ||
"version": "0.3.3", | ||
"private": true, | ||
"files": [ | ||
"build" | ||
], | ||
"license": "Apache-2.0", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/indy-vdr", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/hyperledger/aries-framework-javascript", | ||
"directory": "packages/indy-vdr" | ||
}, | ||
"scripts": { | ||
"build": "yarn run clean && yarn run compile", | ||
"clean": "rimraf ./build", | ||
"compile": "tsc -p tsconfig.build.json", | ||
"prepublishOnly": "yarn run build", | ||
"test": "jest" | ||
}, | ||
"dependencies": { | ||
"@aries-framework/core": "0.3.3", | ||
"indy-vdr-test-shared": "^0.1.3" | ||
}, | ||
"devDependencies": { | ||
"indy-vdr-test-nodejs": "^0.1.3", | ||
"rimraf": "~4.0.7", | ||
"typescript": "~4.9.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { AriesFrameworkError } from '@aries-framework/core' | ||
|
||
export class IndyVdrError extends AriesFrameworkError { | ||
public constructor(message: string, { cause }: { cause?: Error } = {}) { | ||
super(message, { cause }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { IndyVdrError } from './IndyVdrError' | ||
|
||
export class IndyVdrNotConfiguredError extends IndyVdrError { | ||
public constructor(message: string, { cause }: { cause?: Error } = {}) { | ||
super(message, { cause }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { IndyVdrError } from './IndyVdrError' | ||
|
||
export class IndyVdrNotFoundError extends IndyVdrError { | ||
public constructor(message: string, { cause }: { cause?: Error } = {}) { | ||
super(message, { cause }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './IndyVdrError' | ||
export * from './IndyVdrNotFound' | ||
export * from './IndyVdrNotConfiguredError' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
try { | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
require('indy-vdr-test-nodejs') | ||
} catch (error) { | ||
throw new Error('Error registering nodejs bindings for Indy VDR') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import type { Logger, AgentContext, Key } from '@aries-framework/core' | ||
import type { IndyVdrRequest, IndyVdrPool as indyVdrPool } from 'indy-vdr-test-shared' | ||
|
||
import { TypedArrayEncoder } from '@aries-framework/core' | ||
import { | ||
GetTransactionAuthorAgreementRequest, | ||
GetAcceptanceMechanismsRequest, | ||
PoolCreate, | ||
indyVdr, | ||
} from 'indy-vdr-test-shared' | ||
|
||
import { IndyVdrError } from '../error' | ||
|
||
export interface TransactionAuthorAgreement { | ||
version?: `${number}.${number}` | `${number}` | ||
acceptanceMechanism: string | ||
} | ||
|
||
export interface AuthorAgreement { | ||
digest: string | ||
version: string | ||
text: string | ||
ratification_ts: number | ||
acceptanceMechanisms: AcceptanceMechanisms | ||
} | ||
|
||
export interface AcceptanceMechanisms { | ||
aml: Record<string, string> | ||
amlContext: string | ||
version: string | ||
} | ||
|
||
export interface IndyVdrPoolConfig { | ||
genesisTransactions: string | ||
isProduction: boolean | ||
indyNamespace: string | ||
transactionAuthorAgreement?: TransactionAuthorAgreement | ||
} | ||
|
||
export class IndyVdrPool { | ||
private _pool?: indyVdrPool | ||
private logger: Logger | ||
private poolConfig: IndyVdrPoolConfig | ||
public authorAgreement?: AuthorAgreement | null | ||
|
||
public constructor(poolConfig: IndyVdrPoolConfig, logger: Logger) { | ||
this.logger = logger | ||
this.poolConfig = poolConfig | ||
} | ||
|
||
public get indyNamespace(): string { | ||
return this.poolConfig.indyNamespace | ||
} | ||
|
||
public get config() { | ||
return this.poolConfig | ||
} | ||
|
||
public async connect() { | ||
this._pool = new PoolCreate({ | ||
parameters: { | ||
transactions: this.config.genesisTransactions, | ||
}, | ||
}) | ||
|
||
return this.pool.handle | ||
} | ||
|
||
private get pool(): indyVdrPool { | ||
if (!this._pool) { | ||
throw new IndyVdrError('Pool is not connected. Make sure to call .connect() first') | ||
} | ||
|
||
return this._pool | ||
} | ||
|
||
public close() { | ||
if (!this.pool) { | ||
throw new IndyVdrError("Can't close pool. Pool is not connected") | ||
} | ||
|
||
// FIXME: this method doesn't work?? | ||
// this.pool.close() | ||
} | ||
|
||
public async submitWriteRequest<Request extends IndyVdrRequest>( | ||
agentContext: AgentContext, | ||
request: Request, | ||
signingKey: Key | ||
) { | ||
await this.appendTaa(request) | ||
|
||
const signature = await agentContext.wallet.sign({ | ||
data: TypedArrayEncoder.fromString(request.signatureInput), | ||
key: signingKey, | ||
}) | ||
|
||
request.setSignature({ | ||
signature, | ||
}) | ||
|
||
return await this.pool.submitRequest(request) | ||
} | ||
|
||
public async submitReadRequest<Request extends IndyVdrRequest>(request: Request) { | ||
return await this.pool.submitRequest(request) | ||
} | ||
|
||
private async appendTaa(request: IndyVdrRequest) { | ||
const authorAgreement = await this.getTransactionAuthorAgreement() | ||
const poolTaa = this.config.transactionAuthorAgreement | ||
|
||
// If ledger does not have TAA, we can just send request | ||
if (authorAgreement == null) { | ||
return request | ||
} | ||
|
||
// Ledger has taa but user has not specified which one to use | ||
if (!poolTaa) { | ||
throw new IndyVdrError( | ||
`Please, specify a transaction author agreement with version and acceptance mechanism. ${JSON.stringify( | ||
authorAgreement | ||
)}` | ||
) | ||
} | ||
|
||
// Throw an error if the pool doesn't have the specified version and acceptance mechanism | ||
if ( | ||
authorAgreement.version !== poolTaa.version || | ||
!authorAgreement.acceptanceMechanisms.aml[poolTaa.acceptanceMechanism] | ||
) { | ||
// Throw an error with a helpful message | ||
const errMessage = `Unable to satisfy matching TAA with mechanism ${JSON.stringify( | ||
poolTaa.acceptanceMechanism | ||
)} and version ${poolTaa.version} in pool.\n Found ${JSON.stringify( | ||
authorAgreement.acceptanceMechanisms.aml | ||
)} and version ${authorAgreement.version} in pool.` | ||
throw new IndyVdrError(errMessage) | ||
} | ||
|
||
const acceptance = indyVdr.prepareTxnAuthorAgreementAcceptance({ | ||
text: authorAgreement.text, | ||
version: authorAgreement.version, | ||
taaDigest: authorAgreement.digest, | ||
time: Math.floor(new Date().getTime() / 1000), | ||
acceptanceMechanismType: poolTaa.acceptanceMechanism, | ||
}) | ||
|
||
request.setTransactionAuthorAgreementAcceptance({ acceptance }) | ||
} | ||
|
||
private async getTransactionAuthorAgreement(): Promise<AuthorAgreement | null> { | ||
// TODO Replace this condition with memoization | ||
if (this.authorAgreement !== undefined) { | ||
return this.authorAgreement | ||
} | ||
|
||
const taaRequest = new GetTransactionAuthorAgreementRequest({}) | ||
const taaResponse = await this.submitReadRequest(taaRequest) | ||
|
||
const acceptanceMechanismRequest = new GetAcceptanceMechanismsRequest({}) | ||
const acceptanceMechanismResponse = await this.submitReadRequest(acceptanceMechanismRequest) | ||
|
||
const taaData = taaResponse.result.data | ||
|
||
// TAA can be null | ||
if (taaData == null) { | ||
this.authorAgreement = null | ||
return null | ||
} | ||
|
||
// If TAA is not null, we can be sure AcceptanceMechanisms is also not null | ||
const authorAgreement = taaData as Omit<AuthorAgreement, 'acceptanceMechanisms'> | ||
|
||
// FIME: remove cast when https://github.com/hyperledger/indy-vdr/pull/142 is released | ||
const acceptanceMechanisms = acceptanceMechanismResponse.result.data as unknown as AcceptanceMechanisms | ||
this.authorAgreement = { | ||
...authorAgreement, | ||
acceptanceMechanisms, | ||
} | ||
|
||
return this.authorAgreement | ||
} | ||
} |
Oops, something went wrong.