@canton-network/wallet-gateway-remote 1.5.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/config/Config.d.ts +9 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +5 -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 +70 -26
- 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/init.d.ts.map +1 -1
- package/dist/init.js +16 -1
- package/dist/ledger/transaction-service.d.ts +2 -0
- package/dist/ledger/transaction-service.d.ts.map +1 -1
- package/dist/ledger/transaction-service.js +40 -0
- package/dist/ledger/transaction-service.test.js +34 -0
- package/dist/middleware/apiKeyAuth.d.ts.map +1 -1
- package/dist/middleware/apiKeyAuth.js +2 -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-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.map +1 -1
- package/dist/user-api/controller.js +8 -1
- 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-C4hCL2o8.js +5 -0
- package/dist/web/frontend/assets/{activities-DaWkmGqA.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-B3fFD1eM.js → addParty-2ObTLGIL.js} +1 -1
- package/dist/web/frontend/assets/{approve-WbGL4E82.js → approve-BuA5DGC2.js} +2 -2
- package/dist/web/frontend/assets/{callback--tihEryB.js → callback-C5vJWFLq.js} +1 -1
- package/dist/web/frontend/assets/{identityProviders-Dra4qG6t.js → identityProviders-78FbpzYL.js} +2 -2
- package/dist/web/frontend/assets/{index-Bm76pXA_.js → index-BTH_md6b.js} +3 -3
- package/dist/web/frontend/assets/index-CFnOucPK.js +4737 -0
- package/dist/web/frontend/assets/{login-BC8a-kD_.js → login-Dyn1o6fW.js} +2 -2
- package/dist/web/frontend/assets/{networks-B3ztsogL.js → networks-_el10AWP.js} +2 -2
- package/dist/web/frontend/assets/{reviewIdentityProvider-Co420Dbk.js → reviewIdentityProvider-DodUZ2mY.js} +3 -3
- 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-Bq65GwdF.js → state-DkxLvywv.js} +1 -1
- package/dist/web/frontend/assets/{utils-B2iziW8V.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 +20 -20
- package/dist/web/frontend/assets/404-MVLWUPwN.js +0 -5
- package/dist/web/frontend/assets/addIdentityProvider--rBb0ihb.js +0 -28
- package/dist/web/frontend/assets/addNetwork-C6UDsRkU.js +0 -38
- package/dist/web/frontend/assets/index-D1dC4Cl9.js +0 -4477
- package/dist/web/frontend/assets/reviewNetwork-B-Sh8pCH.js +0 -43
- package/dist/web/frontend/assets/settings-DvyUjqLl.js +0 -37
package/dist/config/Config.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export declare const serverConfigSchema: z.ZodObject<{
|
|
|
15
15
|
requestRateLimit: z.ZodDefault<z.ZodNumber>;
|
|
16
16
|
trustProxy: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodString]>>;
|
|
17
17
|
admin: z.ZodOptional<z.ZodString>;
|
|
18
|
+
signingWorker: z.ZodPreprocess<z.ZodObject<{
|
|
19
|
+
pollInterval: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
}, z.core.$strip>>;
|
|
18
21
|
}, z.core.$strip>;
|
|
19
22
|
export declare const rawConfigSchema: z.ZodObject<{
|
|
20
23
|
kernel: z.ZodObject<{
|
|
@@ -33,6 +36,9 @@ export declare const rawConfigSchema: z.ZodObject<{
|
|
|
33
36
|
requestRateLimit: z.ZodDefault<z.ZodNumber>;
|
|
34
37
|
trustProxy: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodString]>>;
|
|
35
38
|
admin: z.ZodOptional<z.ZodString>;
|
|
39
|
+
signingWorker: z.ZodPreprocess<z.ZodObject<{
|
|
40
|
+
pollInterval: z.ZodDefault<z.ZodNumber>;
|
|
41
|
+
}, z.core.$strip>>;
|
|
36
42
|
}, z.core.$strip>>;
|
|
37
43
|
logging: z.ZodOptional<z.ZodPreprocess<z.ZodObject<{
|
|
38
44
|
level: z.ZodOptional<z.ZodEnum<{
|
|
@@ -229,6 +235,9 @@ export declare const configSchema: z.ZodObject<{
|
|
|
229
235
|
requestRateLimit: z.ZodDefault<z.ZodNumber>;
|
|
230
236
|
trustProxy: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodString]>>;
|
|
231
237
|
admin: z.ZodOptional<z.ZodString>;
|
|
238
|
+
signingWorker: z.ZodPreprocess<z.ZodObject<{
|
|
239
|
+
pollInterval: z.ZodDefault<z.ZodNumber>;
|
|
240
|
+
}, z.core.$strip>>;
|
|
232
241
|
}, z.core.$strip>>;
|
|
233
242
|
logging: z.ZodOptional<z.ZodPreprocess<z.ZodObject<{
|
|
234
243
|
level: z.ZodOptional<z.ZodEnum<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/config/Config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AASvB,eAAO,MAAM,gBAAgB;;;;iBAY3B,CAAA;AAEF,eAAO,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/config/Config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AASvB,eAAO,MAAM,gBAAgB;;;;iBAY3B,CAAA;AAEF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;iBA0D7B,CAAA;AAkCF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO1B,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOvB,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAC7D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AACvD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA"}
|
package/dist/config/Config.js
CHANGED
|
@@ -57,6 +57,11 @@ export const serverConfigSchema = z.object({
|
|
|
57
57
|
admin: z.string().optional().meta({
|
|
58
58
|
description: 'The JWT claim (e.g. "sub") identifying the admin user. If set, requests with a matching claim will be granted admin privileges.',
|
|
59
59
|
}),
|
|
60
|
+
signingWorker: z.preprocess((val) => val ?? {}, z.object({
|
|
61
|
+
pollInterval: z.number().int().positive().default(5000).meta({
|
|
62
|
+
description: 'Interval in milliseconds for the signing worker to poll external signing providers on pending transactions. Defaults to 5000.',
|
|
63
|
+
}),
|
|
64
|
+
})),
|
|
60
65
|
});
|
|
61
66
|
const loggingConfigSchema = z
|
|
62
67
|
.object({
|
|
@@ -3,7 +3,11 @@ import { Store } from '@canton-network/core-wallet-store';
|
|
|
3
3
|
import { NotificationService } from '../notification/NotificationService.js';
|
|
4
4
|
import { KernelInfo as KernelInfoConfig } from '../config/Config.js';
|
|
5
5
|
import { Logger } from 'pino';
|
|
6
|
-
|
|
6
|
+
import { SigningDrivers } from '../signing/signing-drivers.js';
|
|
7
|
+
export interface DappControllerDeps {
|
|
8
|
+
signingDrivers: SigningDrivers;
|
|
9
|
+
}
|
|
10
|
+
export declare const dappController: (kernelInfo: KernelInfoConfig, dappUrl: string, userUrl: string, store: Store, notificationService: NotificationService, _logger: Logger, origin: string | null, deps: DappControllerDeps, context?: AuthContext) => {
|
|
7
11
|
status: import("./rpc-gen/typings.js").Status;
|
|
8
12
|
connect: import("./rpc-gen/typings.js").Connect;
|
|
9
13
|
disconnect: import("./rpc-gen/typings.js").Disconnect;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/dapp-api/controller.ts"],"names":[],"mappings":"AAGA,OAAO,EAEH,WAAW,EAEd,MAAM,kCAAkC,CAAA;AAczC,OAAO,EAAE,KAAK,EAAe,MAAM,mCAAmC,CAAA;AAQtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAK7B,eAAO,MAAM,cAAc,GACvB,YAAY,gBAAgB,EAC5B,SAAS,MAAM,EACf,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,SAAS,MAAM,EACf,QAAQ,MAAM,GAAG,IAAI,EACrB,UAAU,WAAW;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/dapp-api/controller.ts"],"names":[],"mappings":"AAGA,OAAO,EAEH,WAAW,EAEd,MAAM,kCAAkC,CAAA;AAczC,OAAO,EAAE,KAAK,EAAe,MAAM,mCAAmC,CAAA;AAQtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAK7B,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAG9D,MAAM,WAAW,kBAAkB;IAC/B,cAAc,EAAE,cAAc,CAAA;CACjC;AAED,eAAO,MAAM,cAAc,GACvB,YAAY,gBAAgB,EAC5B,SAAS,MAAM,EACf,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,SAAS,MAAM,EACf,QAAQ,MAAM,GAAG,IAAI,EACrB,MAAM,kBAAkB,EACxB,UAAU,WAAW;;;;;;;;;;;;;;;;CA2cxB,CAAA"}
|
|
@@ -5,9 +5,17 @@ import buildController from './rpc-gen/index.js';
|
|
|
5
5
|
import { LedgerClient, } from '@canton-network/core-ledger-client';
|
|
6
6
|
import { v4 } from 'uuid';
|
|
7
7
|
import { networkStatus, ledgerPrepareParams, logDynamically } from '../utils.js';
|
|
8
|
+
import { TransactionService } from '../ledger/transaction-service.js';
|
|
8
9
|
import { rpcErrors } from '@canton-network/core-rpc-errors';
|
|
9
|
-
export const dappController = (kernelInfo, dappUrl, userUrl, store, notificationService, _logger, origin, context) => {
|
|
10
|
+
export const dappController = (kernelInfo, dappUrl, userUrl, store, notificationService, _logger, origin, deps, context) => {
|
|
10
11
|
const logger = _logger.child({ component: 'dapp-controller' });
|
|
12
|
+
function assertActAsPartiesBelongToUser(actAs, wallets) {
|
|
13
|
+
for (const party of actAs) {
|
|
14
|
+
if (wallets.find((w) => w.partyId === party) === undefined) {
|
|
15
|
+
throw rpcErrors.invalidRequest(`Acting party ${party} does not belong to user`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
11
19
|
return buildController({
|
|
12
20
|
connect: async () => {
|
|
13
21
|
if (!context || !(await store.getSession())) {
|
|
@@ -127,54 +135,63 @@ export const dappController = (kernelInfo, dappUrl, userUrl, store, notification
|
|
|
127
135
|
return result;
|
|
128
136
|
},
|
|
129
137
|
prepareExecute: async (params) => {
|
|
130
|
-
const
|
|
138
|
+
const primaryWallet = await store.getPrimaryWallet();
|
|
131
139
|
const wallets = await store.getWallets();
|
|
132
140
|
const network = await store.getCurrentNetwork();
|
|
133
141
|
if (context === undefined) {
|
|
134
142
|
throw new Error('Unauthenticated context');
|
|
135
143
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let userId = context.userId;
|
|
144
|
+
// determine user ID
|
|
145
|
+
const gatewayUserId = context.userId;
|
|
146
|
+
let ledgerUserId = context.userId;
|
|
140
147
|
const accessTokenProvider = AuthTokenProvider.fromToken(context.accessToken, logger);
|
|
141
148
|
if (context?.isApiKey) {
|
|
142
149
|
logger.info('Authenticated with API Key, fetching m2m token for ledger access');
|
|
143
|
-
|
|
150
|
+
ledgerUserId = context.ledgerUserId;
|
|
151
|
+
}
|
|
152
|
+
// determine party ID
|
|
153
|
+
let actAs = params.actAs || [];
|
|
154
|
+
if (actAs.length === 0) {
|
|
155
|
+
if (!primaryWallet) {
|
|
156
|
+
throw new Error('No primary wallet found. Create or sync a wallet and set it as primary before prepareExecute.');
|
|
157
|
+
}
|
|
158
|
+
actAs = [primaryWallet.partyId];
|
|
159
|
+
}
|
|
160
|
+
assertActAsPartiesBelongToUser(actAs, wallets);
|
|
161
|
+
// determine wallet
|
|
162
|
+
const wallet = wallets.find((w) => w.partyId === actAs[0]);
|
|
163
|
+
if (wallet === undefined) {
|
|
164
|
+
throw new Error('No wallet found for the first acting party. Create or sync a wallet and set it as primary before prepareExecute.');
|
|
144
165
|
}
|
|
145
166
|
const ledgerClient = new LedgerClient({
|
|
146
167
|
baseUrl: new URL(network.ledgerApi.baseUrl),
|
|
147
168
|
logger,
|
|
148
169
|
accessTokenProvider,
|
|
149
170
|
});
|
|
150
|
-
const notifier = notificationService.getNotifier(
|
|
171
|
+
const notifier = notificationService.getNotifier(gatewayUserId);
|
|
151
172
|
const commandId = params.commandId || v4();
|
|
152
173
|
const transactionId = v4();
|
|
153
174
|
notifier.emit('txChanged', { status: 'pending', commandId });
|
|
154
175
|
const synchronizerId = network.synchronizerId ??
|
|
155
176
|
(await ledgerClient.getSynchronizerId());
|
|
156
|
-
let actAs = [wallet.partyId];
|
|
157
|
-
if (params.actAs && params.actAs.length > 0) {
|
|
158
|
-
actAs = params.actAs;
|
|
159
|
-
for (const actingParty of actAs) {
|
|
160
|
-
if (wallets.find((w) => w.partyId === actingParty) ===
|
|
161
|
-
undefined) {
|
|
162
|
-
throw rpcErrors.invalidRequest(`Acting party ${actingParty} does not belong to user`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
177
|
logDynamically(logger, 'prepareExecute: Submitting request to ledger', {
|
|
167
178
|
info: { transactionId },
|
|
168
|
-
debug: {
|
|
179
|
+
debug: {
|
|
180
|
+
commandId,
|
|
181
|
+
gatewayUserId,
|
|
182
|
+
ledgerUserId,
|
|
183
|
+
actAs,
|
|
184
|
+
params,
|
|
185
|
+
},
|
|
169
186
|
});
|
|
170
|
-
const
|
|
171
|
-
const prepared = await prepareSubmission(context.userId, actAsParty, synchronizerId, params, ledgerClient);
|
|
187
|
+
const prepared = await prepareSubmission(ledgerUserId, actAs, synchronizerId, params, ledgerClient);
|
|
172
188
|
logDynamically(logger, 'prepareExecute: Received response from ledger', {
|
|
173
189
|
info: { transactionId },
|
|
174
190
|
debug: {
|
|
175
191
|
commandId,
|
|
176
|
-
|
|
177
|
-
|
|
192
|
+
gatewayUserId,
|
|
193
|
+
ledgerUserId,
|
|
194
|
+
actAs,
|
|
178
195
|
prepared,
|
|
179
196
|
},
|
|
180
197
|
});
|
|
@@ -189,18 +206,45 @@ export const dappController = (kernelInfo, dappUrl, userUrl, store, notification
|
|
|
189
206
|
createdAt: new Date(),
|
|
190
207
|
};
|
|
191
208
|
logger.info({
|
|
192
|
-
actAs
|
|
209
|
+
actAs,
|
|
193
210
|
readAs: params.readAs || [],
|
|
194
|
-
|
|
211
|
+
gatewayUserId,
|
|
212
|
+
ledgerUserId,
|
|
195
213
|
commandId,
|
|
196
214
|
commands: params.commands?.[0],
|
|
197
215
|
confirmationRequestTrafficCostEstimation: prepared.costEstimation
|
|
198
216
|
?.confirmationRequestTrafficCostEstimation,
|
|
199
217
|
}, 'prepared transaction traffic estimation');
|
|
200
218
|
await store.setTransaction(transaction);
|
|
219
|
+
const approveUrl = `${userUrl}/approve/index.html?transactionId=${transactionId}&commandId=${commandId}&closeafteraction`;
|
|
220
|
+
if (context.isApiKey) {
|
|
221
|
+
logger.info({
|
|
222
|
+
gatewayUserId,
|
|
223
|
+
ledgerUserId,
|
|
224
|
+
commandId,
|
|
225
|
+
transactionId,
|
|
226
|
+
signingProviderId: wallet.signingProviderId,
|
|
227
|
+
}, 'Service account straight-through prepare/sign/execute');
|
|
228
|
+
const transactionService = new TransactionService(store, logger, deps.signingDrivers, notifier);
|
|
229
|
+
try {
|
|
230
|
+
await transactionService.signAndExecute(context, network, wallet, transaction);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
logger.error({
|
|
234
|
+
err: error,
|
|
235
|
+
gatewayUserId,
|
|
236
|
+
ledgerUserId,
|
|
237
|
+
commandId,
|
|
238
|
+
transactionId,
|
|
239
|
+
actAs,
|
|
240
|
+
signingProviderId: wallet.signingProviderId,
|
|
241
|
+
}, 'Service account sign/execute failed after prepare');
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
201
245
|
return {
|
|
202
246
|
// closeafteraction query param flag makes approving or deleting tx close the popup
|
|
203
|
-
userUrl:
|
|
247
|
+
userUrl: approveUrl,
|
|
204
248
|
};
|
|
205
249
|
},
|
|
206
250
|
status: async () => {
|
|
@@ -107,8 +107,8 @@ async function createStore(logger, context, options = {}) {
|
|
|
107
107
|
}
|
|
108
108
|
return store;
|
|
109
109
|
}
|
|
110
|
-
function createController(store, notificationService, logger, context, requestOrigin = origin) {
|
|
111
|
-
return dappController(kernelInfo, dappUrl, userUrl, store, notificationService, logger, requestOrigin, context);
|
|
110
|
+
function createController(store, notificationService, logger, context, requestOrigin = origin, deps) {
|
|
111
|
+
return dappController(kernelInfo, dappUrl, userUrl, store, notificationService, logger, requestOrigin, deps || { signingDrivers: {} }, context);
|
|
112
112
|
}
|
|
113
113
|
describe('dappController', () => {
|
|
114
114
|
let logger;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { Logger } from 'pino';
|
|
3
3
|
import { Store } from '@canton-network/core-wallet-store';
|
|
4
|
-
import {
|
|
4
|
+
import { AuthAware } from '@canton-network/core-wallet-auth';
|
|
5
5
|
import { Server } from 'http';
|
|
6
6
|
import { NotificationService } from '../notification/NotificationService.js';
|
|
7
7
|
import { KernelInfo, ServerConfig } from '../config/Config.js';
|
|
8
|
-
|
|
8
|
+
import { DappControllerDeps } from './controller.js';
|
|
9
|
+
export declare const dapp: (route: string, app: express.Express, logger: Logger, server: Server, kernelInfo: KernelInfo, dappUrl: string, userUrl: string, serverConfig: ServerConfig, notificationService: NotificationService, store: Store & AuthAware<Store>, controllerDeps: DappControllerDeps) => Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>;
|
|
9
10
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dapp-api/server.ts"],"names":[],"mappings":"AAGA,OAAO,OAAO,MAAM,SAAS,CAAA;AAG7B,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAG7B,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AACzD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dapp-api/server.ts"],"names":[],"mappings":"AAGA,OAAO,OAAO,MAAM,SAAS,CAAA;AAG7B,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAG7B,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAOpD,eAAO,MAAM,IAAI,GACb,OAAO,MAAM,EACb,KAAK,OAAO,CAAC,OAAO,EACpB,QAAQ,MAAM,EACd,QAAQ,MAAM,EACd,YAAY,UAAU,EACtB,SAAS,MAAM,EACf,SAAS,MAAM,EACf,cAAc,YAAY,EAC1B,qBAAqB,mBAAmB,EACxC,OAAO,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,EAC/B,gBAAgB,kBAAkB,kGA6FrC,CAAA"}
|
package/dist/dapp-api/server.js
CHANGED
|
@@ -7,7 +7,7 @@ function writeSSE(res, event, data) {
|
|
|
7
7
|
res.write(`event: ${event}\n`);
|
|
8
8
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
9
9
|
}
|
|
10
|
-
export const dapp = (route, app, logger, server, kernelInfo, dappUrl, userUrl, serverConfig, notificationService,
|
|
10
|
+
export const dapp = (route, app, logger, server, kernelInfo, dappUrl, userUrl, serverConfig, notificationService, store, controllerDeps) => {
|
|
11
11
|
app.use(cors({
|
|
12
12
|
origin: serverConfig.allowedOrigins,
|
|
13
13
|
}));
|
|
@@ -67,7 +67,7 @@ export const dapp = (route, app, logger, server, kernelInfo, dappUrl, userUrl, s
|
|
|
67
67
|
app.use(route, (req, res, next) => {
|
|
68
68
|
const origin = req.headers.origin ?? null;
|
|
69
69
|
jsonRpcHandler({
|
|
70
|
-
controller: dappController(kernelInfo, dappUrl, userUrl, store.withAuthContext(req.authContext), notificationService, logger, origin, req.authContext),
|
|
70
|
+
controller: dappController(kernelInfo, dappUrl, userUrl, store.withAuthContext(req.authContext), notificationService, logger, origin, controllerDeps, req.authContext),
|
|
71
71
|
logger,
|
|
72
72
|
})(req, res, next);
|
|
73
73
|
});
|
|
@@ -11,11 +11,6 @@ import { NotificationService } from '../notification/NotificationService.js';
|
|
|
11
11
|
import { pino } from 'pino';
|
|
12
12
|
import { sink } from 'pino-test';
|
|
13
13
|
import { createServer } from 'http';
|
|
14
|
-
const authService = {
|
|
15
|
-
verifyToken: async () => {
|
|
16
|
-
return new Promise((resolve) => resolve({ userId: 'user123', accessToken: 'token123' }));
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
14
|
const configPath = '../test/config.json';
|
|
20
15
|
const config = ConfigUtils.loadConfigFile(configPath);
|
|
21
16
|
const store = new StoreInternal(config.bootstrap, pino(sink()));
|
|
@@ -26,7 +21,7 @@ test('call connect rpc', async () => {
|
|
|
26
21
|
app.use(express.json());
|
|
27
22
|
const server = createServer(app);
|
|
28
23
|
const { dappApiUrl, publicUrl } = deriveUrls(config);
|
|
29
|
-
const response = await request(dapp('/api/v0/dapp', app, pino(sink()), server, config.kernel, dappApiUrl, publicUrl, config.server, notificationService,
|
|
24
|
+
const response = await request(dapp('/api/v0/dapp', app, pino(sink()), server, config.kernel, dappApiUrl, publicUrl, config.server, notificationService, store, { signingDrivers: {} }))
|
|
30
25
|
.post('/api/v0/dapp')
|
|
31
26
|
.send({ jsonrpc: '2.0', id: 0, method: 'connect', params: [] })
|
|
32
27
|
.set('Accept', 'application/json');
|
package/dist/example-config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-config.d.ts","sourceRoot":"","sources":["../src/example-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"example-config.d.ts","sourceRoot":"","sources":["../src/example-config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,wBAqGqB"}
|
package/dist/example-config.js
CHANGED
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAyB7B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAyB7B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAyMvC,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAmLhE"}
|
package/dist/init.js
CHANGED
|
@@ -23,8 +23,10 @@ import { sessionHandler } from './middleware/sessionHandler.js';
|
|
|
23
23
|
import { NotificationService } from './notification/NotificationService.js';
|
|
24
24
|
import { sql } from 'kysely';
|
|
25
25
|
import { Env } from './env.js';
|
|
26
|
+
import { SigningWorker } from './signing/signing-worker.js';
|
|
26
27
|
import { apiKeyAuth } from './middleware/apiKeyAuth.js';
|
|
27
28
|
let isReady = false;
|
|
29
|
+
let signingWorker;
|
|
28
30
|
async function initializeDatabase(config, logger) {
|
|
29
31
|
logger.info('Checking for database migrations...');
|
|
30
32
|
let exists = true;
|
|
@@ -231,8 +233,21 @@ export async function initialize(opts, logger) {
|
|
|
231
233
|
app.use('/api/*splat', express.json(), preAuthRateLimit, apiKeyAuth(store, config.server.dappPath, logger.child({ component: 'ApiKeyHandler' })), jwtAuth(authService, logger.child({ component: 'JwtHandler' })), postAuthRateLimit, sessionHandler(store, allowedPaths, logger.child({ component: 'SessionHandler' })));
|
|
232
234
|
logger.info({ ...config.server, port }, 'Server configuration');
|
|
233
235
|
const kernelInfo = config.kernel;
|
|
236
|
+
const signingWorkerLogger = logger.child({
|
|
237
|
+
component: 'SigningWorker',
|
|
238
|
+
});
|
|
239
|
+
signingWorker = new SigningWorker({
|
|
240
|
+
intervalMs: config.server.signingWorker.pollInterval,
|
|
241
|
+
signingDrivers: drivers,
|
|
242
|
+
store,
|
|
243
|
+
notificationService,
|
|
244
|
+
logger: signingWorkerLogger,
|
|
245
|
+
});
|
|
246
|
+
signingWorker.start();
|
|
234
247
|
// register dapp API handlers
|
|
235
|
-
dapp(config.server.dappPath, app, logger, server, kernelInfo, dappApiUrl, publicUrl, config.server, notificationService,
|
|
248
|
+
dapp(config.server.dappPath, app, logger, server, kernelInfo, dappApiUrl, publicUrl, config.server, notificationService, store, {
|
|
249
|
+
signingDrivers: drivers,
|
|
250
|
+
});
|
|
236
251
|
// register user API handlers
|
|
237
252
|
user(config.server.userPath, app, logger, kernelInfo, publicUrl, notificationService, drivers, store, config.server.admin);
|
|
238
253
|
// register web handler
|
|
@@ -7,6 +7,7 @@ import { ExecuteParams, ExecuteResult, SignParams } from '../user-api/rpc-gen/ty
|
|
|
7
7
|
import { UserId } from '../dapp-api/rpc-gen/typings.js';
|
|
8
8
|
import { Notifier } from '../notification/NotificationService.js';
|
|
9
9
|
import { AuthContext } from '@canton-network/core-wallet-auth';
|
|
10
|
+
export type SignAndExecuteResult = SignResult | ExecuteResult;
|
|
10
11
|
export declare class TransactionService {
|
|
11
12
|
private store;
|
|
12
13
|
private logger;
|
|
@@ -15,6 +16,7 @@ export declare class TransactionService {
|
|
|
15
16
|
constructor(store: Store, logger: Logger, signingDrivers: SigningDrivers | undefined, notifier: Notifier);
|
|
16
17
|
sign(authContext: AuthContext, wallet: Wallet, signParams: SignParams): Promise<SignResult>;
|
|
17
18
|
execute(userId: UserId, wallet: Wallet, transaction: Transaction, executeParams?: ExecuteParams, ledgerClient?: LedgerClient, network?: Network): Promise<ExecuteResult>;
|
|
19
|
+
signAndExecute(authContext: AuthContext, network: Network, wallet: Wallet, transaction: Transaction): Promise<SignAndExecuteResult>;
|
|
18
20
|
private loadPreparedTransactionForSigning;
|
|
19
21
|
private signWithParticipant;
|
|
20
22
|
private signWithWalletKernel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-service.d.ts","sourceRoot":"","sources":["../../src/ledger/transaction-service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACjE,OAAO,EACH,KAAK,EACL,WAAW,EACX,MAAM,EACN,OAAO,EACV,MAAM,mCAAmC,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAOhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AACnE,OAAO,EACH,aAAa,EACb,aAAa,EACb,UAAU,EAEb,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAA;AAMjE,OAAO,
|
|
1
|
+
{"version":3,"file":"transaction-service.d.ts","sourceRoot":"","sources":["../../src/ledger/transaction-service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACjE,OAAO,EACH,KAAK,EACL,WAAW,EACX,MAAM,EACN,OAAO,EACV,MAAM,mCAAmC,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAOhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AACnE,OAAO,EACH,aAAa,EACb,aAAa,EACb,UAAU,EAEb,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAA;AAMjE,OAAO,EACH,WAAW,EAEd,MAAM,kCAAkC,CAAA;AAEzC,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,aAAa,CAAA;AAW7D,qBAAa,kBAAkB;IAEvB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,QAAQ;gBAHR,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,YAAK,EACnC,QAAQ,EAAE,QAAQ;IAGjB,IAAI,CACb,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACvB,OAAO,CAAC,UAAU,CAAC;IAiDf,OAAO,CACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,EACxB,aAAa,CAAC,EAAE,aAAa,EAC7B,YAAY,CAAC,EAAE,YAAY,EAC3B,OAAO,CAAC,EAAE,OAAO,GAClB,OAAO,CAAC,aAAa,CAAC;IAiEZ,cAAc,CACvB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,oBAAoB,CAAC;YAyDlB,iCAAiC;IAkB/C,OAAO,CAAC,mBAAmB;YASb,oBAAoB;YAyDpB,mBAAmB;YAiHnB,kBAAkB;IAqHhC;;;;;OAKG;YACW,YAAY;IA2G1B;;;;OAIG;YACW,eAAe;YAgCf,sBAAsB;YAoDtB,mBAAmB;CAiEpC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { LedgerClient } from '@canton-network/core-ledger-client';
|
|
3
4
|
import { SigningProvider, } from '@canton-network/core-signing-lib';
|
|
4
5
|
import { ledgerPrepareParams, logDynamically, } from '../utils.js';
|
|
6
|
+
import { AuthTokenProvider, } from '@canton-network/core-wallet-auth';
|
|
5
7
|
function handleSigningError(result) {
|
|
6
8
|
if ('error' in result) {
|
|
7
9
|
throw new Error(`Error from signing driver: ${result.error_description}`);
|
|
@@ -45,6 +47,9 @@ export class TransactionService {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
execute(userId, wallet, transaction, executeParams, ledgerClient, network) {
|
|
50
|
+
if (transaction.status === 'executed') {
|
|
51
|
+
throw new Error('Transaction is already executed.');
|
|
52
|
+
}
|
|
48
53
|
switch (wallet.signingProviderId) {
|
|
49
54
|
case SigningProvider.PARTICIPANT: {
|
|
50
55
|
try {
|
|
@@ -82,11 +87,46 @@ export class TransactionService {
|
|
|
82
87
|
throw new Error(`Unsupported signing provider: ${wallet.signingProviderId}`);
|
|
83
88
|
}
|
|
84
89
|
}
|
|
90
|
+
async signAndExecute(authContext, network, wallet, transaction) {
|
|
91
|
+
const signParams = {
|
|
92
|
+
transactionId: transaction.id,
|
|
93
|
+
partyId: wallet.partyId,
|
|
94
|
+
};
|
|
95
|
+
const signResult = await this.sign(authContext, wallet, signParams);
|
|
96
|
+
if (signResult.status === 'pending') {
|
|
97
|
+
return signResult;
|
|
98
|
+
}
|
|
99
|
+
if (signResult.status !== 'signed') {
|
|
100
|
+
throw new Error(`Service account signing failed with status: ${signResult.status}`);
|
|
101
|
+
}
|
|
102
|
+
if (!('signature' in signResult) ||
|
|
103
|
+
signResult.signature === undefined) {
|
|
104
|
+
throw new Error('Service account signing did not return a signature');
|
|
105
|
+
}
|
|
106
|
+
const ledgerClient = new LedgerClient({
|
|
107
|
+
baseUrl: new URL(network.ledgerApi.baseUrl),
|
|
108
|
+
logger: this.logger,
|
|
109
|
+
accessTokenProvider: AuthTokenProvider.fromToken(authContext.accessToken, this.logger),
|
|
110
|
+
});
|
|
111
|
+
const executeParams = {
|
|
112
|
+
transactionId: transaction.id,
|
|
113
|
+
partyId: wallet.partyId,
|
|
114
|
+
signature: signResult.signature,
|
|
115
|
+
signedBy: signResult.signedBy,
|
|
116
|
+
};
|
|
117
|
+
const userId = authContext.isApiKey
|
|
118
|
+
? authContext.ledgerUserId
|
|
119
|
+
: authContext.userId;
|
|
120
|
+
return this.execute(userId, wallet, transaction, executeParams, ledgerClient, network);
|
|
121
|
+
}
|
|
85
122
|
async loadPreparedTransactionForSigning(transactionId) {
|
|
86
123
|
const existingTx = await this.store.getTransaction(transactionId);
|
|
87
124
|
if (!existingTx) {
|
|
88
125
|
throw new Error(`Transaction not found with id: ${transactionId}`);
|
|
89
126
|
}
|
|
127
|
+
if (existingTx.status !== 'pending') {
|
|
128
|
+
throw new Error(`Cannot sign an already ${existingTx.status} transaction`);
|
|
129
|
+
}
|
|
90
130
|
return existingTx;
|
|
91
131
|
}
|
|
92
132
|
signWithParticipant(wallet) {
|
|
@@ -353,4 +353,38 @@ describe('TransactionService', () => {
|
|
|
353
353
|
});
|
|
354
354
|
});
|
|
355
355
|
});
|
|
356
|
+
describe('signAndExecute', () => {
|
|
357
|
+
const participantWallet = walletWithProvider(SigningProvider.PARTICIPANT);
|
|
358
|
+
it('signs and executes when signing completes synchronously', async () => {
|
|
359
|
+
const service = createService(createStore(), {}, notifier, logger);
|
|
360
|
+
const executeSpy = vi
|
|
361
|
+
.spyOn(service, 'execute')
|
|
362
|
+
.mockResolvedValue({ commandId: 'cmd-1' });
|
|
363
|
+
vi.spyOn(service, 'sign').mockResolvedValue({
|
|
364
|
+
status: 'signed',
|
|
365
|
+
signature: 'sig',
|
|
366
|
+
signedBy: 'namespace',
|
|
367
|
+
partyId: participantWallet.partyId,
|
|
368
|
+
});
|
|
369
|
+
const result = await service.signAndExecute(authContext, network, participantWallet, pendingTransaction);
|
|
370
|
+
expect(result).toEqual({ commandId: 'cmd-1' });
|
|
371
|
+
expect(executeSpy).toHaveBeenCalled();
|
|
372
|
+
});
|
|
373
|
+
it('returns pending sign result without executing', async () => {
|
|
374
|
+
const service = createService(createStore(), {}, notifier, logger);
|
|
375
|
+
const executeSpy = vi.spyOn(service, 'execute');
|
|
376
|
+
vi.spyOn(service, 'sign').mockResolvedValue({
|
|
377
|
+
status: 'pending',
|
|
378
|
+
externalTxId: 'ext-1',
|
|
379
|
+
partyId: participantWallet.partyId,
|
|
380
|
+
});
|
|
381
|
+
const result = await service.signAndExecute(authContext, network, participantWallet, pendingTransaction);
|
|
382
|
+
expect(result).toEqual({
|
|
383
|
+
status: 'pending',
|
|
384
|
+
externalTxId: 'ext-1',
|
|
385
|
+
partyId: participantWallet.partyId,
|
|
386
|
+
});
|
|
387
|
+
expect(executeSpy).not.toHaveBeenCalled();
|
|
388
|
+
});
|
|
389
|
+
});
|
|
356
390
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiKeyAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/apiKeyAuth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EACH,SAAS,EAGZ,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAMzD,wBAAgB,UAAU,CACtB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,IAEA,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"apiKeyAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/apiKeyAuth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EACH,SAAS,EAGZ,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAMzD,wBAAgB,UAAU,CACtB,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,IAEA,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,wDAiIhE"}
|
|
@@ -48,6 +48,7 @@ export function apiKeyAuth(store, dappApiPath, logger) {
|
|
|
48
48
|
const network = await authStore.getNetwork(matchingKey.networkId);
|
|
49
49
|
const idp = await authStore.getIdp(network.identityProviderId);
|
|
50
50
|
if (!network.serviceAccountAuth) {
|
|
51
|
+
logger.debug({ network }, 'Network does not have a service account configured');
|
|
51
52
|
return res.status(401).json(jsonRpcResponse(reqId, {
|
|
52
53
|
error: {
|
|
53
54
|
code: rpcErrors.invalidRequest().code,
|
|
@@ -56,6 +57,7 @@ export function apiKeyAuth(store, dappApiPath, logger) {
|
|
|
56
57
|
}));
|
|
57
58
|
}
|
|
58
59
|
if (network.serviceAccountAuth.method !== 'client_credentials') {
|
|
60
|
+
logger.debug({ network }, 'Network does not have a service account configured with client_credentials method');
|
|
59
61
|
return res.status(401).json(jsonRpcResponse(reqId, {
|
|
60
62
|
error: {
|
|
61
63
|
code: rpcErrors.invalidRequest().code,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
import { AuthAware, AuthContext, AuthTokenProvider } from '@canton-network/core-wallet-auth';
|
|
3
|
+
import { Network, Store } from '@canton-network/core-wallet-store';
|
|
4
|
+
export type AccessTokenProviderFactory = (network: Network) => Promise<AuthTokenProvider>;
|
|
5
|
+
export interface AutomationRunContext {
|
|
6
|
+
authContext: AuthContext;
|
|
7
|
+
scopedStore: Store;
|
|
8
|
+
network: Network;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves auth for background completion of pending external transactions.
|
|
12
|
+
* Service-account networks can run without a pre-existing session by minting
|
|
13
|
+
* an access token. Interactive networks still require a valid stored session.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveAutomationRunContext(bootstrapStore: Store & AuthAware<Store>, userId: string, networkId: string, logger: Logger): Promise<AutomationRunContext | undefined>;
|
|
16
|
+
//# sourceMappingURL=service-account-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-account-session.d.ts","sourceRoot":"","sources":["../../src/signing/service-account-session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EACH,SAAS,EACT,WAAW,EACX,iBAAiB,EACpB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAElE,MAAM,MAAM,0BAA0B,GAAG,CACrC,OAAO,EAAE,OAAO,KACf,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE/B,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,WAAW,CAAA;IACxB,WAAW,EAAE,KAAK,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC7C,cAAc,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,EACxC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAkC3C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { AuthTokenProvider, } from '@canton-network/core-wallet-auth';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves auth for background completion of pending external transactions.
|
|
6
|
+
* Service-account networks can run without a pre-existing session by minting
|
|
7
|
+
* an access token. Interactive networks still require a valid stored session.
|
|
8
|
+
*/
|
|
9
|
+
export async function resolveAutomationRunContext(bootstrapStore, userId, networkId, logger) {
|
|
10
|
+
const network = await bootstrapStore.getNetwork(networkId);
|
|
11
|
+
if (!network) {
|
|
12
|
+
logger.warn({ userId, networkId }, 'Skipping signing worker tick: network not found');
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (!network.serviceAccountAuth) {
|
|
16
|
+
logger.debug({ userId, networkId }, 'Skipping signing worker tick: network is not service-account');
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const idp = await bootstrapStore.getIdp(network.identityProviderId);
|
|
20
|
+
const provider = AuthTokenProvider.fromGatewayConfig(idp, network.serviceAccountAuth, logger);
|
|
21
|
+
const authContext = await provider.getAuthContext();
|
|
22
|
+
const scopedStore = bootstrapStore.withAuthContext(authContext);
|
|
23
|
+
logger.debug({ userId, networkId }, 'Signing worker prepared service account session');
|
|
24
|
+
return { authContext, scopedStore, network };
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-account-session.test.d.ts","sourceRoot":"","sources":["../../src/signing/service-account-session.test.ts"],"names":[],"mappings":""}
|