@canton-network/wallet-gateway-remote 0.25.0 → 0.26.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 +25 -0
- package/dist/config/Config.d.ts +5 -2
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +6 -0
- package/dist/config/Config.test.js +1 -1
- package/dist/dapp-api/controller.d.ts +1 -0
- package/dist/dapp-api/controller.d.ts.map +1 -1
- package/dist/dapp-api/controller.js +26 -1
- package/dist/dapp-api/rpc-gen/index.d.ts +3 -0
- package/dist/dapp-api/rpc-gen/index.d.ts.map +1 -1
- package/dist/dapp-api/rpc-gen/index.js +1 -0
- package/dist/dapp-api/rpc-gen/typings.d.ts +3 -2
- package/dist/dapp-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/dapp-api/server.test.js +1 -1
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -0
- package/dist/example-config.d.ts +1 -0
- package/dist/example-config.d.ts.map +1 -1
- package/dist/example-config.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +9 -4
- package/dist/ledger/party-allocation-service.test.js +19 -17
- package/dist/ledger/transaction-service.d.ts +1 -0
- package/dist/ledger/transaction-service.d.ts.map +1 -1
- package/dist/ledger/transaction-service.js +65 -53
- package/dist/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.d.ts.map +1 -1
- package/dist/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.js +2 -1
- package/dist/ledger/wallet-allocation/wallet-allocation-service.test.js +22 -22
- package/dist/ledger/wallet-sync-service.test.js +15 -13
- package/dist/middleware/rateLimit.d.ts +5 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -1
- package/dist/middleware/rateLimit.js +40 -0
- package/dist/middleware/rateLimit.test.d.ts +2 -0
- package/dist/middleware/rateLimit.test.d.ts.map +1 -0
- package/dist/middleware/rateLimit.test.js +30 -0
- package/dist/user-api/controller.d.ts.map +1 -1
- package/dist/user-api/controller.js +6 -5
- package/dist/user-api/rpc-gen/typings.d.ts +12 -14
- package/dist/user-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/user-api/server.test.js +1 -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 +5 -4
- package/dist/web/frontend/assets/{404-F1JWultf.js → 404-CEw-fKbi.js} +1 -1
- package/dist/web/frontend/assets/{activities-Da48Liee.js → activities-BdSUE0hv.js} +3 -2
- package/dist/web/frontend/assets/{addIdentityProvider-DC3NxrKJ.js → addIdentityProvider-Bzz1fUGn.js} +1 -1
- package/dist/web/frontend/assets/{addNetwork-Dr3W42IN.js → addNetwork-B4FpgV7D.js} +1 -1
- package/dist/web/frontend/assets/addParty-D-UmQ0Oh.js +38 -0
- package/dist/web/frontend/assets/approve-DP3Lfyhw.js +21 -0
- package/dist/web/frontend/assets/{callback-OyzKCduG.js → callback-CXLUjUAK.js} +1 -1
- package/dist/web/frontend/assets/{identityProviders-CNGCwCMd.js → identityProviders-CtFhI1gg.js} +1 -1
- package/dist/web/frontend/assets/{index-6feHRhfj.js → index-BuC7gGqj.js} +96 -55
- package/dist/web/frontend/assets/index-DFhaSBOK.js +91 -0
- package/dist/web/frontend/assets/{index-CH8oXI5W.js → index-dDAXt5F2.js} +1 -1
- package/dist/web/frontend/assets/{login-DcYD5VED.js → login-CmwQEYxS.js} +1 -1
- package/dist/web/frontend/assets/{networks--1UXXt0J.js → networks-f8gSZxSb.js} +1 -1
- package/dist/web/frontend/assets/{reviewIdentityProvider-BT1CChnu.js → reviewIdentityProvider-CJFOHumn.js} +2 -2
- package/dist/web/frontend/assets/{reviewNetwork-CGV3UYev.js → reviewNetwork-D3QSssLB.js} +1 -1
- package/dist/web/frontend/assets/{settings-DnbEcRUK.js → settings-Cc-Ij_uP.js} +1 -1
- package/dist/web/frontend/assets/state-CZ6wI2d4.js +1 -0
- package/dist/web/frontend/assets/utils-CVOqcw_M.js +1 -0
- 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 +4 -4
- 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 -3
- package/dist/web/frontend/parties/index.html +4 -3
- package/dist/web/frontend/settings/index.html +3 -3
- package/package.json +25 -28
- package/dist/web/frontend/assets/addParty-C6FKG-e0.js +0 -38
- package/dist/web/frontend/assets/approve-BcD3DHcB.js +0 -21
- package/dist/web/frontend/assets/parties-CjUMJ1Qt.js +0 -91
- package/dist/web/frontend/assets/state-Da4wx0rb.js +0 -1
|
@@ -15,6 +15,13 @@ export class TransactionService {
|
|
|
15
15
|
this.signingDrivers = signingDrivers;
|
|
16
16
|
this.notifier = notifier;
|
|
17
17
|
}
|
|
18
|
+
async loadPreparedTransactionForSigning(commandId) {
|
|
19
|
+
const existingTx = await this.store.getTransaction(commandId);
|
|
20
|
+
if (!existingTx) {
|
|
21
|
+
throw new Error(`Transaction not found with commandId: ${commandId}`);
|
|
22
|
+
}
|
|
23
|
+
return existingTx;
|
|
24
|
+
}
|
|
18
25
|
signWithParticipant(wallet) {
|
|
19
26
|
return {
|
|
20
27
|
status: 'signed',
|
|
@@ -29,11 +36,11 @@ export class TransactionService {
|
|
|
29
36
|
throw new Error('Wallet Kernel signing driver not available');
|
|
30
37
|
}
|
|
31
38
|
const driver = signingProvider.controller(userId);
|
|
32
|
-
const
|
|
39
|
+
const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
|
|
33
40
|
const { signature } = await driver
|
|
34
41
|
.signTransaction({
|
|
35
|
-
tx: preparedTransaction,
|
|
36
|
-
txHash: preparedTransactionHash,
|
|
42
|
+
tx: tx.preparedTransaction,
|
|
43
|
+
txHash: tx.preparedTransactionHash,
|
|
37
44
|
keyIdentifier: {
|
|
38
45
|
publicKey: wallet.publicKey,
|
|
39
46
|
},
|
|
@@ -42,20 +49,19 @@ export class TransactionService {
|
|
|
42
49
|
if (!signature) {
|
|
43
50
|
throw new Error('Failed to sign transaction: ' + JSON.stringify(signature));
|
|
44
51
|
}
|
|
45
|
-
const existingTx = await this.store.getTransaction(commandId);
|
|
46
52
|
const now = new Date();
|
|
47
53
|
const signedTx = {
|
|
48
|
-
commandId,
|
|
54
|
+
commandId: tx.commandId,
|
|
49
55
|
status: 'signed',
|
|
50
|
-
preparedTransaction,
|
|
51
|
-
preparedTransactionHash,
|
|
52
|
-
origin:
|
|
53
|
-
...(
|
|
54
|
-
createdAt:
|
|
56
|
+
preparedTransaction: tx.preparedTransaction,
|
|
57
|
+
preparedTransactionHash: tx.preparedTransactionHash,
|
|
58
|
+
origin: tx?.origin ?? null,
|
|
59
|
+
...(tx?.createdAt && {
|
|
60
|
+
createdAt: tx.createdAt,
|
|
55
61
|
}),
|
|
56
62
|
signedAt: now,
|
|
57
63
|
};
|
|
58
|
-
this.store.
|
|
64
|
+
await this.store.setTransactionSigned(tx.commandId, now);
|
|
59
65
|
this.notifier.emit('txChanged', signedTx);
|
|
60
66
|
return {
|
|
61
67
|
status: 'signed',
|
|
@@ -70,14 +76,13 @@ export class TransactionService {
|
|
|
70
76
|
throw new Error('Blockdaemon signing driver not available');
|
|
71
77
|
}
|
|
72
78
|
const driver = signingProvider.controller(userId);
|
|
73
|
-
const
|
|
79
|
+
const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
|
|
74
80
|
let signingResult;
|
|
75
|
-
|
|
76
|
-
if (existingTx && existingTx.externalTxId) {
|
|
81
|
+
if (tx && tx.externalTxId) {
|
|
77
82
|
signingResult = await driver
|
|
78
83
|
.getTransaction({
|
|
79
84
|
userId,
|
|
80
|
-
txId:
|
|
85
|
+
txId: tx.externalTxId,
|
|
81
86
|
})
|
|
82
87
|
.then(handleSigningError);
|
|
83
88
|
}
|
|
@@ -88,8 +93,8 @@ export class TransactionService {
|
|
|
88
93
|
.substring(0, 16);
|
|
89
94
|
signingResult = await driver
|
|
90
95
|
.signTransaction({
|
|
91
|
-
tx: preparedTransaction,
|
|
92
|
-
txHash: preparedTransactionHash,
|
|
96
|
+
tx: tx.preparedTransaction,
|
|
97
|
+
txHash: tx.preparedTransactionHash,
|
|
93
98
|
keyIdentifier: {
|
|
94
99
|
publicKey: wallet.publicKey,
|
|
95
100
|
},
|
|
@@ -103,18 +108,18 @@ export class TransactionService {
|
|
|
103
108
|
throw new Error('No signature returned from signing driver');
|
|
104
109
|
}
|
|
105
110
|
const signedTx = {
|
|
106
|
-
commandId,
|
|
111
|
+
commandId: tx.commandId,
|
|
107
112
|
status: signingResult.status,
|
|
108
|
-
preparedTransaction,
|
|
109
|
-
preparedTransactionHash,
|
|
110
|
-
origin:
|
|
111
|
-
...(
|
|
112
|
-
createdAt:
|
|
113
|
+
preparedTransaction: tx.preparedTransaction,
|
|
114
|
+
preparedTransactionHash: tx.preparedTransactionHash,
|
|
115
|
+
origin: tx?.origin ?? null,
|
|
116
|
+
...(tx?.createdAt && {
|
|
117
|
+
createdAt: tx.createdAt,
|
|
113
118
|
}),
|
|
114
119
|
signedAt: now,
|
|
115
120
|
externalTxId: signingResult.txId,
|
|
116
121
|
};
|
|
117
|
-
this.store.
|
|
122
|
+
await this.store.setTransactionSigned(tx.commandId, now, signingResult.txId);
|
|
118
123
|
this.notifier.emit('txChanged', signedTx);
|
|
119
124
|
return {
|
|
120
125
|
status: signingResult.status,
|
|
@@ -127,17 +132,19 @@ export class TransactionService {
|
|
|
127
132
|
else {
|
|
128
133
|
const status = signingResult.status === 'pending' ? 'pending' : 'failed';
|
|
129
134
|
const pendingTx = {
|
|
130
|
-
commandId,
|
|
135
|
+
commandId: tx.commandId,
|
|
131
136
|
status,
|
|
132
|
-
preparedTransaction,
|
|
133
|
-
preparedTransactionHash,
|
|
137
|
+
preparedTransaction: tx.preparedTransaction,
|
|
138
|
+
preparedTransactionHash: tx.preparedTransactionHash,
|
|
134
139
|
externalTxId: signingResult.txId,
|
|
135
|
-
origin:
|
|
136
|
-
...(
|
|
137
|
-
createdAt:
|
|
140
|
+
origin: tx?.origin ?? null,
|
|
141
|
+
...(tx?.createdAt && {
|
|
142
|
+
createdAt: tx.createdAt,
|
|
138
143
|
}),
|
|
139
144
|
};
|
|
140
|
-
this.store.
|
|
145
|
+
await this.store.setTransactionStatus(tx.commandId, status, {
|
|
146
|
+
externalTxId: signingResult.txId,
|
|
147
|
+
});
|
|
141
148
|
this.notifier.emit('txChanged', pendingTx);
|
|
142
149
|
return {
|
|
143
150
|
status: signingResult.status,
|
|
@@ -152,14 +159,13 @@ export class TransactionService {
|
|
|
152
159
|
throw new Error('Fireblocks signing driver not available');
|
|
153
160
|
}
|
|
154
161
|
const driver = signingProvider.controller(userId);
|
|
155
|
-
const
|
|
162
|
+
const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
|
|
156
163
|
let signingResult;
|
|
157
|
-
|
|
158
|
-
if (existingTx && existingTx.externalTxId) {
|
|
164
|
+
if (tx && tx.externalTxId) {
|
|
159
165
|
signingResult = await driver
|
|
160
166
|
.getTransaction({
|
|
161
167
|
userId,
|
|
162
|
-
txId:
|
|
168
|
+
txId: tx.externalTxId,
|
|
163
169
|
})
|
|
164
170
|
.then(handleSigningError);
|
|
165
171
|
}
|
|
@@ -167,8 +173,8 @@ export class TransactionService {
|
|
|
167
173
|
signingResult = await driver
|
|
168
174
|
.signTransaction({
|
|
169
175
|
userId,
|
|
170
|
-
tx: preparedTransaction,
|
|
171
|
-
txHash: Buffer.from(preparedTransactionHash, 'base64').toString('hex'),
|
|
176
|
+
tx: tx.preparedTransaction,
|
|
177
|
+
txHash: Buffer.from(tx.preparedTransactionHash, 'base64').toString('hex'),
|
|
172
178
|
keyIdentifier: {
|
|
173
179
|
publicKey: wallet.publicKey,
|
|
174
180
|
},
|
|
@@ -181,18 +187,18 @@ export class TransactionService {
|
|
|
181
187
|
throw new Error('No signature returned from signing driver');
|
|
182
188
|
}
|
|
183
189
|
const signedTx = {
|
|
184
|
-
commandId,
|
|
190
|
+
commandId: tx.commandId,
|
|
185
191
|
status: signingResult.status,
|
|
186
|
-
preparedTransaction,
|
|
187
|
-
preparedTransactionHash,
|
|
188
|
-
origin:
|
|
189
|
-
...(
|
|
190
|
-
createdAt:
|
|
192
|
+
preparedTransaction: tx.preparedTransaction,
|
|
193
|
+
preparedTransactionHash: tx.preparedTransactionHash,
|
|
194
|
+
origin: tx?.origin ?? null,
|
|
195
|
+
...(tx?.createdAt && {
|
|
196
|
+
createdAt: tx.createdAt,
|
|
191
197
|
}),
|
|
192
198
|
signedAt: now,
|
|
193
199
|
externalTxId: signingResult.txId,
|
|
194
200
|
};
|
|
195
|
-
this.store.
|
|
201
|
+
await this.store.setTransactionSigned(tx.commandId, now, signingResult.txId);
|
|
196
202
|
this.notifier.emit('txChanged', signedTx);
|
|
197
203
|
// return signature in format that is already usable in execute
|
|
198
204
|
const decodedSignature = Buffer.from(signingResult.signature, 'hex').toString('base64');
|
|
@@ -207,17 +213,19 @@ export class TransactionService {
|
|
|
207
213
|
else {
|
|
208
214
|
const status = signingResult.status === 'pending' ? 'pending' : 'failed';
|
|
209
215
|
const pendingTx = {
|
|
210
|
-
commandId,
|
|
216
|
+
commandId: tx.commandId,
|
|
211
217
|
status,
|
|
212
|
-
preparedTransaction,
|
|
213
|
-
preparedTransactionHash,
|
|
218
|
+
preparedTransaction: tx.preparedTransaction,
|
|
219
|
+
preparedTransactionHash: tx.preparedTransactionHash,
|
|
214
220
|
externalTxId: signingResult.txId,
|
|
215
|
-
origin:
|
|
216
|
-
...(
|
|
217
|
-
createdAt:
|
|
221
|
+
origin: tx?.origin ?? null,
|
|
222
|
+
...(tx?.createdAt && {
|
|
223
|
+
createdAt: tx.createdAt,
|
|
218
224
|
}),
|
|
219
225
|
};
|
|
220
|
-
this.store.
|
|
226
|
+
await this.store.setTransactionStatus(tx.commandId, status, {
|
|
227
|
+
externalTxId: signingResult.txId,
|
|
228
|
+
});
|
|
221
229
|
this.notifier.emit('txChanged', pendingTx);
|
|
222
230
|
return {
|
|
223
231
|
status: signingResult.status,
|
|
@@ -245,7 +253,9 @@ export class TransactionService {
|
|
|
245
253
|
signedAt: transaction.signedAt,
|
|
246
254
|
}),
|
|
247
255
|
};
|
|
248
|
-
this.store.
|
|
256
|
+
await this.store.setTransactionStatus(commandId, 'executed', {
|
|
257
|
+
payload: res,
|
|
258
|
+
});
|
|
249
259
|
this.notifier.emit('txChanged', executedTx);
|
|
250
260
|
return res;
|
|
251
261
|
}
|
|
@@ -289,7 +299,9 @@ export class TransactionService {
|
|
|
289
299
|
signedAt: transaction.signedAt,
|
|
290
300
|
}),
|
|
291
301
|
};
|
|
292
|
-
this.store.
|
|
302
|
+
await this.store.setTransactionStatus(commandId, 'executed', {
|
|
303
|
+
payload: result,
|
|
304
|
+
});
|
|
293
305
|
this.notifier.emit('txChanged', executedTx);
|
|
294
306
|
return result;
|
|
295
307
|
}
|
package/dist/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blockdaemon-wallet-allocator.d.ts","sourceRoot":"","sources":["../../../../src/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,KAAK,EAAgB,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAC/E,OAAO,EAEH,sBAAsB,EAEzB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAYtE,qBAAa,0BAA2B,YAAW,eAAe;IAE1D,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,aAAa;gBAHb,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,sBAAsB,EACtC,aAAa,EAAE,sBAAsB;IAG3C,YAAY,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,OAAe,GACzB,OAAO,CAAC,MAAM,CAAC;IAwGZ,aAAa,CACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"blockdaemon-wallet-allocator.d.ts","sourceRoot":"","sources":["../../../../src/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,KAAK,EAAgB,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAC/E,OAAO,EAEH,sBAAsB,EAEzB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAYtE,qBAAa,0BAA2B,YAAW,eAAe;IAE1D,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,aAAa;gBAHb,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,sBAAsB,EACtC,aAAa,EAAE,sBAAsB;IAG3C,YAAY,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,OAAe,GACzB,OAAO,CAAC,MAAM,CAAC;IAwGZ,aAAa,CACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC;CAgEnB"}
|
|
@@ -102,7 +102,7 @@ export class BlockdaemonWalletAllocator {
|
|
|
102
102
|
throw new Error('Existing wallet is missing field externalTxId or topologyTransactions');
|
|
103
103
|
}
|
|
104
104
|
const driver = this.signingDriver.controller(email);
|
|
105
|
-
const { signature, status } = await driver
|
|
105
|
+
const { signature, status, metadata } = await driver
|
|
106
106
|
.getTransaction({
|
|
107
107
|
txId: existingWallet.externalTxId,
|
|
108
108
|
})
|
|
@@ -131,6 +131,7 @@ export class BlockdaemonWalletAllocator {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
else {
|
|
134
|
+
this.logger.warn(`Topology transaction for wallet ${existingWallet.partyId} was ${status} with ${JSON.stringify(metadata)}`);
|
|
134
135
|
const reason = status === 'rejected'
|
|
135
136
|
? WALLET_DISABLED_REASON.TOPOLOGY_TRANSACTION_REJECTED
|
|
136
137
|
: WALLET_DISABLED_REASON.TOPOLOGY_TRANSACTION_FAILED;
|
|
@@ -1,6 +1,6 @@
|
|
|
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 {
|
|
3
|
+
import { vi, describe, it, expect, beforeEach, afterEach, } from 'vitest';
|
|
4
4
|
import { pino } from 'pino';
|
|
5
5
|
import { sink } from 'pino-test';
|
|
6
6
|
import { WalletAllocationService } from './wallet-allocation-service.js';
|
|
@@ -34,14 +34,14 @@ function createFireblocksDriver(options) {
|
|
|
34
34
|
};
|
|
35
35
|
const getTransactionResult = options.getTransactionResult ?? signTransactionResult;
|
|
36
36
|
return {
|
|
37
|
-
controller:
|
|
38
|
-
getKeys:
|
|
37
|
+
controller: vi.fn().mockReturnValue({
|
|
38
|
+
getKeys: vi
|
|
39
39
|
.fn()
|
|
40
40
|
.mockResolvedValue(getKeysResult),
|
|
41
|
-
signTransaction:
|
|
41
|
+
signTransaction: vi
|
|
42
42
|
.fn()
|
|
43
43
|
.mockResolvedValue(signTransactionResult),
|
|
44
|
-
getTransaction:
|
|
44
|
+
getTransaction: vi
|
|
45
45
|
.fn()
|
|
46
46
|
.mockResolvedValue(getTransactionResult),
|
|
47
47
|
}),
|
|
@@ -54,16 +54,16 @@ function createBlockdaemonDriver(options) {
|
|
|
54
54
|
};
|
|
55
55
|
const getTransactionResult = options.getTransactionResult ?? signTransactionResult;
|
|
56
56
|
return {
|
|
57
|
-
controller:
|
|
58
|
-
createKey:
|
|
57
|
+
controller: vi.fn().mockReturnValue({
|
|
58
|
+
createKey: vi
|
|
59
59
|
.fn()
|
|
60
60
|
.mockResolvedValue({
|
|
61
61
|
publicKey: 'bd-pk',
|
|
62
62
|
}),
|
|
63
|
-
signTransaction:
|
|
63
|
+
signTransaction: vi
|
|
64
64
|
.fn()
|
|
65
65
|
.mockResolvedValue(signTransactionResult),
|
|
66
|
-
getTransaction:
|
|
66
|
+
getTransaction: vi
|
|
67
67
|
.fn()
|
|
68
68
|
.mockResolvedValue(getTransactionResult),
|
|
69
69
|
}),
|
|
@@ -80,19 +80,19 @@ describe('WalletAllocationService', () => {
|
|
|
80
80
|
beforeEach(async () => {
|
|
81
81
|
mockLogger = pino(sink());
|
|
82
82
|
mockStore = {
|
|
83
|
-
getWallets:
|
|
84
|
-
removeWallet:
|
|
85
|
-
addWallet:
|
|
86
|
-
updateWallet:
|
|
87
|
-
getCurrentNetwork:
|
|
83
|
+
getWallets: vi.fn(),
|
|
84
|
+
removeWallet: vi.fn(),
|
|
85
|
+
addWallet: vi.fn(),
|
|
86
|
+
updateWallet: vi.fn(),
|
|
87
|
+
getCurrentNetwork: vi
|
|
88
88
|
.fn()
|
|
89
89
|
.mockResolvedValue({ id: 'network1' }),
|
|
90
90
|
};
|
|
91
91
|
mockPartyAllocator = {
|
|
92
|
-
allocateParty:
|
|
93
|
-
allocatePartyWithExistingWallet:
|
|
94
|
-
createFingerprintFromKey:
|
|
95
|
-
generateTopologyTransactions:
|
|
92
|
+
allocateParty: vi.fn(),
|
|
93
|
+
allocatePartyWithExistingWallet: vi.fn(),
|
|
94
|
+
createFingerprintFromKey: vi.fn().mockReturnValue('fingerprint'),
|
|
95
|
+
generateTopologyTransactions: vi
|
|
96
96
|
.fn()
|
|
97
97
|
.mockResolvedValue({
|
|
98
98
|
topologyTransactions: ['tx1'],
|
|
@@ -100,14 +100,14 @@ describe('WalletAllocationService', () => {
|
|
|
100
100
|
}),
|
|
101
101
|
};
|
|
102
102
|
mockController = {
|
|
103
|
-
createKey:
|
|
103
|
+
createKey: vi
|
|
104
104
|
.fn()
|
|
105
105
|
.mockResolvedValue({
|
|
106
106
|
id: 'key-id',
|
|
107
107
|
name: 'test-key',
|
|
108
108
|
publicKey: 'new-public-key',
|
|
109
109
|
}),
|
|
110
|
-
signTransaction:
|
|
110
|
+
signTransaction: vi
|
|
111
111
|
.fn()
|
|
112
112
|
.mockResolvedValue({
|
|
113
113
|
txId: 'tx-id',
|
|
@@ -116,14 +116,14 @@ describe('WalletAllocationService', () => {
|
|
|
116
116
|
}),
|
|
117
117
|
};
|
|
118
118
|
mockWalletKernelDriver = {
|
|
119
|
-
controller:
|
|
119
|
+
controller: vi.fn(() => mockController),
|
|
120
120
|
};
|
|
121
121
|
service = createService({
|
|
122
122
|
[SigningProvider.WALLET_KERNEL]: mockWalletKernelDriver,
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
afterEach(() => {
|
|
126
|
-
|
|
126
|
+
vi.restoreAllMocks();
|
|
127
127
|
});
|
|
128
128
|
describe('Participant', () => {
|
|
129
129
|
it('createWallet allocates new party and adds wallet', async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
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 {
|
|
3
|
+
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
4
4
|
import { pino } from 'pino';
|
|
5
5
|
import { sink } from 'pino-test';
|
|
6
6
|
import { SigningProvider, } from '@canton-network/core-signing-lib';
|
|
@@ -11,9 +11,11 @@ import { PartyLevelRight, } from '@canton-network/core-wallet-store';
|
|
|
11
11
|
import { StoreInternal } from '@canton-network/core-wallet-store-inmemory';
|
|
12
12
|
import { WalletSyncService } from './wallet-sync-service.js';
|
|
13
13
|
import { PartyAllocationService } from './party-allocation-service.js';
|
|
14
|
-
const mockLedgerGet =
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const { mockLedgerGet } = vi.hoisted(() => ({
|
|
15
|
+
mockLedgerGet: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
vi.mock('@canton-network/core-ledger-client', () => ({
|
|
18
|
+
LedgerClient: vi.fn(function LedgerClientMock() {
|
|
17
19
|
return {
|
|
18
20
|
getWithRetry: mockLedgerGet,
|
|
19
21
|
};
|
|
@@ -83,7 +85,7 @@ describe('WalletSyncService - resolveSigningProvider', () => {
|
|
|
83
85
|
}, partyAllocator);
|
|
84
86
|
});
|
|
85
87
|
afterEach(() => {
|
|
86
|
-
|
|
88
|
+
vi.restoreAllMocks();
|
|
87
89
|
mockLedgerGet.mockClear();
|
|
88
90
|
});
|
|
89
91
|
it('resolves participant when namespace matches participant namespace', async () => {
|
|
@@ -128,8 +130,8 @@ describe('WalletSyncService - resolveSigningProvider', () => {
|
|
|
128
130
|
const normalizedKey = partyAllocator.normalizePublicKeyToBase64(fireblocksPublicKeyHex);
|
|
129
131
|
const namespace = partyAllocator.createFingerprintFromKey(normalizedKey);
|
|
130
132
|
const mockFireblocksDriver = {
|
|
131
|
-
controller:
|
|
132
|
-
getKeys:
|
|
133
|
+
controller: vi.fn().mockReturnValue({
|
|
134
|
+
getKeys: vi
|
|
133
135
|
.fn()
|
|
134
136
|
.mockResolvedValue({
|
|
135
137
|
keys: [
|
|
@@ -253,7 +255,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
253
255
|
service = new WalletSyncService(store, mockLedgerClient, authContext, mockLogger, {}, partyAllocator);
|
|
254
256
|
});
|
|
255
257
|
afterEach(() => {
|
|
256
|
-
|
|
258
|
+
vi.restoreAllMocks();
|
|
257
259
|
mockLedgerGet.mockClear();
|
|
258
260
|
});
|
|
259
261
|
it('isWalletSyncNeeded should filter by current network', async () => {
|
|
@@ -305,7 +307,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
305
307
|
await store.addNetwork(network1);
|
|
306
308
|
await setSession('network1');
|
|
307
309
|
await store.addWallet(createWallet('party1::namespace', 'network1'));
|
|
308
|
-
const addWalletSpy =
|
|
310
|
+
const addWalletSpy = vi.spyOn(store, 'addWallet');
|
|
309
311
|
mockLedgerGet
|
|
310
312
|
.mockResolvedValueOnce({
|
|
311
313
|
rights: [
|
|
@@ -542,7 +544,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
542
544
|
.mockResolvedValueOnce({
|
|
543
545
|
participantId: 'participant1::namespace',
|
|
544
546
|
});
|
|
545
|
-
const updateWalletSpy =
|
|
547
|
+
const updateWalletSpy = vi.spyOn(store, 'updateWallet');
|
|
546
548
|
await service.syncWallets();
|
|
547
549
|
expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
548
550
|
partyId: 'party1::namespace',
|
|
@@ -575,7 +577,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
575
577
|
.mockResolvedValueOnce({
|
|
576
578
|
participantId: 'participant1::namespace',
|
|
577
579
|
});
|
|
578
|
-
const updateWalletSpy =
|
|
580
|
+
const updateWalletSpy = vi.spyOn(store, 'updateWallet');
|
|
579
581
|
await service.syncWallets();
|
|
580
582
|
expect(updateWalletSpy).not.toHaveBeenCalled();
|
|
581
583
|
const wallets = await store.getWallets();
|
|
@@ -603,7 +605,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
603
605
|
.mockResolvedValueOnce({
|
|
604
606
|
participantId: 'participant1::namespace',
|
|
605
607
|
});
|
|
606
|
-
const updateWalletSpy =
|
|
608
|
+
const updateWalletSpy = vi.spyOn(store, 'updateWallet');
|
|
607
609
|
await service.syncWallets();
|
|
608
610
|
expect(updateWalletSpy).toHaveBeenCalledTimes(2);
|
|
609
611
|
expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
@@ -639,7 +641,7 @@ describe('WalletSyncService - multi-network features', () => {
|
|
|
639
641
|
.mockResolvedValueOnce({
|
|
640
642
|
participantId: 'participant1::namespace',
|
|
641
643
|
});
|
|
642
|
-
const updateWalletSpy =
|
|
644
|
+
const updateWalletSpy = vi.spyOn(store, 'updateWallet');
|
|
643
645
|
await service.syncWallets();
|
|
644
646
|
expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
645
647
|
partyId: 'party1::namespace',
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
import type { Request } from 'express';
|
|
2
|
+
export declare function ipRateLimitKeyGenerator(req: Request): string;
|
|
3
|
+
export declare function rateLimitKeyGenerator(req: Request): string;
|
|
1
4
|
export declare function rateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
|
|
5
|
+
export declare function preAuthIpRateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
|
|
6
|
+
export declare function authenticatedRateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
|
|
2
7
|
//# sourceMappingURL=rateLimit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAWtC,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE5D;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAO1D;AAED,wBAAgB,WAAW,CAAC,gBAAgB,EAAE,MAAM,wDAQnD;AAED,wBAAgB,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,wDAU5D;AAED,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,wDAUhE"}
|
|
@@ -1,10 +1,50 @@
|
|
|
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
3
|
import rateLimit from 'express-rate-limit';
|
|
4
|
+
function hasBearerToken(req) {
|
|
5
|
+
const authHeader = req.headers.authorization;
|
|
6
|
+
if (typeof authHeader === 'string' && authHeader.startsWith('Bearer ')) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return typeof req.query.token === 'string' && req.query.token.length > 0;
|
|
10
|
+
}
|
|
11
|
+
export function ipRateLimitKeyGenerator(req) {
|
|
12
|
+
return `ip:${req.ip || req.socket.remoteAddress || 'unknown'}`;
|
|
13
|
+
}
|
|
14
|
+
export function rateLimitKeyGenerator(req) {
|
|
15
|
+
// Prefer authenticated identity to avoid shared proxy IP buckets.
|
|
16
|
+
if (req.authContext?.userId) {
|
|
17
|
+
return `user:${req.authContext.userId}`;
|
|
18
|
+
}
|
|
19
|
+
return ipRateLimitKeyGenerator(req);
|
|
20
|
+
}
|
|
4
21
|
export function rateLimiter(requestRateLimit) {
|
|
5
22
|
return rateLimit({
|
|
6
23
|
windowMs: 1 * 60 * 1000, // 1 minute
|
|
7
24
|
max: requestRateLimit, // limit each IP to requestRateLimit requests per windowMs
|
|
25
|
+
keyGenerator: rateLimitKeyGenerator,
|
|
26
|
+
standardHeaders: true,
|
|
27
|
+
legacyHeaders: false,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function preAuthIpRateLimiter(requestRateLimit) {
|
|
31
|
+
return rateLimit({
|
|
32
|
+
windowMs: 1 * 60 * 1000,
|
|
33
|
+
max: requestRateLimit,
|
|
34
|
+
keyGenerator: ipRateLimitKeyGenerator,
|
|
35
|
+
// Only protect unauthenticated traffic before JWT verification.
|
|
36
|
+
skip: hasBearerToken,
|
|
37
|
+
standardHeaders: true,
|
|
38
|
+
legacyHeaders: false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function authenticatedRateLimiter(requestRateLimit) {
|
|
42
|
+
return rateLimit({
|
|
43
|
+
windowMs: 1 * 60 * 1000,
|
|
44
|
+
max: requestRateLimit,
|
|
45
|
+
keyGenerator: rateLimitKeyGenerator,
|
|
46
|
+
// Apply only after JWT middleware has attached an authenticated context.
|
|
47
|
+
skip: (req) => !req.authContext?.userId,
|
|
8
48
|
standardHeaders: true,
|
|
9
49
|
legacyHeaders: false,
|
|
10
50
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimit.test.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { describe, expect, test } from 'vitest';
|
|
4
|
+
import { ipRateLimitKeyGenerator, rateLimitKeyGenerator } from './rateLimit.js';
|
|
5
|
+
describe('ipRateLimitKeyGenerator', () => {
|
|
6
|
+
test('uses request ip address', () => {
|
|
7
|
+
const req = {
|
|
8
|
+
ip: '198.51.100.42',
|
|
9
|
+
socket: { remoteAddress: '10.0.0.1' },
|
|
10
|
+
};
|
|
11
|
+
expect(ipRateLimitKeyGenerator(req)).toBe('ip:198.51.100.42');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('rateLimitKeyGenerator', () => {
|
|
15
|
+
test('uses authenticated user id when available', () => {
|
|
16
|
+
const req = {
|
|
17
|
+
authContext: { userId: 'alice' },
|
|
18
|
+
ip: '203.0.113.10',
|
|
19
|
+
socket: { remoteAddress: '10.0.0.1' },
|
|
20
|
+
};
|
|
21
|
+
expect(rateLimitKeyGenerator(req)).toBe('user:alice');
|
|
22
|
+
});
|
|
23
|
+
test('falls back to request ip when user is not authenticated', () => {
|
|
24
|
+
const req = {
|
|
25
|
+
ip: '198.51.100.42',
|
|
26
|
+
socket: { remoteAddress: '10.0.0.1' },
|
|
27
|
+
};
|
|
28
|
+
expect(rateLimitKeyGenerator(req)).toBe('ip:198.51.100.42');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"AA6BA,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;AAChD,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"AA6BA,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;AAChD,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AASzC,KAAK,uBAAuB,GAAG,OAAO,CAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,CAAA;AAED,eAAO,MAAM,cAAc,GACvB,YAAY,UAAU,EACtB,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,aAAa,WAAW,GAAG,SAAS,EACpC,SAAS,uBAAuB,EAChC,SAAS,MAAM,EACf,cAAc,MAAM;;;;;;;;;;;;;;;;;;;;;;;CA+vBvB,CAAA"}
|
|
@@ -311,7 +311,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
311
311
|
accessTokenProvider: AuthTokenProvider.fromToken(accessToken, logger),
|
|
312
312
|
});
|
|
313
313
|
const status = await networkStatus(ledgerClient);
|
|
314
|
-
|
|
314
|
+
const statusEvent = {
|
|
315
315
|
provider: provider,
|
|
316
316
|
connection: {
|
|
317
317
|
isConnected: status.isConnected,
|
|
@@ -325,11 +325,12 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
325
325
|
accessToken: accessToken,
|
|
326
326
|
},
|
|
327
327
|
session: {
|
|
328
|
-
id: newSessionId,
|
|
329
328
|
accessToken: accessToken,
|
|
330
329
|
userId: userId,
|
|
331
330
|
},
|
|
332
|
-
}
|
|
331
|
+
};
|
|
332
|
+
notifier.emit('statusChanged', statusEvent);
|
|
333
|
+
notifier.emit('connected', statusEvent);
|
|
333
334
|
//we only want to automatically perform a sync if it is the first time a session is created
|
|
334
335
|
const wallets = await store.getWallets();
|
|
335
336
|
if (wallets.length == 0) {
|
|
@@ -355,8 +356,8 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
355
356
|
});
|
|
356
357
|
}
|
|
357
358
|
catch (error) {
|
|
358
|
-
logger.error(
|
|
359
|
-
throw new Error(`Failed to add session
|
|
359
|
+
logger.error({ error }, 'Failed to add session');
|
|
360
|
+
throw new Error(`Failed to add session`, {
|
|
360
361
|
cause: error,
|
|
361
362
|
});
|
|
362
363
|
}
|