@openzeppelin/miden-multisig-client 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) 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.d.ts +2 -0
  7. package/dist/account/builder.test.d.ts.map +1 -0
  8. package/dist/account/builder.test.js +115 -0
  9. package/dist/account/builder.test.js.map +1 -0
  10. package/dist/account/index.d.ts +1 -1
  11. package/dist/account/index.d.ts.map +1 -1
  12. package/dist/account/index.js +1 -1
  13. package/dist/account/index.js.map +1 -1
  14. package/dist/account/masm/account-components/auth.d.ts +5 -0
  15. package/dist/account/masm/account-components/auth.d.ts.map +1 -0
  16. package/dist/account/masm/account-components/auth.js +67 -0
  17. package/dist/account/masm/account-components/auth.js.map +1 -0
  18. package/dist/account/masm/auth.d.ts +5 -0
  19. package/dist/account/masm/auth.d.ts.map +1 -0
  20. package/dist/account/masm/auth.js +1395 -0
  21. package/dist/account/masm/auth.js.map +1 -0
  22. package/dist/account/masm/index.d.ts +3 -0
  23. package/dist/account/masm/index.d.ts.map +1 -0
  24. package/dist/account/masm/index.js +5 -0
  25. package/dist/account/masm/index.js.map +1 -0
  26. package/dist/account/masm.d.ts +1 -4
  27. package/dist/account/masm.d.ts.map +1 -1
  28. package/dist/account/masm.js +3 -1034
  29. package/dist/account/masm.js.map +1 -1
  30. package/dist/account/storage.d.ts +2 -2
  31. package/dist/account/storage.d.ts.map +1 -1
  32. package/dist/account/storage.js +18 -16
  33. package/dist/account/storage.js.map +1 -1
  34. package/dist/client.d.ts +67 -9
  35. package/dist/client.d.ts.map +1 -1
  36. package/dist/client.js +73 -22
  37. package/dist/client.js.map +1 -1
  38. package/dist/client.test.js +31 -32
  39. package/dist/client.test.js.map +1 -1
  40. package/dist/index.d.ts +54 -12
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +53 -9
  43. package/dist/index.js.map +1 -1
  44. package/dist/inspector.d.ts +32 -6
  45. package/dist/inspector.d.ts.map +1 -1
  46. package/dist/inspector.js +58 -23
  47. package/dist/inspector.js.map +1 -1
  48. package/dist/inspector.test.js +18 -18
  49. package/dist/inspector.test.js.map +1 -1
  50. package/dist/multisig/proposal/execution.d.ts +6 -5
  51. package/dist/multisig/proposal/execution.d.ts.map +1 -1
  52. package/dist/multisig/proposal/execution.js +49 -27
  53. package/dist/multisig/proposal/execution.js.map +1 -1
  54. package/dist/multisig/proposal/parser.d.ts +3 -3
  55. package/dist/multisig/proposal/parser.d.ts.map +1 -1
  56. package/dist/multisig/proposal/parser.js +23 -23
  57. package/dist/multisig/proposal/parser.js.map +1 -1
  58. package/dist/multisig/signing.d.ts +3 -3
  59. package/dist/multisig/signing.d.ts.map +1 -1
  60. package/dist/multisig/signing.js +3 -3
  61. package/dist/multisig/signing.js.map +1 -1
  62. package/dist/multisig.d.ts +187 -46
  63. package/dist/multisig.d.ts.map +1 -1
  64. package/dist/multisig.js +650 -289
  65. package/dist/multisig.js.map +1 -1
  66. package/dist/multisig.test.js +1680 -241
  67. package/dist/multisig.test.js.map +1 -1
  68. package/dist/procedures.d.ts +46 -24
  69. package/dist/procedures.d.ts.map +1 -1
  70. package/dist/procedures.js +46 -21
  71. package/dist/procedures.js.map +1 -1
  72. package/dist/proposal/factory.d.ts +18 -0
  73. package/dist/proposal/factory.d.ts.map +1 -0
  74. package/dist/proposal/factory.js +99 -0
  75. package/dist/proposal/factory.js.map +1 -0
  76. package/dist/proposal/metadata.d.ts +8 -0
  77. package/dist/proposal/metadata.d.ts.map +1 -0
  78. package/dist/proposal/metadata.js +156 -0
  79. package/dist/proposal/metadata.js.map +1 -0
  80. package/dist/proposal/signatures.d.ts +10 -0
  81. package/dist/proposal/signatures.d.ts.map +1 -0
  82. package/dist/proposal/signatures.js +54 -0
  83. package/dist/proposal/signatures.js.map +1 -0
  84. package/dist/signer.d.ts +2 -0
  85. package/dist/signer.d.ts.map +1 -1
  86. package/dist/signer.js +2 -0
  87. package/dist/signer.js.map +1 -1
  88. package/dist/signers/ecdsa.d.ts +3 -0
  89. package/dist/signers/ecdsa.d.ts.map +1 -1
  90. package/dist/signers/ecdsa.js +8 -4
  91. package/dist/signers/ecdsa.js.map +1 -1
  92. package/dist/signers/ecdsa.test.d.ts +2 -0
  93. package/dist/signers/ecdsa.test.d.ts.map +1 -0
  94. package/dist/signers/ecdsa.test.js +65 -0
  95. package/dist/signers/ecdsa.test.js.map +1 -0
  96. package/dist/signers/falcon.d.ts +3 -0
  97. package/dist/signers/falcon.d.ts.map +1 -1
  98. package/dist/signers/falcon.js +8 -4
  99. package/dist/signers/falcon.js.map +1 -1
  100. package/dist/signers/falcon.test.js +12 -0
  101. package/dist/signers/falcon.test.js.map +1 -1
  102. package/dist/signers/miden-wallet.d.ts +3 -1
  103. package/dist/signers/miden-wallet.d.ts.map +1 -1
  104. package/dist/signers/miden-wallet.js +11 -2
  105. package/dist/signers/miden-wallet.js.map +1 -1
  106. package/dist/signers/miden-wallet.test.js +26 -0
  107. package/dist/signers/miden-wallet.test.js.map +1 -1
  108. package/dist/signers/para.d.ts +3 -3
  109. package/dist/signers/para.d.ts.map +1 -1
  110. package/dist/signers/para.js +15 -6
  111. package/dist/signers/para.js.map +1 -1
  112. package/dist/signers/para.test.js +39 -0
  113. package/dist/signers/para.test.js.map +1 -1
  114. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  115. package/dist/transaction/consumeNotes.js +1 -0
  116. package/dist/transaction/consumeNotes.js.map +1 -1
  117. package/dist/transaction/index.d.ts +2 -1
  118. package/dist/transaction/index.d.ts.map +1 -1
  119. package/dist/transaction/index.js +2 -1
  120. package/dist/transaction/index.js.map +1 -1
  121. package/dist/transaction/options.d.ts +1 -1
  122. package/dist/transaction/options.d.ts.map +1 -1
  123. package/dist/transaction/p2id.d.ts +1 -0
  124. package/dist/transaction/p2id.d.ts.map +1 -1
  125. package/dist/transaction/p2id.js +9 -3
  126. package/dist/transaction/p2id.js.map +1 -1
  127. package/dist/transaction/p2id.test.d.ts +2 -0
  128. package/dist/transaction/p2id.test.d.ts.map +1 -0
  129. package/dist/transaction/p2id.test.js +138 -0
  130. package/dist/transaction/p2id.test.js.map +1 -0
  131. package/dist/transaction/rpoRandomCoin.d.ts +7 -0
  132. package/dist/transaction/rpoRandomCoin.d.ts.map +1 -0
  133. package/dist/transaction/rpoRandomCoin.js +17 -0
  134. package/dist/transaction/rpoRandomCoin.js.map +1 -0
  135. package/dist/transaction/rpoRandomCoin.test.d.ts +2 -0
  136. package/dist/transaction/rpoRandomCoin.test.d.ts.map +1 -0
  137. package/dist/transaction/rpoRandomCoin.test.js +52 -0
  138. package/dist/transaction/rpoRandomCoin.test.js.map +1 -0
  139. package/dist/transaction/updateGuardian.d.ts +7 -0
  140. package/dist/transaction/updateGuardian.d.ts.map +1 -0
  141. package/dist/transaction/{updatePsm.js → updateGuardian.js} +15 -29
  142. package/dist/transaction/updateGuardian.js.map +1 -0
  143. package/dist/transaction/updateProcedureThreshold.d.ts +9 -0
  144. package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -0
  145. package/dist/transaction/updateProcedureThreshold.js +55 -0
  146. package/dist/transaction/updateProcedureThreshold.js.map +1 -0
  147. package/dist/transaction/updateSigners.d.ts.map +1 -1
  148. package/dist/transaction/updateSigners.js +5 -23
  149. package/dist/transaction/updateSigners.js.map +1 -1
  150. package/dist/transaction.d.ts +2 -1
  151. package/dist/transaction.d.ts.map +1 -1
  152. package/dist/transaction.js +2 -1
  153. package/dist/transaction.js.map +1 -1
  154. package/dist/types/proposal.d.ts +32 -15
  155. package/dist/types/proposal.d.ts.map +1 -1
  156. package/dist/types.d.ts +24 -7
  157. package/dist/types.d.ts.map +1 -1
  158. package/dist/utils/digest.d.ts +5 -0
  159. package/dist/utils/digest.d.ts.map +1 -1
  160. package/dist/utils/digest.js +34 -1
  161. package/dist/utils/digest.js.map +1 -1
  162. package/dist/utils/digest.test.d.ts +2 -0
  163. package/dist/utils/digest.test.d.ts.map +1 -0
  164. package/dist/utils/digest.test.js +48 -0
  165. package/dist/utils/digest.test.js.map +1 -0
  166. package/dist/utils/signature.d.ts +3 -1
  167. package/dist/utils/signature.d.ts.map +1 -1
  168. package/dist/utils/signature.js +31 -6
  169. package/dist/utils/signature.js.map +1 -1
  170. package/dist/utils/word.test.js +1 -52
  171. package/dist/utils/word.test.js.map +1 -1
  172. package/masm/account_components/auth/multisig.masm +13 -0
  173. package/masm/account_components/auth/multisig_ecdsa.masm +13 -0
  174. package/masm/account_components/auth/multisig_guardian.masm +17 -0
  175. package/masm/account_components/auth/multisig_guardian_ecdsa.masm +17 -0
  176. package/masm/auth/guardian.masm +177 -0
  177. package/masm/auth/guardian_ecdsa.masm +178 -0
  178. package/masm/{multisig.masm → auth/multisig.masm} +149 -55
  179. package/masm/{multisig_ecdsa.masm → auth/multisig_ecdsa.masm} +148 -56
  180. package/package.json +11 -9
  181. package/src/account/builder.test.ts +165 -0
  182. package/src/account/builder.ts +71 -34
  183. package/src/account/index.ts +1 -1
  184. package/src/account/masm/account-components/auth.ts +70 -0
  185. package/src/account/masm/auth.ts +1398 -0
  186. package/src/account/masm/index.ts +5 -0
  187. package/src/account/masm.ts +3 -1040
  188. package/src/account/storage.ts +18 -16
  189. package/src/client.test.ts +31 -35
  190. package/src/client.ts +98 -26
  191. package/src/index.ts +84 -22
  192. package/src/inspector.test.ts +17 -17
  193. package/src/inspector.ts +63 -29
  194. package/src/multisig/proposal/execution.ts +102 -41
  195. package/src/multisig/proposal/parser.ts +26 -26
  196. package/src/multisig/signing.ts +4 -4
  197. package/src/multisig.test.ts +2097 -248
  198. package/src/multisig.ts +882 -365
  199. package/src/procedures.ts +50 -26
  200. package/src/proposal/factory.ts +171 -0
  201. package/src/proposal/metadata.ts +165 -0
  202. package/src/proposal/signatures.ts +68 -0
  203. package/src/signer.ts +2 -0
  204. package/src/signers/ecdsa.test.ts +80 -0
  205. package/src/signers/ecdsa.ts +15 -4
  206. package/src/signers/falcon.test.ts +19 -0
  207. package/src/signers/falcon.ts +16 -5
  208. package/src/signers/miden-wallet.test.ts +47 -0
  209. package/src/signers/miden-wallet.ts +18 -1
  210. package/src/signers/para.test.ts +57 -0
  211. package/src/signers/para.ts +26 -7
  212. package/src/transaction/consumeNotes.ts +1 -0
  213. package/src/transaction/index.ts +2 -2
  214. package/src/transaction/options.ts +1 -2
  215. package/src/transaction/p2id.test.ts +190 -0
  216. package/src/transaction/p2id.ts +11 -5
  217. package/src/transaction/rpoRandomCoin.test.ts +64 -0
  218. package/src/transaction/rpoRandomCoin.ts +24 -0
  219. package/src/transaction/{updatePsm.ts → updateGuardian.ts} +19 -32
  220. package/src/transaction/updateProcedureThreshold.ts +97 -0
  221. package/src/transaction/updateSigners.ts +12 -27
  222. package/src/transaction.ts +5 -3
  223. package/src/types/proposal.ts +43 -11
  224. package/src/types.ts +29 -11
  225. package/src/utils/digest.test.ts +55 -0
  226. package/src/utils/digest.ts +51 -1
  227. package/src/utils/signature.ts +47 -9
  228. package/src/utils/word.test.ts +1 -55
  229. package/dist/account.d.ts +0 -104
  230. package/dist/account.d.ts.map +0 -1
  231. package/dist/account.js +0 -136
  232. package/dist/account.js.map +0 -1
  233. package/dist/builder.d.ts +0 -54
  234. package/dist/builder.d.ts.map +0 -1
  235. package/dist/builder.js +0 -78
  236. package/dist/builder.js.map +0 -1
  237. package/dist/http.d.ts +0 -64
  238. package/dist/http.d.ts.map +0 -1
  239. package/dist/http.js +0 -162
  240. package/dist/http.js.map +0 -1
  241. package/dist/keystore.d.ts +0 -65
  242. package/dist/keystore.d.ts.map +0 -1
  243. package/dist/keystore.js +0 -121
  244. package/dist/keystore.js.map +0 -1
  245. package/dist/miden.d.ts +0 -9
  246. package/dist/miden.d.ts.map +0 -1
  247. package/dist/miden.js +0 -26
  248. package/dist/miden.js.map +0 -1
  249. package/dist/multisig/metadata.d.ts +0 -18
  250. package/dist/multisig/metadata.d.ts.map +0 -1
  251. package/dist/multisig/metadata.js +0 -100
  252. package/dist/multisig/metadata.js.map +0 -1
  253. package/dist/multisig/metadata.test.d.ts +0 -2
  254. package/dist/multisig/metadata.test.d.ts.map +0 -1
  255. package/dist/multisig/metadata.test.js +0 -323
  256. package/dist/multisig/metadata.test.js.map +0 -1
  257. package/dist/signer.test.d.ts +0 -2
  258. package/dist/signer.test.d.ts.map +0 -1
  259. package/dist/signer.test.js +0 -117
  260. package/dist/signer.test.js.map +0 -1
  261. package/dist/transaction/updatePsm.d.ts +0 -7
  262. package/dist/transaction/updatePsm.d.ts.map +0 -1
  263. package/dist/transaction/updatePsm.js.map +0 -1
  264. package/dist/transport/http.d.ts +0 -64
  265. package/dist/transport/http.d.ts.map +0 -1
  266. package/dist/transport/http.js +0 -162
  267. package/dist/transport/http.js.map +0 -1
  268. package/dist/transport/index.d.ts +0 -5
  269. package/dist/transport/index.d.ts.map +0 -1
  270. package/dist/transport/index.js +0 -5
  271. package/dist/transport/index.js.map +0 -1
  272. package/masm/psm.masm +0 -178
  273. package/masm/psm_ecdsa.masm +0 -179
@@ -0,0 +1,1395 @@
1
+ // This file is generated by scripts/generate-masm.mjs from the MASM contract sources.
2
+ // Do not edit it by hand.
3
+ export const MULTISIG_MASM = `# Multi-Signature RPO Falcon 512 Authentication Core Library
4
+ #
5
+ # This library provides the reusable multisig procedures used by account wrappers.
6
+
7
+ use miden::protocol::active_account
8
+ use miden::protocol::native_account
9
+ use miden::standards::auth
10
+
11
+ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
12
+
13
+ # CONSTANTS
14
+ # =================================================================================================
15
+
16
+ # Auth Request Constants
17
+
18
+ # The event emitted when a signature is not found for a required signer.
19
+ const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
20
+
21
+ # Storage Layout Constants
22
+ #
23
+ # +-------------------------------+----------+--------------+-------------------+
24
+ # | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |
25
+ # | (slot) | (map) | (map) | (map) |
26
+ # +-------------------------------+----------+--------------+-------------------+
27
+ # | 0 | 1 | 2 | 3 |
28
+ # +-------------------------------+----------+--------------+-------------------+
29
+
30
+ # The slot in this component's storage layout where the default signature threshold and
31
+ # number of approvers are stored as:
32
+ # [default_threshold, num_approvers, 0, 0].
33
+ # The threshold is guaranteed to be less than or equal to num_approvers.
34
+ const THRESHOLD_CONFIG_SLOT=word("openzeppelin::multisig::threshold_config")
35
+
36
+ # The slot in this component's storage layout where the public keys map is stored.
37
+ # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY
38
+ const PUBLIC_KEYS_MAP_SLOT=word("openzeppelin::multisig::signer_public_keys")
39
+
40
+ # The slot in this component's storage layout where executed transactions are stored.
41
+ # Map entries: transaction_message => [is_executed, 0, 0, 0]
42
+ const EXECUTED_TXS_SLOT=word("openzeppelin::multisig::executed_transactions")
43
+
44
+ # The slot in this component's storage layout where procedure thresholds are stored.
45
+ # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]
46
+ const PROC_THRESHOLD_ROOTS_SLOT=word("openzeppelin::multisig::procedure_thresholds")
47
+
48
+ # Executed Transaction Flag Constant
49
+ const IS_EXECUTED_FLAG=[1, 0, 0, 0]
50
+
51
+ # ERRORS
52
+ const ERR_TX_ALREADY_EXECUTED="failed to approve multisig transaction as it was already executed"
53
+
54
+ const ERR_MALFORMED_MULTISIG_CONFIG="number of approvers must be equal to or greater than threshold"
55
+
56
+ const ERR_ZERO_IN_MULTISIG_CONFIG="number of approvers or threshold must not be zero"
57
+
58
+ const ERR_PROC_THRESHOLD_NOT_U32="procedure threshold must be a u32 value"
59
+
60
+ const ERR_NUM_APPROVERS_OR_PROC_THRESHOLD_NOT_U32="number of approvers and procedure threshold must be u32"
61
+
62
+ const ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS="procedure threshold exceeds number of approvers"
63
+
64
+ # MULTISIG PROCEDURES
65
+ # =================================================================================================
66
+
67
+ #! Check if transaction has already been executed and add it to executed transactions for replay protection.
68
+ #!
69
+ #! Inputs: [MSG]
70
+ #! Outputs: []
71
+ #!
72
+ #! Panics if:
73
+ #! - the same transaction has already been executed
74
+ pub proc assert_new_tx(msg: BeWord)
75
+ push.IS_EXECUTED_FLAG
76
+ # => [[0, 0, 0, is_executed], MSG]
77
+
78
+ swapw
79
+ # => [MSG, IS_EXECUTED_FLAG]
80
+
81
+ push.EXECUTED_TXS_SLOT[0..2]
82
+ # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG]
83
+
84
+ # Set the key value pair in the map to mark transaction as executed
85
+ exec.native_account::set_map_item
86
+ # => [[0, 0, 0, is_executed]]
87
+
88
+ drop drop drop
89
+ # => [is_executed]
90
+
91
+ assertz.err=ERR_TX_ALREADY_EXECUTED
92
+ # => []
93
+ end
94
+
95
+ #! Remove old approver public keys from the approver public key mapping.
96
+ #!
97
+ #! This procedure cleans up the storage by removing public keys of approvers that are no longer
98
+ #! part of the multisig configuration. This procedure assumes that init_num_of_approvers and
99
+ #! new_num_of_approvers are u32 values.
100
+ #!
101
+ #! Inputs: [init_num_of_approvers, new_num_of_approvers]
102
+ #! Outputs: []
103
+ #!
104
+ #! Where:
105
+ #! - init_num_of_approvers is the original number of approvers before the update
106
+ #! - new_num_of_approvers is the new number of approvers after the update
107
+ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)
108
+ dup.1 dup.1
109
+ u32assert2 u32lt
110
+ # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]
111
+
112
+ while.true
113
+ # => [i, new_num_of_approvers]
114
+
115
+ sub.1
116
+ # => [i-1, new_num_of_approvers]
117
+
118
+ dup
119
+ # => [i-1, i-1, new_num_of_approvers]
120
+
121
+ push.0.0.0
122
+ # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]
123
+
124
+ padw swapw
125
+ # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
126
+
127
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
128
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
129
+
130
+ exec.native_account::set_map_item
131
+ # => [OLD_MAP_VALUE, i-1, new_num_of_approvers]
132
+
133
+ dropw
134
+ # => [i-1, new_num_of_approvers]
135
+
136
+ dup.1 dup.1
137
+ u32lt
138
+ # => [should_loop, i-1, new_num_of_approvers]
139
+ end
140
+
141
+ drop drop
142
+ # => []
143
+ end
144
+
145
+ proc get_threshold_and_num_approvers
146
+ push.THRESHOLD_CONFIG_SLOT[0..2]
147
+ exec.active_account::get_initial_item
148
+ # => [0, 0, num_approvers, threshold]
149
+
150
+ drop drop
151
+ # => [num_approvers, threshold]
152
+
153
+ swap
154
+ # => [threshold, num_approvers]
155
+ end
156
+
157
+ proc assert_proc_thresholds_lte_num_approvers(num_approvers: u32)
158
+ exec.active_account::get_num_procedures
159
+ # => [num_procedures, num_approvers]
160
+
161
+ dup neq.0
162
+ # => [should_continue, num_procedures, num_approvers]
163
+ while.true
164
+ sub.1 dup
165
+ # => [proc_index, proc_index, num_approvers]
166
+
167
+ exec.active_account::get_procedure_root
168
+ # => [PROC_ROOT, proc_index, num_approvers]
169
+
170
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
171
+ # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, proc_index, num_approvers]
172
+
173
+ exec.active_account::get_initial_map_item
174
+ # => [[0, 0, 0, proc_threshold], proc_index, num_approvers]
175
+
176
+ drop drop drop
177
+ # => [proc_threshold, proc_index, num_approvers]
178
+
179
+ dup.2
180
+ # => [num_approvers, proc_threshold, proc_index, num_approvers]
181
+
182
+ u32assert2.err=ERR_PROC_THRESHOLD_NOT_U32
183
+ u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS
184
+ # => [proc_index, num_approvers]
185
+
186
+ dup neq.0
187
+ # => [should_continue, proc_index, num_approvers]
188
+ end
189
+
190
+ drop drop
191
+ # => []
192
+ end
193
+
194
+ # Internal storage helper used by the public \`update_procedure_threshold\`
195
+ # entrypoint after it receives direct stack arguments.
196
+ proc set_procedure_threshold(proc_threshold: u32, proc_root: BeWord)
197
+ exec.get_threshold_and_num_approvers
198
+ # => [threshold, num_approvers, proc_threshold, PROC_ROOT]
199
+
200
+ drop
201
+ # => [num_approvers, proc_threshold, PROC_ROOT]
202
+
203
+ dup.1 swap
204
+ # => [num_approvers, proc_threshold, proc_threshold, PROC_ROOT]
205
+
206
+ u32assert2.err=ERR_NUM_APPROVERS_OR_PROC_THRESHOLD_NOT_U32
207
+ u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS
208
+ # => [proc_threshold, PROC_ROOT]
209
+
210
+ push.0.0.0
211
+ swapw
212
+ # => [PROC_ROOT, PROC_THRESHOLD_WORD]
213
+
214
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
215
+ # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, PROC_THRESHOLD_WORD]
216
+
217
+ exec.native_account::set_map_item
218
+ # => [OLD_VALUE]
219
+
220
+ dropw
221
+ # => []
222
+ end
223
+
224
+ #! Update threshold config and add / remove approvers
225
+ #!
226
+ #! Inputs:
227
+ #! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]
228
+ #! Advice map: {
229
+ #! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]
230
+ #! }
231
+ #! Outputs:
232
+ #! Operand stack: [TX_SUMMARY_COMMITMENT]
233
+ #!
234
+ #! Where:
235
+ #! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector
236
+ #! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]
237
+ #! - PUB_KEY_i is the public key of the i-th signer
238
+ #!
239
+ #! Locals:
240
+ #! 0: new_num_of_approvers
241
+ #! 1: init_num_of_approvers
242
+ @locals(2)
243
+ pub proc update_signers_and_threshold(multisig_config_hash: BeWord)
244
+ adv.push_mapval
245
+ # => [MULTISIG_CONFIG_HASH, pad(12)]
246
+
247
+ adv_loadw
248
+ # => [MULTISIG_CONFIG, pad(12)]
249
+
250
+ # store new_num_of_approvers for later
251
+ dup.2 loc_store.0
252
+ # => [MULTISIG_CONFIG, pad(12)]
253
+
254
+ dup.3 dup.3
255
+ # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
256
+
257
+ # make sure that the threshold is smaller than the number of approvers
258
+ u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG
259
+ u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG
260
+ # => [MULTISIG_CONFIG, pad(12)]
261
+
262
+ dup.3 dup.3
263
+ # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
264
+
265
+ # make sure that threshold or num_approvers are not zero
266
+ eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
267
+ eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
268
+ # => [MULTISIG_CONFIG, pad(12)]
269
+
270
+ loc_load.0
271
+ # => [num_approvers, MULTISIG_CONFIG, pad(12)]
272
+
273
+ exec.assert_proc_thresholds_lte_num_approvers
274
+ # => [MULTISIG_CONFIG, pad(12)]
275
+
276
+ push.THRESHOLD_CONFIG_SLOT[0..2]
277
+ # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)]
278
+
279
+ exec.native_account::set_item
280
+ # => [OLD_THRESHOLD_CONFIG, pad(12)]
281
+
282
+ # store init_num_of_approvers for later
283
+ drop drop loc_store.1 drop
284
+ # => [pad(12)]
285
+
286
+ loc_load.0
287
+ # => [num_approvers]
288
+
289
+ dup neq.0
290
+ while.true
291
+ sub.1
292
+ # => [i-1, pad(12)]
293
+
294
+ dup push.0.0.0
295
+ # => [[0, 0, 0, i-1], i-1, pad(12)]
296
+
297
+ padw adv_loadw
298
+ # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]
299
+
300
+ swapw
301
+ # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
302
+
303
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
304
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
305
+
306
+ exec.native_account::set_map_item
307
+ # => [OLD_VALUE, i-1, pad(12)]
308
+
309
+ dropw
310
+ # => [i-1, pad(12)]
311
+
312
+ dup neq.0
313
+ # => [is_non_zero, i-1, pad(12)]
314
+ end
315
+ # => [pad(13)]
316
+
317
+ drop
318
+ # => [pad(12)]
319
+
320
+ # compare initial vs current multisig config
321
+
322
+ # load init_num_of_approvers & new_num_of_approvers
323
+ loc_load.0 loc_load.1
324
+ # => [init_num_of_approvers, new_num_of_approvers, pad(12)]
325
+
326
+ exec.cleanup_pubkey_mapping
327
+ # => [pad(12)]
328
+ end
329
+
330
+ #! Update or clear the threshold override for a specific procedure.
331
+ #!
332
+ #! Inputs:
333
+ #! Operand stack: [proc_threshold, PROC_ROOT]
334
+ #! Outputs:
335
+ #! Operand stack: []
336
+ #! Invocation: call
337
+ pub proc update_procedure_threshold(proc_threshold: u32, proc_root: BeWord)
338
+ exec.set_procedure_threshold
339
+ end
340
+
341
+ # Computes the effective transaction threshold based on called procedures and per-procedure
342
+ # overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no
343
+ # overrides apply.
344
+ #
345
+ #! Inputs: [default_threshold]
346
+ #! Outputs: [transaction_threshold]
347
+ @locals(1)
348
+ proc compute_transaction_threshold(default_threshold: u32) -> u32
349
+ # 1. initialize transaction_threshold = 0
350
+ # 2. iterate through all account procedures
351
+ # a. check if the procedure was called during the transaction
352
+ # b. if called, get the override threshold of that procedure from the config map
353
+ # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold
354
+ # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
355
+
356
+ # store default_threshold for later
357
+ loc_store.0
358
+ # => []
359
+
360
+ # 1. initialize transaction_threshold = 0
361
+ push.0
362
+ # => [transaction_threshold]
363
+
364
+ # get the number of account procedures
365
+ exec.active_account::get_num_procedures
366
+ # => [num_procedures, transaction_threshold]
367
+
368
+ # 2. iterate through all account procedures
369
+ dup neq.0
370
+ # => [should_continue, num_procedures, transaction_threshold]
371
+ while.true
372
+ sub.1 dup
373
+ # => [num_procedures-1, num_procedures-1, transaction_threshold]
374
+
375
+ # get procedure root of the procedure with index i
376
+ exec.active_account::get_procedure_root dupw
377
+ # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]
378
+
379
+ # 2a. check if this procedure has been called in the transaction
380
+ exec.native_account::was_procedure_called
381
+ # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]
382
+
383
+ # if it has been called, get the override threshold of that procedure
384
+ if.true
385
+ # => [PROC_ROOT, num_procedures-1, transaction_threshold]
386
+
387
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
388
+ # => [PROC_THRESHOLD_ROOTS_SLOT_prefix, PROC_THRESHOLD_ROOTS_SLOT_suffix, PROC_ROOT, num_procedures-1, transaction_threshold]
389
+
390
+ # 2b. get the override proc_threshold of that procedure
391
+ # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]
392
+ exec.active_account::get_initial_map_item
393
+ # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]
394
+
395
+ drop drop drop dup dup.3
396
+ # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]
397
+
398
+ u32assert2.err="transaction threshold or procedure threshold are not u32"
399
+ u32gt
400
+ # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]
401
+ # 2c. if proc_threshold > transaction_threshold, update transaction_threshold
402
+ movup.2 movdn.3
403
+ # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]
404
+ cdrop
405
+ # => [updated_transaction_threshold, num_procedures-1]
406
+ swap
407
+ # => [num_procedures-1, updated_transaction_threshold]
408
+ # if it has not been called during this transaction, nothing to do, move to the next procedure
409
+ else
410
+ dropw
411
+ # => [num_procedures-1, transaction_threshold]
412
+ end
413
+
414
+ dup neq.0
415
+ # => [should_continue, num_procedures-1, transaction_threshold]
416
+ end
417
+
418
+ drop
419
+ # => [transaction_threshold]
420
+
421
+ loc_load.0
422
+ # => [default_threshold, transaction_threshold]
423
+
424
+ # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
425
+ dup.1 eq.0
426
+ # => [is_zero, default_threshold, transaction_threshold]
427
+
428
+ cdrop
429
+ # => [effective_transaction_threshold]
430
+ end
431
+
432
+ #! Authenticate a transaction using the Falcon signature scheme with multi-signature support.
433
+ #!
434
+ #! This procedure implements multi-signature authentication by:
435
+ #! 1. Computing the transaction summary message that needs to be signed
436
+ #! 2. Verifying signatures from multiple required signers against their public keys
437
+ #! 3. Ensuring the minimum threshold of valid signatures is met
438
+ #! 4. Returning the transaction summary commitment for higher-level wrappers.
439
+ #!
440
+ #! Inputs:
441
+ #! Operand stack: [SALT]
442
+ #! Advice map: {
443
+ #! h(SIG_0, MSG): SIG_0,
444
+ #! h(SIG_1, MSG): SIG_1,
445
+ #! h(SIG_n, MSG): SIG_n
446
+ #! }
447
+ #! Outputs:
448
+ #! Operand stack: []
449
+ #!
450
+ #! Where:
451
+ #! - SALT is a cryptographically random nonce that enables multiple concurrent
452
+ #! multisig transactions while maintaining replay protection. Each transaction
453
+ #! must use a unique SALT value to ensure transaction uniqueness.
454
+ #! - SIG_i is the signature from the i-th signer.
455
+ #! - MSG is the transaction message being signed.
456
+ #! - h(SIG_i, MSG) is the hash of the signature and message used as the advice map key.
457
+ #!
458
+ #! Panics if:
459
+ #! - insufficient number of valid signatures (below threshold).
460
+ #! - insufficient number of valid signatures (below threshold).
461
+ #!
462
+ #! Invocation: exec
463
+ @locals(1)
464
+ pub proc auth_tx(salt: BeWord) -> BeWord
465
+ exec.native_account::incr_nonce drop
466
+ # => [SALT]
467
+
468
+ # ------ Computing transaction summary ------
469
+
470
+ exec.auth::create_tx_summary
471
+ # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
472
+
473
+ # to build a tx_summary in the host, we need these four words in the advice provider
474
+ exec.auth::adv_insert_hqword
475
+ # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
476
+
477
+ # the commitment to the tx summary is the message that is signed
478
+ exec.auth::hash_tx_summary
479
+ # => [TX_SUMMARY_COMMITMENT]
480
+
481
+ # ------ Verifying approver signatures ------
482
+
483
+ push.THRESHOLD_CONFIG_SLOT[0..2]
484
+ # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT]
485
+
486
+ exec.active_account::get_initial_item
487
+ # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
488
+
489
+ drop drop
490
+ # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
491
+
492
+ swap movdn.5
493
+ # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
494
+
495
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
496
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
497
+
498
+ exec.::miden::standards::auth::falcon512_rpo::verify_signatures
499
+ # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]
500
+
501
+ # ------ Checking threshold is >= num_verified_signatures ------
502
+
503
+ movup.5
504
+ # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
505
+
506
+ exec.compute_transaction_threshold
507
+ # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
508
+
509
+ u32assert2 u32lt
510
+ # => [is_unauthorized, TX_SUMMARY_COMMITMENT]
511
+
512
+ # If signatures are non-existent the tx will fail here.
513
+ if.true
514
+ emit.AUTH_UNAUTHORIZED_EVENT
515
+ push.0 assert.err="insufficient number of signatures"
516
+ end
517
+
518
+ # => [TX_SUMMARY_COMMITMENT]
519
+ end
520
+ `;
521
+ export const MULTISIG_ECDSA_MASM = `# Multi-Signature ECDSA secp256k1 Authentication Core Library
522
+ #
523
+ # This library provides the reusable multisig procedures used by account wrappers.
524
+
525
+ use miden::protocol::active_account
526
+ use miden::protocol::native_account
527
+ use miden::standards::auth
528
+ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
529
+
530
+ # CONSTANTS
531
+ # =================================================================================================
532
+
533
+ # Auth Request Constants
534
+
535
+ # The event emitted when a signature is not found for a required signer.
536
+ const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
537
+
538
+ # Storage Layout Constants
539
+ #
540
+ # +-------------------------------+----------+--------------+-------------------+
541
+ # | THRESHOLD & APPROVERS CONFIG | PUB KEYS | EXECUTED TXS | PROC THRESHOLDS |
542
+ # | (slot) | (map) | (map) | (map) |
543
+ # +-------------------------------+----------+--------------+-------------------+
544
+ # | 0 | 1 | 2 | 3 |
545
+ # +-------------------------------+----------+--------------+-------------------+
546
+
547
+ # The slot in this component's storage layout where the default signature threshold and
548
+ # number of approvers are stored as:
549
+ # [default_threshold, num_approvers, 0, 0].
550
+ # The threshold is guaranteed to be less than or equal to num_approvers.
551
+ const THRESHOLD_CONFIG_SLOT=word("openzeppelin::multisig::threshold_config")
552
+
553
+ # The slot in this component's storage layout where the public keys map is stored.
554
+ # Map entries: [key_index, 0, 0, 0] => APPROVER_PUBLIC_KEY
555
+ const PUBLIC_KEYS_MAP_SLOT=word("openzeppelin::multisig::signer_public_keys")
556
+
557
+ # The slot in this component's storage layout where executed transactions are stored.
558
+ # Map entries: transaction_message => [is_executed, 0, 0, 0]
559
+ const EXECUTED_TXS_SLOT=word("openzeppelin::multisig::executed_transactions")
560
+
561
+ # The slot in this component's storage layout where procedure thresholds are stored.
562
+ # Map entries: PROC_ROOT => [proc_threshold, 0, 0, 0]
563
+ const PROC_THRESHOLD_ROOTS_SLOT=word("openzeppelin::multisig::procedure_thresholds")
564
+
565
+ # Executed Transaction Flag Constant
566
+ const IS_EXECUTED_FLAG=[1, 0, 0, 0]
567
+
568
+ # ERRORS
569
+ const ERR_TX_ALREADY_EXECUTED="failed to approve multisig transaction as it was already executed"
570
+
571
+ const ERR_MALFORMED_MULTISIG_CONFIG="number of approvers must be equal to or greater than threshold"
572
+
573
+ const ERR_ZERO_IN_MULTISIG_CONFIG="number of approvers or threshold must not be zero"
574
+
575
+ const ERR_PROC_THRESHOLD_NOT_U32="procedure threshold must be a u32 value"
576
+
577
+ const ERR_NUM_APPROVERS_OR_PROC_THRESHOLD_NOT_U32="number of approvers and procedure threshold must be u32"
578
+
579
+ const ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS="procedure threshold exceeds number of approvers"
580
+
581
+ # MULTISIG PROCEDURES
582
+ # =================================================================================================
583
+
584
+ #! Check if transaction has already been executed and add it to executed transactions for replay protection.
585
+ #!
586
+ #! Inputs: [MSG]
587
+ #! Outputs: []
588
+ #!
589
+ #! Panics if:
590
+ #! - the same transaction has already been executed
591
+ pub proc assert_new_tx(msg: BeWord)
592
+ push.IS_EXECUTED_FLAG
593
+ # => [[0, 0, 0, is_executed], MSG]
594
+
595
+ swapw
596
+ # => [MSG, IS_EXECUTED_FLAG]
597
+
598
+ push.EXECUTED_TXS_SLOT[0..2]
599
+ # => [txs_slot_prefix, txs_slot_suffix, MSG, IS_EXECUTED_FLAG]
600
+
601
+ # Set the key value pair in the map to mark transaction as executed
602
+ exec.native_account::set_map_item
603
+ # => [[0, 0, 0, is_executed]]
604
+
605
+ drop drop drop
606
+ # => [is_executed]
607
+
608
+ assertz.err=ERR_TX_ALREADY_EXECUTED
609
+ # => []
610
+ end
611
+
612
+ #! Remove old approver public keys from the approver public key mapping.
613
+ #!
614
+ #! This procedure cleans up the storage by removing public keys of approvers that are no longer
615
+ #! part of the multisig configuration. This procedure assumes that init_num_of_approvers and
616
+ #! new_num_of_approvers are u32 values.
617
+ #!
618
+ #! Inputs: [init_num_of_approvers, new_num_of_approvers]
619
+ #! Outputs: []
620
+ #!
621
+ #! Where:
622
+ #! - init_num_of_approvers is the original number of approvers before the update
623
+ #! - new_num_of_approvers is the new number of approvers after the update
624
+ proc cleanup_pubkey_mapping(init_num_of_approvers: u32, new_num_of_approvers: u32)
625
+ dup.1 dup.1
626
+ u32assert2 u32lt
627
+ # => [should_loop, i = init_num_of_approvers, new_num_of_approvers]
628
+
629
+ while.true
630
+ # => [i, new_num_of_approvers]
631
+
632
+ sub.1
633
+ # => [i-1, new_num_of_approvers]
634
+
635
+ dup
636
+ # => [i-1, i-1, new_num_of_approvers]
637
+
638
+ push.0.0.0
639
+ # => [[0, 0, 0, i-1], i-1, new_num_of_approvers]
640
+
641
+ padw swapw
642
+ # => [[0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
643
+
644
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
645
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], EMPTY_WORD, i-1, new_num_of_approvers]
646
+
647
+ exec.native_account::set_map_item
648
+ # => [OLD_MAP_VALUE, i-1, new_num_of_approvers]
649
+
650
+ dropw
651
+ # => [i-1, new_num_of_approvers]
652
+
653
+ dup.1 dup.1
654
+ u32lt
655
+ # => [should_loop, i-1, new_num_of_approvers]
656
+ end
657
+
658
+ drop drop
659
+ # => []
660
+ end
661
+
662
+ proc get_threshold_and_num_approvers
663
+ push.THRESHOLD_CONFIG_SLOT[0..2]
664
+ exec.active_account::get_initial_item
665
+ # => [0, 0, num_approvers, threshold]
666
+
667
+ drop drop
668
+ # => [num_approvers, threshold]
669
+
670
+ swap
671
+ # => [threshold, num_approvers]
672
+ end
673
+
674
+ proc assert_proc_thresholds_lte_num_approvers(num_approvers: u32)
675
+ exec.active_account::get_num_procedures
676
+ # => [num_procedures, num_approvers]
677
+
678
+ dup neq.0
679
+ # => [should_continue, num_procedures, num_approvers]
680
+ while.true
681
+ sub.1 dup
682
+ # => [proc_index, proc_index, num_approvers]
683
+
684
+ exec.active_account::get_procedure_root
685
+ # => [PROC_ROOT, proc_index, num_approvers]
686
+
687
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
688
+ # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, proc_index, num_approvers]
689
+
690
+ exec.active_account::get_initial_map_item
691
+ # => [[0, 0, 0, proc_threshold], proc_index, num_approvers]
692
+
693
+ drop drop drop
694
+ # => [proc_threshold, proc_index, num_approvers]
695
+
696
+ dup.2
697
+ # => [num_approvers, proc_threshold, proc_index, num_approvers]
698
+
699
+ u32assert2.err=ERR_PROC_THRESHOLD_NOT_U32
700
+ u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS
701
+ # => [proc_index, num_approvers]
702
+
703
+ dup neq.0
704
+ # => [should_continue, proc_index, num_approvers]
705
+ end
706
+
707
+ drop drop
708
+ # => []
709
+ end
710
+
711
+ # Internal storage helper used by the public \`update_procedure_threshold\`
712
+ # entrypoint after it receives direct stack arguments.
713
+ proc set_procedure_threshold(proc_threshold: u32, proc_root: BeWord)
714
+ exec.get_threshold_and_num_approvers
715
+ # => [threshold, num_approvers, proc_threshold, PROC_ROOT]
716
+
717
+ drop
718
+ # => [num_approvers, proc_threshold, PROC_ROOT]
719
+
720
+ dup.1 swap
721
+ # => [num_approvers, proc_threshold, proc_threshold, PROC_ROOT]
722
+
723
+ u32assert2.err=ERR_NUM_APPROVERS_OR_PROC_THRESHOLD_NOT_U32
724
+ u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS
725
+ # => [proc_threshold, PROC_ROOT]
726
+
727
+ push.0.0.0
728
+ swapw
729
+ # => [PROC_ROOT, PROC_THRESHOLD_WORD]
730
+
731
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
732
+ # => [proc_roots_slot_prefix, proc_roots_slot_suffix, PROC_ROOT, PROC_THRESHOLD_WORD]
733
+
734
+ exec.native_account::set_map_item
735
+ # => [OLD_VALUE]
736
+
737
+ dropw
738
+ # => []
739
+ end
740
+
741
+ #! Update threshold config and add / remove approvers
742
+ #!
743
+ #! Inputs:
744
+ #! Operand stack: [MULTISIG_CONFIG_HASH, pad(12)]
745
+ #! Advice map: {
746
+ #! MULTISIG_CONFIG_HASH => [CONFIG, PUB_KEY_N, PUB_KEY_N-1, ..., PUB_KEY_0]
747
+ #! }
748
+ #! Outputs:
749
+ #! Operand stack: []
750
+ #!
751
+ #! Where:
752
+ #! - MULTISIG_CONFIG_HASH is the hash of the threshold and new public key vector
753
+ #! - MULTISIG_CONFIG is [threshold, num_approvers, 0, 0]
754
+ #! - PUB_KEY_i is the public key of the i-th signer
755
+ #!
756
+ #! Locals:
757
+ #! 0: new_num_of_approvers
758
+ #! 1: init_num_of_approvers
759
+ @locals(2)
760
+ pub proc update_signers_and_threshold(multisig_config_hash: BeWord)
761
+ adv.push_mapval
762
+ # => [MULTISIG_CONFIG_HASH, pad(12)]
763
+
764
+ adv_loadw
765
+ # => [MULTISIG_CONFIG, pad(12)]
766
+
767
+ # store new_num_of_approvers for later
768
+ dup.2 loc_store.0
769
+ # => [MULTISIG_CONFIG, pad(12)]
770
+
771
+ dup.3 dup.3
772
+ # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
773
+
774
+ # make sure that the threshold is smaller than the number of approvers
775
+ u32assert2.err=ERR_MALFORMED_MULTISIG_CONFIG
776
+ u32gt assertz.err=ERR_MALFORMED_MULTISIG_CONFIG
777
+ # => [MULTISIG_CONFIG, pad(12)]
778
+
779
+ dup.3 dup.3
780
+ # => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
781
+
782
+ # make sure that threshold or num_approvers are not zero
783
+ eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
784
+ eq.0 assertz.err=ERR_ZERO_IN_MULTISIG_CONFIG
785
+ # => [MULTISIG_CONFIG, pad(12)]
786
+
787
+ loc_load.0
788
+ # => [num_approvers, MULTISIG_CONFIG, pad(12)]
789
+
790
+ exec.assert_proc_thresholds_lte_num_approvers
791
+ # => [MULTISIG_CONFIG, pad(12)]
792
+
793
+ push.THRESHOLD_CONFIG_SLOT[0..2]
794
+ # => [config_slot_prefix, config_slot_suffix, MULTISIG_CONFIG, pad(12)]
795
+
796
+ exec.native_account::set_item
797
+ # => [OLD_THRESHOLD_CONFIG, pad(12)]
798
+
799
+ # store init_num_of_approvers for later
800
+ drop drop loc_store.1 drop
801
+ # => [pad(12)]
802
+
803
+ loc_load.0
804
+ # => [num_approvers]
805
+
806
+ dup neq.0
807
+ while.true
808
+ sub.1
809
+ # => [i-1, pad(12)]
810
+
811
+ dup push.0.0.0
812
+ # => [[0, 0, 0, i-1], i-1, pad(12)]
813
+
814
+ padw adv_loadw
815
+ # => [PUB_KEY, [0, 0, 0, i-1], i-1, pad(12)]
816
+
817
+ swapw
818
+ # => [[0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
819
+
820
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
821
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, [0, 0, 0, i-1], PUB_KEY, i-1, pad(12)]
822
+
823
+ exec.native_account::set_map_item
824
+ # => [OLD_VALUE, i-1, pad(12)]
825
+
826
+ dropw
827
+ # => [i-1, pad(12)]
828
+
829
+ dup neq.0
830
+ # => [is_non_zero, i-1, pad(12)]
831
+ end
832
+ # => [pad(13)]
833
+
834
+ drop
835
+ # => [pad(12)]
836
+
837
+ # compare initial vs current multisig config
838
+
839
+ # load init_num_of_approvers & new_num_of_approvers
840
+ loc_load.0 loc_load.1
841
+ # => [init_num_of_approvers, new_num_of_approvers, pad(12)]
842
+
843
+ exec.cleanup_pubkey_mapping
844
+ # => [pad(12)]
845
+ end
846
+
847
+ #! Update or clear the threshold override for a specific procedure.
848
+ #!
849
+ #! Inputs:
850
+ #! Operand stack: [proc_threshold, PROC_ROOT]
851
+ #! Outputs:
852
+ #! Operand stack: []
853
+ #! Invocation: call
854
+ pub proc update_procedure_threshold(proc_threshold: u32, proc_root: BeWord)
855
+ exec.set_procedure_threshold
856
+ end
857
+
858
+ # Computes the effective transaction threshold based on called procedures and per-procedure
859
+ # overrides stored in PROC_THRESHOLD_ROOTS_SLOT. Falls back to default_threshold if no
860
+ # overrides apply.
861
+ #
862
+ #! Inputs: [default_threshold]
863
+ #! Outputs: [transaction_threshold]
864
+ @locals(1)
865
+ proc compute_transaction_threshold(default_threshold: u32) -> u32
866
+ # 1. initialize transaction_threshold = 0
867
+ # 2. iterate through all account procedures
868
+ # a. check if the procedure was called during the transaction
869
+ # b. if called, get the override threshold of that procedure from the config map
870
+ # c. if proc_threshold > transaction_threshold, set transaction_threshold = proc_threshold
871
+ # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
872
+
873
+ # store default_threshold for later
874
+ loc_store.0
875
+ # => []
876
+
877
+ # 1. initialize transaction_threshold = 0
878
+ push.0
879
+ # => [transaction_threshold]
880
+
881
+ # get the number of account procedures
882
+ exec.active_account::get_num_procedures
883
+ # => [num_procedures, transaction_threshold]
884
+
885
+ # 2. iterate through all account procedures
886
+ dup neq.0
887
+ # => [should_continue, num_procedures, transaction_threshold]
888
+ while.true
889
+ sub.1 dup
890
+ # => [num_procedures-1, num_procedures-1, transaction_threshold]
891
+
892
+ # get procedure root of the procedure with index i
893
+ exec.active_account::get_procedure_root dupw
894
+ # => [PROC_ROOT, PROC_ROOT, num_procedures-1, transaction_threshold]
895
+
896
+ # 2a. check if this procedure has been called in the transaction
897
+ exec.native_account::was_procedure_called
898
+ # => [was_called, PROC_ROOT, num_procedures-1, transaction_threshold]
899
+
900
+ # if it has been called, get the override threshold of that procedure
901
+ if.true
902
+ # => [PROC_ROOT, num_procedures-1, transaction_threshold]
903
+
904
+ push.PROC_THRESHOLD_ROOTS_SLOT[0..2]
905
+ # => [PROC_THRESHOLD_ROOTS_SLOT_prefix, PROC_THRESHOLD_ROOTS_SLOT_suffix, PROC_ROOT, num_procedures-1, transaction_threshold]
906
+
907
+ # 2b. get the override proc_threshold of that procedure
908
+ # if the procedure has no override threshold, the returned map item will be [0, 0, 0, 0]
909
+ exec.active_account::get_initial_map_item
910
+ # => [[0, 0, 0, proc_threshold], num_procedures-1, transaction_threshold]
911
+
912
+ drop drop drop dup dup.3
913
+ # => [transaction_threshold, proc_threshold, proc_threshold, num_procedures-1, transaction_threshold]
914
+
915
+ u32assert2.err="transaction threshold or procedure threshold are not u32"
916
+ u32gt
917
+ # => [is_gt, proc_threshold, num_procedures-1, transaction_threshold]
918
+ # 2c. if proc_threshold > transaction_threshold, update transaction_threshold
919
+ movup.2 movdn.3
920
+ # => [is_gt, proc_threshold, transaction_threshold, num_procedures-1]
921
+ cdrop
922
+ # => [updated_transaction_threshold, num_procedures-1]
923
+ swap
924
+ # => [num_procedures-1, updated_transaction_threshold]
925
+ # if it has not been called during this transaction, nothing to do, move to the next procedure
926
+ else
927
+ dropw
928
+ # => [num_procedures-1, transaction_threshold]
929
+ end
930
+
931
+ dup neq.0
932
+ # => [should_continue, num_procedures-1, transaction_threshold]
933
+ end
934
+
935
+ drop
936
+ # => [transaction_threshold]
937
+
938
+ loc_load.0
939
+ # => [default_threshold, transaction_threshold]
940
+
941
+ # 3. if transaction_threshold == 0 at the end, revert to using default_threshold
942
+ dup.1 eq.0
943
+ # => [is_zero, default_threshold, transaction_threshold]
944
+
945
+ cdrop
946
+ # => [effective_transaction_threshold]
947
+ end
948
+
949
+ #! Authenticate a transaction using ECDSA secp256k1 with multi-signature support.
950
+ #!
951
+ #! This procedure implements multi-signature authentication by:
952
+ #! 1. Computing the transaction summary message that needs to be signed
953
+ #! 2. Verifying ECDSA signatures from multiple required signers against their public keys
954
+ #! 3. Ensuring the minimum threshold of valid signatures is met
955
+ #! 4. Returning the transaction summary commitment for higher-level wrappers.
956
+ #!
957
+ #! Inputs:
958
+ #! Operand stack: [SALT]
959
+ #! Advice map: {
960
+ #! h(PUB_KEY_0, MSG): SIG_0,
961
+ #! h(PUB_KEY_1, MSG): SIG_1,
962
+ #! h(PUB_KEY_n, MSG): SIG_n
963
+ #! }
964
+ #! Outputs:
965
+ #! Operand stack: [TX_SUMMARY_COMMITMENT]
966
+ #!
967
+ #! Where:
968
+ #! - SALT is a cryptographically random nonce that enables multiple concurrent
969
+ #! multisig transactions while maintaining replay protection. Each transaction
970
+ #! must use a unique SALT value to ensure transaction uniqueness.
971
+ #! - SIG_i is the ECDSA signature from the i-th signer.
972
+ #! - MSG is the transaction message being signed.
973
+ #! - h(PUB_KEY_i, MSG) is the hash of the public key and message used as the advice map key.
974
+ #!
975
+ #! Panics if:
976
+ #! - insufficient number of valid signatures (below threshold).
977
+ #! - insufficient number of valid signatures (below threshold).
978
+ #!
979
+ #! Invocation: exec
980
+ @locals(1)
981
+ pub proc auth_tx(salt: BeWord) -> BeWord
982
+ exec.native_account::incr_nonce drop
983
+ # => [SALT]
984
+
985
+ # ------ Computing transaction summary ------
986
+
987
+ exec.auth::create_tx_summary
988
+ # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
989
+
990
+ # to build a tx_summary in the host, we need these four words in the advice provider
991
+ exec.auth::adv_insert_hqword
992
+ # => [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT]
993
+
994
+ # the commitment to the tx summary is the message that is signed
995
+ exec.auth::hash_tx_summary
996
+ # => [TX_SUMMARY_COMMITMENT]
997
+
998
+ # ------ Verifying approver signatures ------
999
+
1000
+ push.THRESHOLD_CONFIG_SLOT[0..2]
1001
+ # => [config_slot_prefix, config_slot_suffix, TX_SUMMARY_COMMITMENT]
1002
+
1003
+ exec.active_account::get_initial_item
1004
+ # => [0, 0, num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
1005
+
1006
+ drop drop
1007
+ # => [num_of_approvers, default_threshold, TX_SUMMARY_COMMITMENT]
1008
+
1009
+ swap movdn.5
1010
+ # => [num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
1011
+
1012
+ push.PUBLIC_KEYS_MAP_SLOT[0..2]
1013
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, num_of_approvers, TX_SUMMARY_COMMITMENT, default_threshold]
1014
+
1015
+ exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures
1016
+ # => [num_verified_signatures, TX_SUMMARY_COMMITMENT, default_threshold]
1017
+
1018
+ # ------ Checking threshold is >= num_verified_signatures ------
1019
+
1020
+ movup.5
1021
+ # => [default_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
1022
+
1023
+ exec.compute_transaction_threshold
1024
+ # => [transaction_threshold, num_verified_signatures, TX_SUMMARY_COMMITMENT]
1025
+
1026
+ u32assert2 u32lt
1027
+ # => [is_unauthorized, TX_SUMMARY_COMMITMENT]
1028
+
1029
+ # If signatures are non-existent the tx will fail here.
1030
+ if.true
1031
+ emit.AUTH_UNAUTHORIZED_EVENT
1032
+ push.0 assert.err="insufficient number of signatures"
1033
+ end
1034
+
1035
+ # => [TX_SUMMARY_COMMITMENT]
1036
+ end
1037
+ `;
1038
+ export const GUARDIAN_MASM = `# Guardian Authentication Component
1039
+ #
1040
+ # This component provides GUARDIAN signature verification for accounts.
1041
+ # It can be used standalone or in conjunction with other auth components like multisig.
1042
+
1043
+ use miden::protocol::active_account
1044
+ use miden::protocol::native_account
1045
+
1046
+ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
1047
+
1048
+ # IMPORTANT SECURITY NOTES
1049
+ # --------------------------------------------------------------------------------
1050
+ # - The selector in \`GUARDIAN_SELECTOR_SLOT\` controls whether the extra GUARDIAN signature
1051
+ # is enforced:
1052
+ # * GUARDIAN_ON => exactly one valid GUARDIAN signature is required.
1053
+ # * GUARDIAN_OFF => GUARDIAN signature is skipped for that call.
1054
+ #
1055
+ # - \`verify_guardian_signature\` reads the selector from initial storage state.
1056
+ # This means changes made during the same transaction won't affect the check.
1057
+ #
1058
+ # - \`enable_guardian\` / \`disable_guardian\` procedures allow explicit control over GUARDIAN state.
1059
+ #
1060
+ # - \`update_guardian_public_key\`:
1061
+ # * Installs a new GUARDIAN public key in the map at \`GUARDIAN_PUBLIC_KEY_MAP_SLOT\`.
1062
+ # * Does not itself perform any signature checks.
1063
+ # * To update the key without requiring GUARDIAN signature, ensure selector is OFF.
1064
+ #
1065
+ # Storage Layout
1066
+ # --------------------------------------------------------------------------------
1067
+ #
1068
+ # +---------------------+---------------+
1069
+ # | DESCRIPTION | SLOT |
1070
+ # +---------------------+---------------+
1071
+ # | GUARDIAN SELECTOR (word) | 0 |
1072
+ # | GUARDIAN PUBLIC KEY MAP | 1 |
1073
+ # +---------------------+---------------+
1074
+ #
1075
+ # - GUARDIAN_SELECTOR_SLOT (0):
1076
+ # * Stores a word that is compared against [1, 0, 0, 0] (GUARDIAN_ON).
1077
+ # * Any value != GUARDIAN_ON is treated as GUARDIAN_OFF.
1078
+ #
1079
+ # - GUARDIAN_PUBLIC_KEY_MAP_SLOT (1):
1080
+ # * A map from a fixed key [0, 0, 0, 0] to the single GUARDIAN public key:
1081
+ # [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1082
+ # * GUARDIAN_PUBLIC_KEY is a RPO Falcon 512 public key represented as a word.
1083
+
1084
+ # CONSTANTS
1085
+ # =================================================================================================
1086
+
1087
+ # Slot where the GUARDIAN selector flag is stored (using word() for named slot access):
1088
+ # - GUARDIAN_ON => GUARDIAN signature required
1089
+ # - GUARDIAN_OFF => GUARDIAN signature skipped
1090
+ const GUARDIAN_SELECTOR_SLOT=word("openzeppelin::guardian::selector")
1091
+
1092
+ # Map slot for GUARDIAN public key
1093
+ # Uses exactly one GUARDIAN public key at index [0, 0, 0, 0]
1094
+ # [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1095
+ const GUARDIAN_PUBLIC_KEY_MAP_SLOT=word("openzeppelin::guardian::public_key")
1096
+
1097
+ # Selector flag values
1098
+ const GUARDIAN_ON=[1, 0, 0, 0]
1099
+ const GUARDIAN_OFF=[0, 0, 0, 0]
1100
+
1101
+ # The event emitted when a signature is not found for a required signer.
1102
+ const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
1103
+
1104
+ # GUARDIAN PROCEDURES
1105
+ # =================================================================================================
1106
+
1107
+ #! Enable GUARDIAN verification by setting the selector to ON.
1108
+ #!
1109
+ #! Operand stack inputs: []
1110
+ #! Outputs: []
1111
+ #!
1112
+ #! Notes:
1113
+ #! - Sets GUARDIAN_SELECTOR_SLOT to GUARDIAN_ON (1)
1114
+ #! - After this, transactions will require GUARDIAN signature verification
1115
+ proc enable_guardian
1116
+ push.GUARDIAN_ON
1117
+ # => [GUARDIAN_ON]
1118
+
1119
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1120
+ # => [selector_slot_prefix, selector_slot_suffix, GUARDIAN_ON]
1121
+
1122
+ exec.native_account::set_item
1123
+ # => [OLD_ROOT]
1124
+
1125
+ dropw
1126
+ # => []
1127
+ end
1128
+
1129
+ #! Disable GUARDIAN verification by setting the selector to OFF.
1130
+ #!
1131
+ #! Operand stack inputs: []
1132
+ #! Outputs: []
1133
+ #!
1134
+ #! Notes:
1135
+ #! - Sets GUARDIAN_SELECTOR_SLOT to GUARDIAN_OFF (0)
1136
+ #! - After this, transactions will NOT require GUARDIAN signature verification
1137
+ proc disable_guardian
1138
+ push.GUARDIAN_OFF
1139
+ # => [GUARDIAN_OFF]
1140
+
1141
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1142
+ # => [selector_slot_prefix, selector_slot_suffix, GUARDIAN_OFF]
1143
+
1144
+ exec.native_account::set_item
1145
+ # => [OLD_ROOT]
1146
+
1147
+ dropw
1148
+ # => []
1149
+ end
1150
+
1151
+ #! Update the GUARDIAN public key.
1152
+ #!
1153
+ #! Operand stack inputs: []
1154
+ #! Advice stack inputs: [PUB_KEY]
1155
+ #! - PUB_KEY is the new GUARDIAN RPO Falcon 512 public key
1156
+ #!
1157
+ #! Notes:
1158
+ #! - Stores PUB_KEY into GUARDIAN_PUBLIC_KEY_MAP_SLOT:
1159
+ #! [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1160
+ #! - To update the key without requiring GUARDIAN signature, ensure
1161
+ #! GUARDIAN_SELECTOR_SLOT = 0 (OFF) before calling this.
1162
+ pub proc update_guardian_public_key
1163
+ exec.disable_guardian
1164
+ # ------ Update the GUARDIAN public key ------
1165
+ adv_loadw
1166
+ # => [PUB_KEY]
1167
+
1168
+ push.0.0.0.0
1169
+ # => [MAP_KEY, PUB_KEY]
1170
+ # Note that MAP_KEY is [0, 0, 0, 0] for a single GUARDIAN_KEY
1171
+
1172
+ push.GUARDIAN_PUBLIC_KEY_MAP_SLOT[0..2]
1173
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, MAP_KEY, PUB_KEY]
1174
+
1175
+ exec.native_account::set_map_item
1176
+ # => [OLD_MAP_VALUE]
1177
+
1178
+ dropw
1179
+ # => []
1180
+ end
1181
+
1182
+ #! Conditionally verify a "GUARDIAN" signature against a stored public key hash.
1183
+ #! The condition is controlled by the selector at GUARDIAN_SELECTOR_SLOT.
1184
+ #!
1185
+ #! Inputs: [MSG]
1186
+ #! Outputs: [MSG]
1187
+ #!
1188
+ #! Panics if:
1189
+ #! - Selector is ON but the provided GUARDIAN signature is invalid or missing.
1190
+ #!
1191
+ #! Notes:
1192
+ #! - MSG is TX_SUMMARY_COMMITMENT provided by auth procedure
1193
+ #! - If selector is OFF (0), GUARDIAN verification is skipped
1194
+ #! - Selector value is read from initial storage state
1195
+ pub proc verify_guardian_signature(msg: BeWord) -> BeWord
1196
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1197
+ exec.active_account::get_item
1198
+ drop drop drop
1199
+ # => [selector, MSG]
1200
+
1201
+ push.1 eq
1202
+ if.true
1203
+ push.1
1204
+ push.GUARDIAN_PUBLIC_KEY_MAP_SLOT[0..2]
1205
+ exec.::miden::standards::auth::falcon512_rpo::verify_signatures
1206
+ push.1 neq
1207
+ if.true
1208
+ emit.AUTH_UNAUTHORIZED_EVENT
1209
+ push.0 assert.err="invalid GUARDIAN signature"
1210
+ end
1211
+ end
1212
+ # => [MSG]
1213
+ exec.enable_guardian
1214
+ end
1215
+ `;
1216
+ export const GUARDIAN_ECDSA_MASM = `# Guardian Authentication Component (ECDSA)
1217
+ #
1218
+ # This component provides GUARDIAN signature verification for accounts using
1219
+ # ECDSA secp256k1 (k256/keccak) signatures.
1220
+ # It can be used standalone or in conjunction with other auth components like multisig.
1221
+
1222
+ use miden::protocol::active_account
1223
+ use miden::protocol::native_account
1224
+
1225
+ type BeWord = struct @bigendian { a: felt, b: felt, c: felt, d: felt }
1226
+
1227
+ # IMPORTANT SECURITY NOTES
1228
+ # --------------------------------------------------------------------------------
1229
+ # - The selector in \`GUARDIAN_SELECTOR_SLOT\` controls whether the extra GUARDIAN signature
1230
+ # is enforced:
1231
+ # * GUARDIAN_ON => exactly one valid GUARDIAN signature is required.
1232
+ # * GUARDIAN_OFF => GUARDIAN signature is skipped for that call.
1233
+ #
1234
+ # - \`verify_guardian_signature\` reads the selector from initial storage state.
1235
+ # This means changes made during the same transaction won't affect the check.
1236
+ #
1237
+ # - \`enable_guardian\` / \`disable_guardian\` procedures allow explicit control over GUARDIAN state.
1238
+ #
1239
+ # - \`update_guardian_public_key\`:
1240
+ # * Installs a new GUARDIAN public key in the map at \`GUARDIAN_PUBLIC_KEY_MAP_SLOT\`.
1241
+ # * Does not itself perform any signature checks.
1242
+ # * To update the key without requiring GUARDIAN signature, ensure selector is OFF.
1243
+ #
1244
+ # Storage Layout
1245
+ # --------------------------------------------------------------------------------
1246
+ #
1247
+ # +---------------------+---------------+
1248
+ # | DESCRIPTION | SLOT |
1249
+ # +---------------------+---------------+
1250
+ # | GUARDIAN SELECTOR (word) | 0 |
1251
+ # | GUARDIAN PUBLIC KEY MAP | 1 |
1252
+ # +---------------------+---------------+
1253
+ #
1254
+ # - GUARDIAN_SELECTOR_SLOT (0):
1255
+ # * Stores a word that is compared against [1, 0, 0, 0] (GUARDIAN_ON).
1256
+ # * Any value != GUARDIAN_ON is treated as GUARDIAN_OFF.
1257
+ #
1258
+ # - GUARDIAN_PUBLIC_KEY_MAP_SLOT (1):
1259
+ # * A map from a fixed key [0, 0, 0, 0] to the single GUARDIAN public key:
1260
+ # [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1261
+ # * GUARDIAN_PUBLIC_KEY is an ECDSA secp256k1 public key represented as a word.
1262
+
1263
+ # CONSTANTS
1264
+ # =================================================================================================
1265
+
1266
+ # Slot where the GUARDIAN selector flag is stored (using word() for named slot access):
1267
+ # - GUARDIAN_ON => GUARDIAN signature required
1268
+ # - GUARDIAN_OFF => GUARDIAN signature skipped
1269
+ const GUARDIAN_SELECTOR_SLOT=word("openzeppelin::guardian::selector")
1270
+
1271
+ # Map slot for GUARDIAN public key
1272
+ # Uses exactly one GUARDIAN public key at index [0, 0, 0, 0]
1273
+ # [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1274
+ const GUARDIAN_PUBLIC_KEY_MAP_SLOT=word("openzeppelin::guardian::public_key")
1275
+
1276
+ # Selector flag values
1277
+ const GUARDIAN_ON=[1, 0, 0, 0]
1278
+ const GUARDIAN_OFF=[0, 0, 0, 0]
1279
+
1280
+ # The event emitted when a signature is not found for a required signer.
1281
+ const AUTH_UNAUTHORIZED_EVENT=event("miden::auth::unauthorized")
1282
+
1283
+ # GUARDIAN PROCEDURES
1284
+ # =================================================================================================
1285
+
1286
+ #! Enable GUARDIAN verification by setting the selector to ON.
1287
+ #!
1288
+ #! Operand stack inputs: []
1289
+ #! Outputs: []
1290
+ #!
1291
+ #! Notes:
1292
+ #! - Sets GUARDIAN_SELECTOR_SLOT to GUARDIAN_ON (1)
1293
+ #! - After this, transactions will require GUARDIAN signature verification
1294
+ proc enable_guardian
1295
+ push.GUARDIAN_ON
1296
+ # => [GUARDIAN_ON]
1297
+
1298
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1299
+ # => [selector_slot_prefix, selector_slot_suffix, GUARDIAN_ON]
1300
+
1301
+ exec.native_account::set_item
1302
+ # => [OLD_ROOT]
1303
+
1304
+ dropw
1305
+ # => []
1306
+ end
1307
+
1308
+ #! Disable GUARDIAN verification by setting the selector to OFF.
1309
+ #!
1310
+ #! Operand stack inputs: []
1311
+ #! Outputs: []
1312
+ #!
1313
+ #! Notes:
1314
+ #! - Sets GUARDIAN_SELECTOR_SLOT to GUARDIAN_OFF (0)
1315
+ #! - After this, transactions will NOT require GUARDIAN signature verification
1316
+ proc disable_guardian
1317
+ push.GUARDIAN_OFF
1318
+ # => [GUARDIAN_OFF]
1319
+
1320
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1321
+ # => [selector_slot_prefix, selector_slot_suffix, GUARDIAN_OFF]
1322
+
1323
+ exec.native_account::set_item
1324
+ # => [OLD_ROOT]
1325
+
1326
+ dropw
1327
+ # => []
1328
+ end
1329
+
1330
+ #! Update the GUARDIAN public key.
1331
+ #!
1332
+ #! Operand stack inputs: []
1333
+ #! Advice stack inputs: [PUB_KEY]
1334
+ #! - PUB_KEY is the new GUARDIAN ECDSA secp256k1 public key
1335
+ #!
1336
+ #! Notes:
1337
+ #! - Stores PUB_KEY into GUARDIAN_PUBLIC_KEY_MAP_SLOT:
1338
+ #! [0, 0, 0, 0] => GUARDIAN_PUBLIC_KEY
1339
+ #! - To update the key without requiring GUARDIAN signature, ensure
1340
+ #! GUARDIAN_SELECTOR_SLOT = 0 (OFF) before calling this.
1341
+ pub proc update_guardian_public_key
1342
+ exec.disable_guardian
1343
+ # ------ Update the GUARDIAN public key ------
1344
+ adv_loadw
1345
+ # => [PUB_KEY]
1346
+
1347
+ push.0.0.0.0
1348
+ # => [MAP_KEY, PUB_KEY]
1349
+ # Note that MAP_KEY is [0, 0, 0, 0] for a single GUARDIAN_KEY
1350
+
1351
+ push.GUARDIAN_PUBLIC_KEY_MAP_SLOT[0..2]
1352
+ # => [pub_key_slot_prefix, pub_key_slot_suffix, MAP_KEY, PUB_KEY]
1353
+
1354
+ exec.native_account::set_map_item
1355
+ # => [OLD_MAP_VALUE]
1356
+
1357
+ dropw
1358
+ # => []
1359
+ end
1360
+
1361
+ #! Conditionally verify a "GUARDIAN" signature against a stored public key hash.
1362
+ #! The condition is controlled by the selector at GUARDIAN_SELECTOR_SLOT.
1363
+ #!
1364
+ #! Inputs: [MSG]
1365
+ #! Outputs: [MSG]
1366
+ #!
1367
+ #! Panics if:
1368
+ #! - Selector is ON but the provided GUARDIAN signature is invalid or missing.
1369
+ #!
1370
+ #! Notes:
1371
+ #! - MSG is TX_SUMMARY_COMMITMENT provided by auth procedure
1372
+ #! - If selector is OFF (0), GUARDIAN verification is skipped
1373
+ #! - Selector value is read from initial storage state
1374
+ pub proc verify_guardian_signature(msg: BeWord) -> BeWord
1375
+ push.GUARDIAN_SELECTOR_SLOT[0..2]
1376
+ exec.active_account::get_item
1377
+ drop drop drop
1378
+ # => [selector, MSG]
1379
+
1380
+ push.1 eq
1381
+ if.true
1382
+ push.1
1383
+ push.GUARDIAN_PUBLIC_KEY_MAP_SLOT[0..2]
1384
+ exec.::miden::standards::auth::ecdsa_k256_keccak::verify_signatures
1385
+ push.1 neq
1386
+ if.true
1387
+ emit.AUTH_UNAUTHORIZED_EVENT
1388
+ push.0 assert.err="invalid GUARDIAN signature"
1389
+ end
1390
+ end
1391
+ # => [MSG]
1392
+ exec.enable_guardian
1393
+ end
1394
+ `;
1395
+ //# sourceMappingURL=auth.js.map