@canton-network/wallet-gateway-remote 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/jwt-auth-service.d.ts.map +1 -1
- package/dist/auth/jwt-auth-service.js +6 -5
- package/dist/auth/jwt-auth-service.test.d.ts +2 -0
- package/dist/auth/jwt-auth-service.test.d.ts.map +1 -0
- package/dist/auth/jwt-auth-service.test.js +259 -0
- package/dist/auth/jwt-unsafe-auth-service.test.d.ts +2 -0
- package/dist/auth/jwt-unsafe-auth-service.test.d.ts.map +1 -0
- package/dist/auth/jwt-unsafe-auth-service.test.js +120 -0
- package/dist/config/Config.d.ts +69 -4
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +6 -0
- package/dist/config/ConfigUtils.d.ts.map +1 -1
- package/dist/config/ConfigUtils.js +3 -0
- package/dist/dapp-api/controller.d.ts +5 -1
- package/dist/dapp-api/controller.d.ts.map +1 -1
- package/dist/dapp-api/controller.js +104 -30
- package/dist/dapp-api/controller.test.js +2 -2
- package/dist/dapp-api/server.d.ts +3 -2
- package/dist/dapp-api/server.d.ts.map +1 -1
- package/dist/dapp-api/server.js +2 -2
- package/dist/dapp-api/server.test.js +1 -6
- package/dist/example-config.d.ts +3 -0
- package/dist/example-config.d.ts.map +1 -1
- package/dist/example-config.js +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +19 -7
- package/dist/ledger/transaction-service.d.ts +16 -11
- package/dist/ledger/transaction-service.d.ts.map +1 -1
- package/dist/ledger/transaction-service.js +135 -8
- package/dist/ledger/transaction-service.test.d.ts +2 -0
- package/dist/ledger/transaction-service.test.d.ts.map +1 -0
- package/dist/ledger/transaction-service.test.js +390 -0
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.d.ts +5 -2
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.d.ts.map +1 -1
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.js +7 -6
- package/dist/ledger/wallet-allocation/wallet-allocation-service.d.ts +11 -5
- package/dist/ledger/wallet-allocation/wallet-allocation-service.d.ts.map +1 -1
- package/dist/ledger/wallet-allocation/wallet-allocation-service.js +28 -14
- package/dist/ledger/wallet-allocation/wallet-allocation-service.test.js +550 -19
- package/dist/ledger/wallet-sync-service.js +3 -3
- package/dist/middleware/apiKeyAuth.d.ts +6 -0
- package/dist/middleware/apiKeyAuth.d.ts.map +1 -0
- package/dist/middleware/apiKeyAuth.js +90 -0
- package/dist/middleware/apiKeyAuth.test.d.ts +2 -0
- package/dist/middleware/apiKeyAuth.test.d.ts.map +1 -0
- package/dist/middleware/apiKeyAuth.test.js +126 -0
- package/dist/middleware/jsonRpcHandler.d.ts +11 -1
- package/dist/middleware/jsonRpcHandler.d.ts.map +1 -1
- package/dist/middleware/jsonRpcHandler.js +14 -15
- package/dist/middleware/jsonRpcHandler.test.d.ts +2 -0
- package/dist/middleware/jsonRpcHandler.test.d.ts.map +1 -0
- package/dist/middleware/jsonRpcHandler.test.js +254 -0
- package/dist/middleware/jwtAuth.d.ts.map +1 -1
- package/dist/middleware/jwtAuth.js +4 -0
- package/dist/middleware/jwtAuth.test.d.ts +2 -0
- package/dist/middleware/jwtAuth.test.d.ts.map +1 -0
- package/dist/middleware/jwtAuth.test.js +93 -0
- package/dist/middleware/rateLimit.test.js +12 -0
- package/dist/middleware/sessionHandler.js +1 -1
- package/dist/middleware/sessionHandler.test.d.ts +2 -0
- package/dist/middleware/sessionHandler.test.d.ts.map +1 -0
- package/dist/middleware/sessionHandler.test.js +121 -0
- package/dist/notification/NotificationService.test.d.ts +2 -0
- package/dist/notification/NotificationService.test.d.ts.map +1 -0
- package/dist/notification/NotificationService.test.js +110 -0
- package/dist/signing/service-account-session.d.ts +16 -0
- package/dist/signing/service-account-session.d.ts.map +1 -0
- package/dist/signing/service-account-session.js +25 -0
- package/dist/signing/service-account-session.test.d.ts +2 -0
- package/dist/signing/service-account-session.test.d.ts.map +1 -0
- package/dist/signing/service-account-session.test.js +89 -0
- package/dist/signing/signing-drivers.d.ts +7 -0
- package/dist/signing/signing-drivers.d.ts.map +1 -0
- package/dist/signing/signing-drivers.js +3 -0
- package/dist/signing/signing-worker.d.ts +83 -0
- package/dist/signing/signing-worker.d.ts.map +1 -0
- package/dist/signing/signing-worker.js +206 -0
- package/dist/signing/signing-worker.test.d.ts +2 -0
- package/dist/signing/signing-worker.test.d.ts.map +1 -0
- package/dist/signing/signing-worker.test.js +152 -0
- package/dist/user-api/controller.d.ts +6 -4
- package/dist/user-api/controller.d.ts.map +1 -1
- package/dist/user-api/controller.js +119 -95
- package/dist/user-api/controller.test.d.ts +2 -0
- package/dist/user-api/controller.test.d.ts.map +1 -0
- package/dist/user-api/controller.test.js +862 -0
- package/dist/user-api/rpc-gen/index.d.ts +12 -0
- package/dist/user-api/rpc-gen/index.d.ts.map +1 -1
- package/dist/user-api/rpc-gen/index.js +4 -0
- package/dist/user-api/rpc-gen/typings.d.ts +52 -3
- package/dist/user-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/utils.d.ts +17 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +53 -2
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +24 -0
- package/dist/web/frontend/404/index.html +2 -2
- package/dist/web/frontend/activities/index.html +3 -3
- package/dist/web/frontend/approve/index.html +4 -4
- package/dist/web/frontend/assets/{404-C7V2yl2z.js → 404-C4hCL2o8.js} +2 -2
- package/dist/web/frontend/assets/{activities-NY8oZAkH.js → activities-Bq7yMMQY.js} +2 -2
- package/dist/web/frontend/assets/addIdentityProvider-DRJWLhHF.js +28 -0
- package/dist/web/frontend/assets/addNetwork-CJUaKP1q.js +38 -0
- package/dist/web/frontend/assets/addParty-2ObTLGIL.js +45 -0
- package/dist/web/frontend/assets/approve-BuA5DGC2.js +21 -0
- package/dist/web/frontend/assets/callback-C5vJWFLq.js +1 -0
- package/dist/web/frontend/assets/{identityProviders-BZ9h_VOJ.js → identityProviders-78FbpzYL.js} +2 -2
- package/dist/web/frontend/assets/{index-B-HiKugW.js → index-BTH_md6b.js} +3 -3
- package/dist/web/frontend/assets/index-CFnOucPK.js +4737 -0
- package/dist/web/frontend/assets/{login-CqwMEG9J.js → login-Dyn1o6fW.js} +2 -2
- package/dist/web/frontend/assets/{networks-BJvRi47M.js → networks-_el10AWP.js} +2 -2
- package/dist/web/frontend/assets/reviewIdentityProvider-DodUZ2mY.js +43 -0
- package/dist/web/frontend/assets/reviewNetwork-BFMtmKEL.js +43 -0
- package/dist/web/frontend/assets/settings-BBmkIfYm.js +37 -0
- package/dist/web/frontend/assets/{state-qAsNw5qf.js → state-DkxLvywv.js} +1 -1
- package/dist/web/frontend/assets/{utils-BDkHxi8V.js → utils-DvJKIYxT.js} +1 -1
- package/dist/web/frontend/callback/index.html +2 -2
- package/dist/web/frontend/identity-providers/add/index.html +3 -3
- package/dist/web/frontend/identity-providers/index.html +3 -3
- package/dist/web/frontend/identity-providers/review/index.html +3 -3
- package/dist/web/frontend/index.html +1 -1
- package/dist/web/frontend/login/index.html +3 -3
- package/dist/web/frontend/networks/add/index.html +3 -3
- package/dist/web/frontend/networks/index.html +3 -3
- package/dist/web/frontend/networks/review/index.html +3 -3
- package/dist/web/frontend/parties/add/index.html +5 -5
- package/dist/web/frontend/parties/index.html +4 -4
- package/dist/web/frontend/settings/index.html +3 -3
- package/package.json +36 -32
- package/dist/web/frontend/assets/addIdentityProvider-38ghz3RT.js +0 -28
- package/dist/web/frontend/assets/addNetwork-BaDhbrZl.js +0 -38
- package/dist/web/frontend/assets/addParty-j3JWZX5a.js +0 -41
- package/dist/web/frontend/assets/approve-43nVzAAC.js +0 -21
- package/dist/web/frontend/assets/callback-BBMiCjHR.js +0 -1
- package/dist/web/frontend/assets/index-DoNqknyE.js +0 -4042
- package/dist/web/frontend/assets/reviewIdentityProvider-ySlQnw5R.js +0 -43
- package/dist/web/frontend/assets/reviewNetwork-DrVxeHd4.js +0 -43
- package/dist/web/frontend/assets/settings-BVNkaUyJ.js +0 -37
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { TransactionService } from '../ledger/transaction-service.js';
|
|
4
|
+
import { resolveAutomationRunContext } from './service-account-session.js';
|
|
5
|
+
/**
|
|
6
|
+
* Background poller that completes service-account transactions stuck in external
|
|
7
|
+
* custody approval.
|
|
8
|
+
*
|
|
9
|
+
* ## When this runs
|
|
10
|
+
*
|
|
11
|
+
* During M2M automation (`prepareExecute` → `signAndExecute`), external signing
|
|
12
|
+
* providers (Fireblocks, Blockdaemon, Dfns) may return `pending` instead of a
|
|
13
|
+
* signature. The gateway persists the transaction with `status: 'pending'` and
|
|
14
|
+
* an `externalTxId` from the provider. This worker picks up those rows on each
|
|
15
|
+
* tick via {@link Store.listAllPendingTransactions} (rows with an
|
|
16
|
+
* `externalTxId` are processed; others are skipped).
|
|
17
|
+
*
|
|
18
|
+
* Participant and wallet-kernel sign synchronously and never appear here.
|
|
19
|
+
*
|
|
20
|
+
* ## What happens on each tick
|
|
21
|
+
*
|
|
22
|
+
* For every pending row the worker resolves an M2M auth context
|
|
23
|
+
* ({@link resolveAutomationRunContext}), loads the user's primary wallet, and
|
|
24
|
+
* calls {@link TransactionService.signAndExecute}.
|
|
25
|
+
*
|
|
26
|
+
* Although the worker always invokes `signAndExecute`, **retries do not submit a
|
|
27
|
+
* new sign request**. `TransactionService.sign()` checks whether the stored
|
|
28
|
+
* transaction already has an `externalTxId`:
|
|
29
|
+
*
|
|
30
|
+
* - **First pass** (no `externalTxId`): `driver.signTransaction()` submits to
|
|
31
|
+
* custody and stores the returned `externalTxId`.
|
|
32
|
+
* - **Subsequent ticks** (`externalTxId` present): `driver.getTransaction()`
|
|
33
|
+
* polls the existing custody request for an updated status/signature.
|
|
34
|
+
*
|
|
35
|
+
* `signAndExecute` then:
|
|
36
|
+
*
|
|
37
|
+
* - returns early while the provider still reports `pending` (worker logs and
|
|
38
|
+
* waits for the next tick), or
|
|
39
|
+
* - calls `execute()` once signing is `signed` (Dfns is special: it broadcasts
|
|
40
|
+
* at sign-time, so execute only reconciles local state).
|
|
41
|
+
*
|
|
42
|
+
* A transaction is re-read before processing so a concurrent DApp call or an
|
|
43
|
+
* earlier tick that already completed it is skipped.
|
|
44
|
+
*
|
|
45
|
+
* ## Lifecycle
|
|
46
|
+
*
|
|
47
|
+
* Started once at gateway init (`start()`). Ticks are serialized: if a tick is
|
|
48
|
+
* still running, the next interval callback is a no-op. Use `stop()` on shutdown.
|
|
49
|
+
*/
|
|
50
|
+
export class SigningWorker {
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.options = options;
|
|
53
|
+
this.running = false;
|
|
54
|
+
}
|
|
55
|
+
/** Starts the interval timer. Idempotent if already started. */
|
|
56
|
+
start() {
|
|
57
|
+
this.options.logger.info('Starting signing worker');
|
|
58
|
+
if (this.timer) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.timer = setInterval(() => {
|
|
62
|
+
void this.tick();
|
|
63
|
+
}, this.options.intervalMs);
|
|
64
|
+
this.options.logger.info({ intervalMs: this.options.intervalMs }, 'Signing worker started');
|
|
65
|
+
}
|
|
66
|
+
/** Clears the interval timer. */
|
|
67
|
+
stop() {
|
|
68
|
+
if (this.timer) {
|
|
69
|
+
clearInterval(this.timer);
|
|
70
|
+
this.timer = undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* One poll cycle: list all pending transactions and attempt to complete
|
|
75
|
+
* external-custody rows via {@link processPending}. Safe to call directly in
|
|
76
|
+
* tests.
|
|
77
|
+
*/
|
|
78
|
+
async tick() {
|
|
79
|
+
this.options.logger.trace(`Signing worker: executing a tick`);
|
|
80
|
+
if (this.running) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.running = true;
|
|
84
|
+
try {
|
|
85
|
+
const pending = await this.options.store.listAllPendingTransactions();
|
|
86
|
+
for (const tx of pending) {
|
|
87
|
+
if (!tx.externalTxId) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
await this.processPending(tx);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
this.options.logger.error({ err: error }, 'Signing worker tick failed');
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
this.running = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Drives a single pending transaction through `signAndExecute`. Skips when
|
|
102
|
+
* automation context, submitting wallet, or refreshed `pending` status is missing.
|
|
103
|
+
* Errors are logged per transaction; other rows in the same tick still run.
|
|
104
|
+
*/
|
|
105
|
+
async processPending(transaction) {
|
|
106
|
+
const logger = this.options.logger;
|
|
107
|
+
const { userId, networkId, externalTxId } = transaction;
|
|
108
|
+
if (!externalTxId || !userId || !networkId) {
|
|
109
|
+
logger.debug({ transaction }, 'Skipping signing worker tick: missing required fields');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const runContext = await resolveAutomationRunContext(this.options.store, userId, networkId, logger);
|
|
113
|
+
if (!runContext) {
|
|
114
|
+
logger.debug({ runContext, transactionId: transaction.id }, 'Skipping signing worker tick: no run context');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const store = runContext.scopedStore;
|
|
118
|
+
const wallet = await resolveWalletFromTransaction(transaction, store, logger);
|
|
119
|
+
if (!wallet) {
|
|
120
|
+
logger.warn({
|
|
121
|
+
userId,
|
|
122
|
+
networkId,
|
|
123
|
+
transactionId: transaction.id,
|
|
124
|
+
commandId: transaction.commandId,
|
|
125
|
+
}, 'Skipping signing worker tick: could not deduce submitting wallet from transaction payload');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const refreshedTx = await store.getTransaction(transaction.id);
|
|
129
|
+
if (!refreshedTx || refreshedTx.status !== 'pending') {
|
|
130
|
+
logger.debug({ refreshedTx }, 'Skipping signing worker tick: transaction no longer pending');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const notifier = this.options.notificationService.getNotifier(userId);
|
|
134
|
+
const transactionLogger = this.options.logger.child({
|
|
135
|
+
component: 'TransactionService',
|
|
136
|
+
});
|
|
137
|
+
const transactionService = new TransactionService(store, transactionLogger, this.options.signingDrivers, notifier);
|
|
138
|
+
try {
|
|
139
|
+
const result = await transactionService.signAndExecute(runContext.authContext, runContext.network, wallet, refreshedTx);
|
|
140
|
+
if ('status' in result && result.status === 'pending') {
|
|
141
|
+
logger.info({
|
|
142
|
+
userId,
|
|
143
|
+
networkId,
|
|
144
|
+
transactionId: transaction.id,
|
|
145
|
+
commandId: transaction.commandId,
|
|
146
|
+
externalTxId: result.externalTxId,
|
|
147
|
+
signingProviderId: wallet.signingProviderId,
|
|
148
|
+
}, 'Signing worker: transaction still awaiting external signing');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
logger.info({
|
|
152
|
+
userId,
|
|
153
|
+
networkId,
|
|
154
|
+
transactionId: transaction.id,
|
|
155
|
+
commandId: transaction.commandId,
|
|
156
|
+
externalTxId: transaction.externalTxId,
|
|
157
|
+
partyId: wallet.partyId,
|
|
158
|
+
signingProviderId: wallet.signingProviderId,
|
|
159
|
+
result,
|
|
160
|
+
}, 'Signing worker completed service account transaction');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
logger.error({
|
|
165
|
+
err: error,
|
|
166
|
+
userId,
|
|
167
|
+
networkId,
|
|
168
|
+
transactionId: transaction.id,
|
|
169
|
+
commandId: transaction.commandId,
|
|
170
|
+
externalTxId: transaction.externalTxId,
|
|
171
|
+
partyId: wallet.partyId,
|
|
172
|
+
signingProviderId: wallet.signingProviderId,
|
|
173
|
+
}, 'Signing worker failed to complete service account transaction');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Given the transaction, attempt to resolve the original wallet that submitted it.
|
|
179
|
+
* It's not guaranteed that the primary wallet was used, so we check the `actAs` field in the payload and find a wallet with a matching party ID.
|
|
180
|
+
*
|
|
181
|
+
* Returns `undefined` if the wallet cannot be resolved.
|
|
182
|
+
*/
|
|
183
|
+
async function resolveWalletFromTransaction(tx, store, logger) {
|
|
184
|
+
logger.debug({ tx, store }, 'Resolving wallet from transaction payload');
|
|
185
|
+
// first see if we can determine the wallet from the payload's actAs field.
|
|
186
|
+
// If not, we will default to the primary wallet.
|
|
187
|
+
let wallet = undefined;
|
|
188
|
+
const payload = tx.payload;
|
|
189
|
+
if (payload && typeof payload === 'object') {
|
|
190
|
+
const actAs = payload.actAs;
|
|
191
|
+
if (Array.isArray(actAs) && actAs.length !== 0) {
|
|
192
|
+
if (actAs.length > 1) {
|
|
193
|
+
logger.warn({ actAs }, 'Transaction has multiple acting parties; using the first one to resolve wallet');
|
|
194
|
+
}
|
|
195
|
+
const partyId = actAs[0];
|
|
196
|
+
const wallets = await store.getAllWallets();
|
|
197
|
+
wallet = wallets.find((wallet) => wallet.partyId === partyId);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!wallet) {
|
|
201
|
+
// No actAs was given: lets default to the primary party.
|
|
202
|
+
wallet = await store.getPrimaryWallet();
|
|
203
|
+
logger.debug({ wallet, tx }, 'Transaction has no actAs; using primary wallet');
|
|
204
|
+
}
|
|
205
|
+
return wallet;
|
|
206
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing-worker.test.d.ts","sourceRoot":"","sources":["../../src/signing/signing-worker.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { pino } from 'pino';
|
|
5
|
+
import { sink } from 'pino-test';
|
|
6
|
+
import { SigningWorker } from './signing-worker.js';
|
|
7
|
+
import { SigningProvider } from '@canton-network/core-signing-lib';
|
|
8
|
+
const mocks = vi.hoisted(() => ({
|
|
9
|
+
signAndExecute: vi.fn().mockResolvedValue({ commandId: 'cmd-1' }),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock('@canton-network/core-wallet-auth', async () => {
|
|
12
|
+
const actual = await vi.importActual('@canton-network/core-wallet-auth');
|
|
13
|
+
return {
|
|
14
|
+
...actual,
|
|
15
|
+
AuthTokenProvider: {
|
|
16
|
+
fromGatewayConfig: vi.fn().mockReturnValue({
|
|
17
|
+
getAuthContext: vi.fn().mockResolvedValue({
|
|
18
|
+
userId: 'service_account',
|
|
19
|
+
accessToken: 'abc',
|
|
20
|
+
}),
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
vi.mock('../ledger/transaction-service.js', () => ({
|
|
26
|
+
TransactionService: vi.fn(function TransactionServiceMock() {
|
|
27
|
+
return { signAndExecute: mocks.signAndExecute };
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
const idp = {
|
|
31
|
+
id: 'idp1',
|
|
32
|
+
type: 'oauth',
|
|
33
|
+
issuer: 'https://issuer.example',
|
|
34
|
+
configUrl: 'https://issuer.example/.well-known/openid-configuration',
|
|
35
|
+
};
|
|
36
|
+
const m2mNetwork = {
|
|
37
|
+
id: 'net-m2m',
|
|
38
|
+
name: 'm2m',
|
|
39
|
+
description: '',
|
|
40
|
+
synchronizerId: 'sync::fp',
|
|
41
|
+
identityProviderId: 'idp1',
|
|
42
|
+
ledgerApi: { baseUrl: 'http://ledger' },
|
|
43
|
+
auth: {
|
|
44
|
+
method: 'authorization_code',
|
|
45
|
+
clientId: 'user',
|
|
46
|
+
audience: 'aud',
|
|
47
|
+
scope: 'scope',
|
|
48
|
+
},
|
|
49
|
+
serviceAccountAuth: {
|
|
50
|
+
method: 'client_credentials',
|
|
51
|
+
clientId: 'svc',
|
|
52
|
+
clientSecret: 'secret',
|
|
53
|
+
audience: 'aud',
|
|
54
|
+
scope: 'scope',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
const wallet = {
|
|
58
|
+
primary: true,
|
|
59
|
+
partyId: 'party::ns',
|
|
60
|
+
status: 'allocated',
|
|
61
|
+
hint: 'party',
|
|
62
|
+
publicKey: 'pk',
|
|
63
|
+
namespace: 'ns',
|
|
64
|
+
networkId: 'net-m2m',
|
|
65
|
+
signingProviderId: SigningProvider.FIREBLOCKS,
|
|
66
|
+
rights: [],
|
|
67
|
+
};
|
|
68
|
+
const aliceWallet = {
|
|
69
|
+
primary: false,
|
|
70
|
+
partyId: 'alice::ns2',
|
|
71
|
+
status: 'allocated',
|
|
72
|
+
hint: 'alice',
|
|
73
|
+
publicKey: 'pk2',
|
|
74
|
+
namespace: 'ns2',
|
|
75
|
+
networkId: 'net-m2m',
|
|
76
|
+
signingProviderId: SigningProvider.FIREBLOCKS,
|
|
77
|
+
rights: [],
|
|
78
|
+
};
|
|
79
|
+
const pendingTransaction = {
|
|
80
|
+
id: 'tx-1',
|
|
81
|
+
commandId: 'cmd-1',
|
|
82
|
+
status: 'pending',
|
|
83
|
+
preparedTransaction: 'blob',
|
|
84
|
+
preparedTransactionHash: 'hash',
|
|
85
|
+
externalTxId: 'ext-1',
|
|
86
|
+
origin: null,
|
|
87
|
+
userId: 'user-1',
|
|
88
|
+
networkId: 'net-m2m',
|
|
89
|
+
};
|
|
90
|
+
function createWorker(storeOverrides = {}) {
|
|
91
|
+
const scopedStore = {
|
|
92
|
+
setSession: vi.fn().mockResolvedValue(undefined),
|
|
93
|
+
getPrimaryWallet: vi.fn().mockResolvedValue(wallet),
|
|
94
|
+
getAllWallets: vi.fn().mockResolvedValue([wallet, aliceWallet]),
|
|
95
|
+
getTransaction: vi.fn().mockResolvedValue(pendingTransaction),
|
|
96
|
+
};
|
|
97
|
+
const store = {
|
|
98
|
+
listAllPendingTransactions: vi
|
|
99
|
+
.fn()
|
|
100
|
+
.mockResolvedValue([pendingTransaction]),
|
|
101
|
+
getIdp: vi.fn().mockResolvedValue(idp),
|
|
102
|
+
getNetwork: vi.fn().mockResolvedValue(m2mNetwork),
|
|
103
|
+
withAuthContext: vi.fn().mockReturnValue(scopedStore),
|
|
104
|
+
...storeOverrides,
|
|
105
|
+
};
|
|
106
|
+
const logger = pino({ level: 'silent' }, sink());
|
|
107
|
+
return {
|
|
108
|
+
worker: new SigningWorker({
|
|
109
|
+
intervalMs: 1000,
|
|
110
|
+
signingDrivers: {},
|
|
111
|
+
store: store,
|
|
112
|
+
notificationService: {
|
|
113
|
+
getNotifier: vi.fn(() => ({ emit: vi.fn() })),
|
|
114
|
+
},
|
|
115
|
+
logger,
|
|
116
|
+
}),
|
|
117
|
+
store,
|
|
118
|
+
scopedStore,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
describe('SigningWorker', () => {
|
|
122
|
+
afterEach(() => vi.clearAllMocks());
|
|
123
|
+
it('completes pending external transactions with a primary wallet', async () => {
|
|
124
|
+
const { worker } = createWorker();
|
|
125
|
+
await worker.tick();
|
|
126
|
+
expect(mocks.signAndExecute.mock.calls[0][2].partyId).toBe('party::ns');
|
|
127
|
+
});
|
|
128
|
+
it('uses a non-primary wallet when actAs payload is present', async () => {
|
|
129
|
+
const { worker } = createWorker({
|
|
130
|
+
listAllPendingTransactions: vi.fn().mockResolvedValue([
|
|
131
|
+
{
|
|
132
|
+
...pendingTransaction,
|
|
133
|
+
payload: { actAs: ['alice::ns2'] },
|
|
134
|
+
},
|
|
135
|
+
]),
|
|
136
|
+
});
|
|
137
|
+
await worker.tick();
|
|
138
|
+
expect(mocks.signAndExecute).toHaveBeenCalledOnce();
|
|
139
|
+
expect(mocks.signAndExecute.mock.calls[0][2].partyId).toBe('alice::ns2');
|
|
140
|
+
});
|
|
141
|
+
it('skips pending transactions without an externalTxId', async () => {
|
|
142
|
+
const { worker } = createWorker({
|
|
143
|
+
listAllPendingTransactions: vi
|
|
144
|
+
.fn()
|
|
145
|
+
.mockResolvedValue([
|
|
146
|
+
{ ...pendingTransaction, externalTxId: undefined },
|
|
147
|
+
]),
|
|
148
|
+
});
|
|
149
|
+
await worker.tick();
|
|
150
|
+
expect(mocks.signAndExecute).not.toHaveBeenCalled();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -3,9 +3,8 @@ import { Logger } from 'pino';
|
|
|
3
3
|
import { NotificationService } from '../notification/NotificationService.js';
|
|
4
4
|
import { AuthContext } from '@canton-network/core-wallet-auth';
|
|
5
5
|
import { KernelInfo } from '../config/Config.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export declare const userController: (kernelInfo: KernelInfo, userUrl: string, store: Store, notificationService: NotificationService, authContext: AuthContext | undefined, drivers: AvailableSigningDrivers, _logger: Logger, adminUserId?: string) => {
|
|
6
|
+
import type { SigningDrivers } from '../signing/signing-drivers.js';
|
|
7
|
+
export declare const userController: (kernelInfo: KernelInfo, userUrl: string, store: Store, notificationService: NotificationService, authContext: AuthContext | undefined, drivers: SigningDrivers, _logger: Logger, adminUserId?: string) => {
|
|
9
8
|
addNetwork: import("./rpc-gen/typings.js").AddNetwork;
|
|
10
9
|
removeNetwork: import("./rpc-gen/typings.js").RemoveNetwork;
|
|
11
10
|
listNetworks: import("./rpc-gen/typings.js").ListNetworks;
|
|
@@ -34,6 +33,9 @@ export declare const userController: (kernelInfo: KernelInfo, userUrl: string, s
|
|
|
34
33
|
listTransactions: import("./rpc-gen/typings.js").ListTransactions;
|
|
35
34
|
deleteTransaction: import("./rpc-gen/typings.js").DeleteTransaction;
|
|
36
35
|
getUser: import("./rpc-gen/typings.js").GetUser;
|
|
36
|
+
generateApiKey: import("./rpc-gen/typings.js").GenerateApiKey;
|
|
37
|
+
listApiKeys: import("./rpc-gen/typings.js").ListApiKeys;
|
|
38
|
+
removeApiKey: import("./rpc-gen/typings.js").RemoveApiKey;
|
|
39
|
+
listSigningProviderVaults: import("./rpc-gen/typings.js").ListSigningProviderVaults;
|
|
37
40
|
};
|
|
38
|
-
export {};
|
|
39
41
|
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,KAAK,EAAW,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAEH,WAAW,EAKd,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAYnE,eAAO,MAAM,cAAc,GACvB,YAAY,UAAU,EACtB,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,aAAa,WAAW,GAAG,SAAS,EACpC,SAAS,cAAc,EACvB,SAAS,MAAM,EACf,cAAc,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgiCvB,CAAA"}
|