@openzeppelin/miden-multisig-client 0.13.1 → 0.13.3

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
@@ -1,1041 +1,4 @@
1
+ // This file is generated by scripts/generate-masm.mjs from the MASM contract sources.
2
+ // Do not edit it by hand.
1
3
 
2
- export const MULTISIG_MASM = `# Multi-Signature RPO Falcon 512 Authentication Component
3
- #
4
- # This component provides multi-signature authentication for accounts.
5
- # It integrates with the PSM component for optional PSM signature verification.
6
-
7
- use miden::protocol::active_account
8
- use miden::protocol::native_account
9
- use miden::standards::auth
10
- use openzeppelin::psm
11
-
12
- type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
13
-
14
- # CONSTANTS
15
- # =================================================================================================
16
-
17
- # Auth Request Constants
18
-
19
- # The event emitted when a signature is not found for a required signer.
20
- const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
21
-
22
- # Storage Layout Constants
23
- #
24
- # +-------------------------------+----------+--------------+-------------------+
25
- # | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |
26
- # | (slot) | (map) | (map) | (map) |
27
- # +-------------------------------+----------+--------------+-------------------+
28
- # | 0 | 1 | 2 | 3 |
29
- # +-------------------------------+----------+--------------+-------------------+
30
-
31
- # The slot in this component's storage layout where the default signature threshold and
32
- # number of approvers are stored as:
33
- # [default_threshold, num_approvers, 0, 0].
34
- # The threshold is guaranteed to be less than or equal to num_approvers.
35
- const THRESHOLD_CONFIG_SLOT=word("openzeppelin::multisig::threshold_config")
36
-
37
- # The slot in this component's storage layout where the public keys map is stored.
38
- # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY
39
- const PUBLIC_KEYS_MAP_SLOT=word("openzeppelin::multisig::signer_public_keys")
40
-
41
- # The slot in this component's storage layout where executed transactions are stored.
42
- # Map entries: transaction_message => [is_executed, 0, 0, 0]
43
- const EXECUTED_TXS_SLOT=word("openzeppelin::multisig::executed_transactions")
44
-
45
- # The slot in this component's storage layout where procedure thresholds are stored.
46
- # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]
47
- const PROC_THRESHOLD_ROOTS_SLOT=word("openzeppelin::multisig::procedure_thresholds")
48
-
49
- # Executed Transaction Flag Constant
50
- const IS_EXECUTED_FLAG=[1, 0, 0, 0]
51
-
52
- # ERRORS
53
- const ERR_TX_ALREADY_EXECUTED="failed to approve multisig transaction as it was already executed"
54
-
55
- const ERR_MALFORMED_MULTISIG_CONFIG="number of approvers must be equal to or greater than threshold"
56
-
57
- const ERR_ZERO_IN_MULTISIG_CONFIG="number of approvers or threshold must not be zero"
58
-
59
- # MULTISIG PROCEDURES
60
- # =================================================================================================
61
-
62
- #! Check if transaction has already been executed and add it to executed transactions for replay protection.
63
- #!
64
- #! Inputs: [MSG]
65
- #! Outputs: []
66
- #!
67
- #! Panics if:
68
- #! - the same transaction has already been executed
69
- proc assert_new_tx(msg: BeWord)
70
- push.IS_EXECUTED_FLAG
71
- # => [[0, 0, 0, is_executed], MSG]
72
-
73
- swapw
74
- # => [MSG, IS_EXECUTED_FLAG]
75
-
76
- push.EXECUTED_TXS_SLOT[0..2]
77
- # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG]
78
-
79
- # Set the key value pair in the map to mark transaction as executed
80
- exec.native_account::set_map_item
81
- # => [[0, 0, 0, is_executed]]
82
-
83
- drop drop drop
84
- # => [is_executed]
85
-
86
- assertz.err=ERR_TX_ALREADY_EXECUTED
87
- # => []
88
- end
89
-
90
- #! Remove old approver public keys from the approver public key mapping.
91
- #!
92
- #! This procedure cleans up the storage by removing public keys of approvers that are no longer
93
- #! part of the multisig configuration. This procedure assumes that init_num_of_approvers and
94
- #! new_num_of_approvers are u32 values.
95
- #!
96
- #! Inputs: [init_num_of_approvers, new_num_of_approvers]
97
- #! Outputs: []
98
- #!
99
- #! Where:
100
- #! - init_num_of_approvers is the original number of approvers before the update
101
- #! - new_num_of_approvers is the new number of approvers after the update
102
- proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)
103
- dup.1 dup.1
104
- u32assert2 u32lt
105
- # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]
106
-
107
- while.true
108
- # => [i, new_num_of_approvers]
109
-
110
- sub.1
111
- # => [i-1, new_num_of_approvers]
112
-
113
- dup
114
- # => [i-1, i-1, new_num_of_approvers]
115
-
116
- push.0.0.0
117
- # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]
118
-
119
- padw swapw
120
- # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
121
-
122
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
123
- # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
124
-
125
- exec.native_account::set_map_item
126
- # => [OLD_MAP_VALUE, i-1, new_num_of_approvers]
127
-
128
- dropw
129
- # => [i-1, new_num_of_approvers]
130
-
131
- dup.1 dup.1
132
- u32lt
133
- # => [should_loop, i-1, new_num_of_approvers]
134
- end
135
-
136
- drop drop
137
- # => []
138
- end
139
-
140
- #! Update threshold config and add / remove approvers
141
- #!
142
- #! Inputs:
143
- #! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]
144
- #! Advice map: {
145
- #! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]
146
- #! }
147
- #! Outputs:
148
- #! Operand stack: []
149
- #!
150
- #! Where:
151
- #! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector
152
- #! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]
153
- #! - PUB_KEY_i is the public key of the i-th signer
154
- #!
155
- #! Locals:
156
- #! 0: new_num_of_approvers
157
- #! 1: init_num_of_approvers
158
- @locals(2)
159
- pub proc update_signers_and_threshold(multisig_config_hash: BeWord)
160
- adv.push_mapval
161
- # => [MULTISIG_CONFIG_HASH, pad(12)]
162
-
163
- adv_loadw
164
- # => [MULTISIG_CONFIG, pad(12)]
165
-
166
- # store new_num_of_approvers for later
167
- dup.2 loc_store.0
168
- # => [MULTISIG_CONFIG, pad(12)]
169
-
170
- dup.3 dup.3
171
- # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
172
-
173
- # make sure that the threshold is smaller than the number of approvers
174
- u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG
175
- u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG
176
- # => [MULTISIG_CONFIG, pad(12)]
177
-
178
- dup.3 dup.3
179
- # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
180
-
181
- # make sure that threshold or num_approvers are not zero
182
- eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
183
- eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
184
- # => [MULTISIG_CONFIG, pad(12)]
185
-
186
- push.THRESHOLD_CONFIG_SLOT[0..2]
187
- # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)]
188
-
189
- exec.native_account::set_item
190
- # => [OLD_THRESHOLD_CONFIG, pad(12)]
191
-
192
- # store init_num_of_approvers for later
193
- drop drop loc_store.1 drop
194
- # => [pad(12)]
195
-
196
- loc_load.0
197
- # => [num_approvers]
198
-
199
- dup neq.0
200
- while.true
201
- sub.1
202
- # => [i-1, pad(12)]
203
-
204
- dup push.0.0.0
205
- # => [[0, 0, 0, i-1], i-1, pad(12)]
206
-
207
- padw adv_loadw
208
- # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]
209
-
210
- swapw
211
- # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
212
-
213
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
214
- # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
215
-
216
- exec.native_account::set_map_item
217
- # => [OLD_VALUE, i-1, pad(12)]
218
-
219
- dropw
220
- # => [i-1, pad(12)]
221
-
222
- dup neq.0
223
- # => [is_non_zero, i-1, pad(12)]
224
- end
225
- # => [pad(13)]
226
-
227
- drop
228
- # => [pad(12)]
229
-
230
- # compare initial vs current multisig config
231
-
232
- # load init_num_of_approvers & new_num_of_approvers
233
- loc_load.0 loc_load.1
234
- # => [init_num_of_approvers, new_num_of_approvers, pad(12)]
235
-
236
- exec.cleanup_pubkey_mapping
237
- # => [pad(12)]
238
- end
239
-
240
- # Computes the effective transaction threshold based on called procedures and per-procedure
241
- # overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no
242
- # overrides apply.
243
- #
244
- #! Inputs: [default_threshold]
245
- #! Outputs: [transaction_threshold]
246
- @locals(1)
247
- proc compute_transaction_threshold(default_threshold: u32) -> u32
248
- # 1. initialize transaction_threshold = 0
249
- # 2. iterate through all account procedures
250
- # a. check if the procedure was called during the transaction
251
- # b. if called, get the override threshold of that procedure from the config map
252
- # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold
253
- # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
254
-
255
- # store default_threshold for later
256
- loc_store.0
257
- # => []
258
-
259
- # 1. initialize transaction_threshold = 0
260
- push.0
261
- # => [transaction_threshold]
262
-
263
- # get the number of account procedures
264
- exec.active_account::get_num_procedures
265
- # => [num_procedures, transaction_threshold]
266
-
267
- # 2. iterate through all account procedures
268
- dup neq.0
269
- # => [should_continue, num_procedures, transaction_threshold]
270
- while.true
271
- sub.1 dup
272
- # => [num_procedures-1, num_procedures-1, transaction_threshold]
273
-
274
- # get procedure root of the procedure with index i
275
- exec.active_account::get_procedure_root dupw
276
- # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]
277
-
278
- # 2a. check if this procedure has been called in the transaction
279
- exec.native_account::was_procedure_called
280
- # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]
281
-
282
- # if it has been called, get the override threshold of that procedure
283
- if.true
284
- # => [PROC_ROOT, num_procedures-1, transaction_threshold]
285
-
286
- push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
287
- # => [PROC_THRESHOLD_ROOTS_SLOT_prefix, PROC_THRESHOLD_ROOTS_SLOT_suffix, PROC_ROOT, num_procedures-1, transaction_threshold]
288
-
289
- # 2b. get the override proc_threshold of that procedure
290
- # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]
291
- exec.active_account::get_initial_map_item
292
- # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]
293
-
294
- drop drop drop dup dup.3
295
- # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]
296
-
297
- u32assert2.err="transaction threshold or procedure threshold are not u32"
298
- u32gt
299
- # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]
300
- # 2c. if proc_threshold > transaction_threshold, update transaction_threshold
301
- movup.2 movdn.3
302
- # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]
303
- cdrop
304
- # => [updated_transaction_threshold, num_procedures-1]
305
- swap
306
- # => [num_procedures-1, updated_transaction_threshold]
307
- # if it has not been called during this transaction, nothing to do, move to the next procedure
308
- else
309
- dropw
310
- # => [num_procedures-1, transaction_threshold]
311
- end
312
-
313
- dup neq.0
314
- # => [should_continue, num_procedures-1, transaction_threshold]
315
- end
316
-
317
- drop
318
- # => [transaction_threshold]
319
-
320
- loc_load.0
321
- # => [default_threshold, transaction_threshold]
322
-
323
- # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
324
- dup.1 eq.0
325
- # => [is_zero, default_threshold, transaction_threshold]
326
-
327
- cdrop
328
- # => [effective_transaction_threshold]
329
- end
330
-
331
- #! Authenticate a transaction using the Falcon signature scheme with multi-signature support.
332
- #!
333
- #! This procedure implements multi-signature authentication by:
334
- #! 1. Computing the transaction summary message that needs to be signed
335
- #! 2. Verifying signatures from multiple required signers against their public keys
336
- #! 3. Ensuring the minimum threshold of valid signatures is met
337
- #! 4. Implementing replay protection by tracking executed transactions
338
- #! 5. Verifying PSM signature if PSM selector is enabled (via PSM component)
339
- #!
340
- #! Inputs:
341
- #! Operand stack: [SALT]
342
- #! Advice map: {
343
- #! h(SIG_0, MSG): SIG_0,
344
- #! h(SIG_1, MSG): SIG_1,
345
- #! h(SIG_n, MSG): SIG_n
346
- #! }
347
- #! Outputs:
348
- #! Operand stack: []
349
- #!
350
- #! Where:
351
- #! - SALT is a cryptographically random nonce that enables multiple concurrent
352
- #! multisig transactions while maintaining replay protection. Each transaction
353
- #! must use a unique SALT value to ensure transaction uniqueness.
354
- #! - SIG_i is the signature from the i-th signer.
355
- #! - MSG is the transaction message being signed.
356
- #! - h(SIG_i, MSG) is the hash of the signature and message used as the advice map key.
357
- #!
358
- #! Panics if:
359
- #! - insufficient number of valid signatures (below threshold).
360
- #! - the same transaction has already been executed (replay protection).
361
- #! - PSM signature verification fails (if PSM is enabled).
362
- #!
363
- #! Invocation: call
364
- @locals(1)
365
- pub proc auth_tx_rpo_falcon512_multisig(salt: BeWord)
366
- exec.native_account::incr_nonce drop
367
- # => [SALT]
368
-
369
- # ------ Computing transaction summary ------
370
-
371
- exec.auth::create_tx_summary
372
- # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
373
-
374
- # to build a tx_summary in the host, we need these four words in the advice provider
375
- exec.auth::adv_insert_hqword
376
- # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
377
-
378
- # the commitment to the tx summary is the message that is signed
379
- exec.auth::hash_tx_summary
380
- # => [TX_SUMMARY_COMMITMENT]
381
-
382
- # ------ Verifying approver signatures ------
383
-
384
- push.THRESHOLD_CONFIG_SLOT[0..2]
385
- # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT]
386
-
387
- exec.active_account::get_initial_item
388
- # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
389
-
390
- drop drop
391
- # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
392
-
393
- swap movdn.5
394
- # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
395
-
396
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
397
- # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
398
-
399
- exec.::miden::standards::auth::falcon512_rpo::verify_signatures
400
- # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]
401
-
402
- # ------ Checking threshold is >= num_verified_signatures ------
403
-
404
- movup.5
405
- # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
406
-
407
- exec.compute_transaction_threshold
408
- # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
409
-
410
- u32assert2 u32lt
411
- # => [is_unauthorized, TX_SUMMARY_COMMITMENT]
412
-
413
- # If signatures are non-existent the tx will fail here.
414
- if.true
415
- emit.AUTH_UNAUTHORIZED_EVENT
416
- push.0 assert.err="insufficient number of signatures"
417
- end
418
-
419
- # ------ Verifying PSM Signature ------
420
- # => [TX_SUMMARY_COMMITMENT]
421
- call.psm::verify_psm_signature
422
-
423
- # ------ Writing executed transaction MSG to map ------
424
- # => [TX_SUMMARY_COMMITMENT]
425
- exec.assert_new_tx
426
- end
427
- `;
428
-
429
- export const MULTISIG_ECDSA_MASM = `# Multi-Signature ECDSA secp256k1 Authentication Component
430
- #
431
- # This component provides multi-signature authentication for accounts using
432
- # ECDSA secp256k1 (k256/keccak) signatures instead of RPO Falcon 512.
433
- # It integrates with the PSM component for optional PSM signature verification.
434
-
435
- use miden::protocol::active_account
436
- use miden::protocol::native_account
437
- use miden::standards::auth
438
- use openzeppelin::psm_ecdsa
439
-
440
- type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
441
-
442
- # CONSTANTS
443
- # =================================================================================================
444
-
445
- # Auth Request Constants
446
-
447
- # The event emitted when a signature is not found for a required signer.
448
- const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
449
-
450
- # Storage Layout Constants
451
- #
452
- # +-------------------------------+----------+--------------+-------------------+
453
- # | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |
454
- # | (slot) | (map) | (map) | (map) |
455
- # +-------------------------------+----------+--------------+-------------------+
456
- # | 0 | 1 | 2 | 3 |
457
- # +-------------------------------+----------+--------------+-------------------+
458
-
459
- # The slot in this component's storage layout where the default signature threshold and
460
- # number of approvers are stored as:
461
- # [default_threshold, num_approvers, 0, 0].
462
- # The threshold is guaranteed to be less than or equal to num_approvers.
463
- const THRESHOLD_CONFIG_SLOT=word("openzeppelin::multisig::threshold_config")
464
-
465
- # The slot in this component's storage layout where the public keys map is stored.
466
- # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY
467
- const PUBLIC_KEYS_MAP_SLOT=word("openzeppelin::multisig::signer_public_keys")
468
-
469
- # The slot in this component's storage layout where executed transactions are stored.
470
- # Map entries: transaction_message => [is_executed, 0, 0, 0]
471
- const EXECUTED_TXS_SLOT=word("openzeppelin::multisig::executed_transactions")
472
-
473
- # The slot in this component's storage layout where procedure thresholds are stored.
474
- # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]
475
- const PROC_THRESHOLD_ROOTS_SLOT=word("openzeppelin::multisig::procedure_thresholds")
476
-
477
- # Executed Transaction Flag Constant
478
- const IS_EXECUTED_FLAG=[1, 0, 0, 0]
479
-
480
- # ERRORS
481
- const ERR_TX_ALREADY_EXECUTED="failed to approve multisig transaction as it was already executed"
482
-
483
- const ERR_MALFORMED_MULTISIG_CONFIG="number of approvers must be equal to or greater than threshold"
484
-
485
- const ERR_ZERO_IN_MULTISIG_CONFIG="number of approvers or threshold must not be zero"
486
-
487
- # MULTISIG PROCEDURES
488
- # =================================================================================================
489
-
490
- #! Check if transaction has already been executed and add it to executed transactions for replay protection.
491
- #!
492
- #! Inputs: [MSG]
493
- #! Outputs: []
494
- #!
495
- #! Panics if:
496
- #! - the same transaction has already been executed
497
- proc assert_new_tx(msg: BeWord)
498
- push.IS_EXECUTED_FLAG
499
- # => [[0, 0, 0, is_executed], MSG]
500
-
501
- swapw
502
- # => [MSG, IS_EXECUTED_FLAG]
503
-
504
- push.EXECUTED_TXS_SLOT[0..2]
505
- # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG]
506
-
507
- # Set the key value pair in the map to mark transaction as executed
508
- exec.native_account::set_map_item
509
- # => [[0, 0, 0, is_executed]]
510
-
511
- drop drop drop
512
- # => [is_executed]
513
-
514
- assertz.err=ERR_TX_ALREADY_EXECUTED
515
- # => []
516
- end
517
-
518
- #! Remove old approver public keys from the approver public key mapping.
519
- #!
520
- #! This procedure cleans up the storage by removing public keys of approvers that are no longer
521
- #! part of the multisig configuration. This procedure assumes that init_num_of_approvers and
522
- #! new_num_of_approvers are u32 values.
523
- #!
524
- #! Inputs: [init_num_of_approvers, new_num_of_approvers]
525
- #! Outputs: []
526
- #!
527
- #! Where:
528
- #! - init_num_of_approvers is the original number of approvers before the update
529
- #! - new_num_of_approvers is the new number of approvers after the update
530
- proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)
531
- dup.1 dup.1
532
- u32assert2 u32lt
533
- # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]
534
-
535
- while.true
536
- # => [i, new_num_of_approvers]
537
-
538
- sub.1
539
- # => [i-1, new_num_of_approvers]
540
-
541
- dup
542
- # => [i-1, i-1, new_num_of_approvers]
543
-
544
- push.0.0.0
545
- # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]
546
-
547
- padw swapw
548
- # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
549
-
550
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
551
- # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
552
-
553
- exec.native_account::set_map_item
554
- # => [OLD_MAP_VALUE, i-1, new_num_of_approvers]
555
-
556
- dropw
557
- # => [i-1, new_num_of_approvers]
558
-
559
- dup.1 dup.1
560
- u32lt
561
- # => [should_loop, i-1, new_num_of_approvers]
562
- end
563
-
564
- drop drop
565
- # => []
566
- end
567
-
568
- #! Update threshold config and add / remove approvers
569
- #!
570
- #! Inputs:
571
- #! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]
572
- #! Advice map: {
573
- #! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]
574
- #! }
575
- #! Outputs:
576
- #! Operand stack: []
577
- #!
578
- #! Where:
579
- #! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector
580
- #! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]
581
- #! - PUB_KEY_i is the public key of the i-th signer
582
- #!
583
- #! Locals:
584
- #! 0: new_num_of_approvers
585
- #! 1: init_num_of_approvers
586
- @locals(2)
587
- pub proc update_signers_and_threshold(multisig_config_hash: BeWord)
588
- adv.push_mapval
589
- # => [MULTISIG_CONFIG_HASH, pad(12)]
590
-
591
- adv_loadw
592
- # => [MULTISIG_CONFIG, pad(12)]
593
-
594
- # store new_num_of_approvers for later
595
- dup.2 loc_store.0
596
- # => [MULTISIG_CONFIG, pad(12)]
597
-
598
- dup.3 dup.3
599
- # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
600
-
601
- # make sure that the threshold is smaller than the number of approvers
602
- u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG
603
- u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG
604
- # => [MULTISIG_CONFIG, pad(12)]
605
-
606
- dup.3 dup.3
607
- # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
608
-
609
- # make sure that threshold or num_approvers are not zero
610
- eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
611
- eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
612
- # => [MULTISIG_CONFIG, pad(12)]
613
-
614
- push.THRESHOLD_CONFIG_SLOT[0..2]
615
- # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)]
616
-
617
- exec.native_account::set_item
618
- # => [OLD_THRESHOLD_CONFIG, pad(12)]
619
-
620
- # store init_num_of_approvers for later
621
- drop drop loc_store.1 drop
622
- # => [pad(12)]
623
-
624
- loc_load.0
625
- # => [num_approvers]
626
-
627
- dup neq.0
628
- while.true
629
- sub.1
630
- # => [i-1, pad(12)]
631
-
632
- dup push.0.0.0
633
- # => [[0, 0, 0, i-1], i-1, pad(12)]
634
-
635
- padw adv_loadw
636
- # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]
637
-
638
- swapw
639
- # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
640
-
641
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
642
- # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
643
-
644
- exec.native_account::set_map_item
645
- # => [OLD_VALUE, i-1, pad(12)]
646
-
647
- dropw
648
- # => [i-1, pad(12)]
649
-
650
- dup neq.0
651
- # => [is_non_zero, i-1, pad(12)]
652
- end
653
- # => [pad(13)]
654
-
655
- drop
656
- # => [pad(12)]
657
-
658
- # compare initial vs current multisig config
659
-
660
- # load init_num_of_approvers & new_num_of_approvers
661
- loc_load.0 loc_load.1
662
- # => [init_num_of_approvers, new_num_of_approvers, pad(12)]
663
-
664
- exec.cleanup_pubkey_mapping
665
- # => [pad(12)]
666
- end
667
-
668
- # Computes the effective transaction threshold based on called procedures and per-procedure
669
- # overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no
670
- # overrides apply.
671
- #
672
- #! Inputs: [default_threshold]
673
- #! Outputs: [transaction_threshold]
674
- @locals(1)
675
- proc compute_transaction_threshold(default_threshold: u32) -> u32
676
- # 1. initialize transaction_threshold = 0
677
- # 2. iterate through all account procedures
678
- # a. check if the procedure was called during the transaction
679
- # b. if called, get the override threshold of that procedure from the config map
680
- # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold
681
- # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
682
-
683
- # store default_threshold for later
684
- loc_store.0
685
- # => []
686
-
687
- # 1. initialize transaction_threshold = 0
688
- push.0
689
- # => [transaction_threshold]
690
-
691
- # get the number of account procedures
692
- exec.active_account::get_num_procedures
693
- # => [num_procedures, transaction_threshold]
694
-
695
- # 2. iterate through all account procedures
696
- dup neq.0
697
- # => [should_continue, num_procedures, transaction_threshold]
698
- while.true
699
- sub.1 dup
700
- # => [num_procedures-1, num_procedures-1, transaction_threshold]
701
-
702
- # get procedure root of the procedure with index i
703
- exec.active_account::get_procedure_root dupw
704
- # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]
705
-
706
- # 2a. check if this procedure has been called in the transaction
707
- exec.native_account::was_procedure_called
708
- # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]
709
-
710
- # if it has been called, get the override threshold of that procedure
711
- if.true
712
- # => [PROC_ROOT, num_procedures-1, transaction_threshold]
713
-
714
- push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
715
- # => [PROC_THRESHOLD_ROOTS_SLOT_prefix, PROC_THRESHOLD_ROOTS_SLOT_suffix, PROC_ROOT, num_procedures-1, transaction_threshold]
716
-
717
- # 2b. get the override proc_threshold of that procedure
718
- # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]
719
- exec.active_account::get_initial_map_item
720
- # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]
721
-
722
- drop drop drop dup dup.3
723
- # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]
724
-
725
- u32assert2.err="transaction threshold or procedure threshold are not u32"
726
- u32gt
727
- # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]
728
- # 2c. if proc_threshold > transaction_threshold, update transaction_threshold
729
- movup.2 movdn.3
730
- # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]
731
- cdrop
732
- # => [updated_transaction_threshold, num_procedures-1]
733
- swap
734
- # => [num_procedures-1, updated_transaction_threshold]
735
- # if it has not been called during this transaction, nothing to do, move to the next procedure
736
- else
737
- dropw
738
- # => [num_procedures-1, transaction_threshold]
739
- end
740
-
741
- dup neq.0
742
- # => [should_continue, num_procedures-1, transaction_threshold]
743
- end
744
-
745
- drop
746
- # => [transaction_threshold]
747
-
748
- loc_load.0
749
- # => [default_threshold, transaction_threshold]
750
-
751
- # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
752
- dup.1 eq.0
753
- # => [is_zero, default_threshold, transaction_threshold]
754
-
755
- cdrop
756
- # => [effective_transaction_threshold]
757
- end
758
-
759
- #! Authenticate a transaction using ECDSA secp256k1 with multi-signature support.
760
- #!
761
- #! This procedure implements multi-signature authentication by:
762
- #! 1. Computing the transaction summary message that needs to be signed
763
- #! 2. Verifying ECDSA signatures from multiple required signers against their public keys
764
- #! 3. Ensuring the minimum threshold of valid signatures is met
765
- #! 4. Implementing replay protection by tracking executed transactions
766
- #! 5. Verifying PSM signature if PSM selector is enabled (via PSM component)
767
- #!
768
- #! Inputs:
769
- #! Operand stack: [SALT]
770
- #! Advice map: {
771
- #! h(PUB_KEY_0, MSG): SIG_0,
772
- #! h(PUB_KEY_1, MSG): SIG_1,
773
- #! h(PUB_KEY_n, MSG): SIG_n
774
- #! }
775
- #! Outputs:
776
- #! Operand stack: []
777
- #!
778
- #! Where:
779
- #! - SALT is a cryptographically random nonce that enables multiple concurrent
780
- #! multisig transactions while maintaining replay protection. Each transaction
781
- #! must use a unique SALT value to ensure transaction uniqueness.
782
- #! - SIG_i is the ECDSA signature from the i-th signer.
783
- #! - MSG is the transaction message being signed.
784
- #! - h(PUB_KEY_i, MSG) is the hash of the public key and message used as the advice map key.
785
- #!
786
- #! Panics if:
787
- #! - insufficient number of valid signatures (below threshold).
788
- #! - the same transaction has already been executed (replay protection).
789
- #! - PSM signature verification fails (if PSM is enabled).
790
- #!
791
- #! Invocation: call
792
- @locals(1)
793
- pub proc auth_tx_ecdsa_multisig(salt: BeWord)
794
- exec.native_account::incr_nonce drop
795
- # => [SALT]
796
-
797
- # ------ Computing transaction summary ------
798
-
799
- exec.auth::create_tx_summary
800
- # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
801
-
802
- # to build a tx_summary in the host, we need these four words in the advice provider
803
- exec.auth::adv_insert_hqword
804
- # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
805
-
806
- # the commitment to the tx summary is the message that is signed
807
- exec.auth::hash_tx_summary
808
- # => [TX_SUMMARY_COMMITMENT]
809
-
810
- # ------ Verifying approver signatures ------
811
-
812
- push.THRESHOLD_CONFIG_SLOT[0..2]
813
- # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT]
814
-
815
- exec.active_account::get_initial_item
816
- # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
817
-
818
- drop drop
819
- # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
820
-
821
- swap movdn.5
822
- # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
823
-
824
- push.PUBLIC_KEYS_MAP_SLOT[0..2]
825
- # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
826
-
827
- exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures
828
- # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]
829
-
830
- # ------ Checking threshold is >= num_verified_signatures ------
831
-
832
- movup.5
833
- # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
834
-
835
- exec.compute_transaction_threshold
836
- # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
837
-
838
- u32assert2 u32lt
839
- # => [is_unauthorized, TX_SUMMARY_COMMITMENT]
840
-
841
- # If signatures are non-existent the tx will fail here.
842
- if.true
843
- emit.AUTH_UNAUTHORIZED_EVENT
844
- push.0 assert.err="insufficient number of signatures"
845
- end
846
-
847
- # ------ Verifying PSM Signature ------
848
- # => [TX_SUMMARY_COMMITMENT]
849
- call.psm_ecdsa::verify_psm_signature
850
-
851
- # ------ Writing executed transaction MSG to map ------
852
- # => [TX_SUMMARY_COMMITMENT]
853
- exec.assert_new_tx
854
- end
855
- `;
856
-
857
- export const PSM_MASM = `# Private State Manager (PSM) Authentication Component
858
- #
859
- # This component provides PSM signature verification for accounts.
860
- # It can be used standalone or in conjunction with other auth components like multisig.
861
-
862
- use miden::protocol::active_account
863
- use miden::protocol::native_account
864
-
865
- type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
866
-
867
- # IMPORTANT SECURITY NOTES
868
- # --------------------------------------------------------------------------------
869
- # - The selector in \`PSM_SELECTOR_SLOT\` controls whether the extra PSM signature
870
- # is enforced:
871
- # * PSM_ON => exactly one valid PSM signature is required.
872
- # * PSM_OFF => PSM signature is skipped for that call.
873
- #
874
- # - \`verify_psm_signature\` reads the selector from initial storage state.
875
- # This means changes made during the same transaction won't affect the check.
876
- #
877
- # - \`enable_psm\` / \`disable_psm\` procedures allow explicit control over PSM state.
878
- #
879
- # - \`update_psm_public_key\`:
880
- # * Installs a new PSM public key in the map at \`PSM_PUBLIC_KEY_MAP_SLOT\`.
881
- # * Does not itself perform any signature checks.
882
- # * To update the key without requiring PSM signature, ensure selector is OFF.
883
- #
884
- # Storage Layout
885
- # --------------------------------------------------------------------------------
886
- #
887
- # +---------------------+---------------+
888
- # | DESCRIPTION | SLOT |
889
- # +---------------------+---------------+
890
- # | PSM SELECTOR (word) | 0 |
891
- # | PSM PUBLIC KEY MAP | 1 |
892
- # +---------------------+---------------+
893
- #
894
- # - PSM_SELECTOR_SLOT (0):
895
- # * Stores a word that is compared against [1, 0, 0, 0] (PSM_ON).
896
- # * Any value != PSM_ON is treated as PSM_OFF.
897
- #
898
- # - PSM_PUBLIC_KEY_MAP_SLOT (1):
899
- # * A map from a fixed key [0, 0, 0, 0] to the single PSM public key:
900
- # [0, 0, 0, 0] => PSM_PUBLIC_KEY
901
- # * PSM_PUBLIC_KEY is a RPO Falcon 512 public key represented as a word.
902
-
903
- # CONSTANTS
904
- # =================================================================================================
905
-
906
- # Slot where the PSM selector flag is stored (using word() for named slot access):
907
- # - PSM_ON => PSM signature required
908
- # - PSM_OFF => PSM signature skipped
909
- const PSM_SELECTOR_SLOT=word("openzeppelin::psm::selector")
910
-
911
- # Map slot for PSM public key
912
- # Uses exactly one PSM public key at index [0, 0, 0, 0]
913
- # [0, 0, 0, 0] => PSM_PUBLIC_KEY
914
- const PSM_PUBLIC_KEY_MAP_SLOT=word("openzeppelin::psm::public_key")
915
-
916
- # Selector flag values
917
- const PSM_ON=[1, 0, 0, 0]
918
- const PSM_OFF=[0, 0, 0, 0]
919
-
920
- # The event emitted when a signature is not found for a required signer.
921
- const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
922
-
923
- # PSM PROCEDURES
924
- # =================================================================================================
925
-
926
- #! Enable PSM verification by setting the selector to ON.
927
- #!
928
- #! Operand stack inputs: []
929
- #! Outputs: []
930
- #!
931
- #! Notes:
932
- #! - Sets PSM_SELECTOR_SLOT to PSM_ON (1)
933
- #! - After this, transactions will require PSM signature verification
934
- proc enable_psm
935
- push.PSM_ON
936
- # => [PSM_ON]
937
-
938
- push.PSM_SELECTOR_SLOT[0..2]
939
- # => [selector_slot_prefix, selector_slot_suffix, PSM_ON]
940
-
941
- exec.native_account::set_item
942
- # => [OLD_ROOT]
943
-
944
- dropw
945
- # => []
946
- end
947
-
948
- #! Disable PSM verification by setting the selector to OFF.
949
- #!
950
- #! Operand stack inputs: []
951
- #! Outputs: []
952
- #!
953
- #! Notes:
954
- #! - Sets PSM_SELECTOR_SLOT to PSM_OFF (0)
955
- #! - After this, transactions will NOT require PSM signature verification
956
- proc disable_psm
957
- push.PSM_OFF
958
- # => [PSM_OFF]
959
-
960
- push.PSM_SELECTOR_SLOT[0..2]
961
- # => [selector_slot_prefix, selector_slot_suffix, PSM_OFF]
962
-
963
- exec.native_account::set_item
964
- # => [OLD_ROOT]
965
-
966
- dropw
967
- # => []
968
- end
969
-
970
- #! Update the PSM public key.
971
- #!
972
- #! Operand stack inputs: []
973
- #! Advice stack inputs: [PUB_KEY]
974
- #! - PUB_KEY is the new PSM RPO Falcon 512 public key
975
- #!
976
- #! Notes:
977
- #! - Stores PUB_KEY into PSM_PUBLIC_KEY_MAP_SLOT:
978
- #! [0, 0, 0, 0] => PSM_PUBLIC_KEY
979
- #! - To update the key without requiring PSM signature, ensure
980
- #! PSM_SELECTOR_SLOT = 0 (OFF) before calling this.
981
- pub proc update_psm_public_key
982
- exec.disable_psm
983
- # ------ Update the PSM public key ------
984
- adv_loadw
985
- # => [PUB_KEY]
986
-
987
- push.0.0.0.0
988
- # => [MAP_KEY, PUB_KEY]
989
- # Note that MAP_KEY is [0, 0, 0, 0] for a single PSM_KEY
990
-
991
- push.PSM_PUBLIC_KEY_MAP_SLOT[0..2]
992
- # => [pub_key_slot_prefix, pub_key_slot_suffix, MAP_KEY, PUB_KEY]
993
-
994
- exec.native_account::set_map_item
995
- # => [OLD_MAP_VALUE]
996
-
997
- dropw
998
- # => []
999
- end
1000
-
1001
- #! Conditionally verify a "PSM" signature against a stored public key hash.
1002
- #! The condition is controlled by the selector at PSM_SELECTOR_SLOT.
1003
- #!
1004
- #! Inputs: [MSG]
1005
- #! Outputs: [MSG]
1006
- #!
1007
- #! Panics if:
1008
- #! - Selector is ON but the provided PSM signature is invalid or missing.
1009
- #!
1010
- #! Notes:
1011
- #! - MSG is TX_SUMMARY_COMMITMENT provided by auth procedure
1012
- #! - If selector is OFF (0), PSM verification is skipped
1013
- #! - Selector value is read from initial storage state
1014
- pub proc verify_psm_signature(msg: BeWord) -> BeWord
1015
- push.PSM_SELECTOR_SLOT[0..2]
1016
- exec.active_account::get_item
1017
- drop drop drop
1018
- # => [selector, MSG]
1019
-
1020
- push.1 eq
1021
- if.true
1022
- push.1
1023
- push.PSM_PUBLIC_KEY_MAP_SLOT[0..2]
1024
- exec.::miden::standards::auth::falcon512_rpo::verify_signatures
1025
- push.1 neq
1026
- if.true
1027
- emit.AUTH_UNAUTHORIZED_EVENT
1028
- push.0 assert.err="invalid PSM signature"
1029
- end
1030
- end
1031
- # => [MSG]
1032
- exec.enable_psm
1033
- end
1034
- `;
1035
-
1036
- export const PSM_ECDSA_MASM = PSM_MASM
1037
- .replaceAll('RPO Falcon 512', 'ECDSA secp256k1')
1038
- .replace(
1039
- 'exec.::miden::standards::auth::falcon512_rpo::verify_signatures',
1040
- 'exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures',
1041
- );
4
+ export * from './masm/index.js';