-
Notifications
You must be signed in to change notification settings - Fork 198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support for postgres wallet type #699
Changes from 9 commits
004a7ed
2ed6194
085f966
3907d17
58cab5a
d4586d9
d5c5ccf
fc3c7ab
a29b72a
c7122c3
e1749ca
b33231c
f81d5bd
e41be46
dfa7487
d8077f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,10 +16,24 @@ export const enum KeyDerivationMethod { | |
Raw = 'RAW', | ||
} | ||
|
||
export enum WalletScheme { | ||
DatabasePerWallet = 'DatabasePerWallet', | ||
MultiWalletSingleTable = 'MultiWalletSingleTable', | ||
MultiWalletSingleTableSharedPool = 'MultiWalletSingleTableSharedPool', | ||
} | ||
|
||
export interface WalletConfig { | ||
id: string | ||
key: string | ||
keyDerivationMethod?: KeyDerivationMethod | ||
storage?: { | ||
type: string | ||
[key: string]: unknown | ||
} | ||
} | ||
|
||
export interface WalletStorageCreds { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, I think we can leave this out |
||
[key: string]: unknown | ||
} | ||
|
||
export interface WalletConfigRekey { | ||
|
@@ -47,6 +61,11 @@ export enum DidCommMimeType { | |
V1 = 'application/didcomm-envelope-enc', | ||
} | ||
|
||
export enum WalletStorageType { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can also be removed now |
||
Default = 'default', | ||
Postgres = 'postgres_storage', | ||
} | ||
|
||
export interface InitConfig { | ||
endpoints?: string[] | ||
label: string | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -6,10 +6,11 @@ import type { | |||||
WalletExportImportConfig, | ||||||
WalletConfigRekey, | ||||||
KeyDerivationMethod, | ||||||
WalletStorageCreds, | ||||||
} from '../types' | ||||||
import type { Buffer } from '../utils/buffer' | ||||||
import type { Wallet, DidInfo, DidConfig } from './Wallet' | ||||||
import type { default as Indy } from 'indy-sdk' | ||||||
import type { default as Indy, WalletStorageConfig } from 'indy-sdk' | ||||||
|
||||||
import { Lifecycle, scoped } from 'tsyringe' | ||||||
|
||||||
|
@@ -67,6 +68,41 @@ export class IndyWallet implements Wallet { | |||||
return this.walletConfig.id | ||||||
} | ||||||
|
||||||
private walletStorageConfig(walletConfig: WalletConfig): Indy.WalletConfig { | ||||||
const walletStorageConfig: Indy.WalletConfig = { | ||||||
id: walletConfig.id, | ||||||
storage_type: walletConfig.storage?.type, | ||||||
} | ||||||
|
||||||
if (walletConfig.storage?.config) { | ||||||
walletStorageConfig.storage_config = walletConfig.storage?.config as WalletStorageConfig | ||||||
} | ||||||
|
||||||
return walletStorageConfig | ||||||
} | ||||||
|
||||||
private walletCredentials( | ||||||
walletConfig: WalletConfig, | ||||||
rekey?: string, | ||||||
rekeyDerivation?: KeyDerivationMethod | ||||||
): Indy.OpenWalletCredentials { | ||||||
const walletCredentials: Indy.OpenWalletCredentials = { | ||||||
key: walletConfig.key, | ||||||
key_derivation_method: walletConfig.keyDerivationMethod, | ||||||
} | ||||||
if (rekey) { | ||||||
walletCredentials.rekey = rekey | ||||||
} | ||||||
if (rekeyDerivation) { | ||||||
walletCredentials.rekey_derivation_method = rekeyDerivation | ||||||
} | ||||||
if (walletConfig.storage?.credentials) { | ||||||
walletCredentials.storage_credentials = walletConfig.storage?.credentials as WalletStorageCreds | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
return walletCredentials | ||||||
} | ||||||
|
||||||
/** | ||||||
* @throws {WalletDuplicateError} if the wallet already exists | ||||||
* @throws {WalletError} if another error occurs | ||||||
|
@@ -84,11 +120,7 @@ export class IndyWallet implements Wallet { | |||||
this.logger.debug(`Creating wallet '${walletConfig.id}' using SQLite storage`) | ||||||
|
||||||
try { | ||||||
await this.indy.createWallet( | ||||||
{ id: walletConfig.id }, | ||||||
{ key: walletConfig.key, key_derivation_method: walletConfig.keyDerivationMethod } | ||||||
) | ||||||
|
||||||
await this.indy.createWallet(this.walletStorageConfig(walletConfig), this.walletCredentials(walletConfig)) | ||||||
this.walletConfig = walletConfig | ||||||
|
||||||
// We usually want to create master secret only once, therefore, we can to do so when creating a wallet. | ||||||
|
@@ -139,7 +171,11 @@ export class IndyWallet implements Wallet { | |||||
throw new WalletError('Wallet rekey undefined!. Please specify the new wallet key') | ||||||
} | ||||||
await this._open( | ||||||
{ id: walletConfig.id, key: walletConfig.key, keyDerivationMethod: walletConfig.keyDerivationMethod }, | ||||||
{ | ||||||
id: walletConfig.id, | ||||||
key: walletConfig.key, | ||||||
keyDerivationMethod: walletConfig.keyDerivationMethod, | ||||||
}, | ||||||
walletConfig.rekey, | ||||||
walletConfig.rekeyDerivationMethod | ||||||
) | ||||||
|
@@ -162,13 +198,8 @@ export class IndyWallet implements Wallet { | |||||
|
||||||
try { | ||||||
this.walletHandle = await this.indy.openWallet( | ||||||
{ id: walletConfig.id }, | ||||||
{ | ||||||
key: walletConfig.key, | ||||||
rekey: rekey, | ||||||
key_derivation_method: walletConfig.keyDerivationMethod, | ||||||
rekey_derivation_method: rekeyDerivation, | ||||||
} | ||||||
this.walletStorageConfig(walletConfig), | ||||||
this.walletCredentials(walletConfig, rekey, rekeyDerivation) | ||||||
) | ||||||
if (rekey) { | ||||||
this.walletConfig = { ...walletConfig, key: rekey, keyDerivationMethod: rekeyDerivation } | ||||||
|
@@ -224,8 +255,8 @@ export class IndyWallet implements Wallet { | |||||
|
||||||
try { | ||||||
await this.indy.deleteWallet( | ||||||
{ id: this.walletConfig.id }, | ||||||
{ key: this.walletConfig.key, key_derivation_method: this.walletConfig.keyDerivationMethod } | ||||||
this.walletStorageConfig(this.walletConfig), | ||||||
this.walletCredentials(this.walletConfig) | ||||||
) | ||||||
} catch (error) { | ||||||
if (isIndyError(error, 'WalletNotFoundError')) { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import type { SubjectMessage } from '../../../tests/transport/SubjectInboundTransport' | ||
import type { IndyPostgresStorageConfig } from '../../node/src' | ||
import type { ConnectionRecord } from '../src/modules/connections' | ||
|
||
import { Subject } from 'rxjs' | ||
|
||
import { SubjectInboundTransport } from '../../../tests/transport/SubjectInboundTransport' | ||
import { SubjectOutboundTransport } from '../../../tests/transport/SubjectOutboundTransport' | ||
import { loadPostgresPlugin } from '../../node/src' | ||
import { Agent } from '../src/agent/Agent' | ||
import { WalletScheme } from '../src/types' | ||
|
||
import { waitForBasicMessage, getBasePostgresConfig } from './helpers' | ||
|
||
const alicePostgresConfig = getBasePostgresConfig('AgentsAlice', { | ||
endpoints: ['rxjs:alice'], | ||
}) | ||
const bobPostgresConfig = getBasePostgresConfig('AgentsBob', { | ||
endpoints: ['rxjs:bob'], | ||
}) | ||
|
||
describe('postgres agents', () => { | ||
let aliceAgent: Agent | ||
let bobAgent: Agent | ||
let aliceConnection: ConnectionRecord | ||
let bobConnection: ConnectionRecord | ||
|
||
afterAll(async () => { | ||
await bobAgent.shutdown() | ||
await bobAgent.wallet.delete() | ||
await aliceAgent.shutdown() | ||
await aliceAgent.wallet.delete() | ||
}) | ||
|
||
test('make a connection between postgres agents', async () => { | ||
const aliceMessages = new Subject<SubjectMessage>() | ||
const bobMessages = new Subject<SubjectMessage>() | ||
|
||
const subjectMap = { | ||
'rxjs:alice': aliceMessages, | ||
'rxjs:bob': bobMessages, | ||
} | ||
|
||
const storageConfig: IndyPostgresStorageConfig = { | ||
type: 'postgres_storage', | ||
config: { | ||
url: 'localhost:5432', | ||
wallet_scheme: WalletScheme.DatabasePerWallet, | ||
}, | ||
credentials: { | ||
account: 'postgres', | ||
password: 'postgres', | ||
admin_account: 'postgres', | ||
admin_password: 'postgres', | ||
}, | ||
} | ||
|
||
// loading the postgres wallet plugin | ||
await loadPostgresPlugin(storageConfig.config, storageConfig.credentials) | ||
|
||
aliceAgent = new Agent(alicePostgresConfig.config, alicePostgresConfig.agentDependencies) | ||
aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) | ||
aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) | ||
await aliceAgent.initialize() | ||
|
||
bobAgent = new Agent(bobPostgresConfig.config, bobPostgresConfig.agentDependencies) | ||
bobAgent.registerInboundTransport(new SubjectInboundTransport(bobMessages)) | ||
bobAgent.registerOutboundTransport(new SubjectOutboundTransport(subjectMap)) | ||
await bobAgent.initialize() | ||
|
||
const aliceConnectionAtAliceBob = await aliceAgent.connections.createConnection() | ||
const bobConnectionAtBobAlice = await bobAgent.connections.receiveInvitation(aliceConnectionAtAliceBob.invitation) | ||
|
||
aliceConnection = await aliceAgent.connections.returnWhenIsConnected(aliceConnectionAtAliceBob.connectionRecord.id) | ||
bobConnection = await bobAgent.connections.returnWhenIsConnected(bobConnectionAtBobAlice.id) | ||
|
||
expect(aliceConnection).toBeConnectedWith(bobConnection) | ||
expect(bobConnection).toBeConnectedWith(aliceConnection) | ||
}) | ||
|
||
test('send a message to connection', async () => { | ||
const message = 'hello, world' | ||
await aliceAgent.basicMessages.sendMessage(aliceConnection.id, message) | ||
|
||
const basicMessage = await waitForBasicMessage(bobAgent, { | ||
content: message, | ||
}) | ||
|
||
expect(basicMessage.content).toBe(message) | ||
}) | ||
|
||
test('can shutdown and re-initialize the same postgres agent', async () => { | ||
expect(aliceAgent.isInitialized).toBe(true) | ||
await aliceAgent.shutdown() | ||
expect(aliceAgent.isInitialized).toBe(false) | ||
await aliceAgent.initialize() | ||
expect(aliceAgent.isInitialized).toBe(true) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,14 +30,18 @@ | |
"dependencies": { | ||
"@aries-framework/core": "0.1.0", | ||
"express": "^4.17.1", | ||
"ffi-napi": "^4.0.3", | ||
"indy-sdk": "^1.16.0-dev-1636", | ||
"node-fetch": "^2.6.1", | ||
"ref-napi": "^3.0.3", | ||
"ws": "^7.5.3" | ||
}, | ||
"devDependencies": { | ||
"@types/express": "^4.17.13", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing |
||
"@types/ffi-napi": "^4.0.5", | ||
"@types/node": "^15.14.4", | ||
"@types/node-fetch": "^2.5.10", | ||
"@types/ref-napi": "^3.0.4", | ||
"@types/ws": "^7.4.6", | ||
"rimraf": "~3.0.2", | ||
"typescript": "~4.3.0" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a postgres wallet feature, so maybe we can also move this to the node package?