@openzeppelin/miden-multisig-client 0.16.2 → 0.17.0-rc.2
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 +144 -7
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +32 -25
- package/dist/account/builder.js.map +1 -1
- package/dist/account/builder.test.js +73 -21
- package/dist/account/builder.test.js.map +1 -1
- package/dist/account/layout.d.ts +31 -0
- package/dist/account/layout.d.ts.map +1 -0
- package/dist/account/layout.js +31 -0
- package/dist/account/layout.js.map +1 -0
- package/dist/account/masm/account-components/auth.d.ts +1 -4
- package/dist/account/masm/account-components/auth.d.ts.map +1 -1
- package/dist/account/masm/account-components/auth.js +36 -53
- package/dist/account/masm/account-components/auth.js.map +1 -1
- package/dist/account/masm/index.d.ts +0 -1
- package/dist/account/masm/index.d.ts.map +1 -1
- package/dist/account/masm/index.js +0 -1
- package/dist/account/masm/index.js.map +1 -1
- package/dist/account/storage.d.ts +4 -0
- package/dist/account/storage.d.ts.map +1 -1
- package/dist/account/storage.js +9 -20
- package/dist/account/storage.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -3
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +68 -15
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/inspector.d.ts +59 -1
- package/dist/inspector.d.ts.map +1 -1
- package/dist/inspector.js +157 -29
- package/dist/inspector.js.map +1 -1
- package/dist/inspector.test.js +246 -37
- package/dist/inspector.test.js.map +1 -1
- package/dist/multisig.d.ts +106 -32
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +289 -95
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +581 -58
- package/dist/multisig.test.js.map +1 -1
- package/dist/procedures.d.ts +6 -7
- package/dist/procedures.d.ts.map +1 -1
- package/dist/procedures.js +6 -7
- package/dist/procedures.js.map +1 -1
- package/dist/proposal/metadata.d.ts.map +1 -1
- package/dist/proposal/metadata.js +13 -2
- package/dist/proposal/metadata.js.map +1 -1
- package/dist/proposal/metadata.test.js +57 -0
- package/dist/proposal/metadata.test.js.map +1 -1
- package/dist/prover/workflow.d.ts +3 -2
- package/dist/prover/workflow.d.ts.map +1 -1
- package/dist/prover/workflow.js +5 -2
- package/dist/prover/workflow.js.map +1 -1
- package/dist/prover/workflow.test.js +6 -4
- package/dist/prover/workflow.test.js.map +1 -1
- package/dist/transaction/index.d.ts +1 -1
- package/dist/transaction/index.d.ts.map +1 -1
- package/dist/transaction/index.js +1 -1
- package/dist/transaction/index.js.map +1 -1
- package/dist/transaction/p2id.d.ts +17 -6
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +22 -31
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/p2id.test.js +62 -24
- package/dist/transaction/p2id.test.js.map +1 -1
- package/dist/transaction/summary.d.ts +45 -2
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +42 -2
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/summary.test.d.ts +2 -0
- package/dist/transaction/summary.test.d.ts.map +1 -0
- package/dist/transaction/summary.test.js +26 -0
- package/dist/transaction/summary.test.js.map +1 -0
- package/dist/transaction/updateGuardian.d.ts.map +1 -1
- package/dist/transaction/updateGuardian.js +16 -18
- package/dist/transaction/updateGuardian.js.map +1 -1
- package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
- package/dist/transaction/updateProcedureThreshold.js +15 -17
- package/dist/transaction/updateProcedureThreshold.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts +6 -1
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +20 -17
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.d.ts +2 -2
- 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 +28 -4
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types/proposal.js +18 -0
- package/dist/types/proposal.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/signature.d.ts +11 -7
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +24 -58
- package/dist/utils/signature.js.map +1 -1
- package/dist/utils/word.d.ts +7 -0
- package/dist/utils/word.d.ts.map +1 -1
- package/dist/utils/word.js +15 -0
- package/dist/utils/word.js.map +1 -1
- package/masm/account_components/auth/guarded_multisig.masm +42 -0
- package/package.json +7 -4
- package/src/account/builder.test.ts +111 -45
- package/src/account/builder.ts +45 -33
- package/src/account/layout.ts +33 -0
- package/src/account/masm/account-components/auth.ts +36 -56
- package/src/account/masm/index.ts +0 -1
- package/src/account/storage.ts +9 -22
- package/src/client.test.ts +80 -15
- package/src/client.ts +5 -3
- package/src/index.ts +26 -1
- package/src/inspector.test.ts +330 -38
- package/src/inspector.ts +196 -33
- package/src/multisig.test.ts +679 -63
- package/src/multisig.ts +361 -107
- package/src/procedures.ts +6 -7
- package/src/proposal/metadata.test.ts +76 -0
- package/src/proposal/metadata.ts +13 -2
- package/src/prover/workflow.test.ts +9 -4
- package/src/prover/workflow.ts +15 -3
- package/src/transaction/index.ts +7 -1
- package/src/transaction/p2id.test.ts +112 -31
- package/src/transaction/p2id.ts +39 -38
- package/src/transaction/summary.test.ts +32 -0
- package/src/transaction/summary.ts +83 -4
- package/src/transaction/updateGuardian.ts +15 -25
- package/src/transaction/updateProcedureThreshold.ts +13 -29
- package/src/transaction/updateSigners.ts +38 -30
- package/src/transaction.ts +8 -1
- package/src/types/proposal.ts +43 -4
- package/src/types.ts +0 -1
- package/src/utils/signature.ts +32 -65
- package/src/utils/word.ts +17 -0
- package/dist/account/masm/auth.d.ts +0 -5
- package/dist/account/masm/auth.d.ts.map +0 -1
- package/dist/account/masm/auth.js +0 -1509
- package/dist/account/masm/auth.js.map +0 -1
- package/masm/account_components/auth/multisig.masm +0 -12
- package/masm/account_components/auth/multisig_ecdsa.masm +0 -12
- package/masm/account_components/auth/multisig_guardian.masm +0 -16
- package/masm/account_components/auth/multisig_guardian_ecdsa.masm +0 -16
- package/masm/auth/guardian.masm +0 -199
- package/masm/auth/guardian_ecdsa.masm +0 -195
- package/masm/auth/multisig.masm +0 -554
- package/masm/auth/multisig_ecdsa.masm +0 -554
- package/src/account/masm/auth.ts +0 -1512
package/dist/multisig.test.js
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { Multisig } from './multisig.js';
|
|
3
3
|
import { GuardianHttpClient } from '@openzeppelin/guardian-client';
|
|
4
|
-
import { buildUpdateProcedureThresholdTransactionRequest, buildUpdateGuardianTransactionRequest, buildUpdateSignersTransactionRequest, executeForSummary, } from './transaction.js';
|
|
5
|
-
const { mockRpcGetAccountDetails, mockAccountDeserialize, mockDetectConfig, mockNoteFileDeserialize } = vi.hoisted(() => ({
|
|
4
|
+
import { buildUpdateProcedureThresholdTransactionRequest, buildUpdateGuardianTransactionRequest, buildUpdateSignersTransactionRequest, chainAnchorFromBase64, executeForSummary, executeForSummaryAt, } from './transaction.js';
|
|
5
|
+
const { mockRpcGetAccountDetails, mockAccountDeserialize, mockDetectConfig, mockNoteFileDeserialize, mockGetSignerCommitments, mockGetGuardianCommitment, } = vi.hoisted(() => ({
|
|
6
6
|
mockRpcGetAccountDetails: vi.fn(),
|
|
7
7
|
mockAccountDeserialize: vi.fn(),
|
|
8
8
|
mockDetectConfig: vi.fn(),
|
|
9
9
|
mockNoteFileDeserialize: vi.fn(),
|
|
10
|
+
mockGetSignerCommitments: vi.fn(),
|
|
11
|
+
mockGetGuardianCommitment: vi.fn(),
|
|
10
12
|
}));
|
|
13
|
+
const { MOCK_CHAIN_ANCHOR_B64, createMockChainAnchor } = vi.hoisted(() => {
|
|
14
|
+
const MOCK_CHAIN_ANCHOR_B64 = 'bW9jay1jaGFpbi1hbmNob3I=';
|
|
15
|
+
const createMockChainAnchor = () => ({
|
|
16
|
+
commitment: () => ({ toHex: () => '0x' + 'b'.repeat(64) }),
|
|
17
|
+
free: () => { },
|
|
18
|
+
serialize: () => new Uint8Array([9, 9, 9]),
|
|
19
|
+
});
|
|
20
|
+
return { MOCK_CHAIN_ANCHOR_B64, createMockChainAnchor };
|
|
21
|
+
});
|
|
11
22
|
// Mock the Miden SDK
|
|
12
23
|
vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
13
24
|
Account: {
|
|
@@ -33,9 +44,10 @@ vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
|
33
44
|
toCommitment: () => ({
|
|
34
45
|
toHex: () => '0x' + 'c'.repeat(64),
|
|
35
46
|
}),
|
|
36
|
-
|
|
37
|
-
toHex: () => '0x' + '
|
|
47
|
+
blockCommitment: () => ({
|
|
48
|
+
toHex: () => '0x' + 'b'.repeat(64),
|
|
38
49
|
}),
|
|
50
|
+
userParams: () => [0, 0, 0, 1, 2, 3, 4],
|
|
39
51
|
serialize: () => new Uint8Array([1, 2, 3]),
|
|
40
52
|
}),
|
|
41
53
|
},
|
|
@@ -70,6 +82,12 @@ vi.mock('@miden-sdk/miden-sdk', () => ({
|
|
|
70
82
|
// Mock transaction module
|
|
71
83
|
vi.mock('./transaction.js', () => ({
|
|
72
84
|
executeForSummary: vi.fn(),
|
|
85
|
+
executeForSummaryAt: vi.fn(),
|
|
86
|
+
chainAnchorToBase64: vi.fn(() => MOCK_CHAIN_ANCHOR_B64),
|
|
87
|
+
chainAnchorFromBase64: vi.fn(() => createMockChainAnchor()),
|
|
88
|
+
summarySalt: vi.fn(() => ({
|
|
89
|
+
toHex: () => '0x' + 'd'.repeat(64),
|
|
90
|
+
})),
|
|
73
91
|
buildUpdateSignersTransactionRequest: vi.fn().mockResolvedValue({
|
|
74
92
|
request: {},
|
|
75
93
|
salt: { toHex: () => '0x' + 'd'.repeat(64) },
|
|
@@ -115,6 +133,9 @@ vi.mock('./utils/signature.js', async () => {
|
|
|
115
133
|
values: [1, 2, 3],
|
|
116
134
|
})),
|
|
117
135
|
signatureHexToBytes: vi.fn((hex) => new Uint8Array([0, 1, 2, 3])),
|
|
136
|
+
// These tests use synthetic signature bytes to exercise advice routing;
|
|
137
|
+
// recoverability is covered by tests/ecdsa-advice-encoding.test.ts.
|
|
138
|
+
assertEcdsaSignatureRecoverable: vi.fn(),
|
|
118
139
|
};
|
|
119
140
|
});
|
|
120
141
|
vi.mock('./utils/encoding.js', async () => {
|
|
@@ -124,11 +145,19 @@ vi.mock('./utils/encoding.js', async () => {
|
|
|
124
145
|
normalizeHexWord: vi.fn((hex) => '0x' + hex.replace(/^0x/i, '').toLowerCase().padStart(64, '0')),
|
|
125
146
|
};
|
|
126
147
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
// Keep the real assertCompleteDetectedConfig so refreshConfigFromAccount's
|
|
149
|
+
// fail-closed validation is exercised.
|
|
150
|
+
vi.mock('./inspector.js', async (importOriginal) => {
|
|
151
|
+
const actual = await importOriginal();
|
|
152
|
+
return {
|
|
153
|
+
...actual,
|
|
154
|
+
AccountInspector: {
|
|
155
|
+
fromAccount: mockDetectConfig,
|
|
156
|
+
getSignerPublicKeyCommitments: mockGetSignerCommitments,
|
|
157
|
+
getGuardianPublicKeyCommitment: mockGetGuardianCommitment,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
});
|
|
132
161
|
// Mock fetch for GUARDIAN client
|
|
133
162
|
const mockFetch = vi.fn();
|
|
134
163
|
vi.stubGlobal('fetch', mockFetch);
|
|
@@ -157,6 +186,15 @@ describe('Multisig', () => {
|
|
|
157
186
|
beforeEach(() => {
|
|
158
187
|
mockFetch.mockReset();
|
|
159
188
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
189
|
+
summary: {
|
|
190
|
+
toCommitment: () => ({
|
|
191
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
192
|
+
}),
|
|
193
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
194
|
+
},
|
|
195
|
+
anchor: createMockChainAnchor(),
|
|
196
|
+
});
|
|
197
|
+
vi.mocked(executeForSummaryAt).mockResolvedValue({
|
|
160
198
|
toCommitment: () => ({
|
|
161
199
|
toHex: () => '0x' + 'c'.repeat(64),
|
|
162
200
|
}),
|
|
@@ -175,7 +213,6 @@ describe('Multisig', () => {
|
|
|
175
213
|
threshold: 1,
|
|
176
214
|
numSigners: 1,
|
|
177
215
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
178
|
-
guardianEnabled: true,
|
|
179
216
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
180
217
|
vaultBalances: [],
|
|
181
218
|
procedureThresholds: new Map(),
|
|
@@ -291,6 +328,34 @@ describe('Multisig', () => {
|
|
|
291
328
|
expect(multisig.accountId).toBe(accountId);
|
|
292
329
|
});
|
|
293
330
|
});
|
|
331
|
+
describe('history (issue #413)', () => {
|
|
332
|
+
it('delegates to the guardian client with the account id and options', async () => {
|
|
333
|
+
const config = {
|
|
334
|
+
threshold: 1,
|
|
335
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
336
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
337
|
+
};
|
|
338
|
+
const multisig = createTestMultisig(config);
|
|
339
|
+
const page = {
|
|
340
|
+
entries: [
|
|
341
|
+
{
|
|
342
|
+
nonce: 3,
|
|
343
|
+
status: 'canonical',
|
|
344
|
+
timestamp: '2026-08-01T12:00:03Z',
|
|
345
|
+
newCommitment: '0x' + 'b'.repeat(64),
|
|
346
|
+
inputNotes: [],
|
|
347
|
+
outputNotes: [],
|
|
348
|
+
decodeWarnings: [],
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
nextCursor: 'cursor-token',
|
|
352
|
+
};
|
|
353
|
+
const spy = vi.spyOn(guardian, 'getDeltaHistory').mockResolvedValue(page);
|
|
354
|
+
const result = await multisig.deltaHistory({ limit: 5, cursor: 'prev' });
|
|
355
|
+
expect(result).toBe(page);
|
|
356
|
+
expect(spy).toHaveBeenCalledWith('0x' + 'a'.repeat(30), { limit: 5, cursor: 'prev' });
|
|
357
|
+
});
|
|
358
|
+
});
|
|
294
359
|
describe('signerCommitment', () => {
|
|
295
360
|
it('should return signer commitment', () => {
|
|
296
361
|
const config = {
|
|
@@ -302,6 +367,45 @@ describe('Multisig', () => {
|
|
|
302
367
|
expect(multisig.signerCommitment).toBe(mockSigner.commitment);
|
|
303
368
|
});
|
|
304
369
|
});
|
|
370
|
+
describe('getSignerPublicKeyCommitments (issue #306)', () => {
|
|
371
|
+
const config = {
|
|
372
|
+
threshold: 1,
|
|
373
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
374
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
375
|
+
};
|
|
376
|
+
it('reads commitments from the store-backed account', async () => {
|
|
377
|
+
const storeAccount = mockedAccount('0x' + 'b'.repeat(64), 1);
|
|
378
|
+
mockWebClient.getAccount.mockResolvedValueOnce(storeAccount);
|
|
379
|
+
const expected = ['0x' + '1'.repeat(64), '0x' + '2'.repeat(64)];
|
|
380
|
+
mockGetSignerCommitments.mockReturnValueOnce(expected);
|
|
381
|
+
const multisig = createTestMultisig(config);
|
|
382
|
+
const commitments = await multisig.getSignerPublicKeyCommitments();
|
|
383
|
+
expect(commitments).toEqual(expected);
|
|
384
|
+
expect(mockGetSignerCommitments).toHaveBeenCalledWith(storeAccount);
|
|
385
|
+
});
|
|
386
|
+
it('falls back to the account snapshot when the store has no record', async () => {
|
|
387
|
+
mockWebClient.getAccount.mockResolvedValueOnce(null);
|
|
388
|
+
mockGetSignerCommitments.mockReturnValueOnce(['0x' + '3'.repeat(64)]);
|
|
389
|
+
const multisig = createTestMultisig(config);
|
|
390
|
+
await multisig.getSignerPublicKeyCommitments();
|
|
391
|
+
expect(mockGetSignerCommitments).toHaveBeenCalledWith(mockAccount);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe('getGuardianPublicKeyCommitment (issue #306)', () => {
|
|
395
|
+
it('reads the guardian commitment from the store-backed account', async () => {
|
|
396
|
+
const storeAccount = mockedAccount('0x' + 'b'.repeat(64), 1);
|
|
397
|
+
mockWebClient.getAccount.mockResolvedValueOnce(storeAccount);
|
|
398
|
+
mockGetGuardianCommitment.mockReturnValueOnce('0x' + '4'.repeat(64));
|
|
399
|
+
const multisig = createTestMultisig({
|
|
400
|
+
threshold: 1,
|
|
401
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
402
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
403
|
+
});
|
|
404
|
+
const commitment = await multisig.getGuardianPublicKeyCommitment();
|
|
405
|
+
expect(commitment).toBe('0x' + '4'.repeat(64));
|
|
406
|
+
expect(mockGetGuardianCommitment).toHaveBeenCalledWith(storeAccount);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
305
409
|
describe('fetchState', () => {
|
|
306
410
|
it('should fetch account state from GUARDIAN', async () => {
|
|
307
411
|
const config = {
|
|
@@ -397,7 +501,6 @@ describe('Multisig', () => {
|
|
|
397
501
|
threshold: 2,
|
|
398
502
|
numSigners: 2,
|
|
399
503
|
signerCommitments: ['0x' + '1'.repeat(64), '0x' + '2'.repeat(64)],
|
|
400
|
-
guardianEnabled: true,
|
|
401
504
|
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
402
505
|
vaultBalances: [],
|
|
403
506
|
procedureThresholds: new Map(),
|
|
@@ -411,6 +514,40 @@ describe('Multisig', () => {
|
|
|
411
514
|
expect(multisig.guardianCommitment).toBe('0x' + 'd'.repeat(64));
|
|
412
515
|
expect(mockWebClient.newAccount).not.toHaveBeenCalled();
|
|
413
516
|
});
|
|
517
|
+
it('keeps the previous config when a refresh reads an incomplete signer set (issue #306 review)', async () => {
|
|
518
|
+
const config = {
|
|
519
|
+
threshold: 1,
|
|
520
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
521
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
522
|
+
};
|
|
523
|
+
const multisig = createTestMultisig(config, mockSigner, '0x' + 'a'.repeat(30));
|
|
524
|
+
mockWebClient.getAccount.mockResolvedValueOnce(mockedAccount('0x' + 'b'.repeat(64), 0));
|
|
525
|
+
mockFetch.mockResolvedValueOnce({
|
|
526
|
+
ok: true,
|
|
527
|
+
json: async () => ({
|
|
528
|
+
account_id: multisig.accountId,
|
|
529
|
+
commitment: '0x' + 'b'.repeat(64),
|
|
530
|
+
state_json: { data: 'AQID' },
|
|
531
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
532
|
+
updated_at: '2024-01-02T00:00:00Z',
|
|
533
|
+
}),
|
|
534
|
+
});
|
|
535
|
+
// Storage reports 3 signers but only 1 entry was readable: adopting
|
|
536
|
+
// this would let membership proposals rewrite the on-chain set without
|
|
537
|
+
// the omitted keys. The refresh must keep the previous config instead.
|
|
538
|
+
mockDetectConfig.mockReturnValueOnce({
|
|
539
|
+
threshold: 2,
|
|
540
|
+
numSigners: 3,
|
|
541
|
+
signerCommitments: ['0x' + '1'.repeat(64)],
|
|
542
|
+
guardianCommitment: '0x' + 'd'.repeat(64),
|
|
543
|
+
vaultBalances: [],
|
|
544
|
+
procedureThresholds: new Map(),
|
|
545
|
+
});
|
|
546
|
+
await multisig.syncState();
|
|
547
|
+
expect(multisig.threshold).toBe(1);
|
|
548
|
+
expect(multisig.signerCommitments).toEqual(['0x' + 'a'.repeat(64)]);
|
|
549
|
+
expect(multisig.guardianCommitment).toBe('0x' + 'c'.repeat(64));
|
|
550
|
+
});
|
|
414
551
|
it('should overwrite local state when account is not found on-chain', async () => {
|
|
415
552
|
const config = {
|
|
416
553
|
threshold: 1,
|
|
@@ -686,6 +823,7 @@ describe('Multisig', () => {
|
|
|
686
823
|
signatures: [],
|
|
687
824
|
metadata: {
|
|
688
825
|
proposal_type: 'add_signer',
|
|
826
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
689
827
|
target_threshold: 1,
|
|
690
828
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
691
829
|
description: '',
|
|
@@ -731,6 +869,7 @@ describe('Multisig', () => {
|
|
|
731
869
|
signatures: [],
|
|
732
870
|
metadata: {
|
|
733
871
|
proposal_type: 'add_signer',
|
|
872
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
734
873
|
target_threshold: 1,
|
|
735
874
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
736
875
|
description: '',
|
|
@@ -777,6 +916,7 @@ describe('Multisig', () => {
|
|
|
777
916
|
signatures: [],
|
|
778
917
|
metadata: {
|
|
779
918
|
proposal_type: 'add_signer',
|
|
919
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
780
920
|
target_threshold: 1,
|
|
781
921
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
782
922
|
description: '',
|
|
@@ -792,13 +932,65 @@ describe('Multisig', () => {
|
|
|
792
932
|
],
|
|
793
933
|
}),
|
|
794
934
|
});
|
|
795
|
-
vi.mocked(
|
|
935
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
796
936
|
toCommitment: () => ({
|
|
797
937
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
798
938
|
}),
|
|
799
939
|
});
|
|
800
940
|
await expect(multisig.syncProposals()).rejects.toThrow('Invalid proposal: metadata does not match tx_summary');
|
|
801
941
|
});
|
|
942
|
+
/// A structurally valid anchor pinned to the wrong block must be rejected
|
|
943
|
+
/// before anything executes against it: its commitment disagrees with the
|
|
944
|
+
/// block commitment signed into the tx_summary.
|
|
945
|
+
it('should reject a proposal whose chain anchor does not match the summary block commitment', async () => {
|
|
946
|
+
const config = {
|
|
947
|
+
threshold: 1,
|
|
948
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
949
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
950
|
+
};
|
|
951
|
+
const multisig = createTestMultisig(config);
|
|
952
|
+
mockFetch.mockResolvedValueOnce({
|
|
953
|
+
ok: true,
|
|
954
|
+
json: async () => ({
|
|
955
|
+
proposals: [
|
|
956
|
+
{
|
|
957
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
958
|
+
nonce: 1,
|
|
959
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
960
|
+
delta_payload: {
|
|
961
|
+
tx_summary: { data: 'AQID' },
|
|
962
|
+
signatures: [],
|
|
963
|
+
metadata: {
|
|
964
|
+
proposal_type: 'add_signer',
|
|
965
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
966
|
+
target_threshold: 1,
|
|
967
|
+
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
968
|
+
description: '',
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
status: {
|
|
972
|
+
status: 'pending',
|
|
973
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
974
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
975
|
+
cosigner_sigs: [],
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
}),
|
|
980
|
+
});
|
|
981
|
+
// Deserializes fine, but pins a different block than the one bound into
|
|
982
|
+
// the summary (mock summary blockCommitment is 'b' * 64).
|
|
983
|
+
const freed = vi.fn();
|
|
984
|
+
vi.mocked(chainAnchorFromBase64).mockReturnValueOnce({
|
|
985
|
+
commitment: () => ({ toHex: () => '0x' + 'e'.repeat(64) }),
|
|
986
|
+
free: freed,
|
|
987
|
+
serialize: () => new Uint8Array([9, 9, 9]),
|
|
988
|
+
});
|
|
989
|
+
const reExecutionsBefore = vi.mocked(executeForSummaryAt).mock.calls.length;
|
|
990
|
+
await expect(multisig.syncProposals()).rejects.toThrow('chain anchor does not match the block commitment bound into the tx_summary');
|
|
991
|
+
expect(vi.mocked(executeForSummaryAt).mock.calls.length).toBe(reExecutionsBefore);
|
|
992
|
+
expect(freed).toHaveBeenCalledTimes(1);
|
|
993
|
+
});
|
|
802
994
|
it('should reject non-32-byte signer IDs from GUARDIAN proposals', async () => {
|
|
803
995
|
const config = {
|
|
804
996
|
threshold: 1,
|
|
@@ -816,6 +1008,7 @@ describe('Multisig', () => {
|
|
|
816
1008
|
signatures: [],
|
|
817
1009
|
metadata: {
|
|
818
1010
|
proposal_type: 'add_signer',
|
|
1011
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
819
1012
|
target_threshold: 1,
|
|
820
1013
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
821
1014
|
description: '',
|
|
@@ -858,6 +1051,7 @@ describe('Multisig', () => {
|
|
|
858
1051
|
signatures: [],
|
|
859
1052
|
metadata: {
|
|
860
1053
|
proposal_type: 'add_signer',
|
|
1054
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
861
1055
|
target_threshold: 2,
|
|
862
1056
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
863
1057
|
description: '',
|
|
@@ -932,6 +1126,7 @@ describe('Multisig', () => {
|
|
|
932
1126
|
});
|
|
933
1127
|
const proposal = await multisig.createProposal(1, 'AQID', {
|
|
934
1128
|
proposalType: 'add_signer',
|
|
1129
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
935
1130
|
targetThreshold: 1,
|
|
936
1131
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
937
1132
|
description: '',
|
|
@@ -970,6 +1165,7 @@ describe('Multisig', () => {
|
|
|
970
1165
|
});
|
|
971
1166
|
await expect(multisig.createProposal(1, 'AQID', {
|
|
972
1167
|
proposalType: 'add_signer',
|
|
1168
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
973
1169
|
targetThreshold: 1,
|
|
974
1170
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
975
1171
|
description: '',
|
|
@@ -1004,13 +1200,14 @@ describe('Multisig', () => {
|
|
|
1004
1200
|
commitment: '0x' + 'c'.repeat(64),
|
|
1005
1201
|
}),
|
|
1006
1202
|
});
|
|
1007
|
-
vi.mocked(
|
|
1203
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
1008
1204
|
toCommitment: () => ({
|
|
1009
1205
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
1010
1206
|
}),
|
|
1011
1207
|
});
|
|
1012
1208
|
await expect(multisig.createProposal(1, 'AQID', {
|
|
1013
1209
|
proposalType: 'add_signer',
|
|
1210
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1014
1211
|
targetThreshold: 1,
|
|
1015
1212
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1016
1213
|
description: '',
|
|
@@ -1021,10 +1218,13 @@ describe('Multisig', () => {
|
|
|
1021
1218
|
it('should include the faucet asset in the proposal description', async () => {
|
|
1022
1219
|
const { executeForSummary } = await import('./transaction.js');
|
|
1023
1220
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1221
|
+
summary: {
|
|
1222
|
+
toCommitment: () => ({
|
|
1223
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1224
|
+
}),
|
|
1225
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1226
|
+
},
|
|
1227
|
+
anchor: createMockChainAnchor(),
|
|
1028
1228
|
});
|
|
1029
1229
|
const config = {
|
|
1030
1230
|
threshold: 1,
|
|
@@ -1041,6 +1241,7 @@ describe('Multisig', () => {
|
|
|
1041
1241
|
signatures: [],
|
|
1042
1242
|
metadata: {
|
|
1043
1243
|
proposal_type: 'p2id',
|
|
1244
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1044
1245
|
recipient_id: '0xrecipient',
|
|
1045
1246
|
faucet_id: '0xfaucet',
|
|
1046
1247
|
amount: '100',
|
|
@@ -1061,17 +1262,20 @@ describe('Multisig', () => {
|
|
|
1061
1262
|
commitment: '0x' + 'c'.repeat(64),
|
|
1062
1263
|
}),
|
|
1063
1264
|
});
|
|
1064
|
-
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, 1);
|
|
1265
|
+
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, { nonce: 1 });
|
|
1065
1266
|
expect(proposal.metadata.description).toBe('Send 100 of asset 0xfaucet... to 0xrecipien...');
|
|
1066
1267
|
});
|
|
1067
1268
|
it('threads a private noteType into the request and wire metadata (issue #322)', async () => {
|
|
1068
1269
|
const { executeForSummary, buildP2idTransactionRequest } = await import('./transaction.js');
|
|
1069
1270
|
const { NoteType } = await import('@miden-sdk/miden-sdk');
|
|
1070
1271
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1272
|
+
summary: {
|
|
1273
|
+
toCommitment: () => ({
|
|
1274
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1275
|
+
}),
|
|
1276
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1277
|
+
},
|
|
1278
|
+
anchor: createMockChainAnchor(),
|
|
1075
1279
|
});
|
|
1076
1280
|
const config = {
|
|
1077
1281
|
threshold: 1,
|
|
@@ -1088,6 +1292,7 @@ describe('Multisig', () => {
|
|
|
1088
1292
|
signatures: [],
|
|
1089
1293
|
metadata: {
|
|
1090
1294
|
proposal_type: 'p2id',
|
|
1295
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1091
1296
|
recipient_id: '0xrecipient',
|
|
1092
1297
|
faucet_id: '0xfaucet',
|
|
1093
1298
|
amount: '100',
|
|
@@ -1109,14 +1314,15 @@ describe('Multisig', () => {
|
|
|
1109
1314
|
commitment: '0x' + 'c'.repeat(64),
|
|
1110
1315
|
}),
|
|
1111
1316
|
});
|
|
1112
|
-
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n,
|
|
1317
|
+
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, {
|
|
1318
|
+
nonce: 1,
|
|
1113
1319
|
noteType: NoteType.Private,
|
|
1114
1320
|
});
|
|
1115
1321
|
// Propose path builds the private note...
|
|
1116
|
-
expect(vi.mocked(buildP2idTransactionRequest)).toHaveBeenCalledWith(expect.any(String), '0xrecipient', '0xfaucet', 100n,
|
|
1322
|
+
expect(vi.mocked(buildP2idTransactionRequest)).toHaveBeenCalledWith(expect.any(String), '0xrecipient', '0xfaucet', 100n, { noteType: NoteType.Private });
|
|
1117
1323
|
// ...and the rebuild-from-metadata path parses note_type back to Private.
|
|
1118
1324
|
const lastCall = vi.mocked(buildP2idTransactionRequest).mock.calls.at(-1);
|
|
1119
|
-
expect(lastCall[
|
|
1325
|
+
expect(lastCall[4]).toMatchObject({ noteType: NoteType.Private });
|
|
1120
1326
|
// The pushed wire metadata carries note_type so cosigners rebuild the
|
|
1121
1327
|
// same private note at verification/execution.
|
|
1122
1328
|
const pushBody = JSON.parse(mockFetch.mock.calls.at(-1)[1].body);
|
|
@@ -1124,6 +1330,121 @@ describe('Multisig', () => {
|
|
|
1124
1330
|
expect(proposal.metadata.proposalType).toBe('p2id');
|
|
1125
1331
|
expect(proposal.metadata.noteType).toBe('private');
|
|
1126
1332
|
});
|
|
1333
|
+
it('threads P2IDE reclaim/timelock heights into the request and wire metadata (issue #366)', async () => {
|
|
1334
|
+
const { executeForSummary, buildP2idTransactionRequest } = await import('./transaction.js');
|
|
1335
|
+
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1336
|
+
summary: {
|
|
1337
|
+
toCommitment: () => ({
|
|
1338
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1339
|
+
}),
|
|
1340
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1341
|
+
},
|
|
1342
|
+
anchor: createMockChainAnchor(),
|
|
1343
|
+
});
|
|
1344
|
+
const config = {
|
|
1345
|
+
threshold: 1,
|
|
1346
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1347
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1348
|
+
};
|
|
1349
|
+
const multisig = createTestMultisig(config);
|
|
1350
|
+
const mockDelta = {
|
|
1351
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
1352
|
+
nonce: 1,
|
|
1353
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
1354
|
+
delta_payload: {
|
|
1355
|
+
tx_summary: { data: 'AQID' },
|
|
1356
|
+
signatures: [],
|
|
1357
|
+
metadata: {
|
|
1358
|
+
proposal_type: 'p2id',
|
|
1359
|
+
recipient_id: '0xrecipient',
|
|
1360
|
+
faucet_id: '0xfaucet',
|
|
1361
|
+
amount: '100',
|
|
1362
|
+
reclaim_height: 12345,
|
|
1363
|
+
timelock_height: 700,
|
|
1364
|
+
description: '',
|
|
1365
|
+
},
|
|
1366
|
+
},
|
|
1367
|
+
status: {
|
|
1368
|
+
status: 'pending',
|
|
1369
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
1370
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
1371
|
+
cosigner_sigs: [],
|
|
1372
|
+
},
|
|
1373
|
+
};
|
|
1374
|
+
mockFetch.mockResolvedValueOnce({
|
|
1375
|
+
ok: true,
|
|
1376
|
+
json: async () => ({
|
|
1377
|
+
delta: mockDelta,
|
|
1378
|
+
commitment: '0x' + 'c'.repeat(64),
|
|
1379
|
+
}),
|
|
1380
|
+
});
|
|
1381
|
+
const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, {
|
|
1382
|
+
nonce: 1,
|
|
1383
|
+
reclaimHeight: 12345,
|
|
1384
|
+
timelockHeight: 700,
|
|
1385
|
+
});
|
|
1386
|
+
// Propose path builds the P2IDE note from the heights...
|
|
1387
|
+
const lastCall = vi.mocked(buildP2idTransactionRequest).mock.calls.at(-1);
|
|
1388
|
+
expect(lastCall[4]).toMatchObject({ reclaimHeight: 12345, timelockHeight: 700 });
|
|
1389
|
+
// ...and the pushed wire metadata carries the heights so cosigners
|
|
1390
|
+
// rebuild the same P2IDE note at verification/execution.
|
|
1391
|
+
const pushBody = JSON.parse(mockFetch.mock.calls.at(-1)[1].body);
|
|
1392
|
+
expect(pushBody.delta_payload.metadata.reclaim_height).toBe(12345);
|
|
1393
|
+
expect(pushBody.delta_payload.metadata.timelock_height).toBe(700);
|
|
1394
|
+
expect(proposal.metadata.proposalType).toBe('p2id');
|
|
1395
|
+
expect(proposal.metadata).toMatchObject({ reclaimHeight: 12345, timelockHeight: 700 });
|
|
1396
|
+
});
|
|
1397
|
+
it('omits the heights from wire metadata for a plain P2ID send (pre-#366 shape)', async () => {
|
|
1398
|
+
const { executeForSummary } = await import('./transaction.js');
|
|
1399
|
+
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1400
|
+
summary: {
|
|
1401
|
+
toCommitment: () => ({
|
|
1402
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1403
|
+
}),
|
|
1404
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1405
|
+
},
|
|
1406
|
+
anchor: createMockChainAnchor(),
|
|
1407
|
+
});
|
|
1408
|
+
const config = {
|
|
1409
|
+
threshold: 1,
|
|
1410
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1411
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1412
|
+
};
|
|
1413
|
+
const multisig = createTestMultisig(config);
|
|
1414
|
+
const mockDelta = {
|
|
1415
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
1416
|
+
nonce: 1,
|
|
1417
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
1418
|
+
delta_payload: {
|
|
1419
|
+
tx_summary: { data: 'AQID' },
|
|
1420
|
+
signatures: [],
|
|
1421
|
+
metadata: {
|
|
1422
|
+
proposal_type: 'p2id',
|
|
1423
|
+
recipient_id: '0xrecipient',
|
|
1424
|
+
faucet_id: '0xfaucet',
|
|
1425
|
+
amount: '100',
|
|
1426
|
+
description: '',
|
|
1427
|
+
},
|
|
1428
|
+
},
|
|
1429
|
+
status: {
|
|
1430
|
+
status: 'pending',
|
|
1431
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
1432
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
1433
|
+
cosigner_sigs: [],
|
|
1434
|
+
},
|
|
1435
|
+
};
|
|
1436
|
+
mockFetch.mockResolvedValueOnce({
|
|
1437
|
+
ok: true,
|
|
1438
|
+
json: async () => ({
|
|
1439
|
+
delta: mockDelta,
|
|
1440
|
+
commitment: '0x' + 'c'.repeat(64),
|
|
1441
|
+
}),
|
|
1442
|
+
});
|
|
1443
|
+
await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, { nonce: 1 });
|
|
1444
|
+
const pushBody = JSON.parse(mockFetch.mock.calls.at(-1)[1].body);
|
|
1445
|
+
expect('reclaim_height' in pushBody.delta_payload.metadata).toBe(false);
|
|
1446
|
+
expect('timelock_height' in pushBody.delta_payload.metadata).toBe(false);
|
|
1447
|
+
});
|
|
1127
1448
|
});
|
|
1128
1449
|
describe('exportNoteToBytes / importNoteFromBytes (issue #356)', () => {
|
|
1129
1450
|
const config = {
|
|
@@ -1219,6 +1540,7 @@ describe('Multisig', () => {
|
|
|
1219
1540
|
const proposal = {
|
|
1220
1541
|
metadata: {
|
|
1221
1542
|
proposalType: 'p2id',
|
|
1543
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1222
1544
|
recipientId: '0x' + 'b'.repeat(30),
|
|
1223
1545
|
faucetId: '0x' + 'c'.repeat(30),
|
|
1224
1546
|
amount: '100',
|
|
@@ -1240,10 +1562,13 @@ describe('Multisig', () => {
|
|
|
1240
1562
|
describe('createChangeThresholdProposal', () => {
|
|
1241
1563
|
it('passes the signer scheme to update-signers requests', async () => {
|
|
1242
1564
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1565
|
+
summary: {
|
|
1566
|
+
toCommitment: () => ({
|
|
1567
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1568
|
+
}),
|
|
1569
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1570
|
+
},
|
|
1571
|
+
anchor: createMockChainAnchor(),
|
|
1247
1572
|
});
|
|
1248
1573
|
const ecdsaSigner = {
|
|
1249
1574
|
...mockSigner,
|
|
@@ -1265,6 +1590,7 @@ describe('Multisig', () => {
|
|
|
1265
1590
|
signatures: [],
|
|
1266
1591
|
metadata: {
|
|
1267
1592
|
proposal_type: 'change_threshold',
|
|
1593
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1268
1594
|
target_threshold: 2,
|
|
1269
1595
|
description: '',
|
|
1270
1596
|
},
|
|
@@ -1284,14 +1610,105 @@ describe('Multisig', () => {
|
|
|
1284
1610
|
}),
|
|
1285
1611
|
});
|
|
1286
1612
|
const multisig = createTestMultisig(config, ecdsaSigner);
|
|
1287
|
-
await multisig.createChangeThresholdProposal(2, 1);
|
|
1613
|
+
await multisig.createChangeThresholdProposal(2, { nonce: 1 });
|
|
1288
1614
|
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, 2, config.signerCommitments, { signatureScheme: 'ecdsa' });
|
|
1289
1615
|
});
|
|
1290
1616
|
});
|
|
1617
|
+
describe('createAddSignerProposal / createRemoveSignerProposal', () => {
|
|
1618
|
+
const config = {
|
|
1619
|
+
threshold: 2,
|
|
1620
|
+
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64), '0x' + 'd'.repeat(64)],
|
|
1621
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1622
|
+
};
|
|
1623
|
+
const mockPushResponse = (proposalType) => {
|
|
1624
|
+
mockFetch.mockResolvedValueOnce({
|
|
1625
|
+
ok: true,
|
|
1626
|
+
json: async () => ({
|
|
1627
|
+
delta: {
|
|
1628
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
1629
|
+
nonce: 1,
|
|
1630
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
1631
|
+
delta_payload: {
|
|
1632
|
+
tx_summary: { data: 'AQID' },
|
|
1633
|
+
signatures: [],
|
|
1634
|
+
metadata: { proposal_type: proposalType, description: '' },
|
|
1635
|
+
},
|
|
1636
|
+
status: {
|
|
1637
|
+
status: 'pending',
|
|
1638
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
1639
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
1640
|
+
cosigner_sigs: [],
|
|
1641
|
+
},
|
|
1642
|
+
},
|
|
1643
|
+
commitment: '0x' + 'c'.repeat(64),
|
|
1644
|
+
}),
|
|
1645
|
+
});
|
|
1646
|
+
};
|
|
1647
|
+
beforeEach(() => {
|
|
1648
|
+
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1649
|
+
summary: {
|
|
1650
|
+
toCommitment: () => ({ toHex: () => '0x' + 'c'.repeat(64) }),
|
|
1651
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1652
|
+
},
|
|
1653
|
+
anchor: createMockChainAnchor(),
|
|
1654
|
+
});
|
|
1655
|
+
});
|
|
1656
|
+
it('add: passes newThreshold from the options bag and appends the commitment', async () => {
|
|
1657
|
+
mockPushResponse('add_signer');
|
|
1658
|
+
const newCommitment = '0x' + 'e'.repeat(64);
|
|
1659
|
+
const multisig = createTestMultisig(config);
|
|
1660
|
+
await multisig.createAddSignerProposal(newCommitment, { nonce: 1, newThreshold: 3 });
|
|
1661
|
+
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, 3, [...config.signerCommitments, newCommitment], { signatureScheme: mockSigner.scheme });
|
|
1662
|
+
});
|
|
1663
|
+
it('add: defaults to the current threshold when newThreshold is omitted', async () => {
|
|
1664
|
+
mockPushResponse('add_signer');
|
|
1665
|
+
const multisig = createTestMultisig(config);
|
|
1666
|
+
await multisig.createAddSignerProposal('0x' + 'e'.repeat(64), { nonce: 1 });
|
|
1667
|
+
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, config.threshold, expect.any(Array), { signatureScheme: mockSigner.scheme });
|
|
1668
|
+
});
|
|
1669
|
+
it('remove: passes newThreshold from the options bag and drops the commitment', async () => {
|
|
1670
|
+
mockPushResponse('remove_signer');
|
|
1671
|
+
const multisig = createTestMultisig(config);
|
|
1672
|
+
await multisig.createRemoveSignerProposal('0x' + 'd'.repeat(64), { nonce: 1, newThreshold: 1 });
|
|
1673
|
+
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, 1, [config.signerCommitments[0], config.signerCommitments[1]], { signatureScheme: mockSigner.scheme });
|
|
1674
|
+
});
|
|
1675
|
+
it('remove: defaults to min(current threshold, remaining signers) when newThreshold is omitted', async () => {
|
|
1676
|
+
mockPushResponse('remove_signer');
|
|
1677
|
+
const multisig = createTestMultisig(config);
|
|
1678
|
+
await multisig.createRemoveSignerProposal('0x' + 'd'.repeat(64), { nonce: 1 });
|
|
1679
|
+
expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(mockWebClient, 2, expect.any(Array), { signatureScheme: mockSigner.scheme });
|
|
1680
|
+
});
|
|
1681
|
+
});
|
|
1682
|
+
describe('legacy positional-caller guard (issue #387)', () => {
|
|
1683
|
+
const config = {
|
|
1684
|
+
threshold: 1,
|
|
1685
|
+
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1686
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1687
|
+
};
|
|
1688
|
+
it('rejects a legacy positional nonce where the options bag is expected', async () => {
|
|
1689
|
+
const multisig = createTestMultisig(config);
|
|
1690
|
+
await expect(multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, 1)).rejects.toThrow(/issue #387/);
|
|
1691
|
+
await expect(multisig.createConsumeNotesProposal(['0x1'], 1)).rejects.toThrow(/issue #387/);
|
|
1692
|
+
await expect(multisig.createAddSignerProposal('0x' + 'e'.repeat(64), 1)).rejects.toThrow(/issue #387/);
|
|
1693
|
+
await expect(multisig.createCustomProposal(new Uint8Array([1]), 'label', 1)).rejects.toThrow(/issue #387/);
|
|
1694
|
+
});
|
|
1695
|
+
it('rejects a legacy trailing argument after the options slot', async () => {
|
|
1696
|
+
const multisig = createTestMultisig(config);
|
|
1697
|
+
// Pre-#387 pattern: createP2idProposal(r, f, amount, nonceHole, { noteType })
|
|
1698
|
+
await expect(multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, undefined, {
|
|
1699
|
+
noteType: 'private',
|
|
1700
|
+
})).rejects.toThrow(/issue #387/);
|
|
1701
|
+
// Pre-#387 pattern: createAddSignerProposal(commitment, nonceHole, newThreshold)
|
|
1702
|
+
await expect(multisig.createAddSignerProposal('0x' + 'e'.repeat(64), undefined, 3)).rejects.toThrow(/issue #387/);
|
|
1703
|
+
});
|
|
1704
|
+
});
|
|
1291
1705
|
describe('createSwitchGuardianProposal', () => {
|
|
1292
1706
|
it('should verify new endpoint commitment before creating proposal', async () => {
|
|
1293
1707
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1294
|
-
|
|
1708
|
+
summary: {
|
|
1709
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1710
|
+
},
|
|
1711
|
+
anchor: createMockChainAnchor(),
|
|
1295
1712
|
});
|
|
1296
1713
|
const config = {
|
|
1297
1714
|
threshold: 1,
|
|
@@ -1331,7 +1748,10 @@ describe('Multisig', () => {
|
|
|
1331
1748
|
});
|
|
1332
1749
|
it('should reject switch proposal when endpoint commitment does not match', async () => {
|
|
1333
1750
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1334
|
-
|
|
1751
|
+
summary: {
|
|
1752
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1753
|
+
},
|
|
1754
|
+
anchor: createMockChainAnchor(),
|
|
1335
1755
|
});
|
|
1336
1756
|
const config = {
|
|
1337
1757
|
threshold: 1,
|
|
@@ -1347,7 +1767,10 @@ describe('Multisig', () => {
|
|
|
1347
1767
|
});
|
|
1348
1768
|
it('should use the signer scheme when resolving new GUARDIAN commitments', async () => {
|
|
1349
1769
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1350
|
-
|
|
1770
|
+
summary: {
|
|
1771
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1772
|
+
},
|
|
1773
|
+
anchor: createMockChainAnchor(),
|
|
1351
1774
|
});
|
|
1352
1775
|
const ecdsaSigner = {
|
|
1353
1776
|
...mockSigner,
|
|
@@ -1392,10 +1815,13 @@ describe('Multisig', () => {
|
|
|
1392
1815
|
describe('createUpdateProcedureThresholdProposal', () => {
|
|
1393
1816
|
it('should create procedure-threshold update proposals', async () => {
|
|
1394
1817
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1818
|
+
summary: {
|
|
1819
|
+
toCommitment: () => ({
|
|
1820
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1821
|
+
}),
|
|
1822
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1823
|
+
},
|
|
1824
|
+
anchor: createMockChainAnchor(),
|
|
1399
1825
|
});
|
|
1400
1826
|
const config = {
|
|
1401
1827
|
threshold: 2,
|
|
@@ -1412,6 +1838,7 @@ describe('Multisig', () => {
|
|
|
1412
1838
|
signatures: [],
|
|
1413
1839
|
metadata: {
|
|
1414
1840
|
proposal_type: 'update_procedure_threshold',
|
|
1841
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1415
1842
|
target_threshold: 1,
|
|
1416
1843
|
target_procedure: 'send_asset',
|
|
1417
1844
|
description: '',
|
|
@@ -1431,7 +1858,7 @@ describe('Multisig', () => {
|
|
|
1431
1858
|
commitment: '0x' + 'c'.repeat(64),
|
|
1432
1859
|
}),
|
|
1433
1860
|
});
|
|
1434
|
-
const proposal = await multisig.createUpdateProcedureThresholdProposal('send_asset', 1, 1);
|
|
1861
|
+
const proposal = await multisig.createUpdateProcedureThresholdProposal('send_asset', 1, { nonce: 1 });
|
|
1435
1862
|
expect(buildUpdateProcedureThresholdTransactionRequest).toHaveBeenCalledWith(mockWebClient, 'send_asset', 1, { signatureScheme: 'falcon' });
|
|
1436
1863
|
expect(proposal.metadata.proposalType).toBe('update_procedure_threshold');
|
|
1437
1864
|
if (proposal.metadata.proposalType === 'update_procedure_threshold') {
|
|
@@ -1441,10 +1868,13 @@ describe('Multisig', () => {
|
|
|
1441
1868
|
});
|
|
1442
1869
|
it('passes the signer scheme to ECDSA procedure-threshold updates', async () => {
|
|
1443
1870
|
vi.mocked(executeForSummary).mockResolvedValue({
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1871
|
+
summary: {
|
|
1872
|
+
toCommitment: () => ({
|
|
1873
|
+
toHex: () => '0x' + 'c'.repeat(64),
|
|
1874
|
+
}),
|
|
1875
|
+
serialize: () => new Uint8Array([1, 2, 3]),
|
|
1876
|
+
},
|
|
1877
|
+
anchor: createMockChainAnchor(),
|
|
1448
1878
|
});
|
|
1449
1879
|
const ecdsaSigner = {
|
|
1450
1880
|
...mockSigner,
|
|
@@ -1467,6 +1897,7 @@ describe('Multisig', () => {
|
|
|
1467
1897
|
signatures: [],
|
|
1468
1898
|
metadata: {
|
|
1469
1899
|
proposal_type: 'update_procedure_threshold',
|
|
1900
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1470
1901
|
target_threshold: 1,
|
|
1471
1902
|
target_procedure: 'send_asset',
|
|
1472
1903
|
description: '',
|
|
@@ -1486,7 +1917,7 @@ describe('Multisig', () => {
|
|
|
1486
1917
|
commitment: '0x' + 'c'.repeat(64),
|
|
1487
1918
|
}),
|
|
1488
1919
|
});
|
|
1489
|
-
await multisig.createUpdateProcedureThresholdProposal('send_asset', 1, 1);
|
|
1920
|
+
await multisig.createUpdateProcedureThresholdProposal('send_asset', 1, { nonce: 1 });
|
|
1490
1921
|
expect(buildUpdateProcedureThresholdTransactionRequest).toHaveBeenCalledWith(mockWebClient, 'send_asset', 1, { signatureScheme: 'ecdsa' });
|
|
1491
1922
|
});
|
|
1492
1923
|
});
|
|
@@ -1523,6 +1954,7 @@ describe('Multisig', () => {
|
|
|
1523
1954
|
});
|
|
1524
1955
|
await multisig.createProposal(1, 'AQID', {
|
|
1525
1956
|
proposalType: 'add_signer',
|
|
1957
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1526
1958
|
targetThreshold: 1,
|
|
1527
1959
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1528
1960
|
description: '',
|
|
@@ -1545,6 +1977,7 @@ describe('Multisig', () => {
|
|
|
1545
1977
|
...mockDelta.delta_payload,
|
|
1546
1978
|
metadata: {
|
|
1547
1979
|
proposal_type: 'add_signer',
|
|
1980
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1548
1981
|
description: '',
|
|
1549
1982
|
target_threshold: 1,
|
|
1550
1983
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -1591,11 +2024,12 @@ describe('Multisig', () => {
|
|
|
1591
2024
|
});
|
|
1592
2025
|
await multisig.createProposal(1, 'AQID', {
|
|
1593
2026
|
proposalType: 'add_signer',
|
|
2027
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1594
2028
|
targetThreshold: 1,
|
|
1595
2029
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1596
2030
|
description: '',
|
|
1597
2031
|
});
|
|
1598
|
-
vi.mocked(
|
|
2032
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
1599
2033
|
toCommitment: () => ({
|
|
1600
2034
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
1601
2035
|
}),
|
|
@@ -1623,6 +2057,7 @@ describe('Multisig', () => {
|
|
|
1623
2057
|
signatures: [],
|
|
1624
2058
|
metadata: {
|
|
1625
2059
|
proposal_type: 'add_signer',
|
|
2060
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1626
2061
|
description: '',
|
|
1627
2062
|
target_threshold: 1,
|
|
1628
2063
|
signer_commitments: [mockSigner.commitment],
|
|
@@ -1650,7 +2085,7 @@ describe('Multisig', () => {
|
|
|
1650
2085
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1651
2086
|
};
|
|
1652
2087
|
const multisig = createTestMultisig(config);
|
|
1653
|
-
vi.mocked(
|
|
2088
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
1654
2089
|
toCommitment: () => ({
|
|
1655
2090
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
1656
2091
|
}),
|
|
@@ -1663,6 +2098,7 @@ describe('Multisig', () => {
|
|
|
1663
2098
|
signatures: [],
|
|
1664
2099
|
metadata: {
|
|
1665
2100
|
proposalType: 'add_signer',
|
|
2101
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1666
2102
|
targetThreshold: 1,
|
|
1667
2103
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1668
2104
|
description: '',
|
|
@@ -1678,7 +2114,7 @@ describe('Multisig', () => {
|
|
|
1678
2114
|
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
1679
2115
|
};
|
|
1680
2116
|
const multisig = createTestMultisig(config);
|
|
1681
|
-
vi.mocked(
|
|
2117
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
1682
2118
|
toCommitment: () => ({
|
|
1683
2119
|
toHex: () => '0x' + 'c'.repeat(64),
|
|
1684
2120
|
}),
|
|
@@ -1691,6 +2127,7 @@ describe('Multisig', () => {
|
|
|
1691
2127
|
signatures: [],
|
|
1692
2128
|
metadata: {
|
|
1693
2129
|
proposalType: 'add_signer',
|
|
2130
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1694
2131
|
targetThreshold: 1,
|
|
1695
2132
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1696
2133
|
description: '',
|
|
@@ -1698,11 +2135,12 @@ describe('Multisig', () => {
|
|
|
1698
2135
|
}));
|
|
1699
2136
|
proposal.metadata = {
|
|
1700
2137
|
proposalType: 'add_signer',
|
|
2138
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1701
2139
|
targetThreshold: 2,
|
|
1702
2140
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1703
2141
|
description: '',
|
|
1704
2142
|
};
|
|
1705
|
-
vi.mocked(
|
|
2143
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
1706
2144
|
toCommitment: () => ({
|
|
1707
2145
|
toHex: () => '0x' + 'f'.repeat(64),
|
|
1708
2146
|
}),
|
|
@@ -1728,6 +2166,7 @@ describe('Multisig', () => {
|
|
|
1728
2166
|
signatures: [],
|
|
1729
2167
|
metadata: {
|
|
1730
2168
|
proposal_type: 'add_signer',
|
|
2169
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1731
2170
|
description: '',
|
|
1732
2171
|
target_threshold: 1,
|
|
1733
2172
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -1777,6 +2216,7 @@ describe('Multisig', () => {
|
|
|
1777
2216
|
signatures: [],
|
|
1778
2217
|
metadata: {
|
|
1779
2218
|
proposal_type: 'change_threshold',
|
|
2219
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1780
2220
|
description: '',
|
|
1781
2221
|
target_threshold: 2,
|
|
1782
2222
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
@@ -1826,7 +2266,7 @@ describe('Multisig', () => {
|
|
|
1826
2266
|
// Feature 009: only a conforming { code, message, meta } envelope is
|
|
1827
2267
|
// folded into the error message; raw text bodies are dropped.
|
|
1828
2268
|
text: async () => JSON.stringify({
|
|
1829
|
-
code: '
|
|
2269
|
+
code: 'proposal_not_found',
|
|
1830
2270
|
message: 'Proposal not found',
|
|
1831
2271
|
meta: { retryable: false },
|
|
1832
2272
|
}),
|
|
@@ -1855,6 +2295,7 @@ describe('Multisig', () => {
|
|
|
1855
2295
|
],
|
|
1856
2296
|
metadata: {
|
|
1857
2297
|
proposalType: 'add_signer',
|
|
2298
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1858
2299
|
targetThreshold: 1,
|
|
1859
2300
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1860
2301
|
description: '',
|
|
@@ -1886,6 +2327,7 @@ describe('Multisig', () => {
|
|
|
1886
2327
|
],
|
|
1887
2328
|
metadata: {
|
|
1888
2329
|
proposalType: 'change_threshold',
|
|
2330
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1889
2331
|
targetThreshold: 1,
|
|
1890
2332
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1891
2333
|
description: '',
|
|
@@ -1924,6 +2366,7 @@ describe('Multisig', () => {
|
|
|
1924
2366
|
],
|
|
1925
2367
|
metadata: {
|
|
1926
2368
|
proposalType: 'change_threshold',
|
|
2369
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1927
2370
|
targetThreshold: 1,
|
|
1928
2371
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
1929
2372
|
description: '',
|
|
@@ -1945,6 +2388,7 @@ describe('Multisig', () => {
|
|
|
1945
2388
|
signatures: [],
|
|
1946
2389
|
metadata: {
|
|
1947
2390
|
proposalType: 'add_signer',
|
|
2391
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
1948
2392
|
targetThreshold: 2,
|
|
1949
2393
|
targetSignerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
1950
2394
|
description: '',
|
|
@@ -2010,6 +2454,7 @@ describe('Multisig', () => {
|
|
|
2010
2454
|
],
|
|
2011
2455
|
metadata: {
|
|
2012
2456
|
proposalType: 'change_threshold',
|
|
2457
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2013
2458
|
targetThreshold: 1,
|
|
2014
2459
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2015
2460
|
description: '',
|
|
@@ -2026,6 +2471,7 @@ describe('Multisig', () => {
|
|
|
2026
2471
|
signatures: [],
|
|
2027
2472
|
metadata: {
|
|
2028
2473
|
proposal_type: 'change_threshold',
|
|
2474
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2029
2475
|
target_threshold: 1,
|
|
2030
2476
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
2031
2477
|
},
|
|
@@ -2051,8 +2497,14 @@ describe('Multisig', () => {
|
|
|
2051
2497
|
await expect(multisig.createTransactionProposalRequest(requestedProposalId)).resolves.toBe(finalRequest);
|
|
2052
2498
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(1, cosignerSignature, 'ecdsa');
|
|
2053
2499
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(2, ackSignature, 'ecdsa');
|
|
2054
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything()
|
|
2055
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything()
|
|
2500
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything());
|
|
2501
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything());
|
|
2502
|
+
// The advice payload now comes from the SDK, so the routing assertion is
|
|
2503
|
+
// the commitment each entry is keyed on: cosigner first, GUARDIAN ack
|
|
2504
|
+
// second. Swapping the two entries must not pass.
|
|
2505
|
+
const adviceCalls = vi.mocked(buildSignatureAdviceEntry).mock.calls;
|
|
2506
|
+
expect(adviceCalls[0][0].toHex()).toBe(config.signerCommitments[0]);
|
|
2507
|
+
expect(adviceCalls[1][0].toHex()).toBe(config.guardianCommitment);
|
|
2056
2508
|
expect(mockWebClient.executeTransaction).not.toHaveBeenCalled();
|
|
2057
2509
|
expect(mockWebClient.proveTransaction).not.toHaveBeenCalled();
|
|
2058
2510
|
expect(mockWebClient.submitProvenTransaction).not.toHaveBeenCalled();
|
|
@@ -2090,6 +2542,7 @@ describe('Multisig', () => {
|
|
|
2090
2542
|
],
|
|
2091
2543
|
metadata: {
|
|
2092
2544
|
proposalType: 'switch_guardian',
|
|
2545
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2093
2546
|
newGuardianPubkey,
|
|
2094
2547
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2095
2548
|
description: '',
|
|
@@ -2132,6 +2585,7 @@ describe('Multisig', () => {
|
|
|
2132
2585
|
signatures: [],
|
|
2133
2586
|
metadata: {
|
|
2134
2587
|
proposal_type: 'add_signer',
|
|
2588
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2135
2589
|
description: '',
|
|
2136
2590
|
target_threshold: 2,
|
|
2137
2591
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
@@ -2166,7 +2620,7 @@ describe('Multisig', () => {
|
|
|
2166
2620
|
};
|
|
2167
2621
|
const multisig = createTestMultisig(config);
|
|
2168
2622
|
const proposalId = '0x' + 'c'.repeat(64);
|
|
2169
|
-
vi.mocked(
|
|
2623
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
2170
2624
|
toCommitment: () => ({
|
|
2171
2625
|
toHex: () => '0x' + 'd'.repeat(64),
|
|
2172
2626
|
}),
|
|
@@ -2186,6 +2640,7 @@ describe('Multisig', () => {
|
|
|
2186
2640
|
],
|
|
2187
2641
|
metadata: {
|
|
2188
2642
|
proposalType: 'change_threshold',
|
|
2643
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2189
2644
|
targetThreshold: 1,
|
|
2190
2645
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2191
2646
|
description: '',
|
|
@@ -2217,6 +2672,7 @@ describe('Multisig', () => {
|
|
|
2217
2672
|
],
|
|
2218
2673
|
metadata: {
|
|
2219
2674
|
proposalType: 'switch_guardian',
|
|
2675
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2220
2676
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2221
2677
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2222
2678
|
description: '',
|
|
@@ -2257,6 +2713,7 @@ describe('Multisig', () => {
|
|
|
2257
2713
|
],
|
|
2258
2714
|
metadata: {
|
|
2259
2715
|
proposalType: 'switch_guardian',
|
|
2716
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2260
2717
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2261
2718
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2262
2719
|
description: '',
|
|
@@ -2324,6 +2781,7 @@ describe('Multisig', () => {
|
|
|
2324
2781
|
],
|
|
2325
2782
|
metadata: {
|
|
2326
2783
|
proposalType: 'change_threshold',
|
|
2784
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2327
2785
|
targetThreshold: 1,
|
|
2328
2786
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2329
2787
|
description: '',
|
|
@@ -2340,6 +2798,7 @@ describe('Multisig', () => {
|
|
|
2340
2798
|
signatures: [],
|
|
2341
2799
|
metadata: {
|
|
2342
2800
|
proposal_type: 'change_threshold',
|
|
2801
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2343
2802
|
target_threshold: 1,
|
|
2344
2803
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
2345
2804
|
},
|
|
@@ -2410,6 +2869,7 @@ describe('Multisig', () => {
|
|
|
2410
2869
|
],
|
|
2411
2870
|
metadata: {
|
|
2412
2871
|
proposalType: 'switch_guardian',
|
|
2872
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2413
2873
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2414
2874
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2415
2875
|
description: '',
|
|
@@ -2446,6 +2906,7 @@ describe('Multisig', () => {
|
|
|
2446
2906
|
signatures: [],
|
|
2447
2907
|
metadata: {
|
|
2448
2908
|
proposal_type: 'add_signer',
|
|
2909
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2449
2910
|
description: '',
|
|
2450
2911
|
target_threshold: 2,
|
|
2451
2912
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
@@ -2473,7 +2934,7 @@ describe('Multisig', () => {
|
|
|
2473
2934
|
// Proposal ID is mocked to return 'c'.repeat(64)
|
|
2474
2935
|
await expect(multisig.executeProposal('0x' + 'c'.repeat(64))).rejects.toThrow('not ready for execution');
|
|
2475
2936
|
});
|
|
2476
|
-
it('should fail when GUARDIAN ack signature is missing
|
|
2937
|
+
it('should fail when GUARDIAN ack signature is missing', async () => {
|
|
2477
2938
|
const config = {
|
|
2478
2939
|
threshold: 1,
|
|
2479
2940
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -2489,6 +2950,7 @@ describe('Multisig', () => {
|
|
|
2489
2950
|
signatures: [],
|
|
2490
2951
|
metadata: {
|
|
2491
2952
|
proposal_type: 'add_signer',
|
|
2953
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2492
2954
|
description: '',
|
|
2493
2955
|
target_threshold: 1,
|
|
2494
2956
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -2570,6 +3032,7 @@ describe('Multisig', () => {
|
|
|
2570
3032
|
],
|
|
2571
3033
|
metadata: {
|
|
2572
3034
|
proposalType: 'change_threshold',
|
|
3035
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2573
3036
|
targetThreshold: 1,
|
|
2574
3037
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2575
3038
|
description: '',
|
|
@@ -2586,6 +3049,7 @@ describe('Multisig', () => {
|
|
|
2586
3049
|
signatures: [],
|
|
2587
3050
|
metadata: {
|
|
2588
3051
|
proposal_type: 'change_threshold',
|
|
3052
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2589
3053
|
target_threshold: 1,
|
|
2590
3054
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
2591
3055
|
},
|
|
@@ -2626,8 +3090,14 @@ describe('Multisig', () => {
|
|
|
2626
3090
|
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2627
3091
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(1, cosignerSignature, 'ecdsa');
|
|
2628
3092
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(2, ackSignature, 'ecdsa');
|
|
2629
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything()
|
|
2630
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything()
|
|
3093
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything());
|
|
3094
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything());
|
|
3095
|
+
// The advice payload now comes from the SDK, so the routing assertion is
|
|
3096
|
+
// the commitment each entry is keyed on: cosigner first, GUARDIAN ack
|
|
3097
|
+
// second. Swapping the two entries must not pass.
|
|
3098
|
+
const adviceCalls = vi.mocked(buildSignatureAdviceEntry).mock.calls;
|
|
3099
|
+
expect(adviceCalls[0][0].toHex()).toBe(config.signerCommitments[0]);
|
|
3100
|
+
expect(adviceCalls[1][0].toHex()).toBe(config.guardianCommitment);
|
|
2631
3101
|
});
|
|
2632
3102
|
it('should execute imported ECDSA proposals with scheme-aware advice', async () => {
|
|
2633
3103
|
const { buildSignatureAdviceEntry, signatureHexToBytes } = await import('./utils/signature.js');
|
|
@@ -2666,6 +3136,7 @@ describe('Multisig', () => {
|
|
|
2666
3136
|
],
|
|
2667
3137
|
metadata: {
|
|
2668
3138
|
proposalType: 'change_threshold',
|
|
3139
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2669
3140
|
targetThreshold: 1,
|
|
2670
3141
|
targetSignerCommitments: ['0x' + 'a'.repeat(64)],
|
|
2671
3142
|
description: '',
|
|
@@ -2682,6 +3153,7 @@ describe('Multisig', () => {
|
|
|
2682
3153
|
signatures: [],
|
|
2683
3154
|
metadata: {
|
|
2684
3155
|
proposal_type: 'change_threshold',
|
|
3156
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2685
3157
|
target_threshold: 1,
|
|
2686
3158
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
2687
3159
|
},
|
|
@@ -2711,8 +3183,14 @@ describe('Multisig', () => {
|
|
|
2711
3183
|
expect(mockWebClient.applyTransaction).toHaveBeenCalledTimes(1);
|
|
2712
3184
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(1, cosignerSignature, 'ecdsa');
|
|
2713
3185
|
expect(vi.mocked(signatureHexToBytes)).toHaveBeenNthCalledWith(2, ackSignature, 'ecdsa');
|
|
2714
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything()
|
|
2715
|
-
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything()
|
|
3186
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(1, expect.anything(), expect.anything(), expect.anything());
|
|
3187
|
+
expect(vi.mocked(buildSignatureAdviceEntry)).toHaveBeenNthCalledWith(2, expect.anything(), expect.anything(), expect.anything());
|
|
3188
|
+
// The advice payload now comes from the SDK, so the routing assertion is
|
|
3189
|
+
// the commitment each entry is keyed on: cosigner first, GUARDIAN ack
|
|
3190
|
+
// second. Swapping the two entries must not pass.
|
|
3191
|
+
const adviceCalls = vi.mocked(buildSignatureAdviceEntry).mock.calls;
|
|
3192
|
+
expect(adviceCalls[0][0].toHex()).toBe(config.signerCommitments[0]);
|
|
3193
|
+
expect(adviceCalls[1][0].toHex()).toBe(config.guardianCommitment);
|
|
2716
3194
|
});
|
|
2717
3195
|
it('should verify switch_guardian endpoint commitment before execution', async () => {
|
|
2718
3196
|
const config = {
|
|
@@ -2738,6 +3216,7 @@ describe('Multisig', () => {
|
|
|
2738
3216
|
],
|
|
2739
3217
|
metadata: {
|
|
2740
3218
|
proposalType: 'switch_guardian',
|
|
3219
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2741
3220
|
newGuardianPubkey,
|
|
2742
3221
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2743
3222
|
description: '',
|
|
@@ -2759,6 +3238,7 @@ describe('Multisig', () => {
|
|
|
2759
3238
|
signatures: [],
|
|
2760
3239
|
metadata: {
|
|
2761
3240
|
proposal_type: 'switch_guardian',
|
|
3241
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2762
3242
|
new_guardian_pubkey: newGuardianPubkey,
|
|
2763
3243
|
new_guardian_endpoint: 'http://new-guardian.com',
|
|
2764
3244
|
},
|
|
@@ -2818,6 +3298,7 @@ describe('Multisig', () => {
|
|
|
2818
3298
|
],
|
|
2819
3299
|
metadata: {
|
|
2820
3300
|
proposalType: 'switch_guardian',
|
|
3301
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2821
3302
|
newGuardianPubkey,
|
|
2822
3303
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2823
3304
|
description: '',
|
|
@@ -2865,6 +3346,7 @@ describe('Multisig', () => {
|
|
|
2865
3346
|
],
|
|
2866
3347
|
metadata: {
|
|
2867
3348
|
proposalType: 'switch_guardian',
|
|
3349
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2868
3350
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2869
3351
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2870
3352
|
description: '',
|
|
@@ -2905,6 +3387,7 @@ describe('Multisig', () => {
|
|
|
2905
3387
|
],
|
|
2906
3388
|
metadata: {
|
|
2907
3389
|
proposalType: 'switch_guardian',
|
|
3390
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2908
3391
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2909
3392
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2910
3393
|
description: '',
|
|
@@ -2950,6 +3433,7 @@ describe('Multisig', () => {
|
|
|
2950
3433
|
],
|
|
2951
3434
|
metadata: {
|
|
2952
3435
|
proposalType: 'switch_guardian',
|
|
3436
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
2953
3437
|
newGuardianPubkey: '0x' + '1'.repeat(64),
|
|
2954
3438
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
2955
3439
|
description: '',
|
|
@@ -2975,9 +3459,32 @@ describe('Multisig', () => {
|
|
|
2975
3459
|
.mockRejectedValueOnce(transient)
|
|
2976
3460
|
.mockRejectedValueOnce(transient)
|
|
2977
3461
|
.mockResolvedValueOnce({});
|
|
3462
|
+
mockFetch.mockResolvedValueOnce({
|
|
3463
|
+
ok: true,
|
|
3464
|
+
json: async () => ({
|
|
3465
|
+
account_id: '0x' + 'a'.repeat(30),
|
|
3466
|
+
nonce: 1,
|
|
3467
|
+
prev_commitment: '0x' + 'b'.repeat(64),
|
|
3468
|
+
delta_payload: {
|
|
3469
|
+
tx_summary: { data: 'AQID' },
|
|
3470
|
+
signatures: [],
|
|
3471
|
+
metadata: {
|
|
3472
|
+
proposal_type: 'b2agg',
|
|
3473
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3474
|
+
description: '',
|
|
3475
|
+
},
|
|
3476
|
+
},
|
|
3477
|
+
status: {
|
|
3478
|
+
status: 'pending',
|
|
3479
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
3480
|
+
proposer_id: '0x' + 'c'.repeat(64),
|
|
3481
|
+
cosigner_sigs: [],
|
|
3482
|
+
},
|
|
3483
|
+
}),
|
|
3484
|
+
});
|
|
2978
3485
|
vi.useFakeTimers();
|
|
2979
3486
|
try {
|
|
2980
|
-
const submission = multisig.submitTransaction({});
|
|
3487
|
+
const submission = multisig.submitTransaction('0x' + 'c'.repeat(64), {});
|
|
2981
3488
|
await vi.runAllTimersAsync();
|
|
2982
3489
|
await submission;
|
|
2983
3490
|
}
|
|
@@ -3002,6 +3509,7 @@ describe('Multisig', () => {
|
|
|
3002
3509
|
signatures: [],
|
|
3003
3510
|
metadata: {
|
|
3004
3511
|
proposal_type: proposalType,
|
|
3512
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3005
3513
|
description: '',
|
|
3006
3514
|
},
|
|
3007
3515
|
},
|
|
@@ -3032,6 +3540,7 @@ describe('Multisig', () => {
|
|
|
3032
3540
|
};
|
|
3033
3541
|
builtinDelta.delta_payload.metadata = {
|
|
3034
3542
|
proposal_type: 'change_threshold',
|
|
3543
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3035
3544
|
description: '',
|
|
3036
3545
|
target_threshold: 1,
|
|
3037
3546
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -3064,7 +3573,7 @@ describe('Multisig', () => {
|
|
|
3064
3573
|
const multisig = createTestMultisig(config);
|
|
3065
3574
|
// Signed commitment comes from TransactionSummary.deserialize -> 'c' * 64.
|
|
3066
3575
|
// Make the binding request derive a different commitment so the check fails.
|
|
3067
|
-
vi.mocked(
|
|
3576
|
+
vi.mocked(executeForSummaryAt).mockResolvedValueOnce({
|
|
3068
3577
|
toCommitment: () => ({
|
|
3069
3578
|
toHex: () => '0x' + '9'.repeat(64),
|
|
3070
3579
|
}),
|
|
@@ -3115,6 +3624,7 @@ describe('Multisig', () => {
|
|
|
3115
3624
|
signatures: [],
|
|
3116
3625
|
metadata: {
|
|
3117
3626
|
proposal_type: 'add_signer',
|
|
3627
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3118
3628
|
target_threshold: 2,
|
|
3119
3629
|
signer_commitments: ['0x1', '0x2'],
|
|
3120
3630
|
},
|
|
@@ -3135,6 +3645,7 @@ describe('Multisig', () => {
|
|
|
3135
3645
|
});
|
|
3136
3646
|
const proposal = await multisig.createProposal(1, 'AQID', {
|
|
3137
3647
|
proposalType: 'add_signer',
|
|
3648
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3138
3649
|
targetThreshold: 2,
|
|
3139
3650
|
targetSignerCommitments: ['0x1', '0x2'],
|
|
3140
3651
|
description: '',
|
|
@@ -3169,6 +3680,7 @@ describe('Multisig', () => {
|
|
|
3169
3680
|
signatures: [],
|
|
3170
3681
|
metadata: {
|
|
3171
3682
|
proposal_type: 'p2id',
|
|
3683
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3172
3684
|
recipient_id: '0xrecipient',
|
|
3173
3685
|
faucet_id: '0xfaucet',
|
|
3174
3686
|
amount: '100',
|
|
@@ -3208,6 +3720,7 @@ describe('Multisig', () => {
|
|
|
3208
3720
|
signatures: [],
|
|
3209
3721
|
metadata: {
|
|
3210
3722
|
proposal_type: 'add_signer',
|
|
3723
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3211
3724
|
target_threshold: 2,
|
|
3212
3725
|
signer_commitments: ['0x1', '0x2'],
|
|
3213
3726
|
description: '',
|
|
@@ -3229,6 +3742,7 @@ describe('Multisig', () => {
|
|
|
3229
3742
|
});
|
|
3230
3743
|
const proposal = await multisig.createProposal(1, 'AQID', {
|
|
3231
3744
|
proposalType: 'consume_notes',
|
|
3745
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3232
3746
|
noteIds: ['0xnote1', '0xnote2'],
|
|
3233
3747
|
description: '',
|
|
3234
3748
|
});
|
|
@@ -3250,6 +3764,7 @@ describe('Multisig', () => {
|
|
|
3250
3764
|
signatures: [],
|
|
3251
3765
|
metadata: {
|
|
3252
3766
|
proposal_type: 'add_signer',
|
|
3767
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3253
3768
|
target_threshold: 1,
|
|
3254
3769
|
signer_commitments: ['0x' + 'a'.repeat(64)],
|
|
3255
3770
|
description: '',
|
|
@@ -3271,6 +3786,7 @@ describe('Multisig', () => {
|
|
|
3271
3786
|
});
|
|
3272
3787
|
const proposal = await multisig.createProposal(1, 'AQID', {
|
|
3273
3788
|
proposalType: 'p2id',
|
|
3789
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3274
3790
|
recipientId: '0xrecipient',
|
|
3275
3791
|
faucetId: '0xfaucet',
|
|
3276
3792
|
amount: '100',
|
|
@@ -3294,6 +3810,7 @@ describe('Multisig', () => {
|
|
|
3294
3810
|
signatures: [],
|
|
3295
3811
|
metadata: {
|
|
3296
3812
|
proposalType: 'add_signer',
|
|
3813
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3297
3814
|
targetThreshold: 2,
|
|
3298
3815
|
targetSignerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
3299
3816
|
description: '',
|
|
@@ -3315,6 +3832,7 @@ describe('Multisig', () => {
|
|
|
3315
3832
|
});
|
|
3316
3833
|
const proposal = await multisig.createProposal(1, 'AQID', {
|
|
3317
3834
|
proposalType: 'switch_guardian',
|
|
3835
|
+
chainAnchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3318
3836
|
newGuardianPubkey: '0xnewpubkey',
|
|
3319
3837
|
newGuardianEndpoint: 'http://new-guardian.com',
|
|
3320
3838
|
description: '',
|
|
@@ -3341,6 +3859,7 @@ describe('Multisig', () => {
|
|
|
3341
3859
|
signatures: [],
|
|
3342
3860
|
metadata: {
|
|
3343
3861
|
proposal_type: 'add_signer',
|
|
3862
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3344
3863
|
target_threshold: 2,
|
|
3345
3864
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
3346
3865
|
description: '',
|
|
@@ -3374,6 +3893,7 @@ describe('Multisig', () => {
|
|
|
3374
3893
|
...mockProposalsPending[0].delta_payload,
|
|
3375
3894
|
metadata: {
|
|
3376
3895
|
proposal_type: 'add_signer',
|
|
3896
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3377
3897
|
target_threshold: 2,
|
|
3378
3898
|
signer_commitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
3379
3899
|
description: '',
|
|
@@ -3465,6 +3985,7 @@ describe('Multisig', () => {
|
|
|
3465
3985
|
signatures: [],
|
|
3466
3986
|
metadata: {
|
|
3467
3987
|
proposal_type: 'change_threshold',
|
|
3988
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3468
3989
|
target_threshold: 3,
|
|
3469
3990
|
signer_commitments: ['0xa', '0xb', '0xc'],
|
|
3470
3991
|
salt: '0xlegacysalt',
|
|
@@ -3509,6 +4030,7 @@ describe('Multisig', () => {
|
|
|
3509
4030
|
signatures: [],
|
|
3510
4031
|
metadata: {
|
|
3511
4032
|
proposal_type: 'p2id',
|
|
4033
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3512
4034
|
recipient_id: '0xrecipient',
|
|
3513
4035
|
faucet_id: '0xfaucet',
|
|
3514
4036
|
amount: '12345',
|
|
@@ -3559,6 +4081,7 @@ describe('Multisig', () => {
|
|
|
3559
4081
|
signatures: [],
|
|
3560
4082
|
metadata: {
|
|
3561
4083
|
proposal_type: 'switch_guardian',
|
|
4084
|
+
chain_anchor: MOCK_CHAIN_ANCHOR_B64,
|
|
3562
4085
|
new_guardian_pubkey: '0xnewpubkey',
|
|
3563
4086
|
new_guardian_endpoint: 'http://new-guardian.com',
|
|
3564
4087
|
salt: '0xsalt',
|