Skip to content

Commit fbd3af7

Browse files
Da 2/integrate signing manager (#91)
* chore: πŸ€– Bump SDK to 14.0.0-alpha.4 version Bump the SDK version to include support for the signing manager integration * chore: πŸ€– Use local signing manager with the SDK * test: πŸ’ Add SigningService tests * feat: 🎸 add support for vault signers Allow signing keys to be stored in hashicorps vault * feat: 🎸 Rename relayer env variables to local prefix Renames RELAYER_DIDS and RELAYER_MNEMONICS to LOCAL_SIGNERS and LOCAL_MNEMONICs to better reflect their usage BREAKING CHANGE: 🧨 RELAYER_DIDS and RELAYER_MNEMONICS were renamed * refactor: πŸ’‘ Cleanup syntax * docs: ✏️ Fix casing * chore: πŸ€– Update hashicorp vault signing manager package * feat: 🎸 log key-address pair when loaded into signing manager * fix: πŸ› Resolve hidden merge conflict Account merge was expecting the moved relayer service which is now signer service * refactor: πŸ’‘ Address PR comments * feat: 🎸 Allow Vault signer to add keys without a restart * feat: 🎸 Lookup key on every call with Vault Signer Perform key lookup with every call to handle deleted keys correctly * test: πŸ’ Refactor signer service test * refactor: πŸ’‘ Make LocalSigner and VaultSigner classes Use different signer classes depending on the type of signer to avoid type switches in the implementation of methods * test: πŸ’ Use mockSignerProvider to provide abstract service * test: πŸ’ Refactor to avoid unnessesary overrides * refactor: πŸ’‘ Address PR comments Ranmes SignerService to SigningService to be more aligned with library naming conventions * refactor: πŸ’‘ Move setSigningManager call to base class * chore: πŸ€– Bump prettier version for override keyword support Adding override to methods caused prettier to error. 2.3.1 adds support for it * fix: πŸ› Fix build after merge with old relayer * feat: 🎸 Add validators for vault env variables * refactor: πŸ’‘ Ensure either vault or local signer is configured Co-authored-by: Victor Vicente <[email protected]>
1 parent a8ec83a commit fbd3af7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+700
-446
lines changed

β€Ž.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@
2424
"/// TODO @\\w+/", // ignore github handles in TODOs
2525
"/0x.+/" // ignore hex values
2626
],
27-
"cSpell.words": ["Isin", "metatype", "POLYX"]
27+
"cSpell.words": ["Hashicorp", "Isin", "metatype", "nand", "POLYX"]
2828
}

β€ŽREADME.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ PORT=## port in which the server will listen. Defaults to 3000 ##
2424
POLYMESH_NODE_URL=## websocket URL for a Polymesh node ##
2525
POLYMESH_MIDDLEWARE_URL=## URL for an instance of the Polymesh GraphQL Middleware service ##
2626
POLYMESH_MIDDLEWARE_API_KEY=## API key for the Middleware GraphQL service ##
27-
RELAYER_DIDS=## list of comma separated DIDs for the relayer service ##
28-
RELAYER_MNEMONICS=## list of comma separated mnemonics for the relayer service (each mnemonic corresponds to a DID in RELAYER_DIDS) ##
27+
LOCAL_SIGNERS=## list of comma separated IDs to refer to the corresponding mnemonic ##
28+
LOCAL_MNEMONICS=## list of comma separated mnemonics for the signer service (each mnemonic corresponds to a signer in LOCAL_SIGNERS) ##
29+
VAULT_URL=## The URL of a Vault transit engine##
30+
VAULT_SECRET=## The access token for authorization with the Vault instance ##
2931
```
3032

33+
### Signing Transactions
34+
35+
There are currently two configurations that the REST API maybe configured in to sign transactions. When Vault is configured it will override the local signers and those values will be ignored.
36+
37+
1. Local Signing:
38+
By using `LOCAL_SIGNERS` and `LOCAL_MNEMONICS` private keys will be initialized in memory. When making a transaction that requires a signer use the corresponding entry in `LOCAL_SIGNERS` (by array offset).
39+
1. Vault Signing:
40+
By setting `VAULT_URL` and `VAULT_SECRET`an external [Vault](https://www.vaultproject.io/) instance will be used to sign transactions. The URL should point to a transit engine in Vault that has Ed25519 keys in it. To refer to a key when signing use the Vault name and version `${name}-${version}` e.g. `alice-1`
41+
3142
## Running the app
3243

3344
```bash
@@ -43,7 +54,7 @@ $ yarn start:prod
4354

4455
### With docker
4556

46-
To pass in the env variables you can use `-e` to pass them indiviudally, or use a file with `--env-file`.
57+
To pass in the env variables you can use `-e` to pass them individually, or use a file with `--env-file`.
4758
For documentation you will need to expose a port that maps to `:3000` (or its `$PORT` if set) in the container.
4859

4960
```bash

β€Žpackage.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
"@nestjs/core": "7.6.15",
3030
"@nestjs/platform-express": "7.6.15",
3131
"@nestjs/swagger": "4.8.0",
32-
"@polymathnetwork/polymesh-sdk": "13.0.0-alpha.31",
32+
"@polymathnetwork/hashicorp-vault-signing-manager": "^1.1.0",
33+
"@polymathnetwork/local-signing-manager": "^1.0.5",
34+
"@polymathnetwork/polymesh-sdk": "14.0.0-alpha.4",
35+
"@polymathnetwork/signing-manager-types": "^1.0.3",
3336
"class-transformer": "0.4.0",
3437
"class-validator": "0.13.1",
3538
"joi": "17.4.0",
@@ -66,7 +69,7 @@
6669
"husky": "6.0.0",
6770
"jest": "26.6.3",
6871
"lint-staged": "11.0.0",
69-
"prettier": "2.2.1",
72+
"prettier": "2.3.1",
7073
"prettier-eslint": "12.0.0",
7174
"prettier-eslint-cli": "5.0.1",
7275
"supertest": "6.1.3",

β€Žsrc/accounts/accounts.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { Module } from '@nestjs/common';
55
import { AccountsController } from '~/accounts/accounts.controller';
66
import { AccountsService } from '~/accounts/accounts.service';
77
import { PolymeshModule } from '~/polymesh/polymesh.module';
8-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
8+
import { SigningModule } from '~/signing/signing.module';
99

1010
@Module({
11-
imports: [PolymeshModule, RelayerAccountsModule],
11+
imports: [PolymeshModule, SigningModule],
1212
controllers: [AccountsController],
1313
providers: [AccountsService],
1414
})

β€Žsrc/accounts/accounts.service.spec.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import { TransactionType } from '~/common/types';
1212
import { POLYMESH_API } from '~/polymesh/polymesh.consts';
1313
import { PolymeshModule } from '~/polymesh/polymesh.module';
1414
import { PolymeshService } from '~/polymesh/polymesh.service';
15-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
16-
import { RelayerAccountsService } from '~/relayer-accounts/relayer-accounts.service';
15+
import { mockSigningProvider } from '~/signing/signing.mock';
16+
import { SigningModule } from '~/signing/signing.module';
1717
import { MockPolymesh, MockTransactionQueue } from '~/test-utils/mocks';
18-
import { MockRelayerAccountsService } from '~/test-utils/service-mocks';
18+
import { MockSigningService } from '~/test-utils/service-mocks';
1919
import { ErrorCase } from '~/test-utils/types';
2020

2121
jest.mock('@polymathnetwork/polymesh-sdk/utils', () => ({
@@ -28,20 +28,18 @@ describe('AccountsService', () => {
2828
let service: AccountsService;
2929
let polymeshService: PolymeshService;
3030
let mockPolymeshApi: MockPolymesh;
31-
let mockRelayerAccountsService: MockRelayerAccountsService;
31+
let mockSigningService: MockSigningService;
3232

3333
beforeEach(async () => {
3434
mockPolymeshApi = new MockPolymesh();
35-
mockRelayerAccountsService = new MockRelayerAccountsService();
35+
mockSigningService = mockSigningProvider.useValue;
3636

3737
const module: TestingModule = await Test.createTestingModule({
38-
imports: [PolymeshModule, RelayerAccountsModule],
39-
providers: [AccountsService],
38+
imports: [PolymeshModule, SigningModule],
39+
providers: [AccountsService, mockSigningProvider],
4040
})
4141
.overrideProvider(POLYMESH_API)
4242
.useValue(mockPolymeshApi)
43-
.overrideProvider(RelayerAccountsService)
44-
.useValue(mockRelayerAccountsService)
4543
.compile();
4644

4745
service = module.get<AccountsService>(AccountsService);
@@ -113,8 +111,8 @@ describe('AccountsService', () => {
113111
memo: 'Sample memo',
114112
};
115113

116-
const address = 'address';
117-
mockRelayerAccountsService.findAddressByDid.mockReturnValue(address);
114+
const someKey = 'someKey';
115+
mockSigningService.getAddressByHandle.mockReturnValue(someKey);
118116

119117
mockPolymeshApi.network.transferPolyx.mockImplementation(() => {
120118
throw polymeshError;
@@ -154,8 +152,8 @@ describe('AccountsService', () => {
154152
memo: 'Sample memo',
155153
};
156154

157-
const address = 'address';
158-
mockRelayerAccountsService.findAddressByDid.mockReturnValue(address);
155+
const keyName = 'someKey';
156+
mockSigningService.getAddressByHandle.mockReturnValue(keyName);
159157

160158
const result = await service.transferPolyx(body);
161159
expect(result).toEqual({

β€Žsrc/accounts/accounts.service.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { AccountBalance } from '@polymathnetwork/polymesh-sdk/types';
44
import { TransferPolyxDto } from '~/accounts/dto/transfer-polyx.dto';
55
import { processQueue, QueueResult } from '~/common/utils';
66
import { PolymeshService } from '~/polymesh/polymesh.service';
7-
import { RelayerAccountsService } from '~/relayer-accounts/relayer-accounts.service';
7+
import { SigningService } from '~/signing/signing.service';
88

99
@Injectable()
1010
export class AccountsService {
1111
constructor(
1212
private readonly polymeshService: PolymeshService,
13-
private readonly relayerAccountsService: RelayerAccountsService
13+
private readonly signingService: SigningService
1414
) {}
1515

1616
public async getAccountBalance(account: string): Promise<AccountBalance> {
@@ -22,12 +22,12 @@ export class AccountsService {
2222

2323
public async transferPolyx(params: TransferPolyxDto): Promise<QueueResult<void>> {
2424
const { signer, ...rest } = params;
25-
const { relayerAccountsService, polymeshService } = this;
25+
const { signingService, polymeshService } = this;
2626

27-
const address = relayerAccountsService.findAddressByDid(signer);
27+
const address = await signingService.getAddressByHandle(signer);
2828

2929
const { transferPolyx } = polymeshService.polymeshApi.network;
3030

31-
return processQueue(transferPolyx, rest, { signer: address });
31+
return processQueue(transferPolyx, rest, { signingAccount: address });
3232
}
3333
}

β€Žsrc/app.module.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import { IdentitiesModule } from '~/identities/identities.module';
1515
import { OfferingsModule } from '~/offerings/offerings.module';
1616
import { PolymeshModule } from '~/polymesh/polymesh.module';
1717
import { PortfoliosModule } from '~/portfolios/portfolios.module';
18-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
1918
import { SettlementsModule } from '~/settlements/settlements.module';
19+
import { SigningModule } from '~/signing/signing.module';
2020

2121
@Module({
2222
imports: [
@@ -26,13 +26,20 @@ import { SettlementsModule } from '~/settlements/settlements.module';
2626
POLYMESH_NODE_URL: Joi.required(),
2727
POLYMESH_MIDDLEWARE_URL: Joi.string(),
2828
POLYMESH_MIDDLEWARE_API_KEY: Joi.string(),
29-
}).and('POLYMESH_MIDDLEWARE_URL', 'POLYMESH_MIDDLEWARE_API_KEY'),
29+
LOCAL_SIGNERS: Joi.string(),
30+
LOCAL_MNEMONICS: Joi.string(),
31+
VAULT_TOKEN: Joi.string(),
32+
VAULT_URL: Joi.string(),
33+
})
34+
.and('POLYMESH_MIDDLEWARE_URL', 'POLYMESH_MIDDLEWARE_API_KEY')
35+
.and('LOCAL_SIGNERS', 'LOCAL_MNEMONICS')
36+
.nand('LOCAL_SIGNERS', 'VAULT_TOKEN'),
3037
}),
3138
AssetsModule,
3239
PolymeshModule,
3340
IdentitiesModule,
3441
SettlementsModule,
35-
RelayerAccountsModule,
42+
SigningModule,
3643
AuthorizationsModule,
3744
PortfoliosModule,
3845
ClaimsModule,

β€Žsrc/assets/assets.controller.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ export class AssetsController {
8989
@Param() { ticker }: TickerParamsDto,
9090
@Query() { size, start }: PaginatedParamsDto
9191
): Promise<PaginatedResultsModel<IdentityBalanceModel>> {
92-
const { data, count: total, next } = await this.assetsService.findHolders(
93-
ticker,
94-
size,
95-
start?.toString()
96-
);
92+
const {
93+
data,
94+
count: total,
95+
next,
96+
} = await this.assetsService.findHolders(ticker, size, start?.toString());
9797

9898
return new PaginatedResultsModel({
9999
results: data.map(
@@ -141,11 +141,11 @@ export class AssetsController {
141141
@Param() { ticker }: TickerParamsDto,
142142
@Query() { size, start }: PaginatedParamsDto
143143
): Promise<PaginatedResultsModel<AssetDocumentModel>> {
144-
const { data, count: total, next } = await this.assetsService.findDocuments(
145-
ticker,
146-
size,
147-
start?.toString()
148-
);
144+
const {
145+
data,
146+
count: total,
147+
next,
148+
} = await this.assetsService.findDocuments(ticker, size, start?.toString());
149149

150150
return new PaginatedResultsModel({
151151
results: data.map(

β€Žsrc/assets/assets.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { AssetsController } from '~/assets/assets.controller';
66
import { AssetsService } from '~/assets/assets.service';
77
import { ComplianceModule } from '~/compliance/compliance.module';
88
import { PolymeshModule } from '~/polymesh/polymesh.module';
9-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
9+
import { SigningModule } from '~/signing/signing.module';
1010

1111
@Module({
12-
imports: [PolymeshModule, RelayerAccountsModule, forwardRef(() => ComplianceModule)],
12+
imports: [PolymeshModule, SigningModule, forwardRef(() => ComplianceModule)],
1313
controllers: [AssetsController],
1414
providers: [AssetsService],
1515
exports: [AssetsService],

β€Žsrc/assets/assets.service.spec.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ import { TransactionType } from '~/common/types';
1313
import { POLYMESH_API } from '~/polymesh/polymesh.consts';
1414
import { PolymeshModule } from '~/polymesh/polymesh.module';
1515
import { PolymeshService } from '~/polymesh/polymesh.service';
16-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
17-
import { RelayerAccountsService } from '~/relayer-accounts/relayer-accounts.service';
16+
import { mockSigningProvider } from '~/signing/signing.mock';
1817
import { MockAsset, MockPolymesh, MockTransactionQueue } from '~/test-utils/mocks';
19-
import { MockRelayerAccountsService } from '~/test-utils/service-mocks';
18+
import { MockSigningService } from '~/test-utils/service-mocks';
2019

2120
jest.mock('@polymathnetwork/polymesh-sdk/utils', () => ({
2221
...jest.requireActual('@polymathnetwork/polymesh-sdk/utils'),
@@ -28,19 +27,17 @@ describe('AssetsService', () => {
2827
let service: AssetsService;
2928
let polymeshService: PolymeshService;
3029
let mockPolymeshApi: MockPolymesh;
31-
let mockRelayerAccountsService: MockRelayerAccountsService;
30+
let mockSigningService: MockSigningService;
3231

3332
beforeEach(async () => {
3433
mockPolymeshApi = new MockPolymesh();
35-
mockRelayerAccountsService = new MockRelayerAccountsService();
34+
mockSigningService = new MockSigningService();
3635
const module: TestingModule = await Test.createTestingModule({
37-
imports: [PolymeshModule, RelayerAccountsModule],
38-
providers: [AssetsService],
36+
imports: [PolymeshModule],
37+
providers: [AssetsService, mockSigningProvider],
3938
})
4039
.overrideProvider(POLYMESH_API)
4140
.useValue(mockPolymeshApi)
42-
.overrideProvider(RelayerAccountsService)
43-
.useValue(mockRelayerAccountsService)
4441
.compile();
4542

4643
service = module.get<AssetsService>(AssetsService);
@@ -333,7 +330,7 @@ describe('AssetsService', () => {
333330
throw expectedError;
334331
});
335332

336-
mockRelayerAccountsService.findAddressByDid.mockReturnValue('address');
333+
mockSigningService.getAddressByHandle.mockReturnValue('address');
337334

338335
let error;
339336
try {
@@ -361,7 +358,7 @@ describe('AssetsService', () => {
361358
mockPolymeshApi.assets.createAsset.mockResolvedValue(mockQueue);
362359

363360
const address = 'address';
364-
mockRelayerAccountsService.findAddressByDid.mockReturnValue(address);
361+
mockSigningService.getAddressByHandle.mockReturnValue(address);
365362
const result = await service.createAsset(createBody);
366363
expect(result).toEqual({
367364
result: mockAsset,
@@ -403,7 +400,7 @@ describe('AssetsService', () => {
403400
findSpy.mockResolvedValue(mockAsset as any);
404401

405402
const address = 'address';
406-
mockRelayerAccountsService.findAddressByDid.mockReturnValue(address);
403+
mockSigningService.getAddressByHandle.mockReturnValue(address);
407404
const result = await service.issue('TICKER', issueBody);
408405
expect(result).toEqual({
409406
result: undefined,
@@ -442,7 +439,7 @@ describe('AssetsService', () => {
442439
};
443440

444441
const address = 'address';
445-
mockRelayerAccountsService.findAddressByDid.mockReturnValue(address);
442+
mockSigningService.getAddressByHandle.mockReturnValue(address);
446443
const result = await service.registerTicker(registerBody);
447444
expect(result).toEqual({
448445
result: undefined,

β€Žsrc/assets/assets.service.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import { IssueDto } from '~/assets/dto/issue.dto';
1515
import { ReserveTickerDto as RegisterTickerDto } from '~/assets/dto/reserve-ticker.dto';
1616
import { processQueue, QueueResult } from '~/common/utils';
1717
import { PolymeshService } from '~/polymesh/polymesh.service';
18-
import { RelayerAccountsService } from '~/relayer-accounts/relayer-accounts.service';
18+
import { SigningService } from '~/signing/signing.service';
1919

2020
@Injectable()
2121
export class AssetsService {
2222
constructor(
2323
private readonly polymeshService: PolymeshService,
24-
private readonly relayerAccountsService: RelayerAccountsService
24+
private readonly signingService: SigningService
2525
) {}
2626

2727
public async findOne(ticker: string): Promise<Asset> {
@@ -75,23 +75,23 @@ export class AssetsService {
7575

7676
public async registerTicker(params: RegisterTickerDto): Promise<QueueResult<TickerReservation>> {
7777
const { signer, ...rest } = params;
78-
const address = this.relayerAccountsService.findAddressByDid(signer);
78+
const address = await this.signingService.getAddressByHandle(signer);
7979
const reserveTicker = this.polymeshService.polymeshApi.assets.reserveTicker;
80-
return processQueue(reserveTicker, rest, { signer: address });
80+
return processQueue(reserveTicker, rest, { signingAccount: address });
8181
}
8282

8383
public async createAsset(params: CreateAssetDto): Promise<QueueResult<Asset>> {
8484
const { signer, ...rest } = params;
85-
const address = this.relayerAccountsService.findAddressByDid(signer);
85+
const signingAccount = await this.signingService.getAddressByHandle(signer);
8686
const createAsset = this.polymeshService.polymeshApi.assets.createAsset;
87-
return processQueue(createAsset, rest, { signer: address });
87+
return processQueue(createAsset, rest, { signingAccount });
8888
}
8989

9090
public async issue(ticker: string, params: IssueDto): Promise<QueueResult<Asset>> {
9191
const { signer, ...rest } = params;
9292
const asset = await this.findOne(ticker);
93-
const address = this.relayerAccountsService.findAddressByDid(signer);
94-
return processQueue(asset.issuance.issue, rest, { signer: address });
93+
const address = await this.signingService.getAddressByHandle(signer);
94+
return processQueue(asset.issuance.issue, rest, { signingAccount: address });
9595
}
9696

9797
public async findTickerReservation(ticker: string): Promise<TickerReservation> {

β€Žsrc/assets/assets.util.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ import { AssetDetailsModel } from '~/assets/models/asset-details.model';
88
* Fetch and assemble data for an Asset
99
*/
1010
export async function createAssetDetailsModel(asset: Asset): Promise<AssetDetailsModel> {
11-
const [
12-
{ owner, assetType, name, totalSupply, isDivisible },
13-
securityIdentifiers,
14-
fundingRound,
15-
] = await Promise.all([asset.details(), asset.getIdentifiers(), asset.currentFundingRound()]);
11+
const [{ owner, assetType, name, totalSupply, isDivisible }, securityIdentifiers, fundingRound] =
12+
await Promise.all([asset.details(), asset.getIdentifiers(), asset.currentFundingRound()]);
1613

1714
return new AssetDetailsModel({
1815
owner,

β€Žsrc/authorizations/authorizations.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { AuthorizationsController } from '~/authorizations/authorizations.contro
66
import { AuthorizationsService } from '~/authorizations/authorizations.service';
77
import { IdentitiesModule } from '~/identities/identities.module';
88
import { PolymeshModule } from '~/polymesh/polymesh.module';
9-
import { RelayerAccountsModule } from '~/relayer-accounts/relayer-accounts.module';
9+
import { SigningModule } from '~/signing/signing.module';
1010

1111
@Module({
12-
imports: [PolymeshModule, RelayerAccountsModule, forwardRef(() => IdentitiesModule)],
12+
imports: [PolymeshModule, SigningModule, forwardRef(() => IdentitiesModule)],
1313
providers: [AuthorizationsService],
1414
exports: [AuthorizationsService],
1515
controllers: [AuthorizationsController],

0 commit comments

Comments
Β (0)