@openzeppelin/miden-multisig-client 0.14.5 → 0.14.8
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 +99 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +20 -0
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +77 -0
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/lookupAuth.d.ts +29 -0
- package/dist/lookupAuth.d.ts.map +1 -0
- package/dist/lookupAuth.js +85 -0
- package/dist/lookupAuth.js.map +1 -0
- package/dist/lookupAuth.test.d.ts +2 -0
- package/dist/lookupAuth.test.d.ts.map +1 -0
- package/dist/lookupAuth.test.js +137 -0
- package/dist/lookupAuth.test.js.map +1 -0
- package/dist/multisig/config.d.ts +8 -0
- package/dist/multisig/config.d.ts.map +1 -0
- package/dist/multisig/config.js +8 -0
- package/dist/multisig/config.js.map +1 -0
- package/dist/multisig/consumeNotesErrors.d.ts +48 -0
- package/dist/multisig/consumeNotesErrors.d.ts.map +1 -0
- package/dist/multisig/consumeNotesErrors.js +58 -0
- package/dist/multisig/consumeNotesErrors.js.map +1 -0
- package/dist/multisig/consumeNotesErrors.test.d.ts +2 -0
- package/dist/multisig/consumeNotesErrors.test.d.ts.map +1 -0
- package/dist/multisig/consumeNotesErrors.test.js +28 -0
- package/dist/multisig/consumeNotesErrors.test.js.map +1 -0
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +63 -4
- package/dist/multisig.js.map +1 -1
- package/dist/proposal/metadata.d.ts.map +1 -1
- package/dist/proposal/metadata.js +4 -0
- package/dist/proposal/metadata.js.map +1 -1
- package/dist/proposal/metadata.test.d.ts +2 -0
- package/dist/proposal/metadata.test.d.ts.map +1 -0
- package/dist/proposal/metadata.test.js +43 -0
- package/dist/proposal/metadata.test.js.map +1 -0
- package/dist/signers/ecdsa.d.ts +5 -0
- package/dist/signers/ecdsa.d.ts.map +1 -1
- package/dist/signers/ecdsa.js +9 -0
- package/dist/signers/ecdsa.js.map +1 -1
- package/dist/signers/falcon.d.ts +5 -0
- package/dist/signers/falcon.d.ts.map +1 -1
- package/dist/signers/falcon.js +9 -0
- package/dist/signers/falcon.js.map +1 -1
- package/dist/signers/miden-wallet.d.ts +5 -0
- package/dist/signers/miden-wallet.d.ts.map +1 -1
- package/dist/signers/miden-wallet.js +12 -0
- package/dist/signers/miden-wallet.js.map +1 -1
- package/dist/signers/miden-wallet.test.js +36 -0
- package/dist/signers/miden-wallet.test.js.map +1 -1
- package/dist/signers/para.d.ts +5 -0
- package/dist/signers/para.d.ts.map +1 -1
- package/dist/signers/para.js +9 -0
- package/dist/signers/para.js.map +1 -1
- package/dist/signers/para.test.js +19 -0
- package/dist/signers/para.test.js.map +1 -1
- package/dist/transaction/consumeNotes.d.ts +13 -1
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +30 -13
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/types/proposal.d.ts +10 -0
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types/proposal.js +10 -1
- package/dist/types/proposal.js.map +1 -1
- package/dist/types/proposal.test.d.ts +2 -0
- package/dist/types/proposal.test.d.ts.map +1 -0
- package/dist/types/proposal.test.js +56 -0
- package/dist/types/proposal.test.js.map +1 -0
- package/dist/utils/encoding.d.ts +8 -0
- package/dist/utils/encoding.d.ts.map +1 -1
- package/dist/utils/encoding.js +8 -0
- package/dist/utils/encoding.js.map +1 -1
- package/package.json +3 -3
- package/src/client.test.ts +97 -0
- package/src/client.ts +36 -0
- package/src/index.ts +30 -1
- package/src/lookupAuth.test.ts +184 -0
- package/src/lookupAuth.ts +93 -0
- package/src/multisig/config.ts +7 -0
- package/src/multisig/consumeNotesErrors.test.ts +43 -0
- package/src/multisig/consumeNotesErrors.ts +74 -0
- package/src/multisig.ts +81 -7
- package/src/proposal/metadata.test.ts +47 -0
- package/src/proposal/metadata.ts +4 -0
- package/src/signers/ecdsa.ts +10 -0
- package/src/signers/falcon.ts +10 -0
- package/src/signers/miden-wallet.test.ts +48 -0
- package/src/signers/miden-wallet.ts +13 -0
- package/src/signers/para.test.ts +26 -0
- package/src/signers/para.ts +10 -0
- package/src/transaction/consumeNotes.ts +40 -17
- package/src/types/proposal.test.ts +68 -0
- package/src/types/proposal.ts +18 -0
- package/src/utils/encoding.ts +13 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,IAAI,oBAAoB,EACpC,eAAe,EAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;AAEnE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;AAE/D,MAAM,MAAM,yBAAyB,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1B,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,4BAA4B,GAAG,sBAAsB,CAAC;AAElE,UAAU,oBAAoB;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,6BAA8B,SAAQ,oBAAoB;IACzE,YAAY,EAAE,YAAY,GAAG,eAAe,GAAG,kBAAkB,CAAC;IAClE,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,8BAA+B,SAAQ,oBAAoB;IAC1E,YAAY,EAAE,iBAAiB,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,wCAAyC,SAAQ,oBAAoB;IACpF,YAAY,EAAE,4BAA4B,CAAC;IAC3C,eAAe,EAAE,aAAa,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;IACxE,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"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"}
|
package/dist/types/proposal.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/** `consume_notes` metadata version. Absence on the wire => v1 (issue #229). */
|
|
2
|
+
export const CONSUME_NOTES_METADATA_VERSION_V2 = 2;
|
|
3
|
+
/** Max serialized v2 metadata, enforced at creation (FR-011). */
|
|
4
|
+
export const MAX_CONSUME_NOTES_METADATA_BYTES = 256 * 1024;
|
|
5
|
+
export function isConsumeNotesV2(md) {
|
|
6
|
+
return md.metadataVersion === CONSUME_NOTES_METADATA_VERSION_V2;
|
|
7
|
+
}
|
|
8
|
+
export function isConsumeNotesV1(md) {
|
|
9
|
+
return md.metadataVersion === undefined || md.metadataVersion === 1;
|
|
10
|
+
}
|
|
2
11
|
//# sourceMappingURL=proposal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/types/proposal.ts"],"names":[],"mappings":"AAmDA,gFAAgF;AAChF,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAU,CAAC;AAE5D,iEAAiE;AACjE,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,GAAG,IAAI,CAAC;AAW3D,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,iCAAiC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAgC;IAC/D,OAAO,EAAE,CAAC,eAAe,KAAK,SAAS,IAAI,EAAE,CAAC,eAAe,KAAK,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposal.test.d.ts","sourceRoot":"","sources":["../../src/types/proposal.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { CONSUME_NOTES_METADATA_VERSION_V2, MAX_CONSUME_NOTES_METADATA_BYTES, isConsumeNotesV1, isConsumeNotesV2, } from './proposal.js';
|
|
3
|
+
describe('consume_notes metadata v1/v2 discriminator (issue #229)', () => {
|
|
4
|
+
it('v1 metadata omits metadataVersion and notes on the wire', () => {
|
|
5
|
+
const md = {
|
|
6
|
+
proposalType: 'consume_notes',
|
|
7
|
+
description: '',
|
|
8
|
+
noteIds: ['0xabc', '0xdef'],
|
|
9
|
+
};
|
|
10
|
+
const json = JSON.parse(JSON.stringify(md));
|
|
11
|
+
expect(json.metadataVersion).toBeUndefined();
|
|
12
|
+
expect(json.notes).toBeUndefined();
|
|
13
|
+
expect(isConsumeNotesV1(md)).toBe(true);
|
|
14
|
+
expect(isConsumeNotesV2(md)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
it('v2 metadata round-trips with discriminator and embedded notes', () => {
|
|
17
|
+
const md = {
|
|
18
|
+
proposalType: 'consume_notes',
|
|
19
|
+
description: '',
|
|
20
|
+
noteIds: ['0xabc'],
|
|
21
|
+
metadataVersion: CONSUME_NOTES_METADATA_VERSION_V2,
|
|
22
|
+
notes: ['YmFzZTY0Tm90ZQ=='],
|
|
23
|
+
};
|
|
24
|
+
const json = JSON.stringify(md);
|
|
25
|
+
const parsed = JSON.parse(json);
|
|
26
|
+
expect(parsed.metadataVersion).toBe(2);
|
|
27
|
+
expect(parsed.notes).toEqual(['YmFzZTY0Tm90ZQ==']);
|
|
28
|
+
expect(isConsumeNotesV2(parsed)).toBe(true);
|
|
29
|
+
expect(isConsumeNotesV1(parsed)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('explicit metadataVersion === 1 is treated as v1', () => {
|
|
32
|
+
const md = {
|
|
33
|
+
proposalType: 'consume_notes',
|
|
34
|
+
description: '',
|
|
35
|
+
noteIds: ['0xabc'],
|
|
36
|
+
metadataVersion: 1, // legacy explicit-v1 case
|
|
37
|
+
};
|
|
38
|
+
expect(isConsumeNotesV1(md)).toBe(true);
|
|
39
|
+
expect(isConsumeNotesV2(md)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
it('unknown future version is neither v1 nor v2; dispatch handles rejection', () => {
|
|
42
|
+
const md = {
|
|
43
|
+
proposalType: 'consume_notes',
|
|
44
|
+
description: '',
|
|
45
|
+
noteIds: ['0xabc'],
|
|
46
|
+
metadataVersion: 99,
|
|
47
|
+
};
|
|
48
|
+
expect(isConsumeNotesV1(md)).toBe(false);
|
|
49
|
+
expect(isConsumeNotesV2(md)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it('pins MAX_CONSUME_NOTES_METADATA_BYTES at 256 KiB', () => {
|
|
52
|
+
expect(MAX_CONSUME_NOTES_METADATA_BYTES).toBe(256 * 1024);
|
|
53
|
+
expect(MAX_CONSUME_NOTES_METADATA_BYTES).toBe(262_144);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=proposal.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposal.test.js","sourceRoot":"","sources":["../../src/types/proposal.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,iCAAiC,EAEjC,gCAAgC,EAChC,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAEvB,QAAQ,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACvE,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,EAAE,GAAiC;YACvC,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;SAC5B,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;QACnC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,EAAE,GAAiC;YACvC,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,eAAe,EAAE,iCAAiC;YAClD,KAAK,EAAE,CAAC,kBAAkB,CAAC;SAC5B,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,EAAE,GAAG;YACT,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,eAAe,EAAE,CAAiB,EAAE,0BAA0B;SAC/B,CAAC;QAClC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,EAAE,GAAG;YACT,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,eAAe,EAAE,EAAkB;SACJ,CAAC;QAClC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/utils/encoding.d.ts
CHANGED
|
@@ -3,5 +3,13 @@ export declare function normalizeHexWord(hex: string): string;
|
|
|
3
3
|
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
4
4
|
export declare function hexToBytes(hex: string): Uint8Array;
|
|
5
5
|
export declare function uint8ArrayToBase64(bytes: Uint8Array): string;
|
|
6
|
+
/** Serialize a Miden `Note` to base64 (v2 `consume_notes` metadata, issue #229). */
|
|
7
|
+
export declare function noteToBase64(note: {
|
|
8
|
+
serialize(): Uint8Array;
|
|
9
|
+
}): string;
|
|
10
|
+
/** Decode a base64 note. `noteCtor` is `Note` from `@miden-sdk/miden-sdk`. */
|
|
11
|
+
export declare function noteFromBase64<TNote>(base64: string, noteCtor: {
|
|
12
|
+
deserialize(bytes: Uint8Array): TNote;
|
|
13
|
+
}): TNote;
|
|
6
14
|
export declare function base64ToUint8Array(base64: string): Uint8Array;
|
|
7
15
|
//# sourceMappingURL=encoding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAMpD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAOlD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAa5D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAgB7D"}
|
|
1
|
+
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAMpD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAOlD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAa5D;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,SAAS,IAAI,UAAU,CAAA;CAAE,GAAG,MAAM,CAEtE;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,KAAK,EAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE;IAAE,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAA;CAAE,GAClD,KAAK,CAEP;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAgB7D"}
|
package/dist/utils/encoding.js
CHANGED
|
@@ -35,6 +35,14 @@ export function uint8ArrayToBase64(bytes) {
|
|
|
35
35
|
}
|
|
36
36
|
throw new Error('No base64 encoder available in this environment');
|
|
37
37
|
}
|
|
38
|
+
/** Serialize a Miden `Note` to base64 (v2 `consume_notes` metadata, issue #229). */
|
|
39
|
+
export function noteToBase64(note) {
|
|
40
|
+
return uint8ArrayToBase64(note.serialize());
|
|
41
|
+
}
|
|
42
|
+
/** Decode a base64 note. `noteCtor` is `Note` from `@miden-sdk/miden-sdk`. */
|
|
43
|
+
export function noteFromBase64(base64, noteCtor) {
|
|
44
|
+
return noteCtor.deserialize(base64ToUint8Array(base64));
|
|
45
|
+
}
|
|
38
46
|
export function base64ToUint8Array(base64) {
|
|
39
47
|
const binary = typeof atob === 'function'
|
|
40
48
|
? atob(base64)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/utils/encoding.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openzeppelin/miden-multisig-client",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.8",
|
|
4
4
|
"description": "TypeScript SDK for Miden multisig accounts with GUARDIAN integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@noble/curves": "^1.9.7",
|
|
31
|
-
"@miden-sdk/miden-sdk": "0.14.
|
|
31
|
+
"@miden-sdk/miden-sdk": "0.14.5",
|
|
32
32
|
"@noble/hashes": "^2.0.1",
|
|
33
|
-
"@openzeppelin/guardian-client": "^0.14.
|
|
33
|
+
"@openzeppelin/guardian-client": "^0.14.8"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"typescript": "^5.7.2",
|
package/src/client.test.ts
CHANGED
|
@@ -250,4 +250,101 @@ describe('MultisigClient', () => {
|
|
|
250
250
|
expect(bindAccountKey).toHaveBeenCalledWith(webClient, '0x' + 'd'.repeat(30));
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
|
+
|
|
254
|
+
// --- recoverByKey -------------------
|
|
255
|
+
|
|
256
|
+
describe('recoverByKey', () => {
|
|
257
|
+
function makeLookupCapableSigner() {
|
|
258
|
+
return {
|
|
259
|
+
commitment: '0x' + 'a'.repeat(64),
|
|
260
|
+
publicKey: '0x' + 'p'.repeat(897),
|
|
261
|
+
scheme: 'falcon' as const,
|
|
262
|
+
signAccountIdWithTimestamp: vi.fn().mockResolvedValue('0x' + 'a'.repeat(128)),
|
|
263
|
+
signRequest: vi.fn().mockReturnValue('0x' + 'a'.repeat(128)),
|
|
264
|
+
signCommitment: vi.fn().mockReturnValue('0x' + 'b'.repeat(128)),
|
|
265
|
+
signLookupMessage: vi.fn().mockResolvedValue('0x' + 'c'.repeat(762)),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function mockServerLookupResponse(accountIds: string[]) {
|
|
270
|
+
mockFetch.mockResolvedValueOnce({
|
|
271
|
+
ok: true,
|
|
272
|
+
json: async () => ({
|
|
273
|
+
accounts: accountIds.map((id) => ({ account_id: id })),
|
|
274
|
+
}),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function mockServerStateResponse(accountId: string) {
|
|
279
|
+
mockFetch.mockResolvedValueOnce({
|
|
280
|
+
ok: true,
|
|
281
|
+
json: async () => ({
|
|
282
|
+
account_id: accountId,
|
|
283
|
+
commitment: '0x' + 'f'.repeat(64),
|
|
284
|
+
state_json: { data: 'base64data' },
|
|
285
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
286
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
287
|
+
}),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
it('returns one (accountId, state) pair when lookup matches a single account', async () => {
|
|
292
|
+
const client = new MultisigClient(webClient);
|
|
293
|
+
const signer = makeLookupCapableSigner();
|
|
294
|
+
const accountId = '0x7bfb0f38b0fafa103f86a805594170';
|
|
295
|
+
|
|
296
|
+
mockServerLookupResponse([accountId]);
|
|
297
|
+
mockServerStateResponse(accountId);
|
|
298
|
+
|
|
299
|
+
const recovered = await client.recoverByKey(signer);
|
|
300
|
+
|
|
301
|
+
expect(recovered).toHaveLength(1);
|
|
302
|
+
expect(recovered[0].accountId).toBe(accountId);
|
|
303
|
+
expect(recovered[0].state.commitment).toBe('0x' + 'f'.repeat(64));
|
|
304
|
+
expect(signer.signLookupMessage).toHaveBeenCalledTimes(1);
|
|
305
|
+
expect(signer.signLookupMessage).toHaveBeenCalledWith(
|
|
306
|
+
signer.commitment,
|
|
307
|
+
expect.any(Number)
|
|
308
|
+
);
|
|
309
|
+
// Lookup + getState = exactly two HTTP requests.
|
|
310
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('returns multiple (accountId, state) pairs when one commitment authorizes several accounts', async () => {
|
|
314
|
+
const client = new MultisigClient(webClient);
|
|
315
|
+
const signer = makeLookupCapableSigner();
|
|
316
|
+
const accountA = '0xaaa1';
|
|
317
|
+
const accountB = '0xbbb2';
|
|
318
|
+
|
|
319
|
+
mockServerLookupResponse([accountA, accountB]);
|
|
320
|
+
mockServerStateResponse(accountA);
|
|
321
|
+
mockServerStateResponse(accountB);
|
|
322
|
+
|
|
323
|
+
const recovered = await client.recoverByKey(signer);
|
|
324
|
+
|
|
325
|
+
expect(recovered.map((r) => r.accountId)).toEqual([accountA, accountB]);
|
|
326
|
+
// 1 lookup + 2 state fetches.
|
|
327
|
+
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('returns empty array when no account authorizes the commitment', async () => {
|
|
331
|
+
const client = new MultisigClient(webClient);
|
|
332
|
+
const signer = makeLookupCapableSigner();
|
|
333
|
+
|
|
334
|
+
mockServerLookupResponse([]);
|
|
335
|
+
|
|
336
|
+
const recovered = await client.recoverByKey(signer);
|
|
337
|
+
|
|
338
|
+
expect(recovered).toEqual([]);
|
|
339
|
+
// Only the lookup HTTP call — no per-account state fetches.
|
|
340
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('throws a clear error when the signer does not implement signLookupMessage', async () => {
|
|
344
|
+
const client = new MultisigClient(webClient);
|
|
345
|
+
// mockSigner from the outer beforeEach lacks signLookupMessage.
|
|
346
|
+
await expect(client.recoverByKey(mockSigner)).rejects.toThrow(/signLookupMessage/);
|
|
347
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
253
350
|
});
|
package/src/client.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { type MidenClient, Account, AccountId } from '@miden-sdk/miden-sdk';
|
|
9
9
|
import { GuardianHttpClient } from '@openzeppelin/guardian-client';
|
|
10
|
+
import type { StateObject } from '@openzeppelin/guardian-client';
|
|
10
11
|
import { Multisig } from './multisig.js';
|
|
11
12
|
import { createMultisigAccount } from './account/index.js';
|
|
12
13
|
import { AccountInspector } from './inspector.js';
|
|
@@ -38,6 +39,16 @@ export interface MultisigClientConfig {
|
|
|
38
39
|
midenRpcEndpoint?: string;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* One match returned by `MultisigClient.recoverByKey`. Pairs the discovered
|
|
44
|
+
* `accountId` with the current `state` snapshot so callers do not need to do a
|
|
45
|
+
* second round-trip per account.
|
|
46
|
+
*/
|
|
47
|
+
export interface RecoveredAccount {
|
|
48
|
+
accountId: string;
|
|
49
|
+
state: StateObject;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
/**
|
|
42
53
|
* Client for creating and loading multisig accounts.
|
|
43
54
|
*
|
|
@@ -92,6 +103,31 @@ export class MultisigClient {
|
|
|
92
103
|
return this._guardianClient;
|
|
93
104
|
}
|
|
94
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Recover the set of accounts a given signer authorizes by querying
|
|
108
|
+
* Guardian's `/state/lookup` endpoint and fetching state for each match.
|
|
109
|
+
* Returns `(accountId, state)` pairs; an empty array means no account on
|
|
110
|
+
* this operator authorizes the commitment (distinct from "wrong key",
|
|
111
|
+
* which would fail authentication first).
|
|
112
|
+
*
|
|
113
|
+
* @throws if `signer` does not implement `signLookupMessage`. The bundled
|
|
114
|
+
* `FalconSigner` and `EcdsaSigner` both do.
|
|
115
|
+
*/
|
|
116
|
+
async recoverByKey(signer: Signer): Promise<RecoveredAccount[]> {
|
|
117
|
+
this._guardianClient.setSigner(signer);
|
|
118
|
+
|
|
119
|
+
const lookup = await this._guardianClient.lookupAccountByKeyCommitment(signer.commitment);
|
|
120
|
+
|
|
121
|
+
// Per-account replay protection is scoped to each account's
|
|
122
|
+
// last_auth_timestamp, so concurrent getState calls do not race.
|
|
123
|
+
return Promise.all(
|
|
124
|
+
lookup.accounts.map(async ({ accountId }) => ({
|
|
125
|
+
accountId,
|
|
126
|
+
state: await this._guardianClient.getState(accountId),
|
|
127
|
+
})),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
95
131
|
/**
|
|
96
132
|
* Create a new multisig account.
|
|
97
133
|
*
|
package/src/index.ts
CHANGED
|
@@ -39,7 +39,12 @@
|
|
|
39
39
|
* ```
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
export {
|
|
42
|
+
export {
|
|
43
|
+
MultisigClient,
|
|
44
|
+
type MultisigClientConfig,
|
|
45
|
+
type RecoveredAccount,
|
|
46
|
+
} from './client.js';
|
|
47
|
+
export { lookupAuthDigest } from './lookupAuth.js';
|
|
43
48
|
export { Multisig, type AccountState } from './multisig.js';
|
|
44
49
|
export { AccountInspector, type DetectedMultisigConfig, type VaultBalance } from './inspector.js';
|
|
45
50
|
export {
|
|
@@ -74,6 +79,30 @@ export {
|
|
|
74
79
|
StorageLayoutBuilder,
|
|
75
80
|
} from './account/index.js';
|
|
76
81
|
|
|
82
|
+
export {
|
|
83
|
+
CONSUME_NOTES_METADATA_VERSION_V2,
|
|
84
|
+
MAX_CONSUME_NOTES_METADATA_BYTES,
|
|
85
|
+
isConsumeNotesV1,
|
|
86
|
+
isConsumeNotesV2,
|
|
87
|
+
} from './types/proposal.js';
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
LEGACY_CONSUME_NOTES_ENABLED,
|
|
91
|
+
} from './multisig/config.js';
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
type ConsumeNotesErrorCode,
|
|
95
|
+
NoteBindingMismatchError,
|
|
96
|
+
UnsupportedMetadataVersionError,
|
|
97
|
+
ConsumeNotesMetadataOversizeError,
|
|
98
|
+
LegacyConsumeNotesNoteMissingError,
|
|
99
|
+
} from './multisig/consumeNotesErrors.js';
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
noteToBase64,
|
|
103
|
+
noteFromBase64,
|
|
104
|
+
} from './utils/encoding.js';
|
|
105
|
+
|
|
77
106
|
export {
|
|
78
107
|
PROCEDURE_ROOTS,
|
|
79
108
|
getProcedureRoot,
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// The Miden SDK is wasm-bound and is mocked across this test suite (matching
|
|
4
|
+
// the pattern in transaction.test.ts, multisig.test.ts, etc.). We cannot run
|
|
5
|
+
// real RPO256 here, but we CAN verify the lookup-digest function builds the
|
|
6
|
+
// FeltArray sequence in the right order and chunking convention. Cross-language
|
|
7
|
+
// parity against `crates/shared/tests/fixtures/lookup_auth_vectors.json` is
|
|
8
|
+
// intentionally a separate runner — see `runRealSdkParity` below for the gated
|
|
9
|
+
// hook.
|
|
10
|
+
|
|
11
|
+
interface FakeFelt {
|
|
12
|
+
__felt: true;
|
|
13
|
+
value: bigint;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface FakeWord {
|
|
17
|
+
__word: true;
|
|
18
|
+
felts: FakeFelt[];
|
|
19
|
+
toFelts: () => FakeFelt[];
|
|
20
|
+
toHex: () => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const hashCalls: { input: FakeFelt[] }[] = [];
|
|
24
|
+
|
|
25
|
+
vi.mock('@miden-sdk/miden-sdk', () => {
|
|
26
|
+
const Felt = vi.fn().mockImplementation((value: bigint) => ({
|
|
27
|
+
__felt: true,
|
|
28
|
+
value,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const FeltArray = vi.fn().mockImplementation((felts: FakeFelt[]) => felts);
|
|
32
|
+
|
|
33
|
+
const makeWord = (felts: FakeFelt[], hex = '0xMOCKED'): FakeWord => ({
|
|
34
|
+
__word: true,
|
|
35
|
+
felts,
|
|
36
|
+
toFelts: () => felts,
|
|
37
|
+
toHex: () => hex,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const Word = {
|
|
41
|
+
fromHex: vi.fn().mockImplementation((hex: string) => {
|
|
42
|
+
// Deterministic four-felt decomposition: split hex into 4 8-byte pieces.
|
|
43
|
+
// The values are not real Word semantics, but they let us assert the
|
|
44
|
+
// implementation passes them through in order.
|
|
45
|
+
const stripped = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
46
|
+
const padded = stripped.padStart(64, '0');
|
|
47
|
+
const felts: FakeFelt[] = [];
|
|
48
|
+
for (let i = 0; i < 4; i += 1) {
|
|
49
|
+
const piece = padded.slice(i * 16, (i + 1) * 16);
|
|
50
|
+
felts.push({ __felt: true, value: BigInt('0x' + piece) });
|
|
51
|
+
}
|
|
52
|
+
return makeWord(felts, hex);
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const Rpo256 = {
|
|
57
|
+
hashElements: vi.fn().mockImplementation((felts: FakeFelt[]) => {
|
|
58
|
+
hashCalls.push({ input: [...felts] });
|
|
59
|
+
// Synthesize a fake digest: 4 felts derived from the input length and
|
|
60
|
+
// first felt's value, so each call's output is distinguishable.
|
|
61
|
+
const seed = felts.length === 0 ? 0n : felts[0].value;
|
|
62
|
+
return makeWord(
|
|
63
|
+
[
|
|
64
|
+
{ __felt: true, value: seed ^ BigInt(felts.length) },
|
|
65
|
+
{ __felt: true, value: seed + 1n },
|
|
66
|
+
{ __felt: true, value: seed + 2n },
|
|
67
|
+
{ __felt: true, value: seed + 3n },
|
|
68
|
+
],
|
|
69
|
+
'0xfakehash',
|
|
70
|
+
);
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return { Felt, FeltArray, Rpo256, Word };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const DOMAIN_TAG_BYTES = new TextEncoder().encode('guardian.lookup.v1');
|
|
78
|
+
|
|
79
|
+
function bytesToExpectedFelts(bytes: Uint8Array): bigint[] {
|
|
80
|
+
const out: bigint[] = [];
|
|
81
|
+
for (let offset = 0; offset < bytes.length; offset += 8) {
|
|
82
|
+
let value = 0n;
|
|
83
|
+
for (let i = 0; i < 8; i += 1) {
|
|
84
|
+
const byte = offset + i < bytes.length ? bytes[offset + i] : 0;
|
|
85
|
+
value |= BigInt(byte) << BigInt(8 * i);
|
|
86
|
+
}
|
|
87
|
+
out.push(value);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let lookupAuthDigest: typeof import('./lookupAuth.js').lookupAuthDigest;
|
|
93
|
+
let _resetLookupDomainTagCacheForTesting: typeof import('./lookupAuth.js')._resetLookupDomainTagCacheForTesting;
|
|
94
|
+
|
|
95
|
+
beforeEach(async () => {
|
|
96
|
+
hashCalls.length = 0;
|
|
97
|
+
// Re-import to pick up a fresh cached domain tag per test, in addition to
|
|
98
|
+
// the explicit reset, so test ordering does not change behavior.
|
|
99
|
+
const mod = await import('./lookupAuth.js');
|
|
100
|
+
lookupAuthDigest = mod.lookupAuthDigest;
|
|
101
|
+
_resetLookupDomainTagCacheForTesting = mod._resetLookupDomainTagCacheForTesting;
|
|
102
|
+
_resetLookupDomainTagCacheForTesting();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('lookupAuthDigest', () => {
|
|
106
|
+
it('hashes the domain-tag bytes via the same 8-byte LE chunking as Rust', () => {
|
|
107
|
+
const keyCommitmentHex = '0x' + 'aa'.repeat(32);
|
|
108
|
+
lookupAuthDigest(1_700_000_000_000, keyCommitmentHex);
|
|
109
|
+
|
|
110
|
+
// First Rpo256.hashElements call must be the domain-tag computation
|
|
111
|
+
// (3 felts for "guardian.lookup.v1" — 18 bytes → ceil(18/8) = 3 chunks).
|
|
112
|
+
expect(hashCalls.length).toBeGreaterThanOrEqual(2);
|
|
113
|
+
const tagCall = hashCalls[0];
|
|
114
|
+
const expected = bytesToExpectedFelts(DOMAIN_TAG_BYTES);
|
|
115
|
+
expect(tagCall.input).toHaveLength(expected.length);
|
|
116
|
+
expect(tagCall.input.length).toBe(3);
|
|
117
|
+
for (let i = 0; i < expected.length; i += 1) {
|
|
118
|
+
expect(tagCall.input[i].value).toBe(expected[i]);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('caches the domain-tag computation across calls', () => {
|
|
123
|
+
const keyCommitmentHex = '0x' + 'bb'.repeat(32);
|
|
124
|
+
lookupAuthDigest(1_700_000_000_000, keyCommitmentHex);
|
|
125
|
+
const callsAfterFirst = hashCalls.length;
|
|
126
|
+
lookupAuthDigest(1_700_000_000_001, keyCommitmentHex);
|
|
127
|
+
const callsAfterSecond = hashCalls.length;
|
|
128
|
+
|
|
129
|
+
// Second invocation should perform exactly one Rpo256.hashElements call
|
|
130
|
+
// (the message hash); the domain-tag hash is cached.
|
|
131
|
+
expect(callsAfterSecond - callsAfterFirst).toBe(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('passes 9 felts in [tag, timestamp, key_commitment] order to the message hash', () => {
|
|
135
|
+
const keyCommitmentHex = '0x' + '01'.repeat(32);
|
|
136
|
+
const timestamp = 1_700_000_000_000n;
|
|
137
|
+
lookupAuthDigest(timestamp, keyCommitmentHex);
|
|
138
|
+
|
|
139
|
+
const messageCall = hashCalls[hashCalls.length - 1];
|
|
140
|
+
expect(messageCall.input).toHaveLength(9);
|
|
141
|
+
|
|
142
|
+
// Felt 4 is the timestamp, reinterpreted as u64 (no change for positive).
|
|
143
|
+
expect(messageCall.input[4].value).toBe(timestamp);
|
|
144
|
+
|
|
145
|
+
// Felts 5..8 are the key-commitment felts, derived deterministically by
|
|
146
|
+
// the mocked Word.fromHex from the hex representation.
|
|
147
|
+
const expectedKcWord = '01'.repeat(32);
|
|
148
|
+
for (let i = 0; i < 4; i += 1) {
|
|
149
|
+
const piece = expectedKcWord.slice(i * 16, (i + 1) * 16);
|
|
150
|
+
expect(messageCall.input[5 + i].value).toBe(BigInt('0x' + piece));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('reinterprets a negative timestamp via two-complement u64 cast', () => {
|
|
155
|
+
const keyCommitmentHex = '0x' + 'cc'.repeat(32);
|
|
156
|
+
lookupAuthDigest(-1, keyCommitmentHex);
|
|
157
|
+
|
|
158
|
+
const messageCall = hashCalls[hashCalls.length - 1];
|
|
159
|
+
// -1 cast to u64 is 0xFFFFFFFFFFFFFFFF.
|
|
160
|
+
expect(messageCall.input[4].value).toBe(0xFFFFFFFFFFFFFFFFn);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('produces distinct digest inputs for distinct commitments', () => {
|
|
164
|
+
lookupAuthDigest(1_700_000_000_000, '0x' + '01'.repeat(32));
|
|
165
|
+
lookupAuthDigest(1_700_000_000_000, '0x' + '02'.repeat(32));
|
|
166
|
+
|
|
167
|
+
// Call 0 is the cached domain-tag hash. Calls 1 and 2 are the two message
|
|
168
|
+
// hashes; the second call hits the domain-tag cache so it does not produce
|
|
169
|
+
// an extra hash call.
|
|
170
|
+
const messageCallA = hashCalls[1];
|
|
171
|
+
const messageCallB = hashCalls[2];
|
|
172
|
+
expect(messageCallA.input[5].value).not.toBe(messageCallB.input[5].value);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('produces distinct digest inputs for distinct timestamps', () => {
|
|
176
|
+
const keyCommitmentHex = '0x' + '01'.repeat(32);
|
|
177
|
+
lookupAuthDigest(1_700_000_000_000, keyCommitmentHex);
|
|
178
|
+
lookupAuthDigest(1_700_000_000_001, keyCommitmentHex);
|
|
179
|
+
|
|
180
|
+
const messageCallA = hashCalls[1];
|
|
181
|
+
const messageCallB = hashCalls[2];
|
|
182
|
+
expect(messageCallA.input[4].value).not.toBe(messageCallB.input[4].value);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Felt, FeltArray, Rpo256, Word } from '@miden-sdk/miden-sdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lookup-bound message format used to sign requests against the Guardian
|
|
5
|
+
* `/state/lookup` endpoint. MUST produce byte-identical digests to
|
|
6
|
+
* `crates/shared/src/lookup_auth_message.rs`; parity is verified against
|
|
7
|
+
* `crates/shared/tests/fixtures/lookup_auth_vectors.json`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const DOMAIN_TAG_BYTES = new TextEncoder().encode('guardian.lookup.v1');
|
|
11
|
+
|
|
12
|
+
let cachedDomainTag: Word | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Convert a byte array to a sequence of Goldilocks-field felts using the same
|
|
16
|
+
* 8-byte little-endian chunking convention as
|
|
17
|
+
* `guardian_shared::auth_request_payload::AuthRequestPayload::from_bytes`. The
|
|
18
|
+
* final chunk is zero-padded if `bytes.length` is not a multiple of 8.
|
|
19
|
+
*/
|
|
20
|
+
function bytesToFelts(bytes: Uint8Array): Felt[] {
|
|
21
|
+
const felts: Felt[] = [];
|
|
22
|
+
for (let offset = 0; offset < bytes.length; offset += 8) {
|
|
23
|
+
let value = 0n;
|
|
24
|
+
for (let i = 0; i < 8; i += 1) {
|
|
25
|
+
const byte = offset + i < bytes.length ? bytes[offset + i] : 0;
|
|
26
|
+
value |= BigInt(byte) << BigInt(8 * i);
|
|
27
|
+
}
|
|
28
|
+
felts.push(new Felt(value));
|
|
29
|
+
}
|
|
30
|
+
return felts;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The 4-felt RPO domain tag prepended to every lookup digest, computed once
|
|
35
|
+
* from `DOMAIN_TAG_BYTES`. Cached on first call.
|
|
36
|
+
*/
|
|
37
|
+
function lookupDomainTag(): Word {
|
|
38
|
+
if (cachedDomainTag === null) {
|
|
39
|
+
cachedDomainTag = Rpo256.hashElements(new FeltArray(bytesToFelts(DOMAIN_TAG_BYTES)));
|
|
40
|
+
}
|
|
41
|
+
return cachedDomainTag;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compute the digest a lookup-bound signer signs.
|
|
46
|
+
*
|
|
47
|
+
* Layout (must mirror `LookupAuthMessage::to_word` in `crates/shared`):
|
|
48
|
+
*
|
|
49
|
+
* ```text
|
|
50
|
+
* RPO256_hash([
|
|
51
|
+
* DOMAIN_TAG_W0, DOMAIN_TAG_W1, DOMAIN_TAG_W2, DOMAIN_TAG_W3,
|
|
52
|
+
* timestamp_ms_felt,
|
|
53
|
+
* key_commitment_W0, key_commitment_W1,
|
|
54
|
+
* key_commitment_W2, key_commitment_W3,
|
|
55
|
+
* ])
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param timestampMs Unix milliseconds. Reinterpreted as `u64` to match the
|
|
59
|
+
* Rust `as u64` cast (so negative inputs wrap into the
|
|
60
|
+
* high range, then are reduced mod the Goldilocks prime by
|
|
61
|
+
* the Felt constructor).
|
|
62
|
+
* @param keyCommitmentHex `0x`-prefixed 32-byte hex string for the queried
|
|
63
|
+
* commitment.
|
|
64
|
+
*/
|
|
65
|
+
export function lookupAuthDigest(timestampMs: number | bigint, keyCommitmentHex: string): Word {
|
|
66
|
+
const tag = lookupDomainTag().toFelts();
|
|
67
|
+
const kc = Word.fromHex(keyCommitmentHex).toFelts();
|
|
68
|
+
const tsBigInt = typeof timestampMs === 'bigint' ? timestampMs : BigInt(timestampMs);
|
|
69
|
+
const timestampU64 = BigInt.asUintN(64, tsBigInt);
|
|
70
|
+
const timestampFelt = new Felt(timestampU64);
|
|
71
|
+
|
|
72
|
+
const message = new FeltArray([
|
|
73
|
+
tag[0],
|
|
74
|
+
tag[1],
|
|
75
|
+
tag[2],
|
|
76
|
+
tag[3],
|
|
77
|
+
timestampFelt,
|
|
78
|
+
kc[0],
|
|
79
|
+
kc[1],
|
|
80
|
+
kc[2],
|
|
81
|
+
kc[3],
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
return Rpo256.hashElements(message);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Reset the cached domain tag. Tests use this to exercise the cache miss path;
|
|
89
|
+
* production callers should not need it.
|
|
90
|
+
*/
|
|
91
|
+
export function _resetLookupDomainTagCacheForTesting(): void {
|
|
92
|
+
cachedDomainTag = null;
|
|
93
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this build accepts v1 `consume_notes` metadata (issue #229).
|
|
3
|
+
* Mirrors the Rust `legacy-consume-notes` Cargo feature. Flip to `false`
|
|
4
|
+
* in the cut-over release; v1 proposals will then be refused with
|
|
5
|
+
* `UnsupportedMetadataVersionError`.
|
|
6
|
+
*/
|
|
7
|
+
export const LEGACY_CONSUME_NOTES_ENABLED = true;
|