@openzeppelin/miden-multisig-client 0.16.1 → 0.17.0-rc.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 +192 -11
- 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 +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +10 -5
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +74 -16
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +5 -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 +109 -33
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +307 -104
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +582 -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/errors.d.ts +5 -0
- package/dist/prover/errors.d.ts.map +1 -1
- package/dist/prover/errors.js +7 -155
- package/dist/prover/errors.js.map +1 -1
- package/dist/prover/errors.test.js +5 -0
- package/dist/prover/errors.test.js.map +1 -1
- package/dist/prover/retry.d.ts +1 -6
- package/dist/prover/retry.d.ts.map +1 -1
- package/dist/prover/retry.js +5 -28
- package/dist/prover/retry.js.map +1 -1
- package/dist/prover/retry.test.js +1 -1
- package/dist/prover/retry.test.js.map +1 -1
- package/dist/prover/workflow.d.ts +4 -3
- 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 +28 -3
- package/dist/prover/workflow.test.js.map +1 -1
- package/dist/retry/classify.d.ts +16 -0
- package/dist/retry/classify.d.ts.map +1 -0
- package/dist/retry/classify.js +187 -0
- package/dist/retry/classify.js.map +1 -0
- package/dist/retry/runtime.d.ts +13 -0
- package/dist/retry/runtime.d.ts.map +1 -0
- package/dist/retry/runtime.js +33 -0
- package/dist/retry/runtime.js.map +1 -0
- package/dist/rpc/config.d.ts +11 -0
- package/dist/rpc/config.d.ts.map +1 -0
- package/dist/rpc/config.js +17 -0
- package/dist/rpc/config.js.map +1 -0
- package/dist/rpc/config.test.d.ts +2 -0
- package/dist/rpc/config.test.d.ts.map +1 -0
- package/dist/rpc/config.test.js +24 -0
- package/dist/rpc/config.test.js.map +1 -0
- package/dist/rpc/errors.d.ts +2 -0
- package/dist/rpc/errors.d.ts.map +1 -0
- package/dist/rpc/errors.js +11 -0
- package/dist/rpc/errors.js.map +1 -0
- package/dist/rpc/errors.test.d.ts +2 -0
- package/dist/rpc/errors.test.d.ts.map +1 -0
- package/dist/rpc/errors.test.js +34 -0
- package/dist/rpc/errors.test.js.map +1 -0
- package/dist/rpc/retry.d.ts +4 -0
- package/dist/rpc/retry.d.ts.map +1 -0
- package/dist/rpc/retry.js +6 -0
- package/dist/rpc/retry.js.map +1 -0
- package/dist/rpc/retry.test.d.ts +2 -0
- package/dist/rpc/retry.test.d.ts.map +1 -0
- package/dist/rpc/retry.test.js +98 -0
- package/dist/rpc/retry.test.js.map +1 -0
- 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 +87 -16
- package/src/client.ts +16 -3
- package/src/index.ts +27 -1
- package/src/inspector.test.ts +330 -38
- package/src/inspector.ts +196 -33
- package/src/multisig.test.ts +680 -63
- package/src/multisig.ts +400 -115
- package/src/procedures.ts +6 -7
- package/src/proposal/metadata.test.ts +76 -0
- package/src/proposal/metadata.ts +13 -2
- package/src/prover/errors.test.ts +8 -0
- package/src/prover/errors.ts +7 -175
- package/src/prover/retry.test.ts +1 -1
- package/src/prover/retry.ts +10 -35
- package/src/prover/workflow.test.ts +40 -4
- package/src/prover/workflow.ts +16 -4
- package/src/retry/classify.ts +220 -0
- package/src/retry/runtime.ts +45 -0
- package/src/rpc/config.test.ts +45 -0
- package/src/rpc/config.ts +30 -0
- package/src/rpc/errors.test.ts +69 -0
- package/src/rpc/errors.ts +12 -0
- package/src/rpc/retry.test.ts +144 -0
- package/src/rpc/retry.ts +12 -0
- 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/src/inspector.test.ts
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { AccountInspector } from './inspector.js';
|
|
3
|
-
|
|
4
|
-
// Storage slot names matching the MASM definitions
|
|
5
|
-
const MULTISIG_SLOT_NAMES = {
|
|
6
|
-
THRESHOLD_CONFIG: 'openzeppelin::multisig::threshold_config',
|
|
7
|
-
SIGNER_PUBLIC_KEYS: 'openzeppelin::multisig::signer_public_keys',
|
|
8
|
-
EXECUTED_TRANSACTIONS: 'openzeppelin::multisig::executed_transactions',
|
|
9
|
-
PROCEDURE_THRESHOLDS: 'openzeppelin::multisig::procedure_thresholds',
|
|
10
|
-
} as const;
|
|
11
|
-
|
|
12
|
-
const GUARDIAN_SLOT_NAMES = {
|
|
13
|
-
SELECTOR: 'openzeppelin::guardian::selector',
|
|
14
|
-
PUBLIC_KEY: 'openzeppelin::guardian::public_key',
|
|
15
|
-
} as const;
|
|
2
|
+
import { AccountInspector, assertCompleteDetectedConfig } from './inspector.js';
|
|
3
|
+
import type { DetectedMultisigConfig } from './inspector.js';
|
|
16
4
|
|
|
17
5
|
// Mock the Miden SDK
|
|
18
6
|
vi.mock('@miden-sdk/miden-sdk', () => {
|
|
@@ -21,15 +9,18 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
21
9
|
toHex: () => '0x' + values.map(v => v.toString(16).padStart(16, '0')).join(''),
|
|
22
10
|
});
|
|
23
11
|
|
|
12
|
+
// Faithful to the real SDK's read semantics (verified against the wasm
|
|
13
|
+
// glue and miden-protocol): getItem returns undefined for an absent slot;
|
|
14
|
+
// getMapItem returns undefined only when the slot is absent or not a map,
|
|
15
|
+
// and the EMPTY word for a missing key in an existing map
|
|
16
|
+
// (StorageMap::get is unwrap_or_default). Neither throws for "not found".
|
|
24
17
|
const createMockStorage = (slots: Map<string, any>, maps: Map<string, Map<string, any>>) => ({
|
|
25
|
-
getItem: (slotName: string) => slots.get(slotName)
|
|
18
|
+
getItem: (slotName: string) => slots.get(slotName),
|
|
26
19
|
getMapItem: (slotName: string, key: any) => {
|
|
27
20
|
const map = maps.get(slotName);
|
|
28
|
-
if (!map)
|
|
21
|
+
if (!map) return undefined;
|
|
29
22
|
const keyStr = key.toU64s?.()[0]?.toString() ?? '0';
|
|
30
|
-
|
|
31
|
-
if (!value) throw new Error('Key not found');
|
|
32
|
-
return value;
|
|
23
|
+
return map.get(keyStr) ?? createMockWord([0n, 0n, 0n, 0n]);
|
|
33
24
|
},
|
|
34
25
|
});
|
|
35
26
|
|
|
@@ -46,7 +37,6 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
46
37
|
// Return different mocked accounts based on test scenario
|
|
47
38
|
// Default: 2-of-3 multisig with GUARDIAN enabled
|
|
48
39
|
const slot0 = createMockWord([2n, 3n, 0n, 0n]); // threshold=2, numSigners=3
|
|
49
|
-
const slot4 = createMockWord([1n, 0n, 0n, 0n]); // GUARDIAN enabled
|
|
50
40
|
|
|
51
41
|
const signerMap = new Map<string, any>();
|
|
52
42
|
signerMap.set('0', createMockWord([BigInt('0x1111111111111111'), BigInt('0x2222222222222222'), BigInt('0x3333333333333333'), BigInt('0x4444444444444444')]));
|
|
@@ -57,15 +47,16 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
57
47
|
guardianMap.set('0', createMockWord([BigInt('0xeeeeeeeeeeeeeeee'), BigInt('0xffffffffffffffff'), BigInt('0x0000000000000001'), BigInt('0x0000000000000002')]));
|
|
58
48
|
|
|
59
49
|
const slots = new Map<string, any>();
|
|
60
|
-
slots.set('
|
|
61
|
-
slots.set('openzeppelin::guardian::selector', slot4);
|
|
50
|
+
slots.set('miden::standards::auth::multisig::threshold_config', slot0);
|
|
62
51
|
|
|
63
52
|
const maps = new Map<string, Map<string, any>>();
|
|
64
|
-
maps.set('
|
|
65
|
-
maps.set('
|
|
53
|
+
maps.set('miden::standards::auth::multisig::approver_public_keys', signerMap);
|
|
54
|
+
maps.set('miden::standards::auth::guardian::pub_key', guardianMap);
|
|
66
55
|
|
|
67
56
|
return {
|
|
68
57
|
storage: () => createMockStorage(slots, maps),
|
|
58
|
+
// The contract-version guard checks for the pinned auth procedure.
|
|
59
|
+
code: () => ({ hasProcedure: () => true }),
|
|
69
60
|
vault: () => createMockVault([
|
|
70
61
|
{ faucetId: '0xfaucet1', amount: 1000n },
|
|
71
62
|
{ faucetId: '0xfaucet2', amount: 500n },
|
|
@@ -73,10 +64,18 @@ vi.mock('@miden-sdk/miden-sdk', () => {
|
|
|
73
64
|
};
|
|
74
65
|
}),
|
|
75
66
|
},
|
|
76
|
-
Word:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
Word: Object.assign(
|
|
68
|
+
vi.fn().mockImplementation((arr: BigUint64Array) => ({
|
|
69
|
+
toU64s: () => Array.from(arr),
|
|
70
|
+
toHex: () => '0x' + Array.from(arr).map(v => v.toString(16).padStart(16, '0')).join(''),
|
|
71
|
+
})),
|
|
72
|
+
{
|
|
73
|
+
fromHex: vi.fn((hex: string) => ({
|
|
74
|
+
toU64s: () => [0n, 0n, 0n, 0n],
|
|
75
|
+
toHex: () => hex,
|
|
76
|
+
})),
|
|
77
|
+
},
|
|
78
|
+
),
|
|
80
79
|
};
|
|
81
80
|
});
|
|
82
81
|
|
|
@@ -94,11 +93,10 @@ describe('AccountInspector', () => {
|
|
|
94
93
|
expect(config.numSigners).toBe(3);
|
|
95
94
|
});
|
|
96
95
|
|
|
97
|
-
it('extracts GUARDIAN
|
|
96
|
+
it('extracts the GUARDIAN commitment', () => {
|
|
98
97
|
const base64 = btoa(String.fromCharCode(...new Uint8Array([1, 2, 3])));
|
|
99
98
|
const config = AccountInspector.fromBase64(base64);
|
|
100
99
|
|
|
101
|
-
expect(config.guardianEnabled).toBe(true);
|
|
102
100
|
expect(config.guardianCommitment).toMatch(/^0x[a-f0-9]+$/);
|
|
103
101
|
});
|
|
104
102
|
|
|
@@ -138,6 +136,21 @@ describe('AccountInspector', () => {
|
|
|
138
136
|
|
|
139
137
|
expect(config.numSigners).toBe(3);
|
|
140
138
|
});
|
|
139
|
+
|
|
140
|
+
it('rejects accounts built from a different contract version', async () => {
|
|
141
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
142
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
143
|
+
// Same account shape, but its code lacks the pinned auth procedure —
|
|
144
|
+
// root-keyed reads against it would silently miss its stored overrides.
|
|
145
|
+
const foreign = {
|
|
146
|
+
...account,
|
|
147
|
+
code: () => ({ hasProcedure: () => false }),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
expect(() => AccountInspector.fromAccount(foreign as never)).toThrow(
|
|
151
|
+
/unsupported contract version/,
|
|
152
|
+
);
|
|
153
|
+
});
|
|
141
154
|
});
|
|
142
155
|
});
|
|
143
156
|
|
|
@@ -146,19 +159,19 @@ describe('AccountInspector edge cases', () => {
|
|
|
146
159
|
vi.clearAllMocks();
|
|
147
160
|
});
|
|
148
161
|
|
|
149
|
-
it('
|
|
162
|
+
it('returns a null GUARDIAN commitment when the guardian pub_key slot is absent', async () => {
|
|
150
163
|
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
151
164
|
|
|
152
|
-
// Override mock for this test
|
|
165
|
+
// Override mock for this test: no guardian pub_key entry present.
|
|
153
166
|
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
167
|
+
code: () => ({ hasProcedure: () => true }),
|
|
154
168
|
storage: () => ({
|
|
155
169
|
getItem: (slotName: string) => {
|
|
156
|
-
if (slotName === '
|
|
157
|
-
if (slotName === 'openzeppelin::guardian::selector') return { toU64s: () => [0n, 0n, 0n, 0n] }; // GUARDIAN disabled
|
|
170
|
+
if (slotName === 'miden::standards::auth::multisig::threshold_config') return { toU64s: () => [1n, 1n, 0n, 0n] };
|
|
158
171
|
return { toU64s: () => [0n, 0n, 0n, 0n] };
|
|
159
172
|
},
|
|
160
173
|
getMapItem: (slotName: string, key: any) => {
|
|
161
|
-
if (slotName === '
|
|
174
|
+
if (slotName === 'miden::standards::auth::multisig::approver_public_keys' && key.toU64s?.()[0] === 0n) {
|
|
162
175
|
return {
|
|
163
176
|
toHex: () => '0x' + 'a'.repeat(64),
|
|
164
177
|
toU64s: () => [1n, 2n, 3n, 4n],
|
|
@@ -175,7 +188,6 @@ describe('AccountInspector edge cases', () => {
|
|
|
175
188
|
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
176
189
|
const config = AccountInspector.fromAccount(account);
|
|
177
190
|
|
|
178
|
-
expect(config.guardianEnabled).toBe(false);
|
|
179
191
|
expect(config.guardianCommitment).toBeNull();
|
|
180
192
|
});
|
|
181
193
|
|
|
@@ -183,6 +195,7 @@ describe('AccountInspector edge cases', () => {
|
|
|
183
195
|
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
184
196
|
|
|
185
197
|
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
198
|
+
code: () => ({ hasProcedure: () => true }),
|
|
186
199
|
storage: () => ({
|
|
187
200
|
getItem: () => ({ toU64s: () => [1n, 1n, 0n, 0n] }),
|
|
188
201
|
getMapItem: () => {
|
|
@@ -204,10 +217,10 @@ describe('AccountInspector edge cases', () => {
|
|
|
204
217
|
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
205
218
|
|
|
206
219
|
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
220
|
+
code: () => ({ hasProcedure: () => true }),
|
|
207
221
|
storage: () => ({
|
|
208
222
|
getItem: (slotName: string) => {
|
|
209
|
-
if (slotName === '
|
|
210
|
-
if (slotName === 'openzeppelin::guardian::selector') return { toU64s: () => [0n, 0n, 0n, 0n] };
|
|
223
|
+
if (slotName === 'miden::standards::auth::multisig::threshold_config') return { toU64s: () => [2n, 5n, 0n, 0n] }; // threshold=2, numSigners=5
|
|
211
224
|
return { toU64s: () => [0n, 0n, 0n, 0n] };
|
|
212
225
|
},
|
|
213
226
|
getMapItem: () => {
|
|
@@ -231,6 +244,7 @@ describe('AccountInspector edge cases', () => {
|
|
|
231
244
|
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
232
245
|
|
|
233
246
|
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
247
|
+
code: () => ({ hasProcedure: () => true }),
|
|
234
248
|
storage: () => ({
|
|
235
249
|
getItem: () => ({ toU64s: () => [1n, 1n, 0n, 0n] }),
|
|
236
250
|
getMapItem: () => {
|
|
@@ -248,3 +262,281 @@ describe('AccountInspector edge cases', () => {
|
|
|
248
262
|
expect(config.vaultBalances).toEqual([]);
|
|
249
263
|
});
|
|
250
264
|
});
|
|
265
|
+
|
|
266
|
+
describe('AccountInspector.getSignerPublicKeyCommitments', () => {
|
|
267
|
+
beforeEach(() => {
|
|
268
|
+
vi.clearAllMocks();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('returns all signer commitments ordered by signer index', async () => {
|
|
272
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
273
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
274
|
+
|
|
275
|
+
const commitments = AccountInspector.getSignerPublicKeyCommitments(account);
|
|
276
|
+
|
|
277
|
+
expect(commitments).toEqual([
|
|
278
|
+
'0x1111111111111111222222222222222233333333333333334444444444444444',
|
|
279
|
+
'0x5555555555555555666666666666666677777777777777778888888888888888',
|
|
280
|
+
'0xaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd',
|
|
281
|
+
]);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('rejects accounts built from a different contract version', async () => {
|
|
285
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
286
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
287
|
+
const foreign = {
|
|
288
|
+
...account,
|
|
289
|
+
code: () => ({ hasProcedure: () => false }),
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(foreign as never)).toThrow(
|
|
293
|
+
/unsupported contract version/,
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('throws when a signer map entry is the empty word instead of truncating', async () => {
|
|
298
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
299
|
+
|
|
300
|
+
// Real SDK semantics: entries 0 and 1 exist, entries 2..4 come back as
|
|
301
|
+
// the empty word (StorageMap::get is unwrap_or_default) — NOT undefined
|
|
302
|
+
// and NOT a throw.
|
|
303
|
+
const entries = new Map<string, any>([
|
|
304
|
+
['0', { toU64s: () => [1n, 2n, 3n, 4n], toHex: () => '0x' + 'a'.repeat(64) }],
|
|
305
|
+
['1', { toU64s: () => [5n, 6n, 7n, 8n], toHex: () => '0x' + 'b'.repeat(64) }],
|
|
306
|
+
]);
|
|
307
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
308
|
+
code: () => ({ hasProcedure: () => true }),
|
|
309
|
+
storage: () => ({
|
|
310
|
+
getItem: (slotName: string) => {
|
|
311
|
+
if (slotName === 'miden::standards::auth::multisig::threshold_config') return { toU64s: () => [2n, 5n, 0n, 0n] };
|
|
312
|
+
return undefined;
|
|
313
|
+
},
|
|
314
|
+
getMapItem: (slotName: string, key: any) => {
|
|
315
|
+
if (slotName !== 'miden::standards::auth::multisig::approver_public_keys') return undefined;
|
|
316
|
+
const keyStr = key.toU64s?.()[0]?.toString() ?? '0';
|
|
317
|
+
return entries.get(keyStr) ?? { toU64s: () => [0n, 0n, 0n, 0n] };
|
|
318
|
+
},
|
|
319
|
+
}),
|
|
320
|
+
} as any);
|
|
321
|
+
|
|
322
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
323
|
+
|
|
324
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
325
|
+
/missing signer public key at index 2/
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('throws when the signer map slot itself is absent', async () => {
|
|
330
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
331
|
+
|
|
332
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
333
|
+
code: () => ({ hasProcedure: () => true }),
|
|
334
|
+
storage: () => ({
|
|
335
|
+
getItem: (slotName: string) => {
|
|
336
|
+
if (slotName === 'miden::standards::auth::multisig::threshold_config') return { toU64s: () => [2n, 3n, 0n, 0n] };
|
|
337
|
+
return undefined;
|
|
338
|
+
},
|
|
339
|
+
getMapItem: () => undefined,
|
|
340
|
+
}),
|
|
341
|
+
} as any);
|
|
342
|
+
|
|
343
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
344
|
+
|
|
345
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
346
|
+
/missing signer public key at index 0/
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('throws when the threshold config slot is absent', async () => {
|
|
351
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
352
|
+
|
|
353
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
354
|
+
code: () => ({ hasProcedure: () => true }),
|
|
355
|
+
storage: () => ({
|
|
356
|
+
getItem: () => undefined,
|
|
357
|
+
getMapItem: () => undefined,
|
|
358
|
+
}),
|
|
359
|
+
} as any);
|
|
360
|
+
|
|
361
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
362
|
+
|
|
363
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
364
|
+
/not a guarded-multisig account/
|
|
365
|
+
);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('throws when the threshold config reports zero signers', async () => {
|
|
369
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
370
|
+
|
|
371
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
372
|
+
code: () => ({ hasProcedure: () => true }),
|
|
373
|
+
storage: () => ({
|
|
374
|
+
getItem: () => ({ toU64s: () => [0n, 0n, 0n, 0n] }),
|
|
375
|
+
getMapItem: () => undefined,
|
|
376
|
+
}),
|
|
377
|
+
} as any);
|
|
378
|
+
|
|
379
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
380
|
+
|
|
381
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
382
|
+
/zero signers/
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// 2n ** 53n is the smallest count whose Number() conversion is no longer a
|
|
387
|
+
// safe integer (i++ would stop advancing without the ceiling guard).
|
|
388
|
+
it.each([4294967295n, 2n ** 53n])(
|
|
389
|
+
'throws on an absurd signer count (%s) instead of looping',
|
|
390
|
+
async (count) => {
|
|
391
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
392
|
+
|
|
393
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
394
|
+
code: () => ({ hasProcedure: () => true }),
|
|
395
|
+
storage: () => ({
|
|
396
|
+
getItem: () => ({ toU64s: () => [2n, count, 0n, 0n] }),
|
|
397
|
+
getMapItem: () => ({ toU64s: () => [1n, 2n, 3n, 4n], toHex: () => '0x' + 'a'.repeat(64) }),
|
|
398
|
+
}),
|
|
399
|
+
} as any);
|
|
400
|
+
|
|
401
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
402
|
+
|
|
403
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
404
|
+
/sanity limit/
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
it('surfaces a descriptive error for an account from a different SDK copy', async () => {
|
|
410
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
411
|
+
|
|
412
|
+
// wasm-bindgen's _assertClass rejects Word instances from another
|
|
413
|
+
// bundled SDK copy with this exact message.
|
|
414
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
415
|
+
code: () => ({ hasProcedure: () => true }),
|
|
416
|
+
storage: () => ({
|
|
417
|
+
getItem: (slotName: string) => {
|
|
418
|
+
if (slotName === 'miden::standards::auth::multisig::threshold_config') return { toU64s: () => [1n, 1n, 0n, 0n] };
|
|
419
|
+
return undefined;
|
|
420
|
+
},
|
|
421
|
+
getMapItem: () => {
|
|
422
|
+
throw new Error('expected instance of Word');
|
|
423
|
+
},
|
|
424
|
+
}),
|
|
425
|
+
} as any);
|
|
426
|
+
|
|
427
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
428
|
+
|
|
429
|
+
expect(() => AccountInspector.getSignerPublicKeyCommitments(account)).toThrow(
|
|
430
|
+
/different copy of @miden-sdk\/miden-sdk/
|
|
431
|
+
);
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
describe('AccountInspector.getGuardianPublicKeyCommitment', () => {
|
|
436
|
+
beforeEach(() => {
|
|
437
|
+
vi.clearAllMocks();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('returns the guardian commitment', async () => {
|
|
441
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
442
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
443
|
+
|
|
444
|
+
const commitment = AccountInspector.getGuardianPublicKeyCommitment(account);
|
|
445
|
+
|
|
446
|
+
expect(commitment).toBe('0xeeeeeeeeeeeeeeeeffffffffffffffff00000000000000010000000000000002');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('rejects accounts built from a different contract version', async () => {
|
|
450
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
451
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
452
|
+
const foreign = {
|
|
453
|
+
...account,
|
|
454
|
+
code: () => ({ hasProcedure: () => false }),
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
expect(() => AccountInspector.getGuardianPublicKeyCommitment(foreign as never)).toThrow(
|
|
458
|
+
/unsupported contract version/,
|
|
459
|
+
);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('throws when the guardian key entry is the empty word', async () => {
|
|
463
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
464
|
+
|
|
465
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
466
|
+
code: () => ({ hasProcedure: () => true }),
|
|
467
|
+
storage: () => ({
|
|
468
|
+
getItem: () => ({ toU64s: () => [1n, 1n, 0n, 0n] }),
|
|
469
|
+
getMapItem: () => ({ toU64s: () => [0n, 0n, 0n, 0n] }),
|
|
470
|
+
}),
|
|
471
|
+
} as any);
|
|
472
|
+
|
|
473
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
474
|
+
|
|
475
|
+
expect(() => AccountInspector.getGuardianPublicKeyCommitment(account)).toThrow(
|
|
476
|
+
/inconsistent account state/
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('propagates genuine storage read failures', async () => {
|
|
481
|
+
const { Account } = await import('@miden-sdk/miden-sdk');
|
|
482
|
+
|
|
483
|
+
vi.mocked(Account.deserialize).mockReturnValueOnce({
|
|
484
|
+
code: () => ({ hasProcedure: () => true }),
|
|
485
|
+
storage: () => ({
|
|
486
|
+
getItem: () => ({ toU64s: () => [1n, 1n, 0n, 0n] }),
|
|
487
|
+
getMapItem: () => {
|
|
488
|
+
throw new Error('storage backend exploded');
|
|
489
|
+
},
|
|
490
|
+
}),
|
|
491
|
+
} as any);
|
|
492
|
+
|
|
493
|
+
const account = Account.deserialize(new Uint8Array([1, 2, 3]));
|
|
494
|
+
|
|
495
|
+
expect(() => AccountInspector.getGuardianPublicKeyCommitment(account)).toThrow(
|
|
496
|
+
'storage backend exploded'
|
|
497
|
+
);
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
describe('assertCompleteDetectedConfig', () => {
|
|
502
|
+
const complete: DetectedMultisigConfig = {
|
|
503
|
+
threshold: 2,
|
|
504
|
+
numSigners: 2,
|
|
505
|
+
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
506
|
+
guardianCommitment: '0x' + 'c'.repeat(64),
|
|
507
|
+
vaultBalances: [],
|
|
508
|
+
procedureThresholds: new Map(),
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
it('accepts a complete config', () => {
|
|
512
|
+
expect(() => assertCompleteDetectedConfig(complete)).not.toThrow();
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('rejects a signer set shorter than the reported count', () => {
|
|
516
|
+
expect(() =>
|
|
517
|
+
assertCompleteDetectedConfig({
|
|
518
|
+
...complete,
|
|
519
|
+
numSigners: 3,
|
|
520
|
+
}),
|
|
521
|
+
).toThrow(/incomplete signer set: storage reports 3 signers, read 2/);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('rejects a zero signer count', () => {
|
|
525
|
+
expect(() =>
|
|
526
|
+
assertCompleteDetectedConfig({
|
|
527
|
+
...complete,
|
|
528
|
+
numSigners: 0,
|
|
529
|
+
signerCommitments: [],
|
|
530
|
+
}),
|
|
531
|
+
).toThrow(/incomplete signer set/);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('rejects a missing guardian commitment', () => {
|
|
535
|
+
expect(() =>
|
|
536
|
+
assertCompleteDetectedConfig({
|
|
537
|
+
...complete,
|
|
538
|
+
guardianCommitment: null,
|
|
539
|
+
}),
|
|
540
|
+
).toThrow(/missing guardian commitment/);
|
|
541
|
+
});
|
|
542
|
+
});
|