@openzeppelin/miden-multisig-client 0.15.2 → 0.16.1
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/README.md +37 -1
- package/dist/account/builder.d.ts +2 -1
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +1 -0
- package/dist/account/builder.js.map +1 -1
- package/dist/account/builder.test.js +2 -2
- package/dist/account/builder.test.js.map +1 -1
- package/dist/client.d.ts +17 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -7
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +46 -15
- package/dist/client.test.js.map +1 -1
- package/dist/connectivity.d.ts +38 -0
- package/dist/connectivity.d.ts.map +1 -0
- package/dist/connectivity.js +97 -0
- package/dist/connectivity.js.map +1 -0
- package/dist/connectivity.test.d.ts +2 -0
- package/dist/connectivity.test.d.ts.map +1 -0
- package/dist/connectivity.test.js +61 -0
- package/dist/connectivity.test.js.map +1 -0
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/multisig.d.ts +130 -8
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +220 -24
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +427 -89
- package/dist/multisig.test.js.map +1 -1
- package/dist/proposal/factory.d.ts.map +1 -1
- package/dist/proposal/factory.js +10 -0
- package/dist/proposal/factory.js.map +1 -1
- package/dist/proposal/factory.test.d.ts +2 -0
- package/dist/proposal/factory.test.d.ts.map +1 -0
- package/dist/proposal/factory.test.js +32 -0
- package/dist/proposal/factory.test.js.map +1 -0
- package/dist/proposal/metadata.d.ts.map +1 -1
- package/dist/proposal/metadata.js +12 -0
- package/dist/proposal/metadata.js.map +1 -1
- package/dist/proposal/metadata.test.js +49 -0
- package/dist/proposal/metadata.test.js.map +1 -1
- package/dist/prover/config.d.ts +16 -0
- package/dist/prover/config.d.ts.map +1 -0
- package/dist/prover/config.js +54 -0
- package/dist/prover/config.js.map +1 -0
- package/dist/prover/config.test.d.ts +2 -0
- package/dist/prover/config.test.d.ts.map +1 -0
- package/dist/prover/config.test.js +54 -0
- package/dist/prover/config.test.js.map +1 -0
- package/dist/prover/errors.d.ts +2 -0
- package/dist/prover/errors.d.ts.map +1 -0
- package/dist/prover/errors.js +158 -0
- package/dist/prover/errors.js.map +1 -0
- package/dist/prover/errors.test.d.ts +2 -0
- package/dist/prover/errors.test.d.ts.map +1 -0
- package/dist/prover/errors.test.js +24 -0
- package/dist/prover/errors.test.js.map +1 -0
- package/dist/prover/retry.d.ts +10 -0
- package/dist/prover/retry.d.ts.map +1 -0
- package/dist/prover/retry.js +32 -0
- package/dist/prover/retry.js.map +1 -0
- package/dist/prover/retry.test.d.ts +2 -0
- package/dist/prover/retry.test.d.ts.map +1 -0
- package/dist/prover/retry.test.js +20 -0
- package/dist/prover/retry.test.js.map +1 -0
- package/dist/prover/workflow.d.ts +11 -0
- package/dist/prover/workflow.d.ts.map +1 -0
- package/dist/prover/workflow.js +18 -0
- package/dist/prover/workflow.js.map +1 -0
- package/dist/prover/workflow.test.d.ts +2 -0
- package/dist/prover/workflow.test.d.ts.map +1 -0
- package/dist/prover/workflow.test.js +80 -0
- package/dist/prover/workflow.test.js.map +1 -0
- package/dist/raw-client.d.ts +2 -2
- package/dist/raw-client.d.ts.map +1 -1
- package/dist/raw-client.js +14 -4
- package/dist/raw-client.js.map +1 -1
- package/dist/raw-client.test.js +25 -3
- package/dist/raw-client.test.js.map +1 -1
- package/dist/transaction/consumeNotes.d.ts +6 -2
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +0 -4
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/options.d.ts +3 -0
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/p2id.d.ts +27 -2
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +56 -4
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/p2id.test.js +66 -6
- package/dist/transaction/p2id.test.js.map +1 -1
- package/dist/transaction/summary.d.ts +2 -1
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/updateGuardian.d.ts +6 -2
- package/dist/transaction/updateGuardian.d.ts.map +1 -1
- package/dist/transaction/updateGuardian.js.map +1 -1
- package/dist/transaction/updateProcedureThreshold.d.ts +7 -2
- package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
- package/dist/transaction/updateProcedureThreshold.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts +7 -2
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.d.ts +1 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +1 -1
- package/dist/transaction.js.map +1 -1
- package/dist/types/proposal.d.ts +5 -0
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types/proposal.js +3 -0
- package/dist/types/proposal.js.map +1 -1
- package/package.json +3 -3
- package/src/account/builder.test.ts +19 -11
- package/src/account/builder.ts +2 -1
- package/src/client.test.ts +67 -15
- package/src/client.ts +35 -12
- package/src/connectivity.test.ts +67 -0
- package/src/connectivity.ts +111 -0
- package/src/index.ts +25 -1
- package/src/multisig.test.ts +526 -94
- package/src/multisig.ts +270 -25
- package/src/proposal/factory.test.ts +40 -0
- package/src/proposal/factory.ts +11 -1
- package/src/proposal/metadata.test.ts +60 -0
- package/src/proposal/metadata.ts +14 -0
- package/src/prover/config.test.ts +90 -0
- package/src/prover/config.ts +78 -0
- package/src/prover/errors.test.ts +53 -0
- package/src/prover/errors.ts +178 -0
- package/src/prover/retry.test.ts +38 -0
- package/src/prover/retry.ts +46 -0
- package/src/prover/test-node.d.ts +6 -0
- package/src/prover/workflow.test.ts +109 -0
- package/src/prover/workflow.ts +19 -0
- package/src/raw-client.test.ts +41 -3
- package/src/raw-client.ts +15 -5
- package/src/transaction/consumeNotes.ts +11 -1
- package/src/transaction/options.ts +4 -0
- package/src/transaction/p2id.test.ts +98 -8
- package/src/transaction/p2id.ts +91 -12
- package/src/transaction/summary.ts +12 -0
- package/src/transaction/updateGuardian.ts +11 -1
- package/src/transaction/updateProcedureThreshold.ts +13 -1
- package/src/transaction/updateSigners.ts +13 -1
- package/src/transaction.ts +4 -0
- package/src/types/proposal.ts +9 -0
package/dist/multisig.test.js
CHANGED
|
@@ -2,10 +2,11 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
2
2
|
import { Multisig } from './multisig.js';
|
|
3
3
|
import { GuardianHttpClient } from '@openzeppelin/guardian-client';
|
|
4
4
|
import { buildUpdateProcedureThresholdTransactionRequest, buildUpdateGuardianTransactionRequest, buildUpdateSignersTransactionRequest, executeForSummary, } from './transaction.js';
|
|
5
|
-
const { mockRpcGetAccountDetails, mockAccountDeserialize, mockDetectConfig } = vi.hoisted(() => ({
|
|
5
|
+
const { mockRpcGetAccountDetails, mockAccountDeserialize, mockDetectConfig, mockNoteFileDeserialize } = vi.hoisted(() => ({
|
|
6
6
|
mockRpcGetAccountDetails: vi.fn(),
|
|
7
7
|
mockAccountDeserialize: vi.fn(),
|
|
8
8
|
mockDetectConfig: vi.fn(),
|
|
9
|
+
mockNoteFileDeserialize: vi.fn(),
|
|
9
10
|
}));
|
|
10
11
|
// Mock the Miden SDK
|
|
11
12
|
vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
@@ -15,6 +16,18 @@ vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
|
15
16
|
AccountId: {
|
|
16
17
|
fromHex: vi.fn((hex) => ({ toString: () => hex })),
|
|
17
18
|
},
|
|
19
|
+
NoteType: {
|
|
20
|
+
Private: 0,
|
|
21
|
+
Public: 1,
|
|
22
|
+
},
|
|
23
|
+
NoteExportFormat: {
|
|
24
|
+
Id: 0,
|
|
25
|
+
Full: 1,
|
|
26
|
+
Details: 2,
|
|
27
|
+
},
|
|
28
|
+
NoteFile: {
|
|
29
|
+
deserialize: mockNoteFileDeserialize,
|
|
30
|
+
},
|
|
18
31
|
TransactionSummary: {
|
|
19
32
|
deserialize: vi.fn().mockReturnValue({
|
|
20
33
|
toCommitment: () => ({
|
|
@@ -79,6 +92,19 @@ vi.mock('./transaction.js', () => ({
|
|
|
79
92
|
request: {},
|
|
80
93
|
salt: { toHex: () => '0x' + 'd'.repeat(64) },
|
|
81
94
|
}),
|
|
95
|
+
buildP2idNoteFromMetadata: vi.fn().mockReturnValue({
|
|
96
|
+
id: () => ({ toString: () => '0x' + 'ab'.repeat(32) }),
|
|
97
|
+
}),
|
|
98
|
+
// Mirrors the real implementations against the mocked NoteType values
|
|
99
|
+
// (Private = 0, Public = 1).
|
|
100
|
+
parseP2idNoteType: vi.fn((value) => {
|
|
101
|
+
if (value === undefined || value === 'public')
|
|
102
|
+
return 1;
|
|
103
|
+
if (value === 'private')
|
|
104
|
+
return 0;
|
|
105
|
+
throw new Error(`unsupported metadata.noteType '${value}': expected 'public' or 'private'`);
|
|
106
|
+
}),
|
|
107
|
+
p2idNoteTypeToMetadata: vi.fn((noteType) => (noteType === 0 ? 'private' : undefined)),
|
|
82
108
|
}));
|
|
83
109
|
vi.mock('./utils/signature.js', async () => {
|
|
84
110
|
const actual = await vi.importActual('./utils/signature.js');
|
|
@@ -106,6 +132,7 @@ vi.mock('./inspector.js', () => ({
|
|
|
106
132
|
// Mock fetch for GUARDIAN client
|
|
107
133
|
const mockFetch = vi.fn();
|
|
108
134
|
vi.stubGlobal('fetch', mockFetch);
|
|
135
|
+
const MIDEN_RPC_ENDPOINT = 'https://rpc.devnet.miden.io';
|
|
109
136
|
function mockedAccount(commitmentHex, nonce = 0) {
|
|
110
137
|
return {
|
|
111
138
|
commitment: () => ({
|
|
@@ -124,6 +151,9 @@ describe('Multisig', () => {
|
|
|
124
151
|
let mockSigner;
|
|
125
152
|
let mockAccount;
|
|
126
153
|
let mockWebClient;
|
|
154
|
+
function createTestMultisig(config, signer = mockSigner, accountId, proverConfig) {
|
|
155
|
+
return new Multisig(mockAccount, config, guardian, signer, mockWebClient, accountId, MIDEN_RPC_ENDPOINT, proverConfig);
|
|
156
|
+
}
|
|
127
157
|
beforeEach(() => {
|
|
128
158
|
mockFetch.mockReset();
|
|
129
159
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
@@ -176,13 +206,36 @@ describe('Multisig', () => {
|
|
|
176
206
|
submitNewTransaction: vi.fn(),
|
|
177
207
|
submitNewTransactionWithProver: vi.fn(),
|
|
178
208
|
transactions: {
|
|
179
|
-
|
|
209
|
+
executeRequest: vi.fn(),
|
|
180
210
|
},
|
|
181
211
|
getConsumableNotes: vi.fn().mockResolvedValue([]),
|
|
182
212
|
syncState: vi.fn(),
|
|
183
213
|
getAccount: vi.fn().mockResolvedValue(null),
|
|
184
214
|
newAccount: vi.fn(),
|
|
185
215
|
};
|
|
216
|
+
mockWebClient.transactions.executeRequest.mockImplementation(async (accountId, request) => {
|
|
217
|
+
const result = await mockWebClient.executeTransaction(accountId, request);
|
|
218
|
+
return {
|
|
219
|
+
result,
|
|
220
|
+
prove: async (options) => {
|
|
221
|
+
const proof = options?.prover === undefined
|
|
222
|
+
? await mockWebClient.proveTransaction(result)
|
|
223
|
+
: await mockWebClient.proveTransaction(result, options.prover);
|
|
224
|
+
return {
|
|
225
|
+
proof,
|
|
226
|
+
result,
|
|
227
|
+
submit: async () => {
|
|
228
|
+
const blockNumber = await mockWebClient.submitProvenTransaction(proof, result);
|
|
229
|
+
return {
|
|
230
|
+
blockNumber,
|
|
231
|
+
result,
|
|
232
|
+
apply: () => mockWebClient.applyTransaction(result, blockNumber),
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
});
|
|
186
239
|
});
|
|
187
240
|
describe('constructor', () => {
|
|
188
241
|
it('should create Multisig with account', () => {
|
|
@@ -191,7 +244,7 @@ describe('Multisig', () => {
|
|
|
191
244
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
192
245
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
193
246
|
};
|
|
194
|
-
const multisig =
|
|
247
|
+
const multisig = createTestMultisig(config);
|
|
195
248
|
expect(multisig.threshold).toBe(2);
|
|
196
249
|
expect(multisig.signerCommitments).toEqual(config.signerCommitments);
|
|
197
250
|
expect(multisig.guardianCommitment).toBe(config.guardianCommitment);
|
|
@@ -204,10 +257,18 @@ describe('Multisig', () => {
|
|
|
204
257
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
205
258
|
};
|
|
206
259
|
const accountId = '0x' + 'd'.repeat(30);
|
|
207
|
-
const multisig =
|
|
260
|
+
const multisig = createTestMultisig(config, mockSigner, accountId);
|
|
208
261
|
expect(multisig.account).toBe(mockAccount);
|
|
209
262
|
expect(multisig.accountId).toBe(accountId);
|
|
210
263
|
});
|
|
264
|
+
it('should reject a missing Miden RPC endpoint immediately', () => {
|
|
265
|
+
const config = {
|
|
266
|
+
threshold: 2,
|
|
267
|
+
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
268
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
269
|
+
};
|
|
270
|
+
expect(() => new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, undefined, undefined)).toThrow('missing required configuration: midenRpcEndpoint');
|
|
271
|
+
});
|
|
211
272
|
});
|
|
212
273
|
describe('accountId', () => {
|
|
213
274
|
it('should return account ID from account', () => {
|
|
@@ -216,7 +277,7 @@ describe('Multisig', () => {
|
|
|
216
277
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
217
278
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
218
279
|
};
|
|
219
|
-
const multisig =
|
|
280
|
+
const multisig = createTestMultisig(config);
|
|
220
281
|
expect(multisig.accountId).toBe('0x' + 'a'.repeat(30));
|
|
221
282
|
});
|
|
222
283
|
it('should return provided account ID when constructor override is set', () => {
|
|
@@ -226,7 +287,7 @@ describe('Multisig', () => {
|
|
|
226
287
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
227
288
|
};
|
|
228
289
|
const accountId = '0x' + 'e'.repeat(30);
|
|
229
|
-
const multisig =
|
|
290
|
+
const multisig = createTestMultisig(config, mockSigner, accountId);
|
|
230
291
|
expect(multisig.accountId).toBe(accountId);
|
|
231
292
|
});
|
|
232
293
|
});
|
|
@@ -237,7 +298,7 @@ describe('Multisig', () => {
|
|
|
237
298
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
238
299
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
239
300
|
};
|
|
240
|
-
const multisig =
|
|
301
|
+
const multisig = createTestMultisig(config);
|
|
241
302
|
expect(multisig.signerCommitment).toBe(mockSigner.commitment);
|
|
242
303
|
});
|
|
243
304
|
});
|
|
@@ -248,7 +309,7 @@ describe('Multisig', () => {
|
|
|
248
309
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
249
310
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
250
311
|
};
|
|
251
|
-
const multisig =
|
|
312
|
+
const multisig = createTestMultisig(config);
|
|
252
313
|
mockFetch.mockResolvedValueOnce({
|
|
253
314
|
ok: true,
|
|
254
315
|
json: async () => ({
|
|
@@ -399,14 +460,15 @@ describe('Multisig', () => {
|
|
|
399
460
|
await expect(multisig.syncState()).rejects.toThrow('Refusing to overwrite local state');
|
|
400
461
|
expect(mockWebClient.newAccount).not.toHaveBeenCalled();
|
|
401
462
|
});
|
|
402
|
-
it('
|
|
463
|
+
it('keeps local state and refreshes config from it when GUARDIAN nonce is behind local', async () => {
|
|
403
464
|
const config = {
|
|
404
465
|
threshold: 1,
|
|
405
466
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
406
467
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
407
468
|
};
|
|
408
469
|
const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, undefined, 'https://rpc.devnet.miden.io');
|
|
409
|
-
|
|
470
|
+
const localAccount = mockedAccount('0x' + 'a'.repeat(64), 3);
|
|
471
|
+
mockWebClient.getAccount.mockResolvedValueOnce(localAccount);
|
|
410
472
|
mockAccountDeserialize.mockReturnValueOnce(mockedAccount('0x' + 'b'.repeat(64), 2));
|
|
411
473
|
mockFetch.mockResolvedValueOnce({
|
|
412
474
|
ok: true,
|
|
@@ -418,8 +480,23 @@ describe('Multisig', () => {
|
|
|
418
480
|
updated_at: '2024-01-02T00:00:00Z',
|
|
419
481
|
}),
|
|
420
482
|
});
|
|
421
|
-
|
|
483
|
+
mockDetectConfig.mockReturnValueOnce({
|
|
484
|
+
threshold: 2,
|
|
485
|
+
numSigners: 2,
|
|
486
|
+
signerCommitments: ['0x' + '1'.repeat(64), '0x' + '2'.repeat(64)],
|
|
487
|
+
guardianEnabled: true,
|
|
488
|
+
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
489
|
+
vaultBalances: [],
|
|
490
|
+
procedureThresholds: new Map(),
|
|
491
|
+
});
|
|
492
|
+
// GUARDIAN behind local (nonce 2 < 3): no throw, local kept, no overwrite,
|
|
493
|
+
// and the decision needs no on-chain round-trip.
|
|
494
|
+
await expect(multisig.syncState()).resolves.toBeDefined();
|
|
422
495
|
expect(mockWebClient.newAccount).not.toHaveBeenCalled();
|
|
496
|
+
expect(mockRpcGetAccountDetails).not.toHaveBeenCalled();
|
|
497
|
+
// Config refreshed from the authoritative local account (UI unfreezes).
|
|
498
|
+
expect(multisig.account).toBe(localAccount);
|
|
499
|
+
expect(multisig.threshold).toBe(2);
|
|
423
500
|
});
|
|
424
501
|
it('should throw when incoming state nonce equals local nonce but commitment differs', async () => {
|
|
425
502
|
const config = {
|
|
@@ -440,8 +517,35 @@ describe('Multisig', () => {
|
|
|
440
517
|
updated_at: '2024-01-02T00:00:00Z',
|
|
441
518
|
}),
|
|
442
519
|
});
|
|
443
|
-
await expect(multisig.syncState()).rejects.toThrow('incoming nonce 2
|
|
520
|
+
await expect(multisig.syncState()).rejects.toThrow('incoming nonce 2 equals local nonce 2 but commitments differ');
|
|
521
|
+
expect(mockWebClient.newAccount).not.toHaveBeenCalled();
|
|
522
|
+
});
|
|
523
|
+
it('unfreezes Multisig.account after execute when GUARDIAN still lags by one nonce (regression, #343)', async () => {
|
|
524
|
+
const config = {
|
|
525
|
+
threshold: 1,
|
|
526
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
527
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
528
|
+
};
|
|
529
|
+
const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, undefined, 'https://rpc.devnet.miden.io');
|
|
530
|
+
// Post-execute: local advanced to nonce 1, GUARDIAN still reports nonce 0
|
|
531
|
+
// (candidate not canonicalized yet). Before the fix this threw and left
|
|
532
|
+
// Multisig.account frozen at the pre-execute snapshot.
|
|
533
|
+
const localAccount = mockedAccount('0x' + 'a'.repeat(64), 1);
|
|
534
|
+
mockWebClient.getAccount.mockResolvedValueOnce(localAccount);
|
|
535
|
+
mockAccountDeserialize.mockReturnValueOnce(mockedAccount('0x' + 'b'.repeat(64), 0));
|
|
536
|
+
mockFetch.mockResolvedValueOnce({
|
|
537
|
+
ok: true,
|
|
538
|
+
json: async () => ({
|
|
539
|
+
account_id: multisig.accountId,
|
|
540
|
+
commitment: '0x' + 'b'.repeat(64),
|
|
541
|
+
state_json: { data: 'AQID' },
|
|
542
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
543
|
+
updated_at: '2024-01-02T00:00:00Z',
|
|
544
|
+
}),
|
|
545
|
+
});
|
|
546
|
+
await expect(multisig.syncState()).resolves.toBeDefined();
|
|
444
547
|
expect(mockWebClient.newAccount).not.toHaveBeenCalled();
|
|
548
|
+
expect(multisig.account).toBe(localAccount);
|
|
445
549
|
});
|
|
446
550
|
});
|
|
447
551
|
describe('verifyStateCommitment', () => {
|
|
@@ -490,7 +594,7 @@ describe('Multisig', () => {
|
|
|
490
594
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
491
595
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
492
596
|
};
|
|
493
|
-
const multisig =
|
|
597
|
+
const multisig = createTestMultisig(config);
|
|
494
598
|
mockFetch.mockResolvedValueOnce({
|
|
495
599
|
ok: true,
|
|
496
600
|
json: async () => ({
|
|
@@ -513,7 +617,7 @@ describe('Multisig', () => {
|
|
|
513
617
|
scheme: 'ecdsa',
|
|
514
618
|
};
|
|
515
619
|
guardian.setSigner(ecdsaSigner);
|
|
516
|
-
const multisig =
|
|
620
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
517
621
|
mockFetch.mockResolvedValueOnce({
|
|
518
622
|
ok: true,
|
|
519
623
|
json: async () => ({
|
|
@@ -537,7 +641,7 @@ describe('Multisig', () => {
|
|
|
537
641
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
538
642
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
539
643
|
};
|
|
540
|
-
const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, '0x' + 'e'.repeat(30));
|
|
644
|
+
const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, '0x' + 'e'.repeat(30), MIDEN_RPC_ENDPOINT);
|
|
541
645
|
mockFetch.mockResolvedValueOnce({
|
|
542
646
|
ok: true,
|
|
543
647
|
json: async () => ({
|
|
@@ -553,7 +657,7 @@ describe('Multisig', () => {
|
|
|
553
657
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
554
658
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
555
659
|
};
|
|
556
|
-
const multisig =
|
|
660
|
+
const multisig = createTestMultisig(config);
|
|
557
661
|
mockFetch.mockResolvedValueOnce({
|
|
558
662
|
ok: true,
|
|
559
663
|
json: async () => ({
|
|
@@ -571,7 +675,7 @@ describe('Multisig', () => {
|
|
|
571
675
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
572
676
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
573
677
|
};
|
|
574
|
-
const multisig =
|
|
678
|
+
const multisig = createTestMultisig(config);
|
|
575
679
|
const mockProposals = [
|
|
576
680
|
{
|
|
577
681
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -616,7 +720,7 @@ describe('Multisig', () => {
|
|
|
616
720
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
617
721
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
618
722
|
};
|
|
619
|
-
const multisig =
|
|
723
|
+
const multisig = createTestMultisig(config);
|
|
620
724
|
const mockProposals = [
|
|
621
725
|
{
|
|
622
726
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -659,7 +763,7 @@ describe('Multisig', () => {
|
|
|
659
763
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
660
764
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
661
765
|
};
|
|
662
|
-
const multisig =
|
|
766
|
+
const multisig = createTestMultisig(config);
|
|
663
767
|
mockFetch.mockResolvedValueOnce({
|
|
664
768
|
ok: true,
|
|
665
769
|
json: async () => ({
|
|
@@ -701,7 +805,7 @@ describe('Multisig', () => {
|
|
|
701
805
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
702
806
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
703
807
|
};
|
|
704
|
-
const multisig =
|
|
808
|
+
const multisig = createTestMultisig(config);
|
|
705
809
|
const mockProposals = [
|
|
706
810
|
{
|
|
707
811
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -743,7 +847,7 @@ describe('Multisig', () => {
|
|
|
743
847
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
744
848
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
745
849
|
};
|
|
746
|
-
const multisig =
|
|
850
|
+
const multisig = createTestMultisig(config);
|
|
747
851
|
const mockProposals = [
|
|
748
852
|
{
|
|
749
853
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -792,7 +896,7 @@ describe('Multisig', () => {
|
|
|
792
896
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
793
897
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
794
898
|
};
|
|
795
|
-
const multisig =
|
|
899
|
+
const multisig = createTestMultisig(config);
|
|
796
900
|
expect(multisig.listProposals()).toEqual([]);
|
|
797
901
|
});
|
|
798
902
|
});
|
|
@@ -803,7 +907,7 @@ describe('Multisig', () => {
|
|
|
803
907
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
804
908
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
805
909
|
};
|
|
806
|
-
const multisig =
|
|
910
|
+
const multisig = createTestMultisig(config);
|
|
807
911
|
const mockDelta = {
|
|
808
912
|
account_id: '0x' + 'a'.repeat(30),
|
|
809
913
|
nonce: 1,
|
|
@@ -841,7 +945,7 @@ describe('Multisig', () => {
|
|
|
841
945
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
842
946
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
843
947
|
};
|
|
844
|
-
const multisig =
|
|
948
|
+
const multisig = createTestMultisig(config);
|
|
845
949
|
const mockDelta = {
|
|
846
950
|
account_id: '0x' + 'a'.repeat(30),
|
|
847
951
|
nonce: 1,
|
|
@@ -877,7 +981,7 @@ describe('Multisig', () => {
|
|
|
877
981
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
878
982
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
879
983
|
};
|
|
880
|
-
const multisig =
|
|
984
|
+
const multisig = createTestMultisig(config);
|
|
881
985
|
const mockDelta = {
|
|
882
986
|
account_id: '0x' + 'a'.repeat(30),
|
|
883
987
|
nonce: 1,
|
|
@@ -927,7 +1031,7 @@ describe('Multisig', () => {
|
|
|
927
1031
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
928
1032
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
929
1033
|
};
|
|
930
|
-
const multisig =
|
|
1034
|
+
const multisig = createTestMultisig(config);
|
|
931
1035
|
const mockDelta = {
|
|
932
1036
|
account_id: '0x' + 'a'.repeat(30),
|
|
933
1037
|
nonce: 1,
|
|
@@ -960,6 +1064,178 @@ describe('Multisig', () => {
|
|
|
960
1064
|
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, 1);
|
|
961
1065
|
expect(proposal.metadata.description).toBe('Send 100 of asset 0xfaucet... to 0xrecipien...');
|
|
962
1066
|
});
|
|
1067
|
+
it('threads a private noteType into the request and wire metadata (issue #322)', async () => {
|
|
1068
|
+
const { executeForSummary, buildP2idTransactionRequest } = await import('./transaction.js');
|
|
1069
|
+
const { NoteType } = await import('@miden-sdk/miden-sdk');
|
|
1070
|
+
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1071
|
+
toCommitment: () => ({
|
|
1072
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1073
|
+
}),
|
|
1074
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1075
|
+
});
|
|
1076
|
+
const config = {
|
|
1077
|
+
threshold: 1,
|
|
1078
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1079
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1080
|
+
};
|
|
1081
|
+
const multisig = createTestMultisig(config);
|
|
1082
|
+
const mockDelta = {
|
|
1083
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
1084
|
+
nonce: 1,
|
|
1085
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
1086
|
+
delta_payload: {
|
|
1087
|
+
tx_summary: { data: 'AQID' },
|
|
1088
|
+
signatures: [],
|
|
1089
|
+
metadata: {
|
|
1090
|
+
proposal_type: 'p2id',
|
|
1091
|
+
recipient_id: '0xrecipient',
|
|
1092
|
+
faucet_id: '0xfaucet',
|
|
1093
|
+
amount: '100',
|
|
1094
|
+
note_type: 'private',
|
|
1095
|
+
description: '',
|
|
1096
|
+
},
|
|
1097
|
+
},
|
|
1098
|
+
status: {
|
|
1099
|
+
status: 'pending',
|
|
1100
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
1101
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
1102
|
+
cosigner_sigs: [],
|
|
1103
|
+
},
|
|
1104
|
+
};
|
|
1105
|
+
mockFetch.mockResolvedValueOnce({
|
|
1106
|
+
ok: true,
|
|
1107
|
+
json: async () => ({
|
|
1108
|
+
delta: mockDelta,
|
|
1109
|
+
commitment: '0x' + 'c'.repeat(64),
|
|
1110
|
+
}),
|
|
1111
|
+
});
|
|
1112
|
+
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, 1, {
|
|
1113
|
+
noteType: NoteType.Private,
|
|
1114
|
+
});
|
|
1115
|
+
// Propose path builds the private note...
|
|
1116
|
+
expect(vi.mocked(buildP2idTransactionRequest)).toHaveBeenCalledWith(expect.any(String), '0xrecipient', '0xfaucet', 100n, expect.anything(), { noteType: NoteType.Private });
|
|
1117
|
+
// ...and the rebuild-from-metadata path parses note_type back to Private.
|
|
1118
|
+
const lastCall = vi.mocked(buildP2idTransactionRequest).mock.calls.at(-1);
|
|
1119
|
+
expect(lastCall[5]).toMatchObject({ noteType: NoteType.Private });
|
|
1120
|
+
// The pushed wire metadata carries note_type so cosigners rebuild the
|
|
1121
|
+
// same private note at verification/execution.
|
|
1122
|
+
const pushBody = JSON.parse(mockFetch.mock.calls.at(-1)[1].body);
|
|
1123
|
+
expect(pushBody.delta_payload.metadata.note_type).toBe('private');
|
|
1124
|
+
expect(proposal.metadata.proposalType).toBe('p2id');
|
|
1125
|
+
expect(proposal.metadata.noteType).toBe('private');
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
describe('exportNoteToBytes / importNoteFromBytes (issue #356)', () => {
|
|
1129
|
+
const config = {
|
|
1130
|
+
threshold: 1,
|
|
1131
|
+
signerCommitments: ['0x' + '1'.repeat(64)],
|
|
1132
|
+
guardianCommitment: '0x' + '3'.repeat(64),
|
|
1133
|
+
};
|
|
1134
|
+
it('exports the full note with proof when the inclusion proof is known', async () => {
|
|
1135
|
+
const noteFile = { serialize: () => new Uint8Array([9, 9, 9]) };
|
|
1136
|
+
mockWebClient.getOutputNote = vi.fn().mockResolvedValue({
|
|
1137
|
+
inclusionProof: () => ({}),
|
|
1138
|
+
});
|
|
1139
|
+
mockWebClient.exportNoteFile = vi.fn().mockResolvedValue(noteFile);
|
|
1140
|
+
const multisig = createTestMultisig(config);
|
|
1141
|
+
const bytes = await multisig.exportNoteToBytes('0x' + 'ab'.repeat(32));
|
|
1142
|
+
expect(bytes).toEqual(new Uint8Array([9, 9, 9]));
|
|
1143
|
+
// NoteExportFormat.Full = 1 in the SDK mock
|
|
1144
|
+
expect(mockWebClient.exportNoteFile).toHaveBeenCalledWith('0x' + 'ab'.repeat(32), 1);
|
|
1145
|
+
});
|
|
1146
|
+
it('falls back to a details-only export before the note commits on chain', async () => {
|
|
1147
|
+
const noteFile = { serialize: () => new Uint8Array([7]) };
|
|
1148
|
+
mockWebClient.getOutputNote = vi.fn().mockResolvedValue({
|
|
1149
|
+
inclusionProof: () => undefined,
|
|
1150
|
+
});
|
|
1151
|
+
mockWebClient.exportNoteFile = vi.fn().mockResolvedValue(noteFile);
|
|
1152
|
+
const multisig = createTestMultisig(config);
|
|
1153
|
+
await multisig.exportNoteToBytes(' 0x' + 'ab'.repeat(32) + ' ');
|
|
1154
|
+
// NoteExportFormat.Details = 2 in the SDK mock; the id is trimmed
|
|
1155
|
+
expect(mockWebClient.exportNoteFile).toHaveBeenCalledWith('0x' + 'ab'.repeat(32), 2);
|
|
1156
|
+
});
|
|
1157
|
+
it('rejects exporting a note the local store does not know', async () => {
|
|
1158
|
+
mockWebClient.getOutputNote = vi.fn().mockRejectedValue(new Error('no such note'));
|
|
1159
|
+
mockWebClient.exportNoteFile = vi.fn();
|
|
1160
|
+
const multisig = createTestMultisig(config);
|
|
1161
|
+
await expect(multisig.exportNoteToBytes('0x' + 'ab'.repeat(32))).rejects.toThrow(/not found in the local store/);
|
|
1162
|
+
expect(mockWebClient.exportNoteFile).not.toHaveBeenCalled();
|
|
1163
|
+
});
|
|
1164
|
+
it('rejects exporting when the store resolves no record', async () => {
|
|
1165
|
+
mockWebClient.getOutputNote = vi.fn().mockResolvedValue(undefined);
|
|
1166
|
+
mockWebClient.exportNoteFile = vi.fn();
|
|
1167
|
+
const multisig = createTestMultisig(config);
|
|
1168
|
+
await expect(multisig.exportNoteToBytes('0x' + 'ab'.repeat(32))).rejects.toThrow(/not found in the local store/);
|
|
1169
|
+
expect(mockWebClient.exportNoteFile).not.toHaveBeenCalled();
|
|
1170
|
+
});
|
|
1171
|
+
it('imports note file bytes and returns the resolved identifier', async () => {
|
|
1172
|
+
const decoded = { marker: 'note-file' };
|
|
1173
|
+
mockNoteFileDeserialize.mockReturnValue(decoded);
|
|
1174
|
+
mockWebClient.importNoteFile = vi.fn().mockResolvedValue('0x' + 'cd'.repeat(32));
|
|
1175
|
+
const multisig = createTestMultisig(config);
|
|
1176
|
+
const noteId = await multisig.importNoteFromBytes(new Uint8Array([1, 2, 3]));
|
|
1177
|
+
expect(mockNoteFileDeserialize).toHaveBeenCalledWith(new Uint8Array([1, 2, 3]));
|
|
1178
|
+
expect(mockWebClient.importNoteFile).toHaveBeenCalledWith(decoded);
|
|
1179
|
+
expect(noteId).toBe('0x' + 'cd'.repeat(32));
|
|
1180
|
+
});
|
|
1181
|
+
it('rejects bytes that do not decode as a note file', async () => {
|
|
1182
|
+
mockNoteFileDeserialize.mockImplementation(() => {
|
|
1183
|
+
throw new Error('bad bytes');
|
|
1184
|
+
});
|
|
1185
|
+
mockWebClient.importNoteFile = vi.fn();
|
|
1186
|
+
const multisig = createTestMultisig(config);
|
|
1187
|
+
await expect(multisig.importNoteFromBytes(new Uint8Array([0]))).rejects.toThrow(/failed to decode note file: bad bytes/);
|
|
1188
|
+
expect(mockWebClient.importNoteFile).not.toHaveBeenCalled();
|
|
1189
|
+
});
|
|
1190
|
+
});
|
|
1191
|
+
describe('exportNoteToFile / importNoteFromFile (issue #356)', () => {
|
|
1192
|
+
const config = {
|
|
1193
|
+
threshold: 1,
|
|
1194
|
+
signerCommitments: ['0x' + '1'.repeat(64)],
|
|
1195
|
+
guardianCommitment: '0x' + '3'.repeat(64),
|
|
1196
|
+
};
|
|
1197
|
+
it('rejects exportNoteToFile outside a browser environment', async () => {
|
|
1198
|
+
const multisig = createTestMultisig(config);
|
|
1199
|
+
await expect(multisig.exportNoteToFile('0x' + 'ab'.repeat(32))).rejects.toThrow(/requires a browser environment/);
|
|
1200
|
+
});
|
|
1201
|
+
it('imports from a File/Blob by delegating to importNoteFromBytes', async () => {
|
|
1202
|
+
const decoded = { marker: 'note-file' };
|
|
1203
|
+
mockNoteFileDeserialize.mockReturnValue(decoded);
|
|
1204
|
+
mockWebClient.importNoteFile = vi.fn().mockResolvedValue('0x' + 'cd'.repeat(32));
|
|
1205
|
+
const multisig = createTestMultisig(config);
|
|
1206
|
+
const noteId = await multisig.importNoteFromFile(new Blob([new Uint8Array([1, 2, 3])]));
|
|
1207
|
+
expect(mockNoteFileDeserialize).toHaveBeenCalledWith(new Uint8Array([1, 2, 3]));
|
|
1208
|
+
expect(noteId).toBe('0x' + 'cd'.repeat(32));
|
|
1209
|
+
});
|
|
1210
|
+
});
|
|
1211
|
+
describe('getP2idNoteId (issue #356)', () => {
|
|
1212
|
+
const config = {
|
|
1213
|
+
threshold: 1,
|
|
1214
|
+
signerCommitments: ['0x' + '1'.repeat(64)],
|
|
1215
|
+
guardianCommitment: '0x' + '3'.repeat(64),
|
|
1216
|
+
};
|
|
1217
|
+
it('computes the deterministic note ID from p2id proposal metadata', async () => {
|
|
1218
|
+
const multisig = createTestMultisig(config);
|
|
1219
|
+
const proposal = {
|
|
1220
|
+
metadata: {
|
|
1221
|
+
proposalType: 'p2id',
|
|
1222
|
+
recipientId: '0x' + 'b'.repeat(30),
|
|
1223
|
+
faucetId: '0x' + 'c'.repeat(30),
|
|
1224
|
+
amount: '100',
|
|
1225
|
+
saltHex: '0x' + 'd'.repeat(64),
|
|
1226
|
+
noteType: 'private',
|
|
1227
|
+
},
|
|
1228
|
+
};
|
|
1229
|
+
const noteId = await multisig.getP2idNoteId(proposal);
|
|
1230
|
+
expect(noteId).toBe('0x' + 'ab'.repeat(32));
|
|
1231
|
+
});
|
|
1232
|
+
it('rejects non-p2id proposals', async () => {
|
|
1233
|
+
const multisig = createTestMultisig(config);
|
|
1234
|
+
const proposal = {
|
|
1235
|
+
metadata: { proposalType: 'consume_notes' },
|
|
1236
|
+
};
|
|
1237
|
+
await expect(multisig.getP2idNoteId(proposal)).rejects.toThrow(/requires a P2ID proposal/);
|
|
1238
|
+
});
|
|
963
1239
|
});
|
|
964
1240
|
describe('createChangeThresholdProposal', () => {
|
|
965
1241
|
it('passes the signer scheme to update-signers requests', async () => {
|
|
@@ -1007,7 +1283,7 @@ describe('Multisig', () => {
|
|
|
1007
1283
|
commitment: '0x' + 'c'.repeat(64),
|
|
1008
1284
|
}),
|
|
1009
1285
|
});
|
|
1010
|
-
const multisig =
|
|
1286
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
1011
1287
|
await multisig.createChangeThresholdProposal(2, 1);
|
|
1012
1288
|
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, 2, config.signerCommitments, { signatureScheme: 'ecdsa' });
|
|
1013
1289
|
});
|
|
@@ -1022,7 +1298,7 @@ describe('Multisig', () => {
|
|
|
1022
1298
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1023
1299
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1024
1300
|
};
|
|
1025
|
-
const multisig =
|
|
1301
|
+
const multisig = createTestMultisig(config);
|
|
1026
1302
|
const newGuardianPubkey = '0x' + '1'.repeat(64);
|
|
1027
1303
|
mockFetch.mockResolvedValueOnce({
|
|
1028
1304
|
ok: true,
|
|
@@ -1062,7 +1338,7 @@ describe('Multisig', () => {
|
|
|
1062
1338
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1063
1339
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1064
1340
|
};
|
|
1065
|
-
const multisig =
|
|
1341
|
+
const multisig = createTestMultisig(config);
|
|
1066
1342
|
mockFetch.mockResolvedValueOnce({
|
|
1067
1343
|
ok: true,
|
|
1068
1344
|
json: async () => ({ commitment: '0x' + '2'.repeat(64) }),
|
|
@@ -1084,7 +1360,7 @@ describe('Multisig', () => {
|
|
|
1084
1360
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1085
1361
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1086
1362
|
};
|
|
1087
|
-
const multisig =
|
|
1363
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
1088
1364
|
const newGuardianCommitment = '0x' + '1'.repeat(64);
|
|
1089
1365
|
mockFetch.mockResolvedValueOnce({
|
|
1090
1366
|
ok: true,
|
|
@@ -1126,7 +1402,7 @@ describe('Multisig', () => {
|
|
|
1126
1402
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1127
1403
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1128
1404
|
};
|
|
1129
|
-
const multisig =
|
|
1405
|
+
const multisig = createTestMultisig(config);
|
|
1130
1406
|
const mockDelta = {
|
|
1131
1407
|
account_id: '0x' + 'a'.repeat(30),
|
|
1132
1408
|
nonce: 1,
|
|
@@ -1181,7 +1457,7 @@ describe('Multisig', () => {
|
|
|
1181
1457
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1182
1458
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1183
1459
|
};
|
|
1184
|
-
const multisig =
|
|
1460
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
1185
1461
|
const mockDelta = {
|
|
1186
1462
|
account_id: '0x' + 'a'.repeat(30),
|
|
1187
1463
|
nonce: 1,
|
|
@@ -1221,7 +1497,7 @@ describe('Multisig', () => {
|
|
|
1221
1497
|
signerCommitments: [mockSigner.commitment],
|
|
1222
1498
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1223
1499
|
};
|
|
1224
|
-
const multisig =
|
|
1500
|
+
const multisig = createTestMultisig(config);
|
|
1225
1501
|
// First create a proposal
|
|
1226
1502
|
const mockDelta = {
|
|
1227
1503
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -1290,7 +1566,7 @@ describe('Multisig', () => {
|
|
|
1290
1566
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1291
1567
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1292
1568
|
};
|
|
1293
|
-
const multisig =
|
|
1569
|
+
const multisig = createTestMultisig(config);
|
|
1294
1570
|
const mockDelta = {
|
|
1295
1571
|
account_id: '0x' + 'a'.repeat(30),
|
|
1296
1572
|
nonce: 1,
|
|
@@ -1332,7 +1608,7 @@ describe('Multisig', () => {
|
|
|
1332
1608
|
signerCommitments: [mockSigner.commitment],
|
|
1333
1609
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1334
1610
|
};
|
|
1335
|
-
const multisig =
|
|
1611
|
+
const multisig = createTestMultisig(config);
|
|
1336
1612
|
const proposalId = '0x' + 'd'.repeat(64);
|
|
1337
1613
|
mockFetch.mockResolvedValueOnce({
|
|
1338
1614
|
ok: true,
|
|
@@ -1373,7 +1649,7 @@ describe('Multisig', () => {
|
|
|
1373
1649
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1374
1650
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1375
1651
|
};
|
|
1376
|
-
const multisig =
|
|
1652
|
+
const multisig = createTestMultisig(config);
|
|
1377
1653
|
vi.mocked(executeForSummary).mockResolvedValueOnce({
|
|
1378
1654
|
toCommitment: () => ({
|
|
1379
1655
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
@@ -1401,7 +1677,7 @@ describe('Multisig', () => {
|
|
|
1401
1677
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1402
1678
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1403
1679
|
};
|
|
1404
|
-
const multisig =
|
|
1680
|
+
const multisig = createTestMultisig(config);
|
|
1405
1681
|
vi.mocked(executeForSummary).mockResolvedValueOnce({
|
|
1406
1682
|
toCommitment: () => ({
|
|
1407
1683
|
toHex: () => '0x' + 'c'.repeat(64),
|
|
@@ -1441,7 +1717,7 @@ describe('Multisig', () => {
|
|
|
1441
1717
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1442
1718
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1443
1719
|
};
|
|
1444
|
-
const multisig =
|
|
1720
|
+
const multisig = createTestMultisig(config);
|
|
1445
1721
|
const mockProposals = [
|
|
1446
1722
|
{
|
|
1447
1723
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -1488,7 +1764,7 @@ describe('Multisig', () => {
|
|
|
1488
1764
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1489
1765
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1490
1766
|
};
|
|
1491
|
-
const multisig =
|
|
1767
|
+
const multisig = createTestMultisig(config);
|
|
1492
1768
|
const publicKey = '0x' + 'd'.repeat(66);
|
|
1493
1769
|
mockFetch.mockResolvedValueOnce({
|
|
1494
1770
|
ok: true,
|
|
@@ -1541,12 +1817,18 @@ describe('Multisig', () => {
|
|
|
1541
1817
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1542
1818
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1543
1819
|
};
|
|
1544
|
-
const multisig =
|
|
1820
|
+
const multisig = createTestMultisig(config);
|
|
1545
1821
|
mockFetch.mockResolvedValueOnce({
|
|
1546
1822
|
ok: false,
|
|
1547
1823
|
status: 404,
|
|
1548
1824
|
statusText: 'Not Found',
|
|
1549
|
-
|
|
1825
|
+
// Feature 009: only a conforming { code, message, meta } envelope is
|
|
1826
|
+
// folded into the error message; raw text bodies are dropped.
|
|
1827
|
+
text: async () => JSON.stringify({
|
|
1828
|
+
code: 'GUARDIAN_PROPOSAL_NOT_FOUND',
|
|
1829
|
+
message: 'Proposal not found',
|
|
1830
|
+
meta: { retryable: false },
|
|
1831
|
+
}),
|
|
1550
1832
|
});
|
|
1551
1833
|
await expect(multisig.exportProposal('0x' + 'nonexistent'.repeat(5))).rejects.toThrow('Proposal not found');
|
|
1552
1834
|
});
|
|
@@ -1558,7 +1840,7 @@ describe('Multisig', () => {
|
|
|
1558
1840
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1559
1841
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1560
1842
|
};
|
|
1561
|
-
const multisig =
|
|
1843
|
+
const multisig = createTestMultisig(config);
|
|
1562
1844
|
const exported = {
|
|
1563
1845
|
accountId: multisig.accountId,
|
|
1564
1846
|
nonce: 1,
|
|
@@ -1585,7 +1867,7 @@ describe('Multisig', () => {
|
|
|
1585
1867
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1586
1868
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1587
1869
|
};
|
|
1588
|
-
const multisig =
|
|
1870
|
+
const multisig = createTestMultisig(config);
|
|
1589
1871
|
const publicKey = '0x' + 'd'.repeat(66);
|
|
1590
1872
|
const proposal = await multisig.importProposal(JSON.stringify({
|
|
1591
1873
|
accountId: multisig.accountId,
|
|
@@ -1626,7 +1908,7 @@ describe('Multisig', () => {
|
|
|
1626
1908
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1627
1909
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1628
1910
|
};
|
|
1629
|
-
const multisig =
|
|
1911
|
+
const multisig = createTestMultisig(config);
|
|
1630
1912
|
await expect(multisig.importProposal(JSON.stringify({
|
|
1631
1913
|
accountId: multisig.accountId,
|
|
1632
1914
|
nonce: 1,
|
|
@@ -1653,7 +1935,7 @@ describe('Multisig', () => {
|
|
|
1653
1935
|
signerCommitments: ['0x' + 'a'.repeat(64), mockSigner.commitment],
|
|
1654
1936
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1655
1937
|
};
|
|
1656
|
-
const multisig =
|
|
1938
|
+
const multisig = createTestMultisig(config);
|
|
1657
1939
|
const exported = {
|
|
1658
1940
|
accountId: multisig.accountId,
|
|
1659
1941
|
nonce: 1,
|
|
@@ -1689,7 +1971,7 @@ describe('Multisig', () => {
|
|
|
1689
1971
|
scheme: 'ecdsa',
|
|
1690
1972
|
publicKey: '0x' + '2'.repeat(66),
|
|
1691
1973
|
};
|
|
1692
|
-
const multisig =
|
|
1974
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
1693
1975
|
const cachedProposalId = '0x' + 'c'.repeat(64);
|
|
1694
1976
|
const requestedProposalId = '0x' + 'C'.repeat(64);
|
|
1695
1977
|
const cosignerPubkey = '0x' + '3'.repeat(66);
|
|
@@ -1781,7 +2063,7 @@ describe('Multisig', () => {
|
|
|
1781
2063
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1782
2064
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1783
2065
|
};
|
|
1784
|
-
const multisig =
|
|
2066
|
+
const multisig = createTestMultisig(config);
|
|
1785
2067
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
1786
2068
|
const newGuardianPubkey = '0x' + '1'.repeat(64);
|
|
1787
2069
|
const finalRequest = { kind: 'final-switch-guardian-request' };
|
|
@@ -1829,7 +2111,7 @@ describe('Multisig', () => {
|
|
|
1829
2111
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1830
2112
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1831
2113
|
};
|
|
1832
|
-
const multisig =
|
|
2114
|
+
const multisig = createTestMultisig(config);
|
|
1833
2115
|
await expect(multisig.createTransactionProposalRequest('0x' + 'nonexistent'.repeat(5))).rejects.toThrow('Proposal not found');
|
|
1834
2116
|
});
|
|
1835
2117
|
it('should throw if proposal is still pending', async () => {
|
|
@@ -1838,7 +2120,7 @@ describe('Multisig', () => {
|
|
|
1838
2120
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1839
2121
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1840
2122
|
};
|
|
1841
|
-
const multisig =
|
|
2123
|
+
const multisig = createTestMultisig(config);
|
|
1842
2124
|
const mockProposals = [
|
|
1843
2125
|
{
|
|
1844
2126
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -1881,7 +2163,7 @@ describe('Multisig', () => {
|
|
|
1881
2163
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1882
2164
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1883
2165
|
};
|
|
1884
|
-
const multisig =
|
|
2166
|
+
const multisig = createTestMultisig(config);
|
|
1885
2167
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
1886
2168
|
vi.mocked(executeForSummary).mockResolvedValueOnce({
|
|
1887
2169
|
toCommitment: () => ({
|
|
@@ -1917,7 +2199,7 @@ describe('Multisig', () => {
|
|
|
1917
2199
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1918
2200
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1919
2201
|
};
|
|
1920
|
-
const multisig =
|
|
2202
|
+
const multisig = createTestMultisig(config);
|
|
1921
2203
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
1922
2204
|
multisig.proposals.set(proposalId, {
|
|
1923
2205
|
id: proposalId,
|
|
@@ -1952,7 +2234,7 @@ describe('Multisig', () => {
|
|
|
1952
2234
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1953
2235
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1954
2236
|
};
|
|
1955
|
-
const multisig =
|
|
2237
|
+
const multisig = createTestMultisig(config);
|
|
1956
2238
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
1957
2239
|
multisig.proposals.set(proposalId, {
|
|
1958
2240
|
id: proposalId,
|
|
@@ -2012,7 +2294,7 @@ describe('Multisig', () => {
|
|
|
2012
2294
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2013
2295
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2014
2296
|
};
|
|
2015
|
-
const multisig =
|
|
2297
|
+
const multisig = createTestMultisig(config);
|
|
2016
2298
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2017
2299
|
const finalRequest = { kind: 'fresh-message-word-request' };
|
|
2018
2300
|
vi.mocked(buildUpdateSignersTransactionRequest)
|
|
@@ -2105,7 +2387,7 @@ describe('Multisig', () => {
|
|
|
2105
2387
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2106
2388
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2107
2389
|
};
|
|
2108
|
-
const multisig =
|
|
2390
|
+
const multisig = createTestMultisig(config);
|
|
2109
2391
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2110
2392
|
multisig.proposals.set(proposalId, {
|
|
2111
2393
|
id: proposalId,
|
|
@@ -2142,7 +2424,7 @@ describe('Multisig', () => {
|
|
|
2142
2424
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2143
2425
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2144
2426
|
};
|
|
2145
|
-
const multisig =
|
|
2427
|
+
const multisig = createTestMultisig(config);
|
|
2146
2428
|
await expect(multisig.executeProposal('0x' + 'nonexistent'.repeat(5))).rejects.toThrow('Proposal not found');
|
|
2147
2429
|
});
|
|
2148
2430
|
it('should throw if proposal is still pending', async () => {
|
|
@@ -2151,7 +2433,7 @@ describe('Multisig', () => {
|
|
|
2151
2433
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2152
2434
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2153
2435
|
};
|
|
2154
|
-
const multisig =
|
|
2436
|
+
const multisig = createTestMultisig(config);
|
|
2155
2437
|
// Sync with pending proposal (only 1 signature)
|
|
2156
2438
|
const mockProposals = [
|
|
2157
2439
|
{
|
|
@@ -2196,7 +2478,7 @@ describe('Multisig', () => {
|
|
|
2196
2478
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2197
2479
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2198
2480
|
};
|
|
2199
|
-
const multisig =
|
|
2481
|
+
const multisig = createTestMultisig(config);
|
|
2200
2482
|
const readyDelta = {
|
|
2201
2483
|
account_id: '0x' + 'a'.repeat(30),
|
|
2202
2484
|
nonce: 1,
|
|
@@ -2258,7 +2540,11 @@ describe('Multisig', () => {
|
|
|
2258
2540
|
scheme: 'ecdsa',
|
|
2259
2541
|
publicKey: '0x' + '2'.repeat(66),
|
|
2260
2542
|
};
|
|
2261
|
-
const multisig =
|
|
2543
|
+
const multisig = createTestMultisig(config, ecdsaSigner, undefined, {
|
|
2544
|
+
kind: 'remote',
|
|
2545
|
+
maxAttempts: 2,
|
|
2546
|
+
createProver: () => ({}),
|
|
2547
|
+
});
|
|
2262
2548
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2263
2549
|
const cosignerPubkey = '0x' + '3'.repeat(66);
|
|
2264
2550
|
const ackPubkey = '0x' + '4'.repeat(66);
|
|
@@ -2321,9 +2607,22 @@ describe('Multisig', () => {
|
|
|
2321
2607
|
ack_scheme: 'ecdsa',
|
|
2322
2608
|
}),
|
|
2323
2609
|
});
|
|
2324
|
-
mockWebClient.
|
|
2325
|
-
|
|
2326
|
-
|
|
2610
|
+
mockWebClient.proveTransaction
|
|
2611
|
+
.mockRejectedValueOnce(Object.assign(new Error('unavailable'), { code: 'Unavailable' }))
|
|
2612
|
+
.mockResolvedValueOnce({});
|
|
2613
|
+
vi.useFakeTimers();
|
|
2614
|
+
try {
|
|
2615
|
+
const execution = multisig.executeProposal(proposalId);
|
|
2616
|
+
await vi.runAllTimersAsync();
|
|
2617
|
+
await expect(execution).resolves.toBeUndefined();
|
|
2618
|
+
}
|
|
2619
|
+
finally {
|
|
2620
|
+
vi.useRealTimers();
|
|
2621
|
+
}
|
|
2622
|
+
expect(mockWebClient.executeTransaction).toHaveBeenCalledTimes(1);
|
|
2623
|
+
expect(mockWebClient.proveTransaction).toHaveBeenCalledTimes(2);
|
|
2624
|
+
expect(mockWebClient.submitProvenTransaction).toHaveBeenCalledTimes(1);
|
|
2625
|
+
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2327
2626
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(1, cosignerSignature, 'ecdsa');
|
|
2328
2627
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(2, ackSignature, 'ecdsa');
|
|
2329
2628
|
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything(), cosignerPubkey, cosignerSignature);
|
|
@@ -2344,7 +2643,7 @@ describe('Multisig', () => {
|
|
|
2344
2643
|
scheme: 'ecdsa',
|
|
2345
2644
|
publicKey: '0x' + '2'.repeat(66),
|
|
2346
2645
|
};
|
|
2347
|
-
const multisig =
|
|
2646
|
+
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
2348
2647
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2349
2648
|
const cosignerPubkey = '0x' + '3'.repeat(66);
|
|
2350
2649
|
const ackPubkey = '0x' + '4'.repeat(66);
|
|
@@ -2404,8 +2703,11 @@ describe('Multisig', () => {
|
|
|
2404
2703
|
ack_scheme: 'ecdsa',
|
|
2405
2704
|
}),
|
|
2406
2705
|
});
|
|
2407
|
-
mockWebClient.transactions.submit.mockResolvedValueOnce({});
|
|
2408
2706
|
await expect(multisig.executeProposal(proposalId)).resolves.toBeUndefined();
|
|
2707
|
+
expect(mockWebClient.executeTransaction).toHaveBeenCalledTimes(1);
|
|
2708
|
+
expect(mockWebClient.proveTransaction).toHaveBeenCalledTimes(1);
|
|
2709
|
+
expect(mockWebClient.submitProvenTransaction).toHaveBeenCalledTimes(1);
|
|
2710
|
+
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2409
2711
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(1, cosignerSignature, 'ecdsa');
|
|
2410
2712
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(2, ackSignature, 'ecdsa');
|
|
2411
2713
|
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything(), cosignerPubkey, cosignerSignature);
|
|
@@ -2417,7 +2719,7 @@ describe('Multisig', () => {
|
|
|
2417
2719
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2418
2720
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2419
2721
|
};
|
|
2420
|
-
const multisig =
|
|
2722
|
+
const multisig = createTestMultisig(config);
|
|
2421
2723
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2422
2724
|
const newGuardianPubkey = '0x' + '1'.repeat(64);
|
|
2423
2725
|
multisig.proposals.set(proposalId, {
|
|
@@ -2485,9 +2787,11 @@ describe('Multisig', () => {
|
|
|
2485
2787
|
ok: true,
|
|
2486
2788
|
json: async () => ({ success: true, message: 'ok', ack_pubkey: '0x' + 'f'.repeat(64) }),
|
|
2487
2789
|
});
|
|
2488
|
-
mockWebClient.transactions.submit.mockResolvedValueOnce({});
|
|
2489
2790
|
await expect(multisig.executeProposal(proposalId)).resolves.toBeUndefined();
|
|
2490
|
-
expect(mockWebClient.
|
|
2791
|
+
expect(mockWebClient.executeTransaction).toHaveBeenCalledTimes(1);
|
|
2792
|
+
expect(mockWebClient.proveTransaction).toHaveBeenCalledTimes(1);
|
|
2793
|
+
expect(mockWebClient.submitProvenTransaction).toHaveBeenCalledTimes(1);
|
|
2794
|
+
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2491
2795
|
});
|
|
2492
2796
|
it('should still switch GUARDIAN when the pre-switch canonicalization push fails', async () => {
|
|
2493
2797
|
const config = {
|
|
@@ -2495,7 +2799,7 @@ describe('Multisig', () => {
|
|
|
2495
2799
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2496
2800
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2497
2801
|
};
|
|
2498
|
-
const multisig =
|
|
2802
|
+
const multisig = createTestMultisig(config);
|
|
2499
2803
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2500
2804
|
const newGuardianPubkey = '0x' + '1'.repeat(64);
|
|
2501
2805
|
multisig.proposals.set(proposalId, {
|
|
@@ -2531,9 +2835,11 @@ describe('Multisig', () => {
|
|
|
2531
2835
|
ok: true,
|
|
2532
2836
|
json: async () => ({ success: true, message: 'ok', ack_pubkey: '0x' + 'f'.repeat(64) }),
|
|
2533
2837
|
});
|
|
2534
|
-
mockWebClient.transactions.submit.mockResolvedValueOnce({});
|
|
2535
2838
|
await expect(multisig.executeProposal(proposalId)).resolves.toBeUndefined();
|
|
2536
|
-
expect(mockWebClient.
|
|
2839
|
+
expect(mockWebClient.executeTransaction).toHaveBeenCalledTimes(1);
|
|
2840
|
+
expect(mockWebClient.proveTransaction).toHaveBeenCalledTimes(1);
|
|
2841
|
+
expect(mockWebClient.submitProvenTransaction).toHaveBeenCalledTimes(1);
|
|
2842
|
+
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2537
2843
|
});
|
|
2538
2844
|
it('should reject switch_guardian execution when endpoint commitment mismatches', async () => {
|
|
2539
2845
|
const config = {
|
|
@@ -2541,7 +2847,7 @@ describe('Multisig', () => {
|
|
|
2541
2847
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2542
2848
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2543
2849
|
};
|
|
2544
|
-
const multisig =
|
|
2850
|
+
const multisig = createTestMultisig(config);
|
|
2545
2851
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2546
2852
|
multisig.proposals.set(proposalId, {
|
|
2547
2853
|
id: proposalId,
|
|
@@ -2576,7 +2882,7 @@ describe('Multisig', () => {
|
|
|
2576
2882
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2577
2883
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2578
2884
|
};
|
|
2579
|
-
const multisig =
|
|
2885
|
+
const multisig = createTestMultisig(config);
|
|
2580
2886
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2581
2887
|
multisig.proposals.set(proposalId, {
|
|
2582
2888
|
id: proposalId,
|
|
@@ -2621,7 +2927,7 @@ describe('Multisig', () => {
|
|
|
2621
2927
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2622
2928
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2623
2929
|
};
|
|
2624
|
-
const multisig =
|
|
2930
|
+
const multisig = createTestMultisig(config);
|
|
2625
2931
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2626
2932
|
multisig.proposals.set(proposalId, {
|
|
2627
2933
|
id: proposalId,
|
|
@@ -2651,6 +2957,38 @@ describe('Multisig', () => {
|
|
|
2651
2957
|
await expect(multisig.executeProposal(proposalId)).rejects.toThrow('Duplicate advice-map key detected');
|
|
2652
2958
|
});
|
|
2653
2959
|
});
|
|
2960
|
+
describe('submitTransaction', () => {
|
|
2961
|
+
it('uses the configured total proof-attempt budget without repeating other stages', async () => {
|
|
2962
|
+
const multisig = createTestMultisig({
|
|
2963
|
+
threshold: 1,
|
|
2964
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2965
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2966
|
+
}, mockSigner, undefined, {
|
|
2967
|
+
kind: 'remote',
|
|
2968
|
+
maxAttempts: 4,
|
|
2969
|
+
createProver: () => ({}),
|
|
2970
|
+
});
|
|
2971
|
+
const transient = Object.assign(new Error('unavailable'), { code: 'Unavailable' });
|
|
2972
|
+
mockWebClient.proveTransaction
|
|
2973
|
+
.mockRejectedValueOnce(transient)
|
|
2974
|
+
.mockRejectedValueOnce(transient)
|
|
2975
|
+
.mockRejectedValueOnce(transient)
|
|
2976
|
+
.mockResolvedValueOnce({});
|
|
2977
|
+
vi.useFakeTimers();
|
|
2978
|
+
try {
|
|
2979
|
+
const submission = multisig.submitTransaction({});
|
|
2980
|
+
await vi.runAllTimersAsync();
|
|
2981
|
+
await submission;
|
|
2982
|
+
}
|
|
2983
|
+
finally {
|
|
2984
|
+
vi.useRealTimers();
|
|
2985
|
+
}
|
|
2986
|
+
expect(mockWebClient.executeTransaction).toHaveBeenCalledTimes(1);
|
|
2987
|
+
expect(mockWebClient.proveTransaction).toHaveBeenCalledTimes(4);
|
|
2988
|
+
expect(mockWebClient.submitProvenTransaction).toHaveBeenCalledTimes(1);
|
|
2989
|
+
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2990
|
+
});
|
|
2991
|
+
});
|
|
2654
2992
|
describe('prepareCustomExecution', () => {
|
|
2655
2993
|
const requestBytes = new Uint8Array([9, 8, 7]);
|
|
2656
2994
|
function customDelta(proposalType, cosignerSigs) {
|
|
@@ -2687,7 +3025,7 @@ describe('Multisig', () => {
|
|
|
2687
3025
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2688
3026
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2689
3027
|
};
|
|
2690
|
-
const multisig =
|
|
3028
|
+
const multisig = createTestMultisig(config);
|
|
2691
3029
|
const builtinDelta = {
|
|
2692
3030
|
...customDelta('change_threshold', [falconSig('0x' + 'a'.repeat(64))]),
|
|
2693
3031
|
};
|
|
@@ -2709,7 +3047,7 @@ describe('Multisig', () => {
|
|
|
2709
3047
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2710
3048
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2711
3049
|
};
|
|
2712
|
-
const multisig =
|
|
3050
|
+
const multisig = createTestMultisig(config);
|
|
2713
3051
|
mockFetch.mockResolvedValueOnce({
|
|
2714
3052
|
ok: true,
|
|
2715
3053
|
json: async () => customDelta('b2agg', [falconSig('0x' + 'a'.repeat(64))]),
|
|
@@ -2722,7 +3060,7 @@ describe('Multisig', () => {
|
|
|
2722
3060
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2723
3061
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2724
3062
|
};
|
|
2725
|
-
const multisig =
|
|
3063
|
+
const multisig = createTestMultisig(config);
|
|
2726
3064
|
// Signed commitment comes from TransactionSummary.deserialize -> 'c' * 64.
|
|
2727
3065
|
// Make the binding request derive a different commitment so the check fails.
|
|
2728
3066
|
vi.mocked(executeForSummary).mockResolvedValueOnce({
|
|
@@ -2743,7 +3081,7 @@ describe('Multisig', () => {
|
|
|
2743
3081
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2744
3082
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2745
3083
|
};
|
|
2746
|
-
const multisig =
|
|
3084
|
+
const multisig = createTestMultisig(config);
|
|
2747
3085
|
const ready = customDelta('b2agg', [falconSig('0x' + 'a'.repeat(64))]);
|
|
2748
3086
|
// getDeltaProposal
|
|
2749
3087
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -2765,7 +3103,7 @@ describe('Multisig', () => {
|
|
|
2765
3103
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2766
3104
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2767
3105
|
};
|
|
2768
|
-
const multisig =
|
|
3106
|
+
const multisig = createTestMultisig(config);
|
|
2769
3107
|
// Create a proposal with metadata
|
|
2770
3108
|
const mockDelta = {
|
|
2771
3109
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -2818,7 +3156,7 @@ describe('Multisig', () => {
|
|
|
2818
3156
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2819
3157
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2820
3158
|
};
|
|
2821
|
-
const multisig =
|
|
3159
|
+
const multisig = createTestMultisig(config);
|
|
2822
3160
|
// Sync proposals - no local proposals exist
|
|
2823
3161
|
const mockProposals = [
|
|
2824
3162
|
{
|
|
@@ -2859,7 +3197,7 @@ describe('Multisig', () => {
|
|
|
2859
3197
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2860
3198
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2861
3199
|
};
|
|
2862
|
-
const multisig =
|
|
3200
|
+
const multisig = createTestMultisig(config);
|
|
2863
3201
|
const mockDelta = {
|
|
2864
3202
|
account_id: '0x' + 'a'.repeat(30),
|
|
2865
3203
|
nonce: 1,
|
|
@@ -2901,7 +3239,7 @@ describe('Multisig', () => {
|
|
|
2901
3239
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2902
3240
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2903
3241
|
};
|
|
2904
|
-
const multisig =
|
|
3242
|
+
const multisig = createTestMultisig(config);
|
|
2905
3243
|
const mockDelta = {
|
|
2906
3244
|
account_id: '0x' + 'a'.repeat(30),
|
|
2907
3245
|
nonce: 1,
|
|
@@ -2945,7 +3283,7 @@ describe('Multisig', () => {
|
|
|
2945
3283
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2946
3284
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2947
3285
|
};
|
|
2948
|
-
const multisig =
|
|
3286
|
+
const multisig = createTestMultisig(config);
|
|
2949
3287
|
const mockDelta = {
|
|
2950
3288
|
account_id: '0x' + 'a'.repeat(30),
|
|
2951
3289
|
nonce: 1,
|
|
@@ -2990,7 +3328,7 @@ describe('Multisig', () => {
|
|
|
2990
3328
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
2991
3329
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
2992
3330
|
};
|
|
2993
|
-
const multisig =
|
|
3331
|
+
const multisig = createTestMultisig(config);
|
|
2994
3332
|
// First sync with 1 signature (pending)
|
|
2995
3333
|
const mockProposalsPending = [
|
|
2996
3334
|
{
|
|
@@ -3074,7 +3412,7 @@ describe('Multisig', () => {
|
|
|
3074
3412
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64), '0x' + 'c'.repeat(64)],
|
|
3075
3413
|
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
3076
3414
|
};
|
|
3077
|
-
const multisig =
|
|
3415
|
+
const multisig = createTestMultisig(config);
|
|
3078
3416
|
expect(multisig.threshold).toBe(3);
|
|
3079
3417
|
});
|
|
3080
3418
|
it('should expose signerCommitments', () => {
|
|
@@ -3084,7 +3422,7 @@ describe('Multisig', () => {
|
|
|
3084
3422
|
signerCommitments,
|
|
3085
3423
|
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
3086
3424
|
};
|
|
3087
|
-
const multisig =
|
|
3425
|
+
const multisig = createTestMultisig(config);
|
|
3088
3426
|
expect(multisig.signerCommitments).toEqual(signerCommitments);
|
|
3089
3427
|
});
|
|
3090
3428
|
it('should expose guardianCommitment', () => {
|
|
@@ -3094,7 +3432,7 @@ describe('Multisig', () => {
|
|
|
3094
3432
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
3095
3433
|
guardianCommitment,
|
|
3096
3434
|
};
|
|
3097
|
-
const multisig =
|
|
3435
|
+
const multisig = createTestMultisig(config);
|
|
3098
3436
|
expect(multisig.guardianCommitment).toBe(guardianCommitment);
|
|
3099
3437
|
});
|
|
3100
3438
|
it('should expose account when provided', () => {
|
|
@@ -3103,7 +3441,7 @@ describe('Multisig', () => {
|
|
|
3103
3441
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
3104
3442
|
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
3105
3443
|
};
|
|
3106
|
-
const multisig =
|
|
3444
|
+
const multisig = createTestMultisig(config);
|
|
3107
3445
|
expect(multisig.account).toBe(mockAccount);
|
|
3108
3446
|
});
|
|
3109
3447
|
});
|
|
@@ -3114,7 +3452,7 @@ describe('Multisig', () => {
|
|
|
3114
3452
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
3115
3453
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
3116
3454
|
};
|
|
3117
|
-
const multisig =
|
|
3455
|
+
const multisig = createTestMultisig(config);
|
|
3118
3456
|
// Simulates a GUARDIAN response with canonical snake_case metadata
|
|
3119
3457
|
const rustProposals = [
|
|
3120
3458
|
{
|
|
@@ -3158,7 +3496,7 @@ describe('Multisig', () => {
|
|
|
3158
3496
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
3159
3497
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
3160
3498
|
};
|
|
3161
|
-
const multisig =
|
|
3499
|
+
const multisig = createTestMultisig(config);
|
|
3162
3500
|
// P2ID proposal with canonical snake_case fields
|
|
3163
3501
|
const p2idProposals = [
|
|
3164
3502
|
{
|
|
@@ -3209,7 +3547,7 @@ describe('Multisig', () => {
|
|
|
3209
3547
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
3210
3548
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
3211
3549
|
};
|
|
3212
|
-
const multisig =
|
|
3550
|
+
const multisig = createTestMultisig(config);
|
|
3213
3551
|
const switchGuardianProposals = [
|
|
3214
3552
|
{
|
|
3215
3553
|
account_id: '0x' + 'a'.repeat(30),
|