@openzeppelin/miden-multisig-client 0.13.1 → 0.13.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.
Files changed (275) hide show
  1. package/README.md +98 -119
  2. package/dist/account/builder.d.ts +18 -0
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +59 -29
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/builder.test.js +108 -32
  7. package/dist/account/builder.test.js.map +1 -1
  8. package/dist/account/index.d.ts +1 -1
  9. package/dist/account/index.d.ts.map +1 -1
  10. package/dist/account/index.js +1 -1
  11. package/dist/account/index.js.map +1 -1
  12. package/dist/account/masm/account-components/auth.d.ts +5 -0
  13. package/dist/account/masm/account-components/auth.d.ts.map +1 -0
  14. package/dist/account/masm/account-components/auth.js +67 -0
  15. package/dist/account/masm/account-components/auth.js.map +1 -0
  16. package/dist/account/masm/auth.d.ts +5 -0
  17. package/dist/account/masm/auth.d.ts.map +1 -0
  18. package/dist/account/masm/auth.js +1395 -0
  19. package/dist/account/masm/auth.js.map +1 -0
  20. package/dist/account/masm/index.d.ts +3 -0
  21. package/dist/account/masm/index.d.ts.map +1 -0
  22. package/dist/account/masm/index.js +5 -0
  23. package/dist/account/masm/index.js.map +1 -0
  24. package/dist/account/masm.d.ts +1 -4
  25. package/dist/account/masm.d.ts.map +1 -1
  26. package/dist/account/masm.js +3 -1034
  27. package/dist/account/masm.js.map +1 -1
  28. package/dist/account/storage.d.ts +2 -2
  29. package/dist/account/storage.d.ts.map +1 -1
  30. package/dist/account/storage.js +18 -16
  31. package/dist/account/storage.js.map +1 -1
  32. package/dist/client.d.ts +67 -9
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +73 -22
  35. package/dist/client.js.map +1 -1
  36. package/dist/client.test.js +31 -32
  37. package/dist/client.test.js.map +1 -1
  38. package/dist/index.d.ts +54 -12
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +53 -9
  41. package/dist/index.js.map +1 -1
  42. package/dist/inspector.d.ts +32 -6
  43. package/dist/inspector.d.ts.map +1 -1
  44. package/dist/inspector.js +58 -23
  45. package/dist/inspector.js.map +1 -1
  46. package/dist/inspector.test.js +18 -18
  47. package/dist/inspector.test.js.map +1 -1
  48. package/dist/multisig/proposal/execution.d.ts +4 -4
  49. package/dist/multisig/proposal/execution.d.ts.map +1 -1
  50. package/dist/multisig/proposal/execution.js +42 -31
  51. package/dist/multisig/proposal/execution.js.map +1 -1
  52. package/dist/multisig/proposal/parser.d.ts +3 -3
  53. package/dist/multisig/proposal/parser.d.ts.map +1 -1
  54. package/dist/multisig/proposal/parser.js +23 -23
  55. package/dist/multisig/proposal/parser.js.map +1 -1
  56. package/dist/multisig/signing.d.ts +3 -3
  57. package/dist/multisig/signing.d.ts.map +1 -1
  58. package/dist/multisig/signing.js +3 -3
  59. package/dist/multisig/signing.js.map +1 -1
  60. package/dist/multisig.d.ts +188 -48
  61. package/dist/multisig.d.ts.map +1 -1
  62. package/dist/multisig.js +650 -308
  63. package/dist/multisig.js.map +1 -1
  64. package/dist/multisig.test.js +1680 -241
  65. package/dist/multisig.test.js.map +1 -1
  66. package/dist/procedures.d.ts +46 -24
  67. package/dist/procedures.d.ts.map +1 -1
  68. package/dist/procedures.js +46 -21
  69. package/dist/procedures.js.map +1 -1
  70. package/dist/proposal/factory.d.ts +18 -0
  71. package/dist/proposal/factory.d.ts.map +1 -0
  72. package/dist/proposal/factory.js +99 -0
  73. package/dist/proposal/factory.js.map +1 -0
  74. package/dist/proposal/metadata.d.ts +5 -12
  75. package/dist/proposal/metadata.d.ts.map +1 -1
  76. package/dist/proposal/metadata.js +118 -174
  77. package/dist/proposal/metadata.js.map +1 -1
  78. package/dist/proposal/signatures.d.ts +10 -0
  79. package/dist/proposal/signatures.d.ts.map +1 -0
  80. package/dist/proposal/signatures.js +54 -0
  81. package/dist/proposal/signatures.js.map +1 -0
  82. package/dist/signer.d.ts +2 -0
  83. package/dist/signer.d.ts.map +1 -1
  84. package/dist/signer.js +2 -0
  85. package/dist/signer.js.map +1 -1
  86. package/dist/signers/ecdsa.d.ts +3 -0
  87. package/dist/signers/ecdsa.d.ts.map +1 -1
  88. package/dist/signers/ecdsa.js +8 -4
  89. package/dist/signers/ecdsa.js.map +1 -1
  90. package/dist/signers/ecdsa.test.d.ts +2 -0
  91. package/dist/signers/ecdsa.test.d.ts.map +1 -0
  92. package/dist/signers/ecdsa.test.js +65 -0
  93. package/dist/signers/ecdsa.test.js.map +1 -0
  94. package/dist/signers/falcon.d.ts +3 -0
  95. package/dist/signers/falcon.d.ts.map +1 -1
  96. package/dist/signers/falcon.js +8 -4
  97. package/dist/signers/falcon.js.map +1 -1
  98. package/dist/signers/falcon.test.js +12 -0
  99. package/dist/signers/falcon.test.js.map +1 -1
  100. package/dist/signers/miden-wallet.d.ts +3 -1
  101. package/dist/signers/miden-wallet.d.ts.map +1 -1
  102. package/dist/signers/miden-wallet.js +11 -2
  103. package/dist/signers/miden-wallet.js.map +1 -1
  104. package/dist/signers/miden-wallet.test.js +26 -0
  105. package/dist/signers/miden-wallet.test.js.map +1 -1
  106. package/dist/signers/para.d.ts +3 -3
  107. package/dist/signers/para.d.ts.map +1 -1
  108. package/dist/signers/para.js +15 -6
  109. package/dist/signers/para.js.map +1 -1
  110. package/dist/signers/para.test.js +39 -0
  111. package/dist/signers/para.test.js.map +1 -1
  112. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  113. package/dist/transaction/consumeNotes.js +1 -0
  114. package/dist/transaction/consumeNotes.js.map +1 -1
  115. package/dist/transaction/index.d.ts +2 -1
  116. package/dist/transaction/index.d.ts.map +1 -1
  117. package/dist/transaction/index.js +2 -1
  118. package/dist/transaction/index.js.map +1 -1
  119. package/dist/transaction/options.d.ts +1 -1
  120. package/dist/transaction/options.d.ts.map +1 -1
  121. package/dist/transaction/p2id.d.ts +1 -0
  122. package/dist/transaction/p2id.d.ts.map +1 -1
  123. package/dist/transaction/p2id.js +9 -3
  124. package/dist/transaction/p2id.js.map +1 -1
  125. package/dist/transaction/updateGuardian.d.ts +7 -0
  126. package/dist/transaction/updateGuardian.d.ts.map +1 -0
  127. package/dist/transaction/{updatePsm.js → updateGuardian.js} +15 -29
  128. package/dist/transaction/updateGuardian.js.map +1 -0
  129. package/dist/transaction/updateProcedureThreshold.d.ts +9 -0
  130. package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -0
  131. package/dist/transaction/updateProcedureThreshold.js +55 -0
  132. package/dist/transaction/updateProcedureThreshold.js.map +1 -0
  133. package/dist/transaction/updateSigners.d.ts.map +1 -1
  134. package/dist/transaction/updateSigners.js +5 -23
  135. package/dist/transaction/updateSigners.js.map +1 -1
  136. package/dist/transaction.d.ts +2 -1
  137. package/dist/transaction.d.ts.map +1 -1
  138. package/dist/transaction.js +2 -1
  139. package/dist/transaction.js.map +1 -1
  140. package/dist/types/proposal.d.ts +32 -15
  141. package/dist/types/proposal.d.ts.map +1 -1
  142. package/dist/types.d.ts +24 -7
  143. package/dist/types.d.ts.map +1 -1
  144. package/dist/utils/digest.d.ts +5 -0
  145. package/dist/utils/digest.d.ts.map +1 -1
  146. package/dist/utils/digest.js +34 -1
  147. package/dist/utils/digest.js.map +1 -1
  148. package/dist/utils/digest.test.d.ts +2 -0
  149. package/dist/utils/digest.test.d.ts.map +1 -0
  150. package/dist/utils/digest.test.js +48 -0
  151. package/dist/utils/digest.test.js.map +1 -0
  152. package/dist/utils/signature.d.ts +3 -1
  153. package/dist/utils/signature.d.ts.map +1 -1
  154. package/dist/utils/signature.js +31 -6
  155. package/dist/utils/signature.js.map +1 -1
  156. package/dist/utils/word.test.js +1 -52
  157. package/dist/utils/word.test.js.map +1 -1
  158. package/masm/account_components/auth/multisig.masm +13 -0
  159. package/masm/account_components/auth/multisig_ecdsa.masm +13 -0
  160. package/masm/account_components/auth/multisig_guardian.masm +17 -0
  161. package/masm/account_components/auth/multisig_guardian_ecdsa.masm +17 -0
  162. package/masm/auth/guardian.masm +177 -0
  163. package/masm/auth/guardian_ecdsa.masm +178 -0
  164. package/masm/{multisig.masm → auth/multisig.masm} +149 -55
  165. package/masm/{multisig_ecdsa.masm → auth/multisig_ecdsa.masm} +148 -56
  166. package/package.json +11 -9
  167. package/src/account/builder.test.ts +165 -0
  168. package/src/account/builder.ts +71 -34
  169. package/src/account/index.ts +1 -1
  170. package/src/account/masm/account-components/auth.ts +70 -0
  171. package/src/account/masm/auth.ts +1398 -0
  172. package/src/account/masm/index.ts +5 -0
  173. package/src/account/masm.ts +3 -1040
  174. package/src/account/storage.ts +18 -16
  175. package/src/client.test.ts +31 -35
  176. package/src/client.ts +98 -26
  177. package/src/index.ts +84 -22
  178. package/src/inspector.test.ts +17 -17
  179. package/src/inspector.ts +63 -29
  180. package/src/multisig/proposal/execution.ts +66 -49
  181. package/src/multisig/proposal/parser.ts +26 -26
  182. package/src/multisig/signing.ts +4 -4
  183. package/src/multisig.test.ts +2097 -248
  184. package/src/multisig.ts +882 -388
  185. package/src/procedures.ts +50 -26
  186. package/src/proposal/factory.ts +171 -0
  187. package/src/proposal/metadata.ts +165 -0
  188. package/src/proposal/signatures.ts +68 -0
  189. package/src/signer.ts +2 -0
  190. package/src/signers/ecdsa.test.ts +80 -0
  191. package/src/signers/ecdsa.ts +15 -4
  192. package/src/signers/falcon.test.ts +19 -0
  193. package/src/signers/falcon.ts +16 -5
  194. package/src/signers/miden-wallet.test.ts +47 -0
  195. package/src/signers/miden-wallet.ts +18 -1
  196. package/src/signers/para.test.ts +57 -0
  197. package/src/signers/para.ts +26 -7
  198. package/src/transaction/consumeNotes.ts +1 -0
  199. package/src/transaction/index.ts +2 -2
  200. package/src/transaction/options.ts +1 -2
  201. package/src/transaction/p2id.test.ts +190 -0
  202. package/src/transaction/p2id.ts +11 -5
  203. package/src/transaction/rpoRandomCoin.test.ts +64 -0
  204. package/src/transaction/rpoRandomCoin.ts +24 -0
  205. package/src/transaction/{updatePsm.ts → updateGuardian.ts} +19 -32
  206. package/src/transaction/updateProcedureThreshold.ts +97 -0
  207. package/src/transaction/updateSigners.ts +12 -27
  208. package/src/transaction.ts +5 -3
  209. package/src/types/proposal.ts +43 -11
  210. package/src/types.ts +29 -11
  211. package/src/utils/digest.test.ts +55 -0
  212. package/src/utils/digest.ts +51 -1
  213. package/src/utils/signature.ts +47 -9
  214. package/src/utils/word.test.ts +1 -55
  215. package/dist/account.d.ts +0 -104
  216. package/dist/account.d.ts.map +0 -1
  217. package/dist/account.js +0 -136
  218. package/dist/account.js.map +0 -1
  219. package/dist/builder.d.ts +0 -54
  220. package/dist/builder.d.ts.map +0 -1
  221. package/dist/builder.js +0 -78
  222. package/dist/builder.js.map +0 -1
  223. package/dist/http.d.ts +0 -64
  224. package/dist/http.d.ts.map +0 -1
  225. package/dist/http.js +0 -162
  226. package/dist/http.js.map +0 -1
  227. package/dist/keystore.d.ts +0 -65
  228. package/dist/keystore.d.ts.map +0 -1
  229. package/dist/keystore.js +0 -121
  230. package/dist/keystore.js.map +0 -1
  231. package/dist/miden.d.ts +0 -9
  232. package/dist/miden.d.ts.map +0 -1
  233. package/dist/miden.js +0 -26
  234. package/dist/miden.js.map +0 -1
  235. package/dist/multisig/metadata.d.ts +0 -18
  236. package/dist/multisig/metadata.d.ts.map +0 -1
  237. package/dist/multisig/metadata.js +0 -100
  238. package/dist/multisig/metadata.js.map +0 -1
  239. package/dist/multisig/metadata.test.d.ts +0 -2
  240. package/dist/multisig/metadata.test.d.ts.map +0 -1
  241. package/dist/multisig/metadata.test.js +0 -323
  242. package/dist/multisig/metadata.test.js.map +0 -1
  243. package/dist/proposal/exported.d.ts +0 -12
  244. package/dist/proposal/exported.d.ts.map +0 -1
  245. package/dist/proposal/exported.js +0 -88
  246. package/dist/proposal/exported.js.map +0 -1
  247. package/dist/signer.test.d.ts +0 -2
  248. package/dist/signer.test.d.ts.map +0 -1
  249. package/dist/signer.test.js +0 -123
  250. package/dist/signer.test.js.map +0 -1
  251. package/dist/transaction/p2id.interop.test.d.ts +0 -2
  252. package/dist/transaction/p2id.interop.test.d.ts.map +0 -1
  253. package/dist/transaction/p2id.interop.test.js +0 -36
  254. package/dist/transaction/p2id.interop.test.js.map +0 -1
  255. package/dist/transaction/updatePsm.d.ts +0 -7
  256. package/dist/transaction/updatePsm.d.ts.map +0 -1
  257. package/dist/transaction/updatePsm.js.map +0 -1
  258. package/dist/transport/http.d.ts +0 -64
  259. package/dist/transport/http.d.ts.map +0 -1
  260. package/dist/transport/http.js +0 -162
  261. package/dist/transport/http.js.map +0 -1
  262. package/dist/transport/index.d.ts +0 -5
  263. package/dist/transport/index.d.ts.map +0 -1
  264. package/dist/transport/index.js +0 -5
  265. package/dist/transport/index.js.map +0 -1
  266. package/dist/utils/state-validation.d.ts +0 -5
  267. package/dist/utils/state-validation.d.ts.map +0 -1
  268. package/dist/utils/state-validation.js +0 -30
  269. package/dist/utils/state-validation.js.map +0 -1
  270. package/dist/utils/validation.d.ts +0 -6
  271. package/dist/utils/validation.d.ts.map +0 -1
  272. package/dist/utils/validation.js +0 -52
  273. package/dist/utils/validation.js.map +0 -1
  274. package/masm/psm.masm +0 -178
  275. package/masm/psm_ecdsa.masm +0 -179
@@ -54,6 +54,17 @@ describe('MidenWalletSigner', () => {
54
54
  expect(signer.publicKey).toBe('0xcommitment');
55
55
  });
56
56
 
57
+ it('should use explicit publicKey when provided', () => {
58
+ const signer = new MidenWalletSigner(
59
+ mockWallet,
60
+ '0xcommitment',
61
+ 'ecdsa',
62
+ undefined,
63
+ '0xwalletpubkey',
64
+ );
65
+ expect(signer.publicKey).toBe('0xwalletpubkey');
66
+ });
67
+
57
68
  it('should use localAuthSigner publicKey when provided', () => {
58
69
  const localSigner: Signer = {
59
70
  commitment: '0xlocal',
@@ -106,6 +117,42 @@ describe('MidenWalletSigner', () => {
106
117
  });
107
118
  });
108
119
 
120
+ describe('signRequest', () => {
121
+ it('delegates to localAuthSigner when present', async () => {
122
+ const localSigner: Signer = {
123
+ commitment: '0xlocal',
124
+ publicKey: '0xlocalpubkey',
125
+ scheme: 'ecdsa',
126
+ signAccountIdWithTimestamp: vi.fn(),
127
+ signRequest: vi.fn().mockResolvedValue('0xlocalsig'),
128
+ signCommitment: vi.fn(),
129
+ };
130
+ const signer = new MidenWalletSigner(mockWallet, '0xcommitment', 'ecdsa', localSigner);
131
+
132
+ const result = await signer.signRequest(
133
+ '0x' + 'aa'.repeat(15),
134
+ 1700000000,
135
+ { toBytes: () => new Uint8Array([1, 2, 3, 4]) } as never,
136
+ );
137
+
138
+ expect(result).toBe('0xlocalsig');
139
+ expect(localSigner.signRequest).toHaveBeenCalled();
140
+ expect(mockWallet.signBytes).not.toHaveBeenCalled();
141
+ });
142
+
143
+ it('uses request-bound auth digest for wallet-backed ECDSA signing', async () => {
144
+ const signer = new MidenWalletSigner(mockWallet, '0xcommitment', 'ecdsa');
145
+
146
+ await signer.signRequest(
147
+ '0x' + 'aa'.repeat(15),
148
+ 1700000000,
149
+ { toBytes: () => new Uint8Array([1, 2, 3, 4]) } as never,
150
+ );
151
+
152
+ expect(mockWallet.signBytes).toHaveBeenCalledWith(expect.any(Uint8Array), 'word');
153
+ });
154
+ });
155
+
109
156
  describe('signature byte stripping', () => {
110
157
  it('should strip the first byte from wallet signature response', async () => {
111
158
  (mockWallet.signBytes as ReturnType<typeof vi.fn>).mockResolvedValue(
@@ -1,3 +1,4 @@
1
+ import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
1
2
  import type { Signer, SignatureScheme } from '../types.js';
2
3
  import { AuthDigest } from '../utils/digest.js';
3
4
  import { bytesToHex } from '../utils/encoding.js';
@@ -19,12 +20,13 @@ export class MidenWalletSigner implements Signer {
19
20
  commitment: string,
20
21
  scheme: SignatureScheme,
21
22
  localAuthSigner?: Signer,
23
+ publicKey?: string,
22
24
  ) {
23
25
  this.wallet = wallet;
24
26
  this.commitment = commitment;
25
27
  this.scheme = scheme;
26
28
  this.localAuthSigner = localAuthSigner ?? null;
27
- this.publicKey = localAuthSigner?.publicKey ?? commitment;
29
+ this.publicKey = publicKey ?? localAuthSigner?.publicKey ?? commitment;
28
30
  }
29
31
 
30
32
  async signAccountIdWithTimestamp(accountId: string, timestamp: number): Promise<string> {
@@ -35,6 +37,21 @@ export class MidenWalletSigner implements Signer {
35
37
  return this.signWord(word);
36
38
  }
37
39
 
40
+ async signRequest(
41
+ accountId: string,
42
+ timestamp: number,
43
+ requestPayload: RequestAuthPayload,
44
+ ): Promise<string> {
45
+ if (this.localAuthSigner?.signRequest) {
46
+ return this.localAuthSigner.signRequest(accountId, timestamp, requestPayload);
47
+ }
48
+
49
+ if (this.scheme === 'falcon') {
50
+ return this.signWord(AuthDigest.fromRequest(accountId, timestamp, requestPayload));
51
+ }
52
+ return this.signWord(AuthDigest.fromRequest(accountId, timestamp, requestPayload));
53
+ }
54
+
38
55
  async signCommitment(commitmentHex: string): Promise<string> {
39
56
  const word = AuthDigest.fromCommitmentHex(commitmentHex);
40
57
  return this.signWord(word);
@@ -1,6 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { ParaSigner } from './para.js';
3
3
  import type { ParaSigningContext } from './para.js';
4
+ import { keccak_256 } from '@noble/hashes/sha3.js';
5
+ import { uint8ArrayToBase64 } from '../utils/encoding.js';
6
+ import { wordToBytes } from '../utils/word.js';
4
7
 
5
8
  vi.mock('@miden-sdk/miden-sdk', () => ({
6
9
  Word: {
@@ -81,6 +84,60 @@ describe('ParaSigner', () => {
81
84
  );
82
85
  expect(result).toMatch(/^0x/);
83
86
  });
87
+
88
+ it('hashes the auth digest word bytes before signing', async () => {
89
+ const signer = new ParaSigner(mockPara, 'wallet-1', '0xcommitment', validCompressedKey);
90
+ await signer.signAccountIdWithTimestamp('0x' + 'aa'.repeat(15), 1700000000);
91
+
92
+ const expectedDigestBytes = keccak_256(
93
+ wordToBytes({
94
+ toFelts: () => [
95
+ { asInt: () => 10n },
96
+ { asInt: () => 20n },
97
+ { asInt: () => 30n },
98
+ { asInt: () => 40n },
99
+ ],
100
+ }),
101
+ );
102
+ const expectedBase64 = uint8ArrayToBase64(expectedDigestBytes);
103
+
104
+ expect(mockPara.signMessage).toHaveBeenCalledWith(
105
+ expect.objectContaining({
106
+ walletId: 'wallet-1',
107
+ messageBase64: expectedBase64,
108
+ }),
109
+ );
110
+ });
111
+ });
112
+
113
+ describe('signRequest', () => {
114
+ it('hashes the request-bound auth digest word bytes before signing', async () => {
115
+ const signer = new ParaSigner(mockPara, 'wallet-1', '0xcommitment', validCompressedKey);
116
+ await signer.signRequest(
117
+ '0x' + 'aa'.repeat(15),
118
+ 1700000000,
119
+ { toBytes: () => new Uint8Array([1, 2, 3, 4]) } as never,
120
+ );
121
+
122
+ const expectedDigestBytes = keccak_256(
123
+ wordToBytes({
124
+ toFelts: () => [
125
+ { asInt: () => 10n },
126
+ { asInt: () => 20n },
127
+ { asInt: () => 30n },
128
+ { asInt: () => 40n },
129
+ ],
130
+ }),
131
+ );
132
+ const expectedBase64 = uint8ArrayToBase64(expectedDigestBytes);
133
+
134
+ expect(mockPara.signMessage).toHaveBeenCalledWith(
135
+ expect.objectContaining({
136
+ walletId: 'wallet-1',
137
+ messageBase64: expectedBase64,
138
+ }),
139
+ );
140
+ });
84
141
  });
85
142
 
86
143
  describe('signCommitment', () => {
@@ -1,3 +1,4 @@
1
+ import type { RequestAuthPayload } from '@openzeppelin/guardian-client';
1
2
  import type { Signer, SignatureScheme } from '../types.js';
2
3
  import { AuthDigest } from '../utils/digest.js';
3
4
  import { EcdsaFormat } from '../utils/ecdsa.js';
@@ -5,7 +6,16 @@ import { hexToBytes, uint8ArrayToBase64 } from '../utils/encoding.js';
5
6
  import { wordToBytes } from '../utils/word.js';
6
7
 
7
8
  export interface ParaSigningContext {
8
- signMessage(params: { walletId: string; messageBase64: string }): Promise<{ signature?: string }>;
9
+ signMessage(params: { walletId: string; messageBase64: string }): Promise<unknown>;
10
+ }
11
+
12
+ function extractSignature(response: unknown): string | null {
13
+ if (!response || typeof response !== 'object') {
14
+ return null;
15
+ }
16
+
17
+ const signature = (response as { signature?: unknown }).signature;
18
+ return typeof signature === 'string' ? signature : null;
9
19
  }
10
20
 
11
21
  export class ParaSigner implements Signer {
@@ -35,26 +45,35 @@ export class ParaSigner implements Signer {
35
45
  return this.signWord(digest);
36
46
  }
37
47
 
48
+ async signRequest(
49
+ accountId: string,
50
+ timestamp: number,
51
+ requestPayload: RequestAuthPayload,
52
+ ): Promise<string> {
53
+ const digest = AuthDigest.fromRequest(accountId, timestamp, requestPayload);
54
+ return this.signWord(digest);
55
+ }
56
+
38
57
  async signCommitment(commitmentHex: string): Promise<string> {
39
58
  const word = AuthDigest.fromCommitmentHex(commitmentHex);
40
59
  return this.signWord(word);
41
60
  }
42
61
 
43
62
  private async signWord(word: { toHex: () => string; toFelts: () => Array<{ asInt: () => bigint }> }): Promise<string> {
44
- const wordBytes = wordToBytes(word);
45
- const hashedHex = EcdsaFormat.keccakDigestHex(wordBytes);
46
- const cleanHex = hashedHex.slice(2);
47
- const messageBase64 = uint8ArrayToBase64(hexToBytes(cleanHex));
63
+ const messageBase64 = uint8ArrayToBase64(
64
+ hexToBytes(EcdsaFormat.keccakDigestHex(wordToBytes(word))),
65
+ );
48
66
 
49
67
  const res = await this.para.signMessage({
50
68
  walletId: this.walletId,
51
69
  messageBase64,
52
70
  });
53
71
 
54
- if (!res.signature) {
72
+ const signature = extractSignature(res);
73
+ if (!signature) {
55
74
  throw new Error('Para signing was denied by user');
56
75
  }
57
76
 
58
- return EcdsaFormat.normalizeRecoveryByte(res.signature);
77
+ return EcdsaFormat.normalizeRecoveryByte(signature);
59
78
  }
60
79
  }
@@ -13,6 +13,7 @@ export async function buildConsumeNotesTransactionRequest(
13
13
  throw new Error('At least one note ID is required');
14
14
  }
15
15
 
16
+ // Fetch full Note objects from the client store
16
17
  const noteAndArgsArray = new NoteAndArgsArray();
17
18
  for (const noteIdHex of noteIds) {
18
19
  const inputNoteRecord = await webClient.getInputNote(noteIdHex);
@@ -1,7 +1,7 @@
1
1
  export { executeForSummary } from './summary.js';
2
2
  export { buildUpdateSignersTransactionRequest } from './updateSigners.js';
3
- export { buildUpdatePsmTransactionRequest } from './updatePsm.js';
3
+ export { buildUpdateProcedureThresholdTransactionRequest } from './updateProcedureThreshold.js';
4
+ export { buildUpdateGuardianTransactionRequest } from './updateGuardian.js';
4
5
  export { buildConsumeNotesTransactionRequest } from './consumeNotes.js';
5
6
  export { buildP2idTransactionRequest } from './p2id.js';
6
7
  export type { SignatureOptions } from './options.js';
7
-
@@ -1,9 +1,8 @@
1
1
  import type { AdviceMap, Word } from '@miden-sdk/miden-sdk';
2
- import type { SignatureScheme } from '@openzeppelin/psm-client';
2
+ import type { SignatureScheme } from '../types.js';
3
3
 
4
4
  export interface SignatureOptions {
5
5
  salt?: Word;
6
6
  signatureAdviceMap?: AdviceMap;
7
7
  signatureScheme?: SignatureScheme;
8
8
  }
9
-
@@ -0,0 +1,190 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { Word } from '@miden-sdk/miden-sdk';
3
+
4
+ const {
5
+ mockHashElements,
6
+ mockNormalizeHexWord,
7
+ mockRandomWord,
8
+ mockWordFromHex,
9
+ saltFelts,
10
+ } = vi.hoisted(() => {
11
+ const saltFelts = [
12
+ { id: 'felt-0' },
13
+ { id: 'felt-1' },
14
+ { id: 'felt-2' },
15
+ { id: 'felt-3' },
16
+ ];
17
+
18
+ return {
19
+ mockHashElements: vi.fn().mockReturnValue({ toString: () => 'serial' }),
20
+ mockNormalizeHexWord: vi.fn((hex: string) => hex),
21
+ mockRandomWord: vi.fn().mockReturnValue({
22
+ toHex: () => '0x' + 'aa'.repeat(32),
23
+ }),
24
+ mockWordFromHex: vi.fn((hex: string) => ({
25
+ toHex: () => hex,
26
+ toFelts: () => saltFelts,
27
+ })),
28
+ saltFelts,
29
+ };
30
+ });
31
+
32
+ vi.mock('@miden-sdk/miden-sdk', () => {
33
+ class Felt {
34
+ readonly value: bigint;
35
+
36
+ constructor(value: bigint) {
37
+ this.value = value;
38
+ }
39
+ }
40
+
41
+ class FeltArray {
42
+ readonly values: unknown[];
43
+
44
+ constructor(values: unknown[]) {
45
+ this.values = values;
46
+ }
47
+ }
48
+
49
+ class NoteAssets {
50
+ constructor(_assets: unknown[]) {}
51
+ }
52
+
53
+ class NoteInputs {
54
+ constructor(_inputs: FeltArray) {}
55
+ }
56
+
57
+ class NoteMetadata {
58
+ constructor(
59
+ _sender: unknown,
60
+ _noteType: unknown,
61
+ _noteTag: unknown,
62
+ ) {}
63
+ }
64
+
65
+ class NoteRecipient {
66
+ constructor(
67
+ _serialNum: unknown,
68
+ _noteScript: unknown,
69
+ _noteInputs: unknown,
70
+ ) {}
71
+ }
72
+
73
+ class Note {
74
+ constructor(
75
+ _assets: unknown,
76
+ _metadata: unknown,
77
+ _recipient: unknown,
78
+ ) {}
79
+ }
80
+
81
+ class FungibleAsset {
82
+ constructor(_faucet: unknown, _amount: bigint) {}
83
+ }
84
+
85
+ class OutputNoteArray {
86
+ constructor(_notes: unknown[]) {}
87
+ }
88
+
89
+ class TransactionRequestBuilder {
90
+ withOwnOutputNotes(_notes: unknown): this {
91
+ return this;
92
+ }
93
+
94
+ withAuthArg(_authArg: unknown): this {
95
+ return this;
96
+ }
97
+
98
+ extendAdviceMap(_adviceMap: unknown): this {
99
+ return this;
100
+ }
101
+
102
+ build(): { kind: 'request' } {
103
+ return { kind: 'request' };
104
+ }
105
+ }
106
+
107
+ return {
108
+ AccountId: {
109
+ fromHex: vi.fn((hex: string) => ({
110
+ hex,
111
+ prefix: () => 1,
112
+ suffix: () => 2,
113
+ })),
114
+ },
115
+ Felt,
116
+ FeltArray,
117
+ FungibleAsset,
118
+ MidenArrays: {
119
+ OutputNoteArray,
120
+ },
121
+ Note,
122
+ NoteAssets,
123
+ NoteInputs,
124
+ NoteMetadata,
125
+ NoteRecipient,
126
+ NoteScript: {
127
+ p2id: vi.fn(() => ({ kind: 'p2id-script' })),
128
+ },
129
+ NoteTag: {
130
+ withAccountTarget: vi.fn(() => ({ kind: 'tag' })),
131
+ },
132
+ NoteType: {
133
+ Public: 'public',
134
+ },
135
+ OutputNote: {
136
+ full: vi.fn((note: unknown) => ({ note })),
137
+ },
138
+ Rpo256: {
139
+ hashElements: mockHashElements,
140
+ },
141
+ TransactionRequestBuilder,
142
+ Word: {
143
+ fromHex: mockWordFromHex,
144
+ },
145
+ };
146
+ });
147
+
148
+ vi.mock('../utils/encoding.js', () => ({
149
+ normalizeHexWord: mockNormalizeHexWord,
150
+ }));
151
+
152
+ vi.mock('../utils/random.js', () => ({
153
+ randomWord: mockRandomWord,
154
+ }));
155
+
156
+ import { buildP2idTransactionRequest } from './p2id.js';
157
+
158
+ describe('buildP2idTransactionRequest', () => {
159
+ beforeEach(() => {
160
+ mockHashElements.mockClear();
161
+ mockNormalizeHexWord.mockClear();
162
+ mockRandomWord.mockClear();
163
+ mockWordFromHex.mockClear();
164
+ });
165
+
166
+ it('derives serial number from salt felts plus four zero felts', () => {
167
+ const salt = { toHex: () => '0x' + '11'.repeat(32) } as unknown as Word;
168
+
169
+ buildP2idTransactionRequest(
170
+ '0x7bfb0f38b0fafa103f86a805594170',
171
+ '0x8a65fc5a39e4cd106d648e3eb4ab5f',
172
+ '0x7bfb0f38b0fafa103f86a805594171',
173
+ 10n,
174
+ { salt },
175
+ );
176
+
177
+ expect(mockRandomWord).not.toHaveBeenCalled();
178
+ expect(mockHashElements).toHaveBeenCalledTimes(1);
179
+
180
+ const [feltArrayArg] = mockHashElements.mock.calls[0] as [{ values: unknown[] }];
181
+ const values = feltArrayArg.values;
182
+
183
+ expect(values).toHaveLength(8);
184
+ expect(values.slice(0, 4)).toEqual(saltFelts);
185
+
186
+ for (const felt of values.slice(4)) {
187
+ expect((felt as { value: bigint }).value).toBe(0n);
188
+ }
189
+ });
190
+ });
@@ -22,6 +22,16 @@ import { randomWord } from '../utils/random.js';
22
22
  import { normalizeHexWord } from '../utils/encoding.js';
23
23
  import type { SignatureOptions } from './options.js';
24
24
 
25
+ export function deriveP2idSerialNumber(salt: Word): Word {
26
+ return Rpo256.hashElements(new FeltArray([
27
+ ...salt.toFelts(),
28
+ new Felt(0n),
29
+ new Felt(0n),
30
+ new Felt(0n),
31
+ new Felt(0n),
32
+ ]));
33
+ }
34
+
25
35
  function buildP2idNote(
26
36
  sender: AccountId,
27
37
  recipient: AccountId,
@@ -30,10 +40,7 @@ function buildP2idNote(
30
40
  saltHex: string,
31
41
  ): Note {
32
42
  const salt = WordType.fromHex(normalizeHexWord(saltHex));
33
- const serialNum = Rpo256.hashElements(new FeltArray([
34
- ...salt.toFelts(),
35
- new Felt(0n),
36
- ]));
43
+ const serialNum = deriveP2idSerialNumber(salt);
37
44
 
38
45
  const noteScript = NoteScript.p2id();
39
46
  const noteInputs = new NoteInputs(new FeltArray([
@@ -97,4 +104,3 @@ export function buildP2idTransactionRequest(
97
104
  salt: authSaltForReturn,
98
105
  };
99
106
  }
100
-
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import type { Word } from '@miden-sdk/miden-sdk';
3
+
4
+ const { mockHashElements, seedFelts } = vi.hoisted(() => {
5
+ const seedFelts = [
6
+ { id: 'seed-0' },
7
+ { id: 'seed-1' },
8
+ { id: 'seed-2' },
9
+ { id: 'seed-3' },
10
+ ];
11
+
12
+ return {
13
+ mockHashElements: vi.fn().mockReturnValue({ id: 'drawn-word' }),
14
+ seedFelts,
15
+ };
16
+ });
17
+
18
+ vi.mock('@miden-sdk/miden-sdk', () => {
19
+ class Felt {
20
+ readonly value: bigint;
21
+
22
+ constructor(value: bigint) {
23
+ this.value = value;
24
+ }
25
+ }
26
+
27
+ class FeltArray {
28
+ readonly values: unknown[];
29
+
30
+ constructor(values: unknown[]) {
31
+ this.values = values;
32
+ }
33
+ }
34
+
35
+ return {
36
+ Felt,
37
+ FeltArray,
38
+ Rpo256: {
39
+ hashElements: mockHashElements,
40
+ },
41
+ };
42
+ });
43
+
44
+ import { RpoRandomCoin } from './rpoRandomCoin.js';
45
+
46
+ describe('RpoRandomCoin', () => {
47
+ it('drawWord hashes seed felts plus four zero felts', () => {
48
+ const seed = { toFelts: () => seedFelts } as unknown as Word;
49
+ const coin = new RpoRandomCoin(seed);
50
+ const drawn = coin.drawWord();
51
+
52
+ expect(drawn).toEqual({ id: 'drawn-word' });
53
+ expect(mockHashElements).toHaveBeenCalledTimes(1);
54
+
55
+ const [feltArrayArg] = mockHashElements.mock.calls[0] as [{ values: unknown[] }];
56
+ const values = feltArrayArg.values;
57
+
58
+ expect(values).toHaveLength(8);
59
+ expect(values.slice(0, 4)).toEqual(seedFelts);
60
+ for (const felt of values.slice(4)) {
61
+ expect((felt as { value: bigint }).value).toBe(0n);
62
+ }
63
+ });
64
+ });
@@ -0,0 +1,24 @@
1
+ import {
2
+ Felt,
3
+ FeltArray,
4
+ Rpo256,
5
+ type Word,
6
+ } from '@miden-sdk/miden-sdk';
7
+
8
+ export class RpoRandomCoin {
9
+ private readonly seed: Word;
10
+
11
+ constructor(seed: Word) {
12
+ this.seed = seed;
13
+ }
14
+
15
+ drawWord(): Word {
16
+ return Rpo256.hashElements(new FeltArray([
17
+ ...this.seed.toFelts(),
18
+ new Felt(0n),
19
+ new Felt(0n),
20
+ new Felt(0n),
21
+ new Felt(0n),
22
+ ]));
23
+ }
24
+ }
@@ -8,62 +8,49 @@ import {
8
8
  Word,
9
9
  Word as WordType,
10
10
  } from '@miden-sdk/miden-sdk';
11
- import { PSM_ECDSA_MASM, PSM_MASM } from '../account/masm.js';
11
+ import { GUARDIAN_ECDSA_MASM, GUARDIAN_MASM } from '../account/masm/auth.js';
12
12
  import { normalizeHexWord } from '../utils/encoding.js';
13
13
  import { randomWord } from '../utils/random.js';
14
14
  import type { SignatureOptions } from './options.js';
15
+ import type { SignatureScheme } from '../types.js';
15
16
 
16
- function buildUpdatePsmFalconScript(webClient: WebClient): TransactionScript {
17
- const libBuilder = webClient.createCodeBuilder();
18
- const psmLib = libBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
19
- libBuilder.linkDynamicLibrary(psmLib);
20
-
21
- const scriptSource = `
22
- use openzeppelin::psm
23
-
24
- begin
25
- adv.push_mapval
26
- dropw
27
- call.psm::update_psm_public_key
28
- end
29
- `;
30
-
31
- return libBuilder.compileTxScript(scriptSource);
32
- }
33
-
34
- function buildUpdatePsmEcdsaScript(webClient: WebClient): TransactionScript {
17
+ function buildUpdateGuardianScript(
18
+ webClient: WebClient,
19
+ signatureScheme: SignatureScheme,
20
+ ): TransactionScript {
35
21
  const libBuilder = webClient.createCodeBuilder();
36
- const psmLib = libBuilder.buildLibrary('openzeppelin::psm_ecdsa', PSM_ECDSA_MASM);
37
- libBuilder.linkDynamicLibrary(psmLib);
22
+ const guardianLibraryPath = signatureScheme === 'ecdsa' ? 'openzeppelin::guardian_ecdsa' : 'openzeppelin::guardian';
23
+ const guardianMasm = signatureScheme === 'ecdsa' ? GUARDIAN_ECDSA_MASM : GUARDIAN_MASM;
24
+ const guardianProcedure = signatureScheme === 'ecdsa' ? 'guardian_ecdsa' : 'guardian';
25
+ const guardianLib = libBuilder.buildLibrary(guardianLibraryPath, guardianMasm);
26
+ libBuilder.linkDynamicLibrary(guardianLib);
38
27
 
39
28
  const scriptSource = `
40
- use openzeppelin::psm_ecdsa
29
+ use openzeppelin::${guardianProcedure}
41
30
 
42
31
  begin
43
32
  adv.push_mapval
44
33
  dropw
45
- call.psm_ecdsa::update_psm_public_key
34
+ call.${guardianProcedure}::update_guardian_public_key
46
35
  end
47
36
  `;
48
37
 
49
38
  return libBuilder.compileTxScript(scriptSource);
50
39
  }
51
40
 
52
- export async function buildUpdatePsmTransactionRequest(
41
+ export async function buildUpdateGuardianTransactionRequest(
53
42
  webClient: WebClient,
54
- newPsmPubkey: string,
43
+ newGuardianPubkey: string,
55
44
  options: SignatureOptions = {},
56
45
  ): Promise<{ request: TransactionRequest; salt: Word }> {
57
46
  const signatureScheme = options.signatureScheme ?? 'falcon';
58
- const script = signatureScheme === 'ecdsa'
59
- ? buildUpdatePsmEcdsaScript(webClient)
60
- : buildUpdatePsmFalconScript(webClient);
47
+ const script = buildUpdateGuardianScript(webClient, signatureScheme);
61
48
 
62
49
  const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
63
50
 
64
- const pubkeyWordForAdvice = WordType.fromHex(normalizeHexWord(newPsmPubkey));
65
- const pubkeyWordForFelts = WordType.fromHex(normalizeHexWord(newPsmPubkey));
66
- const pubkeyWordForScript = WordType.fromHex(normalizeHexWord(newPsmPubkey));
51
+ const pubkeyWordForAdvice = WordType.fromHex(normalizeHexWord(newGuardianPubkey));
52
+ const pubkeyWordForFelts = WordType.fromHex(normalizeHexWord(newGuardianPubkey));
53
+ const pubkeyWordForScript = WordType.fromHex(normalizeHexWord(newGuardianPubkey));
67
54
 
68
55
  const advice = new AdviceMap();
69
56
  advice.insert(pubkeyWordForAdvice, new FeltArray(pubkeyWordForFelts.toFelts()));