@openzeppelin/compact-contracts 0.5.0-alpha.1 → 0.5.0-alpha.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 (51) hide show
  1. package/README.md +9 -4
  2. package/access/AccessControl.compact +8 -9
  3. package/access/Ownable.compact +19 -20
  4. package/access/ShieldedAccessControl.compact +1 -1
  5. package/access/ZOwnablePK.compact +3 -3
  6. package/crypto/Ecdh.compact +1 -1
  7. package/crypto/EcdhMask.compact +61 -22
  8. package/crypto/Ecdsa.compact +1 -1
  9. package/crypto/Eip712.compact +1 -1
  10. package/crypto/ElGamal.compact +13 -13
  11. package/math/UintN.compact +82 -0
  12. package/multisig/ForwarderPrivate.compact +1 -1
  13. package/multisig/ForwarderShielded.compact +1 -1
  14. package/multisig/ForwarderUnshielded.compact +1 -1
  15. package/multisig/ProposalManager.compact +3 -3
  16. package/multisig/Secp256k1EcdsaMultisig.compact +1 -1
  17. package/multisig/ShieldedTreasury.compact +1 -1
  18. package/multisig/ShieldedTreasuryStateless.compact +5 -11
  19. package/multisig/Signer.compact +1 -1
  20. package/multisig/UnshieldedTreasury.compact +1 -1
  21. package/multisig/examples/ForwarderPrivateExample.compact +1 -1
  22. package/multisig/examples/ForwarderShieldedExample.compact +1 -1
  23. package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
  24. package/multisig/examples/{ShieldedMultiSigV2Example.compact → NativeShieldedStatelessTreasuryExample.compact} +23 -23
  25. package/multisig/examples/NativeShieldedTokenIssuerExample.compact +1 -1
  26. package/multisig/presets/NativeShieldedStatelessTreasury.compact +443 -0
  27. package/multisig/presets/NativeShieldedTokenIssuer.compact +1 -1
  28. package/package.json +11 -11
  29. package/security/Allowlist.compact +1 -1
  30. package/security/Blocklist.compact +1 -1
  31. package/security/Initializable.compact +1 -1
  32. package/security/Pausable.compact +1 -1
  33. package/token/ConfidentialFungibleToken.compact +1 -1
  34. package/token/ConfidentialNoteFungibleToken.compact +1 -1
  35. package/token/FungibleToken.compact +34 -30
  36. package/token/MultiToken.compact +25 -21
  37. package/token/NativeShieldedToken.compact +2 -2
  38. package/token/NativeShieldedTokenCore.compact +1 -1
  39. package/token/NativeShieldedTokenFamily.compact +4 -4
  40. package/token/NonFungibleToken.compact +26 -22
  41. package/token/examples/ZOwnableNativeShieldedTokenExample.compact +1 -1
  42. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
  43. package/token/extensions/ConfidentialNoteFungibleTokenDelivery.compact +274 -0
  44. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
  45. package/token/extensions/NativeShieldedTokenFamilyPublicSupply.compact +1 -1
  46. package/token/extensions/NativeShieldedTokenPublicSupply.compact +1 -1
  47. package/token/extensions/NativeShieldedTokenPublicSupplyCore.compact +1 -1
  48. package/token/presets/ZOwnableNativeShieldedToken.compact +1 -1
  49. package/utils/EvmAbi.compact +1 -1
  50. package/utils/Utils.compact +1 -1
  51. package/multisig/presets/ShieldedMultiSigV2.compact +0 -289
package/README.md CHANGED
@@ -10,7 +10,7 @@ This project is built on the Midnight Network.
10
10
 
11
11
  > ## ⚠️ WARNING! ⚠️
12
12
  >
13
- > This repo contains highly experimental code.
13
+ > Pre-release versions (`-alpha`, `-rc`) contain experimental, unaudited code.
14
14
  > Expect rapid iteration.
15
15
  > **Use at your own risk.**
16
16
 
@@ -193,7 +193,7 @@ ZKIR v3: the library targets ZKIR v2, the compiler 0.34.0 default.
193
193
  Pass `--feature-zkir-v3` only when the contract reaches a module that needs it:
194
194
  the secp256k1-based `crypto/Ecdsa` and `multisig/Secp256k1EcdsaMultisig`,
195
195
  or the `keccak256`-based `crypto/Eip712`.
196
- `multisig/presets/ShieldedMultiSigV2`, `multisig/presets/NativeShieldedTokenIssuer`
196
+ `multisig/presets/NativeShieldedStatelessTreasury`, `multisig/presets/NativeShieldedTokenIssuer`
197
197
  and the `multisig/examples/` contracts reach both.
198
198
  `utils/EvmAbi` needs no `keccak256` of its own and compiles on either backend.
199
199
  Compiling the other modules under v3 currently hits upstream blockers,
@@ -233,6 +233,8 @@ yarn compile
233
233
  yarn test
234
234
  ```
235
235
 
236
+ Integration and live suites are described in [TESTING.md](TESTING.md).
237
+
236
238
  ### Check/apply Biome formatter
237
239
 
238
240
  ```bash
@@ -247,6 +249,7 @@ yarn lint:fix
247
249
  ```bash
248
250
  yarn compile:access
249
251
  yarn compile:archive
252
+ yarn compile:math
250
253
  ...
251
254
  ```
252
255
 
@@ -278,9 +281,11 @@ rm -rf .turbo/
278
281
 
279
282
  ## Security
280
283
 
281
- This project is still in a very early and experimental phase. It has never been audited nor thoroughly reviewed for security vulnerabilities. DO NOT USE IT IN PRODUCTION.
284
+ For production, use a stable release (no `-alpha` or `-rc` suffix) on the Midnight ledger it targets. Pre-releases are not audited and should not be used in production.
285
+
286
+ Scopes and audited commits are listed in [audits](./audits/README.md).
282
287
 
283
- Please report any security issues you find to <security@openzeppelin.com>.
288
+ Please report any security issues you find to <security@openzeppelin.com>. See [SECURITY.md](SECURITY.md).
284
289
 
285
290
  ### Provenance
286
291
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (access/AccessControl.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (access/AccessControl.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -61,18 +61,17 @@ pragma language_version >= 0.26.0;
61
61
  * through the main role approval circuits (`grantRole` and `_grantRole`).
62
62
  * In other words, role approvals to contract addresses are disallowed through these
63
63
  * circuits.
64
- * This is because Compact currently does not support contract-to-contract calls which means
65
- * if a contract is granted a role, the contract cannot directly call the protected
66
- * circuit.
64
+ * This is because this library has not yet migrated to contract-to-contract calls, which means
65
+ * a contract granted a role cannot directly call the protected circuit.
67
66
  *
68
67
  * @notice This module does offer an experimental circuit that allows roles to be granted
69
68
  * to contract addresses (`_unsafeGrantRole`).
70
69
  * Note that the circuit name is very explicit ("unsafe") with this experimental circuit.
71
- * Until contract-to-contract calls are supported,
70
+ * Until this library migrates to contract-to-contract calls,
72
71
  * there is no direct way for a contract to call protected circuits.
73
72
  *
74
- * @notice The unsafe circuits are planned to become deprecated once contract-to-contract calls
75
- * are supported.
73
+ * @notice The unsafe circuits are planned to become deprecated once this library migrates to
74
+ * contract-to-contract calls.
76
75
  *
77
76
  * @dev Migration plan (safe -> C2C-capable role grants)
78
77
  *
@@ -84,8 +83,8 @@ pragma language_version >= 0.26.0;
84
83
  * which is the only kind of upgrade the CMA supports. Widening the type later would instead force
85
84
  * a ledger-layout rewrite, which the CMA cannot perform.
86
85
  *
87
- * When C2C lands, an already-deployed contract migrates without changing its address or ledger
88
- * state:
86
+ * When the C2C migration lands, an already-deployed contract migrates without changing its address
87
+ * or ledger state:
89
88
  *
90
89
  * - Minor release: mark `_unsafeGrantRole` deprecated (and emit a warning if possible); keep its
91
90
  * implementation intact so existing callers continue to work.
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (access/Ownable.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (access/Ownable.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -28,18 +28,17 @@ pragma language_version >= 0.26.0;
28
28
  * through the main transfer circuits (`transferOwnership` and `_transferOwnership`).
29
29
  * In other words, ownership transfers to contract addresses are disallowed through these
30
30
  * circuits.
31
- * This is because Compact currently does not support contract-to-contract calls which means
32
- * if a contract is granted ownership, the owner contract cannot directly call the protected
33
- * circuit.
31
+ * This is because this library has not yet migrated to contract-to-contract calls, which means
32
+ * a contract granted ownership cannot directly call the protected circuit.
34
33
  *
35
34
  * @notice This module does offer experimental circuits that allow ownership to be granted
36
35
  * to contract addresses (`_unsafeTransferOwnership` and `_unsafeUncheckedTransferOwnership`).
37
36
  * Note that the circuit names are very explicit ("unsafe") with these experimental circuits.
38
- * Until contract-to-contract calls are supported, there is no direct way for a contract
37
+ * Until this library migrates to contract-to-contract calls, there is no direct way for a contract
39
38
  * to authenticate as an owner.
40
39
  *
41
- * @notice The unsafe circuits are planned to become deprecated once contract-to-contract calls
42
- * are supported.
40
+ * @notice The unsafe circuits are planned to become deprecated once this library migrates to
41
+ * contract-to-contract calls.
43
42
  *
44
43
  * @dev Migration plan (safe -> C2C-capable ownership transfers)
45
44
  *
@@ -51,8 +50,8 @@ pragma language_version >= 0.26.0;
51
50
  * which is the only kind of upgrade the CMA supports. Widening the type later would instead force
52
51
  * a ledger-layout rewrite, which the CMA cannot perform.
53
52
  *
54
- * When C2C lands, an already-deployed contract migrates without changing its address or ledger
55
- * state:
53
+ * When the C2C migration lands, an already-deployed contract migrates without changing its address
54
+ * or ledger state:
56
55
  *
57
56
  * - Minor release: mark `_unsafeTransferOwnership` (and `_unsafeUncheckedTransferOwnership`)
58
57
  * deprecated (and emit a warning if possible); keep their implementation intact so existing
@@ -180,8 +179,8 @@ module Ownable {
180
179
  /**
181
180
  * @description Transfers ownership of the contract to `newOwner`.
182
181
  *
183
- * @notice Ownership transfers to contract addresses are currently disallowed until contract-to-contract
184
- * interactions are supported in Compact.
182
+ * @notice Ownership transfers to contract addresses are disallowed until this library migrates to
183
+ * contract-to-contract calls.
185
184
  * This restriction prevents permanently disabling access to a circuit.
186
185
  *
187
186
  * @constraints k=13, rows=2947
@@ -206,10 +205,10 @@ module Ownable {
206
205
  /**
207
206
  * @description Unsafe variant of `transferOwnership`.
208
207
  *
209
- * @warning Ownership transfers to contract addresses are considered unsafe because contract-to-contract
210
- * calls are not currently supported.
208
+ * @warning Ownership transfers to contract addresses are considered unsafe because this library
209
+ * has not yet migrated to contract-to-contract calls.
211
210
  * Ownership privileges sent to a contract address may become uncallable.
212
- * Once contract-to-contract calls are supported, this circuit may be deprecated.
211
+ * Once this library migrates to contract-to-contract calls, this circuit may be deprecated.
213
212
  *
214
213
  * @constraints k=13, rows=2944
215
214
  *
@@ -248,7 +247,7 @@ module Ownable {
248
247
  assertOnlyOwner();
249
248
 
250
249
  // We use `left` here because `assertOnlyOwner` would return a misleading
251
- // error msg (c2c is not currently supported)
250
+ // error msg (this library has not yet migrated to c2c)
252
251
  const zero = Either<Bytes<32>, ContractAddress> {
253
252
  is_left: true, left: default<Bytes<32>>, right: default<ContractAddress>
254
253
  };
@@ -284,8 +283,8 @@ module Ownable {
284
283
  * @description Transfers ownership of the contract to `newOwner` without
285
284
  * enforcing permission checks on the caller.
286
285
  *
287
- * @notice Ownership transfers to contract addresses are currently disallowed until contract-to-contract
288
- * interactions are supported in Compact.
286
+ * @notice Ownership transfers to contract addresses are disallowed until this library migrates to
287
+ * contract-to-contract calls.
289
288
  * This restriction prevents circuits from being inadvertently locked in contracts.
290
289
  *
291
290
  * @constraints k=10, rows=598
@@ -308,10 +307,10 @@ module Ownable {
308
307
  /**
309
308
  * @description Unsafe variant of `_transferOwnership`.
310
309
  *
311
- * @warning Ownership transfers to contract addresses are considered unsafe because contract-to-contract
312
- * calls are not currently supported.
310
+ * @warning Ownership transfers to contract addresses are considered unsafe because this library
311
+ * has not yet migrated to contract-to-contract calls.
313
312
  * Ownership privileges sent to a contract address may become uncallable.
314
- * Once contract-to-contract calls are supported, this circuit may be deprecated.
313
+ * Once this library migrates to contract-to-contract calls, this circuit may be deprecated.
315
314
  *
316
315
  * @constraints k=10, rows=595
317
316
  *
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (access/ShieldedAccessControl.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (access/ShieldedAccessControl.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (access/ZOwnablePK.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (access/ZOwnablePK.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -15,8 +15,8 @@ pragma language_version >= 0.26.0;
15
15
  * For the strongest security guarantees, use an Air-Gapped Public Key.
16
16
  *
17
17
  * @notice This module explicitly supports commitments derived from public keys;
18
- * however, it may be possible to use contract addresses when contract-to-contract
19
- * calls become available. This will be revisited when it's known if/how witnesses
18
+ * however, it may be possible to use contract addresses once this library migrates to
19
+ * contract-to-contract calls. This will be revisited when it's known if/how witnesses
20
20
  * are used from a contract address context.
21
21
  *
22
22
  * @dev Features:
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (crypto/Ecdh.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (crypto/Ecdh.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (crypto/EcdhMask.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (crypto/EcdhMask.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -9,7 +9,7 @@ pragma language_version >= 0.26.0;
9
9
  * Jubjub public key, built on the key agreement in `crypto/Ecdh`. Delivers a
10
10
  * value to whoever holds the secret behind a Jubjub public key WITHOUT encoding
11
11
  * it in the exponent, so recovery needs no discrete-log search and the value is
12
- * unbounded within `Uint<128>`. This is the direct-decrypt counterpart to the
12
+ * unbounded within `Uint<248>`. This is the direct-decrypt counterpart to the
13
13
  * homomorphic `crypto/ElGamal`: use ElGamal to accumulate ciphertexts, use this
14
14
  * to hand a recipient the plaintext.
15
15
  *
@@ -28,6 +28,9 @@ pragma language_version >= 0.26.0;
28
28
  * - `S = pk^e = g^{ek*e}`: the shared point, from `crypto/Ecdh` [`w = u^β`].
29
29
  * - `mask = KDF(S, domain)`: the domain-separated field mask, see `kdf`
30
30
  * [`k = H(w)`]. E_EG hashes `(v, w)`; E'_EG hashes `w` alone, as here.
31
+ * `kdf` is `kdfFromDigest(pointDigest(S), domain)`, a one-step hash KDF
32
+ * (the `H(Z ‖ FixedInfo)` shape of NIST SP 800-56C §4, one block, 248-bit
33
+ * output); a consumer deriving several masks from one `S` digests it once.
31
34
  * - `ct = value + mask`: the field one-time pad [`c = E_s(k, m)`].
32
35
  * - `Ciphertext { ephemeralPk: E, ct }`: the ciphertext [`(v, c)`].
33
36
  * - Recipient recovers `S = E^ek`, `mask = KDF(S, domain)`, `value = ct - mask`.
@@ -73,23 +76,27 @@ pragma language_version >= 0.26.0;
73
76
  *
74
77
  * @dev Security. This is hashed ElGamal over Jubjub: Boneh and Shoup, *A
75
78
  * Graduate Course in Applied Cryptography* v0.6, §11.5, in the Remark 11.1
76
- * form that keys the KDF on the shared point alone. It inherits that scheme's
79
+ * form that keys the KDF on the shared point alone. For plaintexts well below
80
+ * the pad's width (see the hiding bound below) it inherits that scheme's
77
81
  * guarantee: IND-CPA under CDH in the random-oracle model (Theorem 11.4, the
78
82
  * `persistentHash` KDF keyed by the secret point `S`), and so also under the
79
83
  * stronger Oracle Diffie-Hellman assumption. It is NOT IND-CCA: Theorem 12.4
80
84
  * needs a MAC, and the additive pad is malleable (see Integrity), which
81
85
  * is acceptable here only because the on-chain ciphertext is bound in-circuit to
82
- * the value it commits to elsewhere. Hiding carries a large margin: `kdf` returns
83
- * a `degradeToTransient` output in `[0, 2^248)` while `value < 2^128`, so even
84
- * treating the KDF output as uniform the value is hidden with about `2^-120`
85
- * statistical slack, leaving only the assumption that the hash of the DH secret
86
- * is PRF-like. Confidentiality relies on the runtime constraining every
87
- * `JubjubPoint` into the prime-order subgroup (see `crypto/ElGamal`), so `S`
88
- * cannot be a low-order point that leaks `ek`. Binding the ephemeral `E` into the
89
- * KDF is deliberately omitted (Remark 11.1): for a fixed `recipientPk`, `E = g^e`
90
- * and `S = pk^e` are both determined by `e`, so `KDF(S)` already varies with `E`;
91
- * folding it in only tightens the CCA reduction (Remark 12.2)
92
- * which this module does not claim.
86
+ * the value it commits to elsewhere. Hiding depends on the plaintext width. The
87
+ * `kdf` output is a `degradeToTransient` value in `[0, 2^248)`. Taken as
88
+ * uniform, it hides a value below `2^w` within `2^(w-248)` statistical slack. A
89
+ * `Uint<128>` keeps about `2^-120`, leaving only the assumption that the hash of
90
+ * the DH secret is PRF-like. A full `Uint<248>` has no margin: a random one
91
+ * loses under one bit on average, but two chosen values far apart are
92
+ * distinguishable. The type excludes anything at or above `2^248`.
93
+ * Confidentiality relies on the runtime constraining every `JubjubPoint` into
94
+ * the prime-order subgroup (see `crypto/ElGamal`), so `S` cannot be a
95
+ * low-order point that leaks `ek`.
96
+ * Binding the ephemeral `E` into the KDF is deliberately omitted (Remark 11.1):
97
+ * for a fixed `recipientPk`, `E = g^e` and `S = pk^e` are both determined by
98
+ * `e`, so `KDF(S)` already varies with `E`; folding it in only tightens the CCA
99
+ * reduction (Remark 12.2) which this module does not claim.
93
100
  *
94
101
  * @dev Shared key with `crypto/ElGamal` (in scope). A consumer MAY use the same
95
102
  * recipient Jubjub key for both these memos (hashed ElGamal) and exponential-
@@ -131,7 +138,7 @@ module EcdhMask {
131
138
  *
132
139
  * @notice `e` MUST be fresh per call (see the module freshness precondition).
133
140
  *
134
- * @constraints k=14, rows=9269
141
+ * @constraints k=14, rows=9318 (zkir-v3)
135
142
  *
136
143
  * Requirements:
137
144
  *
@@ -139,14 +146,15 @@ module EcdhMask {
139
146
  * - `e` is non-zero, a valid Jubjub scalar (`< ℓ`), secret, and fresh per call.
140
147
  *
141
148
  * @param recipientPk - The recipient's Jubjub public key (`g^ek`).
142
- * @param value - The value to deliver (any `Uint<128>`; no `2^48` bound).
149
+ * @param value - The value to deliver, any `Uint<248>`. The hiding margin
150
+ * shrinks as its width grows (see the module security note).
143
151
  * @param e - A fresh ephemeral scalar.
144
152
  * @param domain - The consumer's domain-separation tag (must match decrypt).
145
153
  * @return The ciphertext `{ ephemeralPk = g^e, ct = value + KDF(pk^e) }`.
146
154
  */
147
155
  export pure circuit encrypt(
148
156
  recipientPk: JubjubPoint,
149
- value: Uint<128>,
157
+ value: Uint<248>,
150
158
  e: JubjubScalar,
151
159
  domain: Bytes<32>
152
160
  ): Ciphertext {
@@ -167,7 +175,7 @@ module EcdhMask {
167
175
  * @dev Uses `persistentHash` deliberately: the recipient reproduces this mask
168
176
  * to decrypt, possibly across a platform upgrade, so it must be upgrade-stable.
169
177
  *
170
- * @constraints k=13, rows=7917
178
+ * @constraints k=13, rows=7914 (zkir-v3)
171
179
  *
172
180
  * @param sShared - The ECDH shared secret point (`pk^e` for the sender,
173
181
  * `E^ek` for the recipient; equal by construction).
@@ -175,9 +183,40 @@ module EcdhMask {
175
183
  * @return The field mask.
176
184
  */
177
185
  export pure circuit kdf(sShared: JubjubPoint, domain: Bytes<32>): Field {
178
- const pointHash = persistentHash<JubjubPoint>(sShared);
186
+ return kdfFromDigest(pointDigest(sShared), domain);
187
+ }
188
+
189
+ /**
190
+ * @description `H(sShared)`, the secret input of `kdf`. A consumer deriving
191
+ * several masks from one shared point digests it once and calls
192
+ * `kdfFromDigest` per tag.
193
+ *
194
+ * @constraints k=13, rows=4098 (zkir-v3)
195
+ *
196
+ * @param sShared - The ECDH shared secret point.
197
+ * @return The point's digest, secret while `sShared` is.
198
+ */
199
+ export pure circuit pointDigest(sShared: JubjubPoint): Bytes<32> {
200
+ return persistentHash<JubjubPoint>(sShared);
201
+ }
202
+
203
+ /**
204
+ * @description The mask for `domain` from a point digest:
205
+ * `kdf(S, domain) == kdfFromDigest(pointDigest(S), domain)`.
206
+ *
207
+ * @notice One mask per `(digest, domain)`. Pad one value under each pair,
208
+ * and give every mask drawn from one digest its own tag; two values under
209
+ * one pair share the pad and leak their difference.
210
+ *
211
+ * @constraints k=13, rows=3956 (zkir-v3)
212
+ *
213
+ * @param digest - `pointDigest` of the shared point.
214
+ * @param domain - The consumer's domain-separation tag.
215
+ * @return The field mask.
216
+ */
217
+ export pure circuit kdfFromDigest(digest: Bytes<32>, domain: Bytes<32>): Field {
179
218
  return degradeToTransient(
180
- persistentHash<Vector<2, Bytes<32>>>([pointHash, domain])
219
+ persistentHash<Vector<2, Bytes<32>>>([digest, domain])
181
220
  );
182
221
  }
183
222
 
@@ -189,14 +228,14 @@ module EcdhMask {
189
228
  * (SEC 1 §5.1.4, action 3). `encrypt` cannot produce one, so a wallet that
190
229
  * meets one holds a placeholder, not an encryption.
191
230
  *
192
- * @constraints k=14, rows=8976
231
+ * @constraints k=14, rows=8976 (zkir-v3)
193
232
  *
194
233
  * @param ciphertext - The ciphertext to decrypt.
195
234
  * @param ekScalar - The recipient's secret scalar (`crypto/ElGamal`'s
196
235
  * `secretToScalar(EK)`).
197
236
  * @param domain - The consumer's domain-separation tag (must match encrypt).
198
237
  * @return The recovered value as a `Field` (equal to the original `value`,
199
- * which is `< 2^128`).
238
+ * which is `< 2^248`).
200
239
  */
201
240
  export pure circuit decrypt(ciphertext: Ciphertext, ekScalar: JubjubScalar, domain: Bytes<32>): Field {
202
241
  // `encrypt` never emits an identity ephemeral; one here is a malformed
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (crypto/Ecdsa.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (crypto/Ecdsa.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (crypto/Eip712.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (crypto/Eip712.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (crypto/ElGamal.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (crypto/ElGamal.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -71,16 +71,17 @@ pragma language_version >= 0.26.0;
71
71
  * @dev Weak keys. Encrypting under the identity point yields `c2 = m` with no
72
72
  * masking (the plaintext is exposed), and the identity is a valid subgroup
73
73
  * point so the runtime subgroup guarantee does NOT catch it. `encryptPoint`
74
- * therefore asserts `pk != identity`, and every encryption path routes through
75
- * it, so confidentiality is protected regardless of the importer. An importing
74
+ * therefore takes its mask from `crypto/Ecdh`'s `deriveShared`, which rejects
75
+ * an identity `pk`, and every encryption path routes through it, so
76
+ * confidentiality is protected regardless of the importer. An importing
76
77
  * module MAY additionally reject an identity `pk` where keys enter the system
77
78
  * (e.g. at registration) to fail fast with a clear error rather than at first
78
79
  * encryption — a UX nicety, not a confidentiality requirement.
79
80
  *
80
- * @dev Randomness. `encryptPoint` asserts `r != 0` — like `pk = identity`,
81
- * `r = 0` zeros the mask (`r*pk = O`), yielding the non-hiding `(O, m)`; it is
82
- * the unique mask-zeroing randomness, so the two asserts together guarantee a
83
- * non-trivial mask. The remaining obligations the circuit CANNOT enforce:
81
+ * @dev Randomness. `r = 0` zeros the mask (`r*pk = O`), yielding the
82
+ * non-hiding `(O, m)`; `deriveShared` rejects it as a zero ephemeral, so
83
+ * together with the identity-`pk` guard the mask is always non-trivial. The
84
+ * remaining obligations the circuit CANNOT enforce:
84
85
  * every `r` MUST be a valid Jubjub scalar (`< ℓ`, the subgroup order; larger
85
86
  * values fault `ecMul`), and MUST be fresh and secret. Reusing an `r` under the
86
87
  * same `pk` leaks the difference of the two plaintexts (`c1` collides, so
@@ -89,6 +90,7 @@ pragma language_version >= 0.26.0;
89
90
  */
90
91
  module ElGamal {
91
92
  import CompactStandardLibrary;
93
+ import "./Ecdh" prefix Ecdh_;
92
94
 
93
95
  // ---------------------------------------------------------------------------
94
96
  // Types
@@ -215,12 +217,10 @@ module ElGamal {
215
217
  * @return The ciphertext `(g^r, pk^r * m)`.
216
218
  */
217
219
  export pure circuit encryptPoint(pk: JubjubPoint, m: JubjubPoint, r: JubjubScalar): Ciphertext {
218
- assert(pk != ecMulGenerator(0 as JubjubScalar), "ElGamal: identity pk");
219
- assert(r != 0 as JubjubScalar, "ElGamal: zero randomness");
220
- const c1 = ecMulGenerator(r);
221
- const mask = ecMul(pk, r);
222
- const c2 = ecAdd(mask, m);
223
- return Ciphertext { c1: c1, c2: c2 };
220
+ // The weak-input guards come from `deriveShared`, not repeated here.
221
+ const shared = Ecdh_deriveShared(pk, r);
222
+ const c2 = ecAdd(shared.sShared, m);
223
+ return Ciphertext { c1: shared.ephemeralPk, c2: c2 };
224
224
  }
225
225
 
226
226
  /**
@@ -0,0 +1,82 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (math/UintN.compact)
3
+
4
+ pragma language_version >= 0.26.0;
5
+
6
+ /**
7
+ * @module UintN
8
+ * @description Wrapping arithmetic on `Uint<N>` for any width `N`: results
9
+ * are reduced modulo `2^N`, and no input fails. Instantiate with a width, e.g.
10
+ * `UintN<64>`, `UintN<248>`.
11
+ *
12
+ * @dev Names and semantics follow Rust's `wrapping_add` and `wrapping_sub`.
13
+ * The `Field` shape follows Noir's `WrappingAdd` and `WrappingSub`, with
14
+ * arkworks' `UInt<N>` as the generic-width precedent. Noir's `as` truncates,
15
+ * but `as Uint<N>` fails out of range. Each circuit therefore reduces
16
+ * explicitly, selects between two `Field` arms, and casts once, outside the
17
+ * select.
18
+ *
19
+ * @dev The `Field` arithmetic is exact while `a + b` stays below the field
20
+ * modulus, which holds for `N <= 253` and so for every width `Uint<N>` admits.
21
+ *
22
+ * Supported Operations:
23
+ * - MODULUS(): `2^N` as a `Field`.
24
+ * - wrappingAdd(a, b): `(a + b) mod 2^N`.
25
+ * - wrappingSub(a, b): `(a - b) mod 2^N`.
26
+ */
27
+ module UintN<#N> {
28
+ /**
29
+ * @description `2^N` as a `Field`: the modulus of every circuit in this
30
+ * module.
31
+ *
32
+ * @constraints k=5, rows=23 (zkir-v2, any N)
33
+ * @constraints k=6, rows=31 (zkir-v3, any N)
34
+ *
35
+ * @returns {Field} `2^N`.
36
+ */
37
+ export pure circuit MODULUS(): Field {
38
+ return fold((acc: Field, _bit: Boolean): Field => acc * 2,
39
+ 1 as Field,
40
+ default<Vector<N, Boolean>>);
41
+ }
42
+
43
+ /**
44
+ * @description Wrapping (modular) addition. Computes `a + b`, wrapping
45
+ * around at the boundary of the type.
46
+ *
47
+ * @dev The wrap test is `a > MAX - b`, which needs no `Uint<N + 1>`.
48
+ *
49
+ * @constraints depends on N:
50
+ * - UintN<8>: k=9, rows=98 (zkir-v2), k=9, rows=85 (zkir-v3)
51
+ * - UintN<64>: k=9, rows=308 (zkir-v2), k=9, rows=133 (zkir-v3)
52
+ * - UintN<248>: k=10, rows=980 (zkir-v2), k=9, rows=307 (zkir-v3)
53
+ *
54
+ * @param {Uint<N>} a - The first operand.
55
+ * @param {Uint<N>} b - The second operand.
56
+ * @returns {Uint<N>} `(a + b) mod 2^N`.
57
+ */
58
+ export pure circuit wrappingAdd(a: Uint<N>, b: Uint<N>): Uint<N> {
59
+ const modulus = MODULUS();
60
+ const wraps = a > ((modulus - 1) as Uint<N>) - b;
61
+ const sum = (a as Field) + (b as Field);
62
+ return (wraps ? sum - modulus : sum) as Uint<N>;
63
+ }
64
+
65
+ /**
66
+ * @description Wrapping (modular) subtraction. Computes `a - b`, wrapping
67
+ * around at the boundary of the type.
68
+ *
69
+ * @constraints depends on N:
70
+ * - UintN<8>: k=9, rows=84 (zkir-v2), k=9, rows=73 (zkir-v3)
71
+ * - UintN<64>: k=9, rows=273 (zkir-v2), k=9, rows=118 (zkir-v3)
72
+ * - UintN<248>: k=10, rows=885 (zkir-v2), k=9, rows=274 (zkir-v3)
73
+ *
74
+ * @param {Uint<N>} a - The minuend.
75
+ * @param {Uint<N>} b - The subtrahend.
76
+ * @returns {Uint<N>} `(a - b) mod 2^N`.
77
+ */
78
+ export pure circuit wrappingSub(a: Uint<N>, b: Uint<N>): Uint<N> {
79
+ const diff = (a as Field) - (b as Field);
80
+ return (a < b ? diff + MODULUS() : diff) as Uint<N>;
81
+ }
82
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ForwarderPrivate.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ForwarderPrivate.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ForwarderShielded.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ForwarderShielded.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ForwarderUnshielded.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ForwarderUnshielded.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ProposalManager.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ProposalManager.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -107,8 +107,8 @@ module ProposalManager {
107
107
  * checks and the deadline comparison above. `_proposals` is exported ledger
108
108
  * state and readable without a proof. `executedState` and
109
109
  * `cancelledState` are pure, so a reader can obtain the sentinel values without
110
- * hardcoding them. A state-reading accessor will be revisited once contract-to-contract
111
- * interactions are available.
110
+ * hardcoding them. A state-reading accessor will be revisited once this library migrates to
111
+ * contract-to-contract calls.
112
112
  */
113
113
  export struct Proposal {
114
114
  to: Recipient,
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/Secp256k1EcdsaMultisig.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/Secp256k1EcdsaMultisig.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ShieldedTreasury.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ShieldedTreasury.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/ShieldedTreasuryStateless.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ShieldedTreasuryStateless.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -63,16 +63,10 @@ module ShieldedTreasuryStateless {
63
63
  * self — `sendShielded` already routes it to self.) No balance or
64
64
  * received/sent accounting is tracked on the ledger.
65
65
  *
66
- * @warning The returned `ShieldedSendResult` holds the sent and change coins
67
- * in plaintext (`nonce`, `color`, `value`). Consuming it in-circuit is safe
68
- * (passing `result.change` to `sendImmediateShielded`, for instance). Returning
69
- * it from your entry point, writing it to ledger, or passing it to another
70
- * contract publishes those coin details, including the amount left over from
71
- * the coin that was spent. Note the compiler will NOT stop you: the
72
- * `disclose` calls below clear the witness taint, so such a return compiles
73
- * with no `disclose` of your own and no marker at the disclosure site. If you
74
- * do not need in-circuit change handling, ignore the result. The change is
75
- * recoverable off-chain from the transaction's private proving state.
66
+ * @warning The result holds the sent and change coins in plaintext.
67
+ * - Returning it from an entry point is safe: it stays with the caller.
68
+ * - Writing it to the ledger publishes both coins. The compiler will not
69
+ * warn, because `_send` already wraps its inputs in `disclose`.
76
70
  *
77
71
  * @notice Access control is NOT enforced here.
78
72
  * The consuming contract must gate this behind its own
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.5.0-alpha.1 (multisig/Signer.compact)
2
+ // OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/Signer.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5