@openzeppelin/miden-multisig-client 0.12.6 → 0.12.7

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 (89) hide show
  1. package/README.md +70 -75
  2. package/dist/account/builder.d.ts +0 -18
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +16 -24
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/masm.d.ts +2 -0
  7. package/dist/account/masm.d.ts.map +1 -1
  8. package/dist/account/masm.js +428 -1
  9. package/dist/account/masm.js.map +1 -1
  10. package/dist/account/storage.d.ts.map +1 -1
  11. package/dist/account/storage.js +5 -6
  12. package/dist/account/storage.js.map +1 -1
  13. package/dist/client.d.ts +5 -57
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.js +7 -53
  16. package/dist/client.js.map +1 -1
  17. package/dist/client.test.js +28 -0
  18. package/dist/client.test.js.map +1 -1
  19. package/dist/index.d.ts +3 -46
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -45
  22. package/dist/index.js.map +1 -1
  23. package/dist/inspector.d.ts +4 -30
  24. package/dist/inspector.d.ts.map +1 -1
  25. package/dist/inspector.js +13 -43
  26. package/dist/inspector.js.map +1 -1
  27. package/dist/multisig/helpers.d.ts.map +1 -1
  28. package/dist/multisig/helpers.js.map +1 -1
  29. package/dist/multisig.d.ts +39 -161
  30. package/dist/multisig.d.ts.map +1 -1
  31. package/dist/multisig.js +190 -241
  32. package/dist/multisig.js.map +1 -1
  33. package/dist/multisig.test.js +41 -31
  34. package/dist/multisig.test.js.map +1 -1
  35. package/dist/procedures.d.ts +20 -47
  36. package/dist/procedures.d.ts.map +1 -1
  37. package/dist/procedures.js +17 -50
  38. package/dist/procedures.js.map +1 -1
  39. package/dist/signer.d.ts +11 -26
  40. package/dist/signer.d.ts.map +1 -1
  41. package/dist/signer.js +41 -26
  42. package/dist/signer.js.map +1 -1
  43. package/dist/transaction/options.d.ts +2 -0
  44. package/dist/transaction/options.d.ts.map +1 -1
  45. package/dist/transaction/updatePsm.d.ts.map +1 -1
  46. package/dist/transaction/updatePsm.js +21 -3
  47. package/dist/transaction/updatePsm.js.map +1 -1
  48. package/dist/transaction/updateSigners.d.ts.map +1 -1
  49. package/dist/transaction/updateSigners.js +21 -3
  50. package/dist/transaction/updateSigners.js.map +1 -1
  51. package/dist/transaction.d.ts.map +1 -1
  52. package/dist/transaction.js +0 -2
  53. package/dist/transaction.js.map +1 -1
  54. package/dist/transaction.test.js +5 -0
  55. package/dist/transaction.test.js.map +1 -1
  56. package/dist/types/proposal.d.ts +16 -8
  57. package/dist/types/proposal.d.ts.map +1 -1
  58. package/dist/types.d.ts +18 -14
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/utils/signature.d.ts +10 -2
  61. package/dist/utils/signature.d.ts.map +1 -1
  62. package/dist/utils/signature.js +77 -5
  63. package/dist/utils/signature.js.map +1 -1
  64. package/dist/utils/signature.test.js +16 -1
  65. package/dist/utils/signature.test.js.map +1 -1
  66. package/masm/multisig_ecdsa.masm +424 -0
  67. package/masm/psm_ecdsa.masm +179 -0
  68. package/package.json +2 -2
  69. package/src/account/builder.ts +18 -25
  70. package/src/account/masm.ts +433 -2
  71. package/src/account/storage.ts +5 -6
  72. package/src/client.test.ts +36 -0
  73. package/src/client.ts +10 -59
  74. package/src/index.ts +13 -62
  75. package/src/inspector.ts +15 -44
  76. package/src/multisig/helpers.ts +1 -2
  77. package/src/multisig.test.ts +45 -34
  78. package/src/multisig.ts +246 -273
  79. package/src/procedures.ts +21 -56
  80. package/src/signer.ts +48 -28
  81. package/src/transaction/options.ts +2 -0
  82. package/src/transaction/updatePsm.ts +24 -3
  83. package/src/transaction/updateSigners.ts +25 -3
  84. package/src/transaction.test.ts +6 -0
  85. package/src/transaction.ts +0 -2
  86. package/src/types/proposal.ts +15 -8
  87. package/src/types.ts +26 -16
  88. package/src/utils/signature.test.ts +24 -1
  89. package/src/utils/signature.ts +86 -4
package/README.md CHANGED
@@ -5,6 +5,7 @@ TypeScript SDK for private multisignature workflows on Miden. This package wraps
5
5
  - Create multisig accounts, register them with a PSM, and keep state off-chain
6
6
  - Propose, sign, and execute transactions with threshold enforcement
7
7
  - Export/import proposals as files for sharing using side channels
8
+ - Integrate external wallets via the external signing API
8
9
 
9
10
  ## How Private Multisigs & PSM Work
10
11
 
@@ -33,31 +34,21 @@ const webClient = await WebClient.createClient('https://rpc.testnet.miden.io:443
33
34
  const secretKey = SecretKey.rpoFalconWithRNG(seed);
34
35
  const signer = new FalconSigner(secretKey);
35
36
 
36
- // Create MultisigClient
37
+ // Create MultisigClient and fetch PSM info
37
38
  const client = new MultisigClient(webClient, {
38
39
  psmEndpoint: 'http://localhost:3000',
39
40
  });
41
+ const { psmCommitment } = await client.initialize();
40
42
  ```
41
43
 
42
44
  ## Usage
43
45
 
44
- ### Get PSM Public Key
45
-
46
- Before creating a multisig, get the PSM server's public key commitment:
47
-
48
- ```typescript
49
- const psmCommitment = await client.psmClient.getPubkey();
50
- ```
51
-
52
46
  ### Create a Multisig Account
53
47
 
54
48
  ```typescript
55
49
  const config = {
56
- threshold: 2, // Require 2 signatures
57
- signerCommitments: [
58
- signer.commitment, // Your commitment
59
- otherSigner.commitment, // Cosigner's commitment
60
- ],
50
+ threshold: 2,
51
+ signerCommitments: [signer.commitment, otherSignerCommitment],
61
52
  psmCommitment,
62
53
  };
63
54
 
@@ -81,52 +72,23 @@ The configuration is automatically detected from the account's on-chain storage:
81
72
  const multisig = await client.load(accountId, signer);
82
73
  ```
83
74
 
84
- ### Fetch Account State
85
-
86
- ```typescript
87
- const state = await multisig.fetchState();
88
- console.log('Commitment:', state.commitment);
89
- console.log('Created:', state.createdAt);
90
- ```
91
-
92
- ### Create a Proposal (Add Signer)
93
-
94
- ```typescript
95
- // Create a proposal to add a new signer
96
- const nonce = Math.floor(Math.random() * 1_000_000_000);
97
- const proposal = await multisig.createAddSignerProposal(
98
- webClient,
99
- newSignerCommitment, // Commitment of signer to add
100
- nonce, // Optional nonce (random value)
101
- 3, // Optional new threshold
102
- );
103
- console.log('Proposal ID:', proposal.id);
104
- ```
105
-
106
- ### Sign a Proposal
107
-
108
- ```typescript
109
- const signedProposal = await multisig.signProposal(proposal.id);
110
- console.log('Signatures:', signedProposal.signatures.length);
111
- ```
112
-
113
- ### Sync Proposals
75
+ ### Sync Everything
114
76
 
115
- Fetches proposals from the PSM server and updates local state:
77
+ Fetch proposals, state, consumable notes, and config in one call:
116
78
 
117
79
  ```typescript
118
- const proposals = await multisig.syncProposals();
80
+ const { proposals, state, notes, config } = await multisig.syncAll();
119
81
  for (const p of proposals) {
120
82
  console.log(`${p.id}: ${p.status.type}`);
121
83
  }
122
84
  ```
123
85
 
124
- ### Check Proposal Status
86
+ ### List Cached Proposals
125
87
 
126
88
  Returns cached proposals without making a network request:
127
89
 
128
90
  ```typescript
129
- const proposals = multisig.listProposals();
91
+ const proposals = multisig.listTransactionProposals();
130
92
  for (const p of proposals) {
131
93
  if (p.status.type === 'pending') {
132
94
  console.log(`Pending: ${p.status.signaturesCollected}/${p.status.signaturesRequired}`);
@@ -136,49 +98,82 @@ for (const p of proposals) {
136
98
  }
137
99
  ```
138
100
 
101
+ ### Create Proposals
102
+
103
+ All create methods return `{ proposal, proposals }` — the new proposal plus an auto-synced full list:
104
+
105
+ ```typescript
106
+ // Add a signer
107
+ const { proposal, proposals } = await multisig.createAddSignerProposal(
108
+ newSignerCommitment,
109
+ { newThreshold: 3 },
110
+ );
111
+
112
+ // Remove a signer
113
+ await multisig.createRemoveSignerProposal(signerToRemove);
114
+
115
+ // Change threshold
116
+ await multisig.createChangeThresholdProposal(3);
117
+
118
+ // Consume notes
119
+ await multisig.createConsumeNotesProposal(noteIds);
120
+
121
+ // Send payment (P2ID)
122
+ await multisig.createSendProposal(recipientId, faucetId, amount);
123
+
124
+ // Switch PSM provider
125
+ await multisig.createSwitchPsmProposal(newEndpoint, newPubkey);
126
+ ```
127
+
128
+ ### Sign a Proposal
129
+
130
+ ```typescript
131
+ const proposals = await multisig.signTransactionProposal(proposal.commitment);
132
+ ```
133
+
139
134
  ### Execute a Proposal
140
135
 
141
136
  When a proposal has enough signatures:
142
137
 
143
138
  ```typescript
144
139
  if (proposal.status.type === 'ready') {
145
- await multisig.executeProposal(proposal.id, webClient);
146
- console.log('Transaction executed on-chain!');
140
+ await multisig.executeTransactionProposal(proposal.commitment);
147
141
  }
148
142
  ```
149
143
 
150
- ### Export Proposal for Offline Signing
144
+ ### External Signing
145
+
146
+ For wallet integrations where the signing key is external (e.g., a browser wallet):
151
147
 
152
148
  ```typescript
153
- const exported = await multisig.exportProposal(proposal.id);
154
- // Send `exported` to offline signer
155
- console.log('TX Summary:', exported.txSummaryBase64);
156
- console.log('Commitment to sign:', exported.commitment);
149
+ // Fetch proposals
150
+ const proposals = multisig.listTransactionProposals();
151
+
152
+ // Sign the commitment externally
153
+ const signature = await wallet.sign(proposals[0].commitment);
154
+
155
+ // Submit the external signature
156
+ await multisig.signTransactionProposalExternal({
157
+ commitment: proposals[0].commitment,
158
+ signature,
159
+ publicKey: wallet.publicKey,
160
+ scheme: 'ecdsa',
161
+ });
157
162
  ```
158
163
 
159
- ## Transaction Utilities
164
+ ### Export/Import Proposals
160
165
 
161
- The package also exports utility functions for building transactions:
166
+ Share proposals via side channels for offline signing:
162
167
 
163
168
  ```typescript
164
- import {
165
- normalizeHexWord,
166
- hexToUint8Array,
167
- signatureHexToBytes,
168
- buildSignatureAdviceEntry,
169
- } from '@openzeppelin/miden-multisig-client';
170
-
171
- // Normalize hex for Word.fromHex (pads to 64 chars)
172
- const normalized = normalizeHexWord('abc123');
173
- // => '0x0000...abc123'
174
-
175
- // Convert hex to bytes
176
- const bytes = hexToUint8Array('deadbeef');
177
- // => Uint8Array([0xde, 0xad, 0xbe, 0xef])
178
-
179
- // Add auth scheme prefix to signature
180
- const sigBytes = signatureHexToBytes(signatureHex);
181
- // => Uint8Array with 0x00 prefix (RpoFalcon512)
169
+ // Export
170
+ const json = multisig.exportTransactionProposalToJson(proposal.commitment);
171
+
172
+ // Sign offline and get updated JSON
173
+ const signedJson = multisig.signTransactionProposalOffline(proposal.commitment);
174
+
175
+ // Import
176
+ const { proposal, proposals } = multisig.importTransactionProposal(json);
182
177
  ```
183
178
 
184
179
  ## Testing
@@ -1,23 +1,5 @@
1
- /**
2
- * Account builder for creating multisig accounts with PSM authentication.
3
- *
4
- * This module provides functionality to create multisig accounts.
5
- */
6
1
  import { type WebClient } from '@demox-labs/miden-sdk';
7
2
  import type { MultisigConfig, CreateAccountResult } from '../types.js';
8
- /**
9
- * Creates a multisig account with PSM authentication.
10
- *
11
- * @param webClient - Initialized Miden WebClient
12
- * @param config - Multisig configuration
13
- * @returns The created account and seed
14
- */
15
3
  export declare function createMultisigAccount(webClient: WebClient, config: MultisigConfig): Promise<CreateAccountResult>;
16
- /**
17
- * Validates a multisig configuration.
18
- *
19
- * @param config - The configuration to validate
20
- * @throws Error if configuration is invalid
21
- */
22
4
  export declare function validateMultisigConfig(config: MultisigConfig): void;
23
5
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIvE;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,mBAAmB,CAAC,CAyC9B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAmCnE"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIvE,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,mBAAmB,CAAC,CA4C9B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CA4CnE"}
@@ -1,33 +1,24 @@
1
- /**
2
- * Account builder for creating multisig accounts with PSM authentication.
3
- *
4
- * This module provides functionality to create multisig accounts.
5
- */
6
1
  import { AccountBuilder, AccountComponent, AccountType, AccountStorageMode, } from '@demox-labs/miden-sdk';
7
2
  import { buildMultisigStorageSlots, buildPsmStorageSlots } from './storage.js';
8
- import { MULTISIG_MASM, PSM_MASM } from './masm.js';
9
- /**
10
- * Creates a multisig account with PSM authentication.
11
- *
12
- * @param webClient - Initialized Miden WebClient
13
- * @param config - Multisig configuration
14
- * @returns The created account and seed
15
- */
3
+ import { MULTISIG_MASM, MULTISIG_ECDSA_MASM, PSM_MASM, PSM_ECDSA_MASM } from './masm.js';
16
4
  export async function createMultisigAccount(webClient, config) {
17
5
  validateMultisigConfig(config);
6
+ const signatureScheme = config.signatureScheme ?? 'falcon';
18
7
  const multisigSlots = buildMultisigStorageSlots(config);
19
8
  const psmSlots = buildPsmStorageSlots(config);
20
9
  const psmBuilder = webClient.createScriptBuilder();
10
+ const psmMasm = signatureScheme === 'ecdsa' ? PSM_ECDSA_MASM : PSM_MASM;
21
11
  const psmComponent = AccountComponent
22
- .compile(PSM_MASM, psmBuilder, psmSlots)
12
+ .compile(psmMasm, psmBuilder, psmSlots)
23
13
  .withSupportsAllTypes();
14
+ const multisigMasm = signatureScheme === 'ecdsa' ? MULTISIG_ECDSA_MASM : MULTISIG_MASM;
15
+ const psmLibraryPath = signatureScheme === 'ecdsa' ? 'openzeppelin::psm_ecdsa' : 'openzeppelin::psm';
24
16
  const multisigBuilder = webClient.createScriptBuilder();
25
- const psmLib = multisigBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
17
+ const psmLib = multisigBuilder.buildLibrary(psmLibraryPath, psmMasm);
26
18
  multisigBuilder.linkStaticLibrary(psmLib);
27
19
  const multisigComponent = AccountComponent
28
- .compile(MULTISIG_MASM, multisigBuilder, multisigSlots)
20
+ .compile(multisigMasm, multisigBuilder, multisigSlots)
29
21
  .withSupportsAllTypes();
30
- // Generate random seed
31
22
  const seed = new Uint8Array(32);
32
23
  crypto.getRandomValues(seed);
33
24
  const storageMode = config.storageMode === 'public'
@@ -46,12 +37,6 @@ export async function createMultisigAccount(webClient, config) {
46
37
  seed,
47
38
  };
48
39
  }
49
- /**
50
- * Validates a multisig configuration.
51
- *
52
- * @param config - The configuration to validate
53
- * @throws Error if configuration is invalid
54
- */
55
40
  export function validateMultisigConfig(config) {
56
41
  if (config.threshold === 0) {
57
42
  throw new Error('threshold must be greater than 0');
@@ -59,13 +44,20 @@ export function validateMultisigConfig(config) {
59
44
  if (config.signerCommitments.length === 0) {
60
45
  throw new Error('at least one signer commitment is required');
61
46
  }
47
+ for (const commitment of config.signerCommitments) {
48
+ const stripped = commitment.startsWith('0x') || commitment.startsWith('0X')
49
+ ? commitment.slice(2)
50
+ : commitment;
51
+ if (stripped.length > 64) {
52
+ throw new Error(`signerCommitments must be 32-byte commitment hex (64 chars), got ${stripped.length} chars`);
53
+ }
54
+ }
62
55
  if (config.threshold > config.signerCommitments.length) {
63
56
  throw new Error(`threshold (${config.threshold}) cannot exceed number of signers (${config.signerCommitments.length})`);
64
57
  }
65
58
  if (!config.psmCommitment) {
66
59
  throw new Error('PSM commitment is required');
67
60
  }
68
- // Validate procedure thresholds if provided
69
61
  if (config.procedureThresholds) {
70
62
  const seen = new Set();
71
63
  for (const pt of config.procedureThresholds) {
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/account/builder.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
1
  export declare const MULTISIG_MASM = "# Multi-Signature RPO Falcon 512 Authentication Component\n#\n# This component provides multi-signature authentication for accounts.\n# It integrates with the PSM component for optional PSM signature verification.\n\nuse miden::active_account\nuse miden::native_account\nuse miden::auth\nuse openzeppelin::psm\n\n# Type definitions for v0.12 syntax\ntype BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }\n\n# CONSTANTS\n# =================================================================================================\n\n# Auth Request Constants\n\n# The event emitted when a signature is not found for a required signer.\nconst AUTH_UNAUTHORIZED_EVENT = event(\"miden::auth::unauthorized\")\n\n# Storage Layout Constants\n#\n# +-------------------------------+----------+--------------+-------------------+\n# | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |\n# | (slot) | (map) | (map) | (map) |\n# +-------------------------------+----------+--------------+-------------------+\n# | 0 | 1 | 2 | 3 |\n# +-------------------------------+----------+--------------+-------------------+\n\n# The slot in this component's storage layout where the default signature threshold and\n# number of approvers are stored as:\n# [default_threshold, num_approvers, 0, 0].\n# The threshold is guaranteed to be less than or equal to num_approvers.\nconst THRESHOLD_CONFIG_SLOT = 0\n\n# The slot in this component's storage layout where the public keys map is stored.\n# Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY\nconst PUBLIC_KEYS_MAP_SLOT = 1\n\n# The slot in this component's storage layout where executed transactions are stored.\n# Map entries: transaction_message => [is_executed, 0, 0, 0]\nconst EXECUTED_TXS_SLOT = 2\n\n# The slot in this component's storage layout where procedure thresholds are stored.\n# Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]\nconst PROC_THRESHOLD_ROOTS_SLOT = 3\n\n# Executed Transaction Flag Constant\nconst IS_EXECUTED_FLAG = [1, 0, 0, 0]\n\n# ERRORS\nconst ERR_TX_ALREADY_EXECUTED = \"failed to approve multisig transaction as it was already executed\"\n\nconst ERR_MALFORMED_MULTISIG_CONFIG = \"number of approvers must be equal to or greater than threshold\"\n\nconst ERR_ZERO_IN_MULTISIG_CONFIG = \"number of approvers or threshold must not be zero\"\n\n# MULTISIG PROCEDURES\n# =================================================================================================\n\n#! Check if transaction has already been executed and add it to executed transactions for replay protection.\n#!\n#! Inputs: [MSG]\n#! Outputs: []\n#!\n#! Panics if:\n#! - the same transaction has already been executed\nproc assert_new_tx(msg: BeWord)\n push.IS_EXECUTED_FLAG\n # => [[0, 0, 0, is_executed], MSG]\n\n swapw\n # => [MSG, IS_EXECUTED_FLAG]\n\n push.EXECUTED_TXS_SLOT\n # => [index, MSG, IS_EXECUTED_FLAG]\n\n # Set the key value pair in the map to mark transaction as executed\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, [0, 0, 0, is_executed]]\n\n dropw drop drop drop\n # => [is_executed]\n\n assertz.err=ERR_TX_ALREADY_EXECUTED\n # => []\nend\n\n#! Remove old approver public keys from the approver public key mapping.\n#!\n#! This procedure cleans up the storage by removing public keys of approvers that are no longer\n#! part of the multisig configuration. This procedure assumes that init_num_of_approvers and\n#! new_num_of_approvers are u32 values.\n#!\n#! Inputs: [init_num_of_approvers, new_num_of_approvers]\n#! Outputs: []\n#!\n#! Where:\n#! - init_num_of_approvers is the original number of approvers before the update\n#! - new_num_of_approvers is the new number of approvers after the update\nproc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)\n dup.1 dup.1\n u32assert2 u32lt\n # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]\n\n while.true\n # => [i, new_num_of_approvers]\n\n sub.1\n # => [i-1, new_num_of_approvers]\n\n dup\n # => [i-1, i-1, new_num_of_approvers]\n\n push.0.0.0\n # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]\n\n padw swapw\n # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]\n\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers]\n\n dropw dropw\n # => [i-1, new_num_of_approvers]\n\n dup.1 dup.1\n u32lt\n # => [should_loop, i-1, new_num_of_approvers]\n end\n\n drop drop\n # => []\nend\n\n#! Update threshold config and add / remove approvers\n#!\n#! Inputs:\n#! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]\n#! Advice map: {\n#! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]\n#! }\n#! Outputs:\n#! Operand stack: []\n#!\n#! Where:\n#! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector\n#! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]\n#! - PUB_KEY_i is the public key of the i-th signer\n#!\n#! Locals:\n#! 0: new_num_of_approvers\n#! 1: init_num_of_approvers\npub proc update_signers_and_threshold.2(multisig_config_hash: BeWord)\n adv.push_mapval\n # => [MULTISIG_CONFIG_HASH, pad(12)]\n\n adv_loadw\n # => [MULTISIG_CONFIG, pad(12)]\n\n # store new_num_of_approvers for later\n dup.2 loc_store.0\n # => [MULTISIG_CONFIG, pad(12)]\n\n dup.3 dup.3\n # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]\n\n # make sure that the threshold is smaller than the number of approvers\n u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG\n u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG\n # => [MULTISIG_CONFIG, pad(12)]\n\n dup.3 dup.3\n # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]\n\n # make sure that threshold or num_approvers are not zero\n eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG\n eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG\n # => [MULTISIG_CONFIG, pad(12)]\n\n push.THRESHOLD_CONFIG_SLOT\n # => [slot, MULTISIG_CONFIG, pad(12)]\n\n exec.native_account::set_item\n # => [OLD_THRESHOLD_CONFIG, pad(12)]\n\n # store init_num_of_approvers for later\n drop drop loc_store.1 drop\n # => [pad(12)]\n\n loc_load.0\n # => [num_approvers]\n\n dup neq.0\n while.true\n sub.1\n # => [i-1, pad(12)]\n\n dup push.0.0.0\n # => [[0, 0, 0, i-1], i-1, pad(12)]\n\n padw adv_loadw\n # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]\n\n swapw\n # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]\n\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)]\n\n dropw dropw\n # => [i-1, pad(12)]\n\n dup neq.0\n # => [is_non_zero, i-1, pad(12)]\n end\n # => [pad(13)]\n\n drop\n # => [pad(12)]\n\n # compare initial vs current multisig config\n\n # load init_num_of_approvers & new_num_of_approvers\n loc_load.0 loc_load.1\n # => [init_num_of_approvers, new_num_of_approvers, pad(12)]\n\n exec.cleanup_pubkey_mapping\n # => [pad(12)]\nend\n\n# Computes the effective transaction threshold based on called procedures and per-procedure\n# overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no\n# overrides apply.\n#\n#! Inputs: [default_threshold]\n#! Outputs: [transaction_threshold]\nproc compute_transaction_threshold.1(default_threshold: u32) -> u32\n # 1. initialize transaction_threshold = 0\n # 2. iterate through all account procedures\n # a. check if the procedure was called during the transaction\n # b. if called, get the override threshold of that procedure from the config map\n # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold\n # 3. if transaction_threshold == 0 at the end, revert to using default_threshold\n\n # store default_threshold for later\n loc_store.0\n # => []\n\n # 1. initialize transaction_threshold = 0\n push.0\n # => [transaction_threshold]\n\n # get the number of account procedures\n exec.active_account::get_num_procedures\n # => [num_procedures, transaction_threshold]\n\n # 2. iterate through all account procedures\n dup neq.0\n # => [should_continue, num_procedures, transaction_threshold]\n while.true\n sub.1 dup\n # => [num_procedures-1, num_procedures-1, transaction_threshold]\n\n # get procedure root of the procedure with index i\n exec.active_account::get_procedure_root dupw\n # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # 2a. check if this procedure has been called in the transaction\n exec.native_account::was_procedure_called\n # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # if it has been called, get the override threshold of that procedure\n if.true\n # => [PROC_ROOT, num_procedures-1, transaction_threshold]\n\n push.PROC_THRESHOLD_ROOTS_SLOT\n # => [PROC_THRESHOLD_ROOTS_SLOT, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # 2b. get the override proc_threshold of that procedure\n # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]\n exec.active_account::get_initial_map_item\n # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]\n\n drop drop drop dup dup.3\n # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]\n\n u32assert2.err=\"transaction threshold or procedure threshold are not u32\"\n u32gt\n # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]\n # 2c. if proc_threshold > transaction_threshold, update transaction_threshold\n movup.2 movdn.3\n # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]\n cdrop\n # => [updated_transaction_threshold, num_procedures-1]\n swap\n # => [num_procedures-1, updated_transaction_threshold]\n # if it has not been called during this transaction, nothing to do, move to the next procedure\n else\n dropw\n # => [num_procedures-1, transaction_threshold]\n end\n\n dup neq.0\n # => [should_continue, num_procedures-1, transaction_threshold]\n end\n\n drop\n # => [transaction_threshold]\n\n loc_load.0\n # => [default_threshold, transaction_threshold]\n\n # 3. if transaction_threshold == 0 at the end, revert to using default_threshold\n dup.1 eq.0\n # => [is_zero, default_threshold, transaction_threshold]\n\n cdrop\n # => [effective_transaction_threshold]\nend\n\n#! Authenticate a transaction using the Falcon signature scheme with multi-signature support.\n#!\n#! This procedure implements multi-signature authentication by:\n#! 1. Computing the transaction summary message that needs to be signed\n#! 2. Verifying signatures from multiple required signers against their public keys\n#! 3. Ensuring the minimum threshold of valid signatures is met\n#! 4. Implementing replay protection by tracking executed transactions\n#! 5. Verifying PSM signature if PSM selector is enabled (via PSM component)\n#!\n#! Inputs:\n#! Operand stack: [SALT]\n#! Advice map: {\n#! h(SIG_0, MSG): SIG_0,\n#! h(SIG_1, MSG): SIG_1,\n#! h(SIG_n, MSG): SIG_n\n#! }\n#! Outputs:\n#! Operand stack: []\n#!\n#! Where:\n#! - SALT is a cryptographically random nonce that enables multiple concurrent\n#! multisig transactions while maintaining replay protection. Each transaction\n#! must use a unique SALT value to ensure transaction uniqueness.\n#! - SIG_i is the signature from the i-th signer.\n#! - MSG is the transaction message being signed.\n#! - h(SIG_i, MSG) is the hash of the signature and message used as the advice map key.\n#!\n#! Panics if:\n#! - insufficient number of valid signatures (below threshold).\n#! - the same transaction has already been executed (replay protection).\n#! - PSM signature verification fails (if PSM is enabled).\n#!\n#! Invocation: call\npub proc auth_tx_rpo_falcon512_multisig.1(salt: BeWord)\n exec.native_account::incr_nonce drop\n # => [SALT]\n\n # ------ Computing transaction summary ------\n\n exec.auth::create_tx_summary\n # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]\n\n # to build a tx_summary in the host, we need these four words in the advice provider\n exec.auth::adv_insert_hqword\n # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]\n\n # the commitment to the tx summary is the message that is signed\n exec.auth::hash_tx_summary\n # => [TX_SUMMARY_COMMITMENT]\n\n # ------ Verifying approver signatures ------\n\n push.THRESHOLD_CONFIG_SLOT\n # => [index, TX_SUMMARY_COMMITMENT]\n\n exec.active_account::get_initial_item\n # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]\n\n drop drop\n # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]\n\n swap movdn.5\n # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]\n\n exec.::miden::auth::rpo_falcon512::verify_signatures\n # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]\n\n # ------ Checking threshold is >= num_verified_signatures ------\n\n movup.5\n # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]\n\n exec.compute_transaction_threshold\n # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]\n\n u32assert2 u32lt\n # => [is_unauthorized, TX_SUMMARY_COMMITMENT]\n\n # If signatures are non-existent the tx will fail here.\n if.true\n emit.AUTH_UNAUTHORIZED_EVENT\n push.0 assert.err=\"insufficient number of signatures\"\n end\n\n # ------ Verifying PSM Signature ------\n # => [TX_SUMMARY_COMMITMENT]\n call.psm::verify_psm_signature\n\n # ------ Writing executed transaction MSG to map ------\n # => [TX_SUMMARY_COMMITMENT]\n exec.assert_new_tx\nend\n";
2
+ export declare const MULTISIG_ECDSA_MASM = "# Multi-Signature ECDSA secp256k1 Authentication Component\n#\n# This component provides multi-signature authentication for accounts using\n# ECDSA secp256k1 (k256/keccak) signatures instead of RPO Falcon 512.\n# It integrates with the PSM component for optional PSM signature verification.\n\nuse miden::active_account\nuse miden::native_account\nuse miden::auth\nuse openzeppelin::psm_ecdsa\n\n# Type definitions for v0.12 syntax\ntype BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }\n\n# CONSTANTS\n# =================================================================================================\n\n# Auth Request Constants\n\n# The event emitted when a signature is not found for a required signer.\nconst AUTH_UNAUTHORIZED_EVENT = event(\"miden::auth::unauthorized\")\n\n# Storage Layout Constants\n#\n# +-------------------------------+----------+--------------+-------------------+\n# | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |\n# | (slot) | (map) | (map) | (map) |\n# +-------------------------------+----------+--------------+-------------------+\n# | 0 | 1 | 2 | 3 |\n# +-------------------------------+----------+--------------+-------------------+\n\n# The slot in this component's storage layout where the default signature threshold and\n# number of approvers are stored as:\n# [default_threshold, num_approvers, 0, 0].\n# The threshold is guaranteed to be less than or equal to num_approvers.\nconst THRESHOLD_CONFIG_SLOT = 0\n\n# The slot in this component's storage layout where the public keys map is stored.\n# Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY\nconst PUBLIC_KEYS_MAP_SLOT = 1\n\n# The slot in this component's storage layout where executed transactions are stored.\n# Map entries: transaction_message => [is_executed, 0, 0, 0]\nconst EXECUTED_TXS_SLOT = 2\n\n# The slot in this component's storage layout where procedure thresholds are stored.\n# Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]\nconst PROC_THRESHOLD_ROOTS_SLOT = 3\n\n# Executed Transaction Flag Constant\nconst IS_EXECUTED_FLAG = [1, 0, 0, 0]\n\n# ERRORS\nconst ERR_TX_ALREADY_EXECUTED = \"failed to approve multisig transaction as it was already executed\"\n\nconst ERR_MALFORMED_MULTISIG_CONFIG = \"number of approvers must be equal to or greater than threshold\"\n\nconst ERR_ZERO_IN_MULTISIG_CONFIG = \"number of approvers or threshold must not be zero\"\n\n# MULTISIG PROCEDURES\n# =================================================================================================\n\n#! Check if transaction has already been executed and add it to executed transactions for replay protection.\n#!\n#! Inputs: [MSG]\n#! Outputs: []\n#!\n#! Panics if:\n#! - the same transaction has already been executed\nproc assert_new_tx(msg: BeWord)\n push.IS_EXECUTED_FLAG\n # => [[0, 0, 0, is_executed], MSG]\n\n swapw\n # => [MSG, IS_EXECUTED_FLAG]\n\n push.EXECUTED_TXS_SLOT\n # => [index, MSG, IS_EXECUTED_FLAG]\n\n # Set the key value pair in the map to mark transaction as executed\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, [0, 0, 0, is_executed]]\n\n dropw drop drop drop\n # => [is_executed]\n\n assertz.err=ERR_TX_ALREADY_EXECUTED\n # => []\nend\n\n#! Remove old approver public keys from the approver public key mapping.\n#!\n#! This procedure cleans up the storage by removing public keys of approvers that are no longer\n#! part of the multisig configuration. This procedure assumes that init_num_of_approvers and\n#! new_num_of_approvers are u32 values.\n#!\n#! Inputs: [init_num_of_approvers, new_num_of_approvers]\n#! Outputs: []\n#!\n#! Where:\n#! - init_num_of_approvers is the original number of approvers before the update\n#! - new_num_of_approvers is the new number of approvers after the update\nproc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)\n dup.1 dup.1\n u32assert2 u32lt\n # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]\n\n while.true\n # => [i, new_num_of_approvers]\n\n sub.1\n # => [i-1, new_num_of_approvers]\n\n dup\n # => [i-1, i-1, new_num_of_approvers]\n\n push.0.0.0\n # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]\n\n padw swapw\n # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]\n\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, new_num_of_approvers]\n\n dropw dropw\n # => [i-1, new_num_of_approvers]\n\n dup.1 dup.1\n u32lt\n # => [should_loop, i-1, new_num_of_approvers]\n end\n\n drop drop\n # => []\nend\n\n#! Update threshold config and add / remove approvers\n#!\n#! Inputs:\n#! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]\n#! Advice map: {\n#! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]\n#! }\n#! Outputs:\n#! Operand stack: []\n#!\n#! Where:\n#! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector\n#! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]\n#! - PUB_KEY_i is the public key of the i-th signer\n#!\n#! Locals:\n#! 0: new_num_of_approvers\n#! 1: init_num_of_approvers\npub proc update_signers_and_threshold.2(multisig_config_hash: BeWord)\n adv.push_mapval\n # => [MULTISIG_CONFIG_HASH, pad(12)]\n\n adv_loadw\n # => [MULTISIG_CONFIG, pad(12)]\n\n # store new_num_of_approvers for later\n dup.2 loc_store.0\n # => [MULTISIG_CONFIG, pad(12)]\n\n dup.3 dup.3\n # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]\n\n # make sure that the threshold is smaller than the number of approvers\n u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG\n u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG\n # => [MULTISIG_CONFIG, pad(12)]\n\n dup.3 dup.3\n # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]\n\n # make sure that threshold or num_approvers are not zero\n eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG\n eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG\n # => [MULTISIG_CONFIG, pad(12)]\n\n push.THRESHOLD_CONFIG_SLOT\n # => [slot, MULTISIG_CONFIG, pad(12)]\n\n exec.native_account::set_item\n # => [OLD_THRESHOLD_CONFIG, pad(12)]\n\n # store init_num_of_approvers for later\n drop drop loc_store.1 drop\n # => [pad(12)]\n\n loc_load.0\n # => [num_approvers]\n\n dup neq.0\n while.true\n sub.1\n # => [i-1, pad(12)]\n\n dup push.0.0.0\n # => [[0, 0, 0, i-1], i-1, pad(12)]\n\n padw adv_loadw\n # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]\n\n swapw\n # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]\n\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, OLD_MAP_VALUE, i-1, pad(12)]\n\n dropw dropw\n # => [i-1, pad(12)]\n\n dup neq.0\n # => [is_non_zero, i-1, pad(12)]\n end\n # => [pad(13)]\n\n drop\n # => [pad(12)]\n\n # compare initial vs current multisig config\n\n # load init_num_of_approvers & new_num_of_approvers\n loc_load.0 loc_load.1\n # => [init_num_of_approvers, new_num_of_approvers, pad(12)]\n\n exec.cleanup_pubkey_mapping\n # => [pad(12)]\nend\n\n# Computes the effective transaction threshold based on called procedures and per-procedure\n# overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no\n# overrides apply.\n#\n#! Inputs: [default_threshold]\n#! Outputs: [transaction_threshold]\nproc compute_transaction_threshold.1(default_threshold: u32) -> u32\n # 1. initialize transaction_threshold = 0\n # 2. iterate through all account procedures\n # a. check if the procedure was called during the transaction\n # b. if called, get the override threshold of that procedure from the config map\n # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold\n # 3. if transaction_threshold == 0 at the end, revert to using default_threshold\n\n # store default_threshold for later\n loc_store.0\n # => []\n\n # 1. initialize transaction_threshold = 0\n push.0\n # => [transaction_threshold]\n\n # get the number of account procedures\n exec.active_account::get_num_procedures\n # => [num_procedures, transaction_threshold]\n\n # 2. iterate through all account procedures\n dup neq.0\n # => [should_continue, num_procedures, transaction_threshold]\n while.true\n sub.1 dup\n # => [num_procedures-1, num_procedures-1, transaction_threshold]\n\n # get procedure root of the procedure with index i\n exec.active_account::get_procedure_root dupw\n # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # 2a. check if this procedure has been called in the transaction\n exec.native_account::was_procedure_called\n # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # if it has been called, get the override threshold of that procedure\n if.true\n # => [PROC_ROOT, num_procedures-1, transaction_threshold]\n\n push.PROC_THRESHOLD_ROOTS_SLOT\n # => [PROC_THRESHOLD_ROOTS_SLOT, PROC_ROOT, num_procedures-1, transaction_threshold]\n\n # 2b. get the override proc_threshold of that procedure\n # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]\n exec.active_account::get_initial_map_item\n # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]\n\n drop drop drop dup dup.3\n # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]\n\n u32assert2.err=\"transaction threshold or procedure threshold are not u32\"\n u32gt\n # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]\n # 2c. if proc_threshold > transaction_threshold, update transaction_threshold\n movup.2 movdn.3\n # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]\n cdrop\n # => [updated_transaction_threshold, num_procedures-1]\n swap\n # => [num_procedures-1, updated_transaction_threshold]\n # if it has not been called during this transaction, nothing to do, move to the next procedure\n else\n dropw\n # => [num_procedures-1, transaction_threshold]\n end\n\n dup neq.0\n # => [should_continue, num_procedures-1, transaction_threshold]\n end\n\n drop\n # => [transaction_threshold]\n\n loc_load.0\n # => [default_threshold, transaction_threshold]\n\n # 3. if transaction_threshold == 0 at the end, revert to using default_threshold\n dup.1 eq.0\n # => [is_zero, default_threshold, transaction_threshold]\n\n cdrop\n # => [effective_transaction_threshold]\nend\n\n#! Authenticate a transaction using ECDSA secp256k1 with multi-signature support.\n#!\n#! This procedure implements multi-signature authentication by:\n#! 1. Computing the transaction summary message that needs to be signed\n#! 2. Verifying ECDSA signatures from multiple required signers against their public keys\n#! 3. Ensuring the minimum threshold of valid signatures is met\n#! 4. Implementing replay protection by tracking executed transactions\n#! 5. Verifying PSM signature if PSM selector is enabled (via PSM component)\n#!\n#! Inputs:\n#! Operand stack: [SALT]\n#! Advice map: {\n#! h(PUB_KEY_0, MSG): SIG_0,\n#! h(PUB_KEY_1, MSG): SIG_1,\n#! h(PUB_KEY_n, MSG): SIG_n\n#! }\n#! Outputs:\n#! Operand stack: []\n#!\n#! Where:\n#! - SALT is a cryptographically random nonce that enables multiple concurrent\n#! multisig transactions while maintaining replay protection. Each transaction\n#! must use a unique SALT value to ensure transaction uniqueness.\n#! - SIG_i is the ECDSA signature from the i-th signer.\n#! - MSG is the transaction message being signed.\n#! - h(PUB_KEY_i, MSG) is the hash of the public key and message used as the advice map key.\n#!\n#! Panics if:\n#! - insufficient number of valid signatures (below threshold).\n#! - the same transaction has already been executed (replay protection).\n#! - PSM signature verification fails (if PSM is enabled).\n#!\n#! Invocation: call\npub proc auth_tx_ecdsa_multisig.1(salt: BeWord)\n exec.native_account::incr_nonce drop\n # => [SALT]\n\n # ------ Computing transaction summary ------\n\n exec.auth::create_tx_summary\n # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]\n\n # to build a tx_summary in the host, we need these four words in the advice provider\n exec.auth::adv_insert_hqword\n # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]\n\n # the commitment to the tx summary is the message that is signed\n exec.auth::hash_tx_summary\n # => [TX_SUMMARY_COMMITMENT]\n\n # ------ Verifying approver signatures ------\n\n push.THRESHOLD_CONFIG_SLOT\n # => [index, TX_SUMMARY_COMMITMENT]\n\n exec.active_account::get_initial_item\n # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]\n\n drop drop\n # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]\n\n swap movdn.5\n # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]\n\n push.PUBLIC_KEYS_MAP_SLOT\n # => [pub_key_slot_idx, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]\n\n exec.::miden::auth::ecdsa_k256_keccak::verify_signatures\n # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]\n\n # ------ Checking threshold is >= num_verified_signatures ------\n\n movup.5\n # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]\n\n exec.compute_transaction_threshold\n # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]\n\n u32assert2 u32lt\n # => [is_unauthorized, TX_SUMMARY_COMMITMENT]\n\n # If signatures are non-existent the tx will fail here.\n if.true\n emit.AUTH_UNAUTHORIZED_EVENT\n push.0 assert.err=\"insufficient number of signatures\"\n end\n\n # ------ Verifying PSM Signature ------\n # => [TX_SUMMARY_COMMITMENT]\n call.psm_ecdsa::verify_psm_signature\n\n # ------ Writing executed transaction MSG to map ------\n # => [TX_SUMMARY_COMMITMENT]\n exec.assert_new_tx\nend\n";
2
3
  export declare const PSM_MASM = "# Private State Manager (PSM) Authentication Component\n#\n# This component provides PSM signature verification for accounts.\n# It can be used standalone or in conjunction with other auth components like multisig.\n\nuse miden::active_account\nuse miden::native_account\n\n# Type definitions for v0.12 syntax\ntype BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }\n\n# IMPORTANT SECURITY NOTES\n# --------------------------------------------------------------------------------\n# - The selector in `PSM_SELECTOR_SLOT` controls whether the extra PSM signature\n# is enforced:\n# * PSM_ON => exactly one valid PSM signature is required.\n# * PSM_OFF => PSM signature is skipped for that call.\n#\n# - `verify_psm_signature` reads the selector from initial storage state.\n# This means changes made during the same transaction won't affect the check.\n#\n# - `enable_psm` / `disable_psm` procedures allow explicit control over PSM state.\n#\n# - `update_psm_public_key`:\n# * Installs a new PSM public key in the map at `PSM_PUBLIC_KEY_MAP_SLOT`.\n# * Does not itself perform any signature checks.\n# * To update the key without requiring PSM signature, ensure selector is OFF.\n#\n# Storage Layout\n# --------------------------------------------------------------------------------\n#\n# +---------------------+---------------+\n# | DESCRIPTION | SLOT |\n# +---------------------+---------------+\n# | PSM SELECTOR (word) | 0 |\n# | PSM PUBLIC KEY MAP | 1 |\n# +---------------------+---------------+\n#\n# - PSM_SELECTOR_SLOT (0):\n# * Stores a word that is compared against [1, 0, 0, 0] (PSM_ON).\n# * Any value != PSM_ON is treated as PSM_OFF.\n#\n# - PSM_PUBLIC_KEY_MAP_SLOT (1):\n# * A map from a fixed key [0, 0, 0, 0] to the single PSM public key:\n# [0, 0, 0, 0] => PSM_PUBLIC_KEY\n# * PSM_PUBLIC_KEY is a RPO Falcon 512 public key represented as a word.\n\n# CONSTANTS\n# =================================================================================================\n\n# Slot where the PSM selector flag is stored:\n# - PSM_ON => PSM signature required\n# - PSM_OFF => PSM signature skipped\nconst PSM_SELECTOR_SLOT = 0\n\n# Map slot for PSM public key\n# Uses exactly one PSM public key at index [0, 0, 0, 0]\n# [0, 0, 0, 0] => PSM_PUBLIC_KEY\nconst PSM_PUBLIC_KEY_MAP_SLOT = 1\n\n# Selector flag values\nconst PSM_ON = [1, 0, 0, 0]\nconst PSM_OFF = [0, 0, 0, 0]\n\n# The event emitted when a signature is not found for a required signer.\nconst AUTH_UNAUTHORIZED_EVENT = event(\"miden::auth::unauthorized\")\n\n# PSM PROCEDURES\n# =================================================================================================\n\n#! Enable PSM verification by setting the selector to ON.\n#!\n#! Operand stack inputs: []\n#! Outputs: []\n#!\n#! Notes:\n#! - Sets PSM_SELECTOR_SLOT to PSM_ON (1)\n#! - After this, transactions will require PSM signature verification\nproc.enable_psm\n push.PSM_ON\n # => [PSM_ON]\n\n push.PSM_SELECTOR_SLOT\n # => [PSM_SELECTOR_SLOT, PSM_ON]\n\n exec.native_account::set_item\n # => [OLD_ROOT]\n\n dropw\n # => []\nend\n\n#! Disable PSM verification by setting the selector to OFF.\n#!\n#! Operand stack inputs: []\n#! Outputs: []\n#!\n#! Notes:\n#! - Sets PSM_SELECTOR_SLOT to PSM_OFF (0)\n#! - After this, transactions will NOT require PSM signature verification\nproc.disable_psm\n push.PSM_OFF\n # => [PSM_OFF]\n\n push.PSM_SELECTOR_SLOT\n # => [PSM_SELECTOR_SLOT, PSM_OFF]\n\n exec.native_account::set_item\n # => [OLD_ROOT]\n\n dropw\n # => []\nend\n\n#! Update the PSM public key.\n#!\n#! Operand stack inputs: []\n#! Advice stack inputs: [PUB_KEY]\n#! - PUB_KEY is the new PSM RPO Falcon 512 public key\n#!\n#! Notes:\n#! - Stores PUB_KEY into PSM_PUBLIC_KEY_MAP_SLOT:\n#! [0, 0, 0, 0] => PSM_PUBLIC_KEY\n#! - To update the key without requiring PSM signature, ensure\n#! PSM_SELECTOR_SLOT = 0 (OFF) before calling this.\npub proc update_psm_public_key\n exec.disable_psm\n # ------ Update the PSM public key ------\n adv_loadw\n # => [PUB_KEY]\n\n push.0.0.0.0\n # => [MAP_KEY, PUB_KEY]\n # Note that MAP_KEY is [0, 0, 0, 0] for a single PSM_KEY\n\n push.PSM_PUBLIC_KEY_MAP_SLOT\n # => [index, MAP_KEY, PUB_KEY]\n\n exec.native_account::set_map_item\n # => [OLD_MAP_ROOT, OLD_MAP_VALUE]\n\n dropw dropw\n # => []\nend\n\n#! Conditionally verify a \"PSM\" signature against a stored public key hash.\n#! The condition is controlled by the selector at PSM_SELECTOR_SLOT.\n#!\n#! Inputs: [MSG]\n#! Outputs: [MSG]\n#!\n#! Panics if:\n#! - Selector is ON but the provided PSM signature is invalid or missing.\n#!\n#! Notes:\n#! - MSG is TX_SUMMARY_COMMITMENT provided by auth procedure\n#! - If selector is OFF (0), PSM verification is skipped\n#! - Selector value is read from initial storage state\npub proc verify_psm_signature(msg: BeWord)\n push.PSM_SELECTOR_SLOT\n exec.active_account::get_item\n drop drop drop\n # => [selector, MSG]\n\n push.1 eq\n if.true\n push.1\n push.PSM_PUBLIC_KEY_MAP_SLOT\n exec.::miden::auth::rpo_falcon512::verify_signatures\n push.1 neq\n if.true\n emit.AUTH_UNAUTHORIZED_EVENT\n push.0 assert.err=\"invalid PSM signature\"\n end\n end\n # => [MSG]\n exec.enable_psm\nend\n";
4
+ export declare const PSM_ECDSA_MASM: string;
3
5
  //# sourceMappingURL=masm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"masm.d.ts","sourceRoot":"","sources":["../../src/account/masm.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,i/cAuazB,CAAC;AAEF,eAAO,MAAM,QAAQ,01KAkLpB,CAAC"}
1
+ {"version":3,"file":"masm.d.ts","sourceRoot":"","sources":["../../src/account/masm.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,i/cAuazB,CAAC;AAEF,eAAO,MAAM,mBAAmB,uldAwa/B,CAAC;AAEF,eAAO,MAAM,QAAQ,01KAkLpB,CAAC;AAEF,eAAO,MAAM,cAAc,QAKxB,CAAC"}