@openzeppelin/compact-contracts 0.4.0-alpha.4 → 0.4.0-alpha.6

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 (44) hide show
  1. package/README.md +1 -1
  2. package/access/AccessControl.compact +1 -1
  3. package/access/Ownable.compact +1 -1
  4. package/access/ShieldedAccessControl.compact +1 -1
  5. package/access/ZOwnablePK.compact +13 -4
  6. package/crypto/EcdhMask.compact +1 -1
  7. package/crypto/Ecdsa.compact +1 -1
  8. package/crypto/Eip712.compact +1 -1
  9. package/crypto/ElGamal.compact +1 -1
  10. package/multisig/ForwarderPrivate.compact +1 -1
  11. package/multisig/ForwarderShielded.compact +14 -5
  12. package/multisig/ForwarderUnshielded.compact +1 -1
  13. package/multisig/ProposalManager.compact +1 -1
  14. package/multisig/Secp256k1EcdsaMultisig.compact +275 -0
  15. package/multisig/ShieldedTreasury.compact +1 -1
  16. package/multisig/ShieldedTreasuryStateless.compact +1 -1
  17. package/multisig/Signer.compact +1 -1
  18. package/multisig/UnshieldedTreasury.compact +5 -3
  19. package/multisig/examples/ForwarderPrivateExample.compact +1 -1
  20. package/multisig/examples/ForwarderShieldedExample.compact +8 -5
  21. package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
  22. package/multisig/examples/NativeShieldedTokenIssuerExample.compact +10 -8
  23. package/multisig/examples/ShieldedMultiSigV2Example.compact +6 -8
  24. package/multisig/presets/NativeShieldedTokenIssuer.compact +126 -108
  25. package/multisig/presets/ShieldedMultiSigV2.compact +46 -45
  26. package/package.json +2 -2
  27. package/security/Allowlist.compact +1 -1
  28. package/security/Blocklist.compact +1 -1
  29. package/security/Initializable.compact +1 -1
  30. package/security/Pausable.compact +1 -1
  31. package/token/ConfidentialFungibleToken.compact +17 -1
  32. package/token/FungibleToken.compact +1 -1
  33. package/token/MultiToken.compact +1 -1
  34. package/token/NativeShieldedToken.compact +2 -2
  35. package/token/NativeShieldedTokenCore.compact +11 -18
  36. package/token/NativeShieldedTokenFamily.compact +2 -2
  37. package/token/NonFungibleToken.compact +1 -1
  38. package/token/examples/ZOwnableNativeShieldedTokenExample.compact +109 -0
  39. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
  40. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
  41. package/token/presets/ZOwnableNativeShieldedToken.compact +314 -0
  42. package/utils/EvmAbi.compact +1 -1
  43. package/utils/Utils.compact +1 -1
  44. package/multisig/EcdsaSignerManager.compact +0 -246
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/ShieldedMultiSigV2Example.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.6 (multisig/examples/ShieldedMultiSigV2Example.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -15,28 +15,26 @@ pragma language_version >= 0.26.0;
15
15
 
16
16
  import CompactStandardLibrary;
17
17
 
18
- import "../presets/ShieldedMultiSigV2" prefix ShieldedMultiSigV2_;
18
+ import "../presets/ShieldedMultiSigV2"<2> prefix ShieldedMultiSigV2_;
19
19
 
20
20
  export { ShieldedCoinInfo, QualifiedShieldedCoinInfo, ShieldedSendResult };
21
21
 
22
22
  // Surface the preset's domain separator unprefixed so `ledger()` readers see
23
23
  // the key name the preset published.
24
- import { _domainSeparator } from "../presets/ShieldedMultiSigV2";
24
+ import { _domainSeparator } from "../presets/ShieldedMultiSigV2"<2>;
25
25
  export { _domainSeparator };
26
26
 
27
27
  /**
28
- * @description Deploys the wallet with 3 signer commitments and a threshold.
28
+ * @description Deploys the wallet with 3 signer commitments.
29
29
  *
30
30
  * @param {Bytes<32>} instanceSalt - Random salt for commitment derivation.
31
31
  * @param {Vector<3, Bytes<32>>} signerCommitments - Hashed signer identities.
32
- * @param {Uint<8>} thresh - Minimum approvals required.
33
32
  */
34
33
  constructor(
35
34
  instanceSalt: Bytes<32>,
36
- signerCommitments: Vector<3, Bytes<32>>,
37
- thresh: Uint<8>,
35
+ signerCommitments: Vector<3, Bytes<32>>
38
36
  ) {
39
- ShieldedMultiSigV2_initialize(instanceSalt, signerCommitments, thresh);
37
+ ShieldedMultiSigV2_initialize<3>(instanceSalt, signerCommitments);
40
38
  }
41
39
 
42
40
  export circuit deposit(coin: ShieldedCoinInfo): [] {
@@ -1,19 +1,19 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/presets/NativeShieldedTokenIssuer.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.6 (multisig/presets/NativeShieldedTokenIssuer.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
6
6
  /**
7
7
  * @module NativeShieldedTokenIssuer
8
8
  * @description
9
- * A 2-of-3 ECDSA multisig that mints and burns one native shielded token,
9
+ * A threshold ECDSA multisig that mints and burns one native shielded token,
10
10
  * for tokenized deposits. Value enters only through `mint` (to a holder's
11
11
  * coin public key) or `mintToSelf` (a coin this contract holds) and leaves
12
12
  * only through `burn` (a holder's coin, paid into the tx) or `burnFromSelf`
13
- * (a coin this contract holds); each is a single tx carrying two registered
14
- * signers' approvals, so no single party creates or destroys tokens.
13
+ * (a coin this contract holds); each is a single tx carrying `t` registered
14
+ * signers' approvals.
15
15
  *
16
- * Composes `EcdsaSignerManager` (signer commitments, approval checks),
16
+ * Composes `Secp256k1EcdsaMultisig` (signer commitments, approval checks),
17
17
  * `NativeShieldedToken` as `NST_` (one token; domain, `name`, `symbol` and
18
18
  * `decimals` fixed at construction) and `NativeShieldedTokenDerivedNonce` as
19
19
  * `NSTDerivedNonce_` (coin nonces derived from a counter).
@@ -44,20 +44,23 @@ pragma language_version >= 0.26.0;
44
44
  * - MintToSelf digest: `keccak(0x1901 ‖ DS ‖ keccak([MINT_TO_SELF_TYPEHASH,
45
45
  * self, uint64Word(opNonce), uint64Word(amount)]))`.
46
46
  * - Burn digest: `keccak(0x1901 ‖ DS ‖ keccak([BURN_TYPEHASH, self,
47
- * refundTo.bytes, uint64Word(opNonce), uint64Word(amount)]))`.
47
+ * refundTo.bytes, uint64Word(opNonce), uint128Word(amount)]))`.
48
48
  * - BurnFromSelf digest: `keccak(0x1901 ‖ DS ‖ keccak([
49
- * BURN_FROM_SELF_TYPEHASH, self, uint64Word(opNonce), uint64Word(amount)]))`.
49
+ * BURN_FROM_SELF_TYPEHASH, self, uint64Word(opNonce), uint128Word(amount),
50
+ * coin.nonce, uint128Word(coin.value)]))`. The last two words name the
51
+ * holding, so approvals authorize one coin rather than an amount.
50
52
  * - Type hashes: `MINT_TYPEHASH = keccak("Mint(bytes32 contractAddress,
51
53
  * bytes32 recipient,uint256 nonce,uint256 amount)")`,
52
54
  * `MINT_TO_SELF_TYPEHASH = keccak("MintToSelf(bytes32 contractAddress,
53
55
  * uint256 nonce,uint256 amount)")`, `BURN_TYPEHASH = keccak("Burn(bytes32 contractAddress,bytes32 refundTo,
54
56
  * uint256 nonce,uint256 amount)")` and `BURN_FROM_SELF_TYPEHASH =
55
57
  * keccak("BurnFromSelf(bytes32 contractAddress,uint256 nonce,
56
- * uint256 amount)")`, written as literal words.
58
+ * uint256 amount,bytes32 coinNonce,uint256 coinValue)")`, written as
59
+ * literal words.
57
60
  * - Signer commitment: `cm = H<SignerCommitmentInput>({ pkX, pkY, salt,
58
- * domain: pad(32, "multisig:signer:") })`, `pkX` / `pkY` the affine
59
- * coordinates as `Bytes<32>`. `_calculateSignerId` is the exported pure
60
- * circuit.
61
+ * domain: pad(32, "Secp256k1EcdsaMultisig:signer") })`, `pkX` / `pkY`
62
+ * the affine coordinates as `Bytes<32>`. `_calculateSignerId` is the
63
+ * exported pure circuit.
61
64
  * - Coin nonce: `evolveNonce(counter, pad(32, "NativeShieldedToken:nonce"))`
62
65
  * over the derived-nonce counter after its increment.
63
66
  * - Encoding: `H` hashes the Compact encoding, not a byte concatenation.
@@ -69,25 +72,31 @@ pragma language_version >= 0.26.0;
69
72
  *
70
73
  * @notice Implementing contracts register the signer set from their
71
74
  * constructor by calling this module's `initialize`, and MUST NOT import
72
- * `EcdsaSignerManager` directly.
75
+ * `Signer` or `Secp256k1EcdsaMultisig` directly.
73
76
  *
74
77
  * @dev Parameterization:
75
- * None: the signer set is hardcoded to 3 and the threshold to 2.
78
+ * `t` is the approval width and the threshold, fixed at import
79
+ * (`"../presets/NativeShieldedTokenIssuer"<t>`). The implementing contract
80
+ * fixes the signer count `n` at `initialize`. Import one width per contract:
81
+ * two widths share one signer registry, see `Secp256k1EcdsaMultisig`.
82
+ * `NativeShieldedTokenIssuerExample` fixes 2-of-3. The `@constraints` tags on
83
+ * the gated circuits are measured at `t = 2`.
76
84
  *
77
85
  * @dev Compatibility:
78
86
  * The wire format the custodian's signer implements is the EIP-712 domain
79
87
  * (`NativeShieldedTokenIssuer`, version `1`, salt), the `Mint` /
80
- * `MintToSelf` / `Burn` / `BurnFromSelf` type strings, the `multisig:signer:` commitment domain (generic prefix,
81
- * owned by `EcdsaSignerManager`), the `cm` preimage struct, and the
82
- * `Vector<2>` / `Vector<3>` shapes. Changing any of them breaks every
83
- * existing signer setup. The test suite pins them through
84
- * `EcdsaTestUtils.ts`.
88
+ * `MintToSelf` / `Burn` / `BurnFromSelf` type strings, the
89
+ * `Secp256k1EcdsaMultisig:signer` commitment domain (owned by
90
+ * `Secp256k1EcdsaMultisig`), the `cm` preimage struct, and the
91
+ * `Vector<t>` / `Vector<n>` shapes the implementing contract fixes. Changing
92
+ * any of them breaks every existing signer setup. The test suite pins them
93
+ * through `EcdsaTestUtils.ts`.
85
94
  *
86
95
  * @notice Privacy:
87
96
  * - Public, per tx:
88
97
  * - Entry point: which of the four operations ran.
89
- * - Approvers: the two signer commitments (`disclose(commitment)` in
90
- * `Signer_assertSigner`), so WHICH two of the three signers approved.
98
+ * - Approvers: the `t` signer commitments (`disclose(commitment)` in
99
+ * `Signer_assertSigner`), so WHICH signers approved.
91
100
  * - Mint: `amount`, as the `shieldedMints` effect under the token domain,
92
101
  * and the coin commitment as a spend claim. Recipient and nonce are not
93
102
  * published. The nonce is `evolveNonce` of the public
@@ -133,22 +142,24 @@ pragma language_version >= 0.26.0;
133
142
  *
134
143
  * @notice Security:
135
144
  * - Digest binding: each struct binds its type hash, `self`, `opNonce` and
136
- * `amount` (`mint` also `recipient`, `burn` also `refundTo`) under `DS`,
137
- * so a
145
+ * `amount` (`mint` also `recipient`, `burn` also `refundTo`,
146
+ * `burnFromSelf` also `coin.nonce` and `coin.value`) under `DS`, so a
138
147
  * signature authorizes one operation on one instance at one nonce, and is
139
148
  * neither replayable as another operation, nor under another domain,
140
149
  * nor redirectable. Removing the `self` word removes cross-instance
141
- * replay protection, since `DS` cannot carry the address. `Burn` does not
142
- * bind `coin`: the payer picks it, so a signature moved to another coin
143
- * burns only the payer's own value and refunds the bound `refundTo`.
150
+ * replay protection, since `DS` cannot carry the address. Only
151
+ * `burnFromSelf` binds its coin, because only there does the submitter
152
+ * pick it from the contract's own holdings. `Burn` needs no such word. Its
153
+ * coin is paid into the tx by whoever submits it so replaying a
154
+ * signature against a different coin destroys the submitter's own funds and
155
+ * still refunds the bound `refundTo`.
144
156
  * - Low-s: verification goes through `Ecdsa_secp256k1EcdsaVerifyLowS`, so a
145
157
  * high-s encoding is an invalid signature.
146
- * - Distinct signers: `assertApprovals` asserts both entries and rejects a
147
- * repeated commitment, so two distinct registered signers are always
148
- * required. The threshold `2` is the ceiling `EcdsaSignerManager` accepts.
158
+ * - Distinct signers: every gated circuit verifies `t` distinct registered
159
+ * signers, so no set of fewer than `t` keys creates or destroys tokens.
149
160
  * - Immutable signer set: no signer reconfiguration circuit is exported, so
150
- * the three commitments and the threshold are fixed. Losing two of the
151
- * three keys freezes the contract.
161
+ * the commitments and the threshold are fixed. Losing more than `n - t`
162
+ * keys freezes the contract.
152
163
  * - Griefing: the derived-nonce mints are the only mint paths and both are
153
164
  * threshold-gated, so `NativeShieldedTokenDerivedNonce`'s
154
165
  * collision-griefing precondition (a caller-nonce mint at another trust
@@ -159,8 +170,8 @@ pragma language_version >= 0.26.0;
159
170
  * @notice Concurrency:
160
171
  * - Replay counter: `mint`, `mintToSelf`, `burn` and `burnFromSelf` read
161
172
  * `_counter` into the digest. The read is pinned, so any two of them in one block
162
- * conflict: at most one operation lands per block, and the losing signer
163
- * pair re-signs at the next nonce.
173
+ * conflict: at most one operation lands per block, and the losing signers
174
+ * re-sign at the next nonce.
164
175
  * - Derived-nonce counter: both mints also pin it (`evolveNonce` consumes
165
176
  * it); the `_counter` conflict already covers those pairs.
166
177
  * - `getNonce`: submitted as a tx it pins `_counter` and conflicts with any
@@ -178,10 +189,10 @@ pragma language_version >= 0.26.0;
178
189
  *
179
190
  * @notice Client-side duties:
180
191
  * - Before deploy: pick a random `instanceSalt`, derive each `cm` with
181
- * `_calculateSignerId(pk, salt)`, pass the three as `signerCommitments`.
192
+ * `_calculateSignerId(pk, salt)`, pass them as `signerCommitments`.
182
193
  * - Per operation: read `getNonce()`, rebuild the digest as
183
- * `EcdsaTestUtils.ts` does, collect two low-s signatures off-chain, submit
184
- * keys and signatures in matching `Vector<2>` positions.
194
+ * `EcdsaTestUtils.ts` does, collect `t` low-s signatures off-chain, submit
195
+ * keys and signatures in matching `Vector<t>` positions.
185
196
  * - `mint`: hand the returned `ShieldedCoinInfo` to the recipient out of
186
197
  * band; contract-initiated mints create no ciphertext, so a wallet cannot
187
198
  * find the coin by scanning.
@@ -192,19 +203,24 @@ pragma language_version >= 0.26.0;
192
203
  * re-nonces the coin first (a wallet self-transfer yields a fresh secret
193
204
  * nonce); the minted coin as-is is valid but linkable. The contract cannot
194
205
  * enforce this.
206
+ * - `burnFromSelf` coin selection: pick the coin before collecting
207
+ * signatures, not after. Its `nonce` and `value` are in the digest, so
208
+ * signers approve one holding and the submitted coin must be that one.
195
209
  * - `burnFromSelf` coin tracking: keep contract-held coins off-chain,
196
210
  * recovering each coin's `mt_index` from the indexer's `ZswapOutput` /
197
211
  * ledger events to build the `QualifiedShieldedCoinInfo` it takes.
198
- * - `burnFromSelf` change coin: persist the one it returns. It replaces the
199
- * spent coin as the contract's holding and is not otherwise recoverable.
212
+ * - `burnFromSelf` change coin: it replaces the spent coin as the contract's
213
+ * holding. Either persist the coin the circuit returns, or rebuild it from
214
+ * the signed `coinNonce` and `coinValue`, which determine it, so recovery
215
+ * doesn't need to rest on the submitter alone. Rebuilding leans on the protocol's
216
+ * nonce derivation which this module neither defines nor pins.
200
217
  *
201
218
  * @notice Scalability:
202
219
  * - Throughput: one operation per block (see Concurrency).
203
- * - State: constant-size, two counters and a three-entry signer set. Nothing
220
+ * - State: constant-size, two counters and an `n`-entry signer set. Nothing
204
221
  * grows per operation.
205
- * - Amounts: both mints cap at `Uint<64>` by `mintShieldedToken`; both burns
206
- * take `Uint<64>` by their signatures and widen into the module's
207
- * `Uint<128>`.
222
+ * - Amounts: mints cap at `Uint<64>` by `mintShieldedToken`. Burns take
223
+ * `Uint<128>`, the coin value range.
208
224
  *
209
225
  * @dev Composition:
210
226
  * - Recipient privacy: pass a caller-supplied secret random nonce to
@@ -214,10 +230,10 @@ pragma language_version >= 0.26.0;
214
230
  *
215
231
  * @notice Requires ZKIR v3: `keccak256` is unavailable on the v2 backend.
216
232
  */
217
- module NativeShieldedTokenIssuer {
233
+ module NativeShieldedTokenIssuer<#t> {
218
234
  import CompactStandardLibrary;
219
235
 
220
- import "../EcdsaSignerManager" prefix EcdsaSignerManager_;
236
+ import "../Secp256k1EcdsaMultisig"<t> prefix Secp256k1EcdsaMultisig_;
221
237
  import "../../crypto/Eip712" prefix Eip712_;
222
238
  import "../../utils/EvmAbi" prefix EvmAbi_;
223
239
  import "../../token/NativeShieldedToken" prefix NST_;
@@ -227,7 +243,7 @@ module NativeShieldedTokenIssuer {
227
243
  // implementing contract can surface every ledger field under its own key.
228
244
  // The circuits stay behind the prefixes above. The derived-nonce counter is
229
245
  // aliased because this module's replay counter already owns `_counter`.
230
- import { _instanceSalt, _signers, _signerCount, _threshold } from "../EcdsaSignerManager";
246
+ import { _instanceSalt, _signers, _signerCount, _threshold } from "../Secp256k1EcdsaMultisig"<t>;
231
247
  import { _domain, _name, _symbol, _decimals, _isInitialized } from "../../token/NativeShieldedToken";
232
248
  import { _counter as _derivedNonceCounter } from "../../token/extensions/NativeShieldedTokenDerivedNonce";
233
249
  export { _instanceSalt, _signers, _signerCount, _threshold };
@@ -251,9 +267,8 @@ module NativeShieldedTokenIssuer {
251
267
  export sealed ledger _domainSeparator: Bytes<32>;
252
268
 
253
269
  /**
254
- * @description Registers the signer set, initializes the token metadata
255
- * and domain, and fixes the EIP-712 domain separator. The threshold is
256
- * fixed at 2.
270
+ * @description Registers the signer set at threshold `t`, initializes the
271
+ * token metadata and domain, and fixes the EIP-712 domain separator.
257
272
  *
258
273
  * @notice Call from the implementing contract's constructor only. It writes
259
274
  * the token module's `sealed` domain and `_domainSeparator`, so
@@ -270,24 +285,25 @@ module NativeShieldedTokenIssuer {
270
285
  *
271
286
  * - `instanceSalt` should be cryptographically random.
272
287
  * - `signerCommitments` must not contain duplicates.
288
+ * - `n` must be at least `t`.
273
289
  *
274
290
  * @param {Bytes<32>} instanceSalt - Per-deployment salt for commitment derivation.
275
291
  * @param {Bytes<32>} tokenDomain - Domain separator for this contract's token color.
276
292
  * @param {Opaque<"string">} name_ - The token name.
277
293
  * @param {Opaque<"string">} symbol_ - The token symbol.
278
294
  * @param {Uint<8>} decimals_ - The token decimals.
279
- * @param {Vector<3, Bytes<32>>} signerCommitments - Hashed signer identities.
295
+ * @param {Vector<n, Bytes<32>>} signerCommitments - Hashed signer identities.
280
296
  * @returns {[]} Empty tuple.
281
297
  */
282
- export circuit initialize(
298
+ export circuit initialize<#n>(
283
299
  instanceSalt: Bytes<32>,
284
300
  tokenDomain: Bytes<32>,
285
301
  name_: Opaque<"string">,
286
302
  symbol_: Opaque<"string">,
287
303
  decimals_: Uint<8>,
288
- signerCommitments: Vector<3, Bytes<32>>,
304
+ signerCommitments: Vector<n, Bytes<32>>
289
305
  ): [] {
290
- EcdsaSignerManager_initialize<3>(instanceSalt, signerCommitments, 2);
306
+ Secp256k1EcdsaMultisig_initialize<n>(instanceSalt, signerCommitments);
291
307
  NST_initialize(tokenDomain, name_, symbol_, decimals_);
292
308
 
293
309
  const hashedName = keccak256<Bytes<25>>(pad(25, "NativeShieldedTokenIssuer"));
@@ -361,7 +377,7 @@ module NativeShieldedTokenIssuer {
361
377
 
362
378
  /**
363
379
  * @description `keccak256` of the `BurnFromSelf` type string:
364
- * `BurnFromSelf(bytes32 contractAddress,uint256 nonce,uint256 amount)`
380
+ * `BurnFromSelf(bytes32 contractAddress,uint256 nonce,uint256 amount,bytes32 coinNonce,uint256 coinValue)`
365
381
  *
366
382
  * @dev Written as a literal word. `compactc` emits a full keccak
367
383
  * permutation even for a constant argument, so hashing the type string
@@ -372,10 +388,10 @@ module NativeShieldedTokenIssuer {
372
388
  */
373
389
  pure circuit BURN_FROM_SELF_TYPEHASH(): Bytes<32> {
374
390
  const typeHash: Vector<32, Uint<8>> = [
375
- 0x08, 0xba, 0x21, 0x80, 0x7e, 0x7f, 0x75, 0x47,
376
- 0xa2, 0x70, 0x1d, 0x8c, 0x53, 0x06, 0x39, 0x7d,
377
- 0x97, 0xad, 0xfd, 0x3e, 0xa6, 0xf6, 0xa2, 0x8c,
378
- 0x10, 0x0c, 0xbd, 0x64, 0x9c, 0x7f, 0x5d, 0x94
391
+ 0x50, 0xa1, 0xf2, 0x88, 0xba, 0xb8, 0x77, 0x4b,
392
+ 0x1f, 0x55, 0x86, 0x31, 0xc7, 0x75, 0xda, 0x49,
393
+ 0xec, 0x20, 0x4e, 0x07, 0xcf, 0x88, 0x53, 0x9b,
394
+ 0xca, 0x8e, 0xb9, 0x8c, 0xac, 0xf4, 0x5b, 0xe9
379
395
  ];
380
396
 
381
397
  return typeHash as Bytes<32>;
@@ -383,7 +399,7 @@ module NativeShieldedTokenIssuer {
383
399
 
384
400
  /**
385
401
  * @description Computes a signer commitment from a secp256k1 public key,
386
- * as `persistentHash(pkX, pkY, salt, "multisig:signer:")`.
402
+ * as `persistentHash(pkX, pkY, salt, "Secp256k1EcdsaMultisig:signer")`.
387
403
  *
388
404
  * Pure, so the deployer can derive constructor commitments off-chain.
389
405
  *
@@ -399,7 +415,7 @@ module NativeShieldedTokenIssuer {
399
415
  pk: Secp256k1Point,
400
416
  salt: Bytes<32>
401
417
  ): Bytes<32> {
402
- return EcdsaSignerManager_calculateSignerId(pk, salt);
418
+ return Secp256k1EcdsaMultisig_calculateSignerId(pk, salt);
403
419
  }
404
420
 
405
421
  /**
@@ -412,27 +428,26 @@ module NativeShieldedTokenIssuer {
412
428
  * @notice The returned coin info is the only copy available to the
413
429
  * recipient; deliver it out of band.
414
430
  *
415
- * @constraints k=17, rows=89815 (zkir-v3)
431
+ * @constraints k=17, rows=89807 (zkir-v3)
416
432
  *
417
433
  * Requirements:
418
434
  *
419
- * - Both public keys must hash to registered signer commitments.
420
- * - Both signatures must be valid over the mint message hash.
421
- * - Signers must not be duplicates.
422
- * - Threshold must be met.
435
+ * - No two entries may be the same signer.
436
+ * - Every public key must hash to a registered signer commitment.
437
+ * - Every signature must be valid over the mint message hash.
423
438
  * - `recipient` is not zero.
424
439
  *
425
440
  * @param {Uint<64>} amount - The token amount to mint.
426
441
  * @param {ZswapCoinPublicKey} recipient - The coin public key to receive the minted tokens.
427
- * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
428
- * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint hash.
442
+ * @param {Vector<t, Secp256k1Point>} pubkeys - Public keys of approving signers.
443
+ * @param {Vector<t, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint hash.
429
444
  * @returns {ShieldedCoinInfo} The minted coin (nonce, color, value).
430
445
  */
431
446
  export circuit mint(
432
447
  amount: Uint<64>,
433
448
  recipient: ZswapCoinPublicKey,
434
- pubkeys: Vector<2, Secp256k1Point>,
435
- signatures: Vector<2, Secp256k1EcdsaSignature>
449
+ pubkeys: Vector<t, Secp256k1Point>,
450
+ signatures: Vector<t, Secp256k1EcdsaSignature>
436
451
  ): ShieldedCoinInfo {
437
452
  const opNonce = _counter;
438
453
  _counter.increment(1);
@@ -448,7 +463,7 @@ module NativeShieldedTokenIssuer {
448
463
  EvmAbi_uint64Word(amount)
449
464
  ]));
450
465
 
451
- EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
466
+ Secp256k1EcdsaMultisig_assertApprovals(msgHash, pubkeys, signatures);
452
467
 
453
468
  return NST__mint(recipient, amount, NSTDerivedNonce__deriveNonce());
454
469
  }
@@ -461,24 +476,23 @@ module NativeShieldedTokenIssuer {
461
476
  * @notice The returned coin info is the contract's only record of the
462
477
  * holding; persist it for `burnFromSelf`.
463
478
  *
464
- * @constraints k=17, rows=91259 (zkir-v3)
479
+ * @constraints k=17, rows=85586 (zkir-v3)
465
480
  *
466
481
  * Requirements:
467
482
  *
468
- * - Both public keys must hash to registered signer commitments.
469
- * - Both signatures must be valid over the mint-to-self message hash.
470
- * - Signers must not be duplicates.
471
- * - Threshold must be met.
483
+ * - No two entries may be the same signer.
484
+ * - Every public key must hash to a registered signer commitment.
485
+ * - Every signature must be valid over the mint-to-self message hash.
472
486
  *
473
487
  * @param {Uint<64>} amount - The token amount to mint.
474
- * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
475
- * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint-to-self hash.
488
+ * @param {Vector<t, Secp256k1Point>} pubkeys - Public keys of approving signers.
489
+ * @param {Vector<t, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint-to-self hash.
476
490
  * @returns {ShieldedCoinInfo} The minted coin (nonce, color, value).
477
491
  */
478
492
  export circuit mintToSelf(
479
493
  amount: Uint<64>,
480
- pubkeys: Vector<2, Secp256k1Point>,
481
- signatures: Vector<2, Secp256k1EcdsaSignature>
494
+ pubkeys: Vector<t, Secp256k1Point>,
495
+ signatures: Vector<t, Secp256k1EcdsaSignature>
482
496
  ): ShieldedCoinInfo {
483
497
  const opNonce = _counter;
484
498
  _counter.increment(1);
@@ -493,7 +507,7 @@ module NativeShieldedTokenIssuer {
493
507
  EvmAbi_uint64Word(amount)
494
508
  ]));
495
509
 
496
- EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
510
+ Secp256k1EcdsaMultisig_assertApprovals(msgHash, pubkeys, signatures);
497
511
 
498
512
  return NST__mintToSelf(amount, NSTDerivedNonce__deriveNonce());
499
513
  }
@@ -506,32 +520,31 @@ module NativeShieldedTokenIssuer {
506
520
  * @notice The returned refund coin info is the only copy available to
507
521
  * `refundTo`; deliver it out of band.
508
522
  *
509
- * @constraints k=17, rows=118642 (zkir-v3)
523
+ * @constraints k=17, rows=119818 (zkir-v3)
510
524
  *
511
525
  * Requirements:
512
526
  *
513
- * - Both public keys must hash to registered signer commitments.
514
- * - Both signatures must be valid over the burn message hash.
515
- * - Signers must not be duplicates.
516
- * - Threshold must be met.
527
+ * - No two entries may be the same signer.
528
+ * - Every public key must hash to a registered signer commitment.
529
+ * - Every signature must be valid over the burn message hash.
517
530
  * - `coin.color` is this contract's token color.
518
531
  * - `amount` is less than or equal to `coin.value`.
519
532
  * - `refundTo` is not zero.
520
533
  *
521
534
  * @param {ShieldedCoinInfo} coin - The holder's coin to burn from.
522
- * @param {Uint<64>} amount - The token amount to burn.
535
+ * @param {Uint<128>} amount - The token amount to burn.
523
536
  * @param {ZswapCoinPublicKey} refundTo - Receives `coin.value - amount`.
524
- * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
525
- * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn hash.
537
+ * @param {Vector<t, Secp256k1Point>} pubkeys - Public keys of approving signers.
538
+ * @param {Vector<t, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn hash.
526
539
  * @returns {Maybe<ShieldedCoinInfo>} The refund coin created for `refundTo`,
527
540
  * or `none` if the coin was burned in full.
528
541
  */
529
542
  export circuit burn(
530
543
  coin: ShieldedCoinInfo,
531
- amount: Uint<64>,
544
+ amount: Uint<128>,
532
545
  refundTo: ZswapCoinPublicKey,
533
- pubkeys: Vector<2, Secp256k1Point>,
534
- signatures: Vector<2, Secp256k1EcdsaSignature>
546
+ pubkeys: Vector<t, Secp256k1Point>,
547
+ signatures: Vector<t, Secp256k1EcdsaSignature>
535
548
  ): Maybe<ShieldedCoinInfo> {
536
549
  const opNonce = _counter;
537
550
  _counter.increment(1);
@@ -544,10 +557,10 @@ module NativeShieldedTokenIssuer {
544
557
  kernel.self().bytes,
545
558
  refundTo.bytes,
546
559
  EvmAbi_uint64Word(opNonce),
547
- EvmAbi_uint64Word(amount)
560
+ EvmAbi_uint128Word(amount)
548
561
  ]));
549
562
 
550
- EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
563
+ Secp256k1EcdsaMultisig_assertApprovals(msgHash, pubkeys, signatures);
551
564
 
552
565
  return NST__burn(coin, amount, refundTo);
553
566
  }
@@ -561,44 +574,49 @@ module NativeShieldedTokenIssuer {
561
574
  * and returned; it replaces `coin` as the contract's holding, so the
562
575
  * operator must persist it.
563
576
  *
564
- * @constraints k=17, rows=97232 (zkir-v3)
577
+ * The digest binds `coin.nonce` and `coin.value`, so approvals authorize one
578
+ * specific holding rather than an amount.
579
+ *
580
+ * @constraints k=17, rows=104973 (zkir-v3)
565
581
  *
566
582
  * Requirements:
567
583
  *
568
- * - Both public keys must hash to registered signer commitments.
569
- * - Both signatures must be valid over the burn-from-self message hash.
570
- * - Signers must not be duplicates.
571
- * - Threshold must be met.
584
+ * - No two entries may be the same signer.
585
+ * - Every public key must hash to a registered signer commitment.
586
+ * - Every signature must be valid over the burn-from-self message hash.
572
587
  * - `coin.color` is this contract's token color.
573
588
  * - `amount` is less than or equal to `coin.value`.
589
+ * - `coin.nonce` and `coin.value` match the approved coin.
574
590
  *
575
591
  * @param {QualifiedShieldedCoinInfo} coin - The contract-held coin to burn from.
576
- * @param {Uint<64>} amount - The token amount to burn.
577
- * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
578
- * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn-from-self hash.
592
+ * @param {Uint<128>} amount - The token amount to burn.
593
+ * @param {Vector<t, Secp256k1Point>} pubkeys - Public keys of approving signers.
594
+ * @param {Vector<t, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn-from-self hash.
579
595
  * @returns {Maybe<ShieldedCoinInfo>} The change coin retained by the
580
596
  * contract, or `none` if the coin was burned in full.
581
597
  */
582
598
  export circuit burnFromSelf(
583
599
  coin: QualifiedShieldedCoinInfo,
584
- amount: Uint<64>,
585
- pubkeys: Vector<2, Secp256k1Point>,
586
- signatures: Vector<2, Secp256k1EcdsaSignature>
600
+ amount: Uint<128>,
601
+ pubkeys: Vector<t, Secp256k1Point>,
602
+ signatures: Vector<t, Secp256k1EcdsaSignature>
587
603
  ): Maybe<ShieldedCoinInfo> {
588
604
  const opNonce = _counter;
589
605
  _counter.increment(1);
590
606
 
591
- const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<4, Bytes<32>>>([
607
+ const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<6, Bytes<32>>>([
592
608
  BURN_FROM_SELF_TYPEHASH(),
593
609
  // Binds the digest to this deployment. The domain cannot carry the
594
610
  // address, so removing this word removes cross-instance replay
595
611
  // protection.
596
612
  kernel.self().bytes,
597
613
  EvmAbi_uint64Word(opNonce),
598
- EvmAbi_uint64Word(amount)
614
+ EvmAbi_uint128Word(amount),
615
+ coin.nonce,
616
+ EvmAbi_uint128Word(coin.value)
599
617
  ]));
600
618
 
601
- EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
619
+ Secp256k1EcdsaMultisig_assertApprovals(msgHash, pubkeys, signatures);
602
620
 
603
621
  return NST__burnFromSelf(coin, amount);
604
622
  }
@@ -667,7 +685,7 @@ module NativeShieldedTokenIssuer {
667
685
  * @returns {Uint<8>} The signer count.
668
686
  */
669
687
  export circuit getSignerCount(): Uint<8> {
670
- return EcdsaSignerManager_getSignerCount();
688
+ return Secp256k1EcdsaMultisig_getSignerCount();
671
689
  }
672
690
 
673
691
  /**
@@ -678,7 +696,7 @@ module NativeShieldedTokenIssuer {
678
696
  * @returns {Uint<8>} The threshold.
679
697
  */
680
698
  export circuit getThreshold(): Uint<8> {
681
- return EcdsaSignerManager_getThreshold();
699
+ return Secp256k1EcdsaMultisig_getThreshold();
682
700
  }
683
701
 
684
702
  /**
@@ -690,6 +708,6 @@ module NativeShieldedTokenIssuer {
690
708
  * @returns {Boolean} True if registered.
691
709
  */
692
710
  export circuit isSigner(commitment: Bytes<32>): Boolean {
693
- return EcdsaSignerManager_isSigner(commitment);
711
+ return Secp256k1EcdsaMultisig_isSigner(commitment);
694
712
  }
695
713
  }