@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.
- package/README.md +1 -1
- package/access/AccessControl.compact +1 -1
- package/access/Ownable.compact +1 -1
- package/access/ShieldedAccessControl.compact +1 -1
- package/access/ZOwnablePK.compact +13 -4
- package/crypto/EcdhMask.compact +1 -1
- package/crypto/Ecdsa.compact +1 -1
- package/crypto/Eip712.compact +1 -1
- package/crypto/ElGamal.compact +1 -1
- package/multisig/ForwarderPrivate.compact +1 -1
- package/multisig/ForwarderShielded.compact +14 -5
- package/multisig/ForwarderUnshielded.compact +1 -1
- package/multisig/ProposalManager.compact +1 -1
- package/multisig/Secp256k1EcdsaMultisig.compact +275 -0
- package/multisig/ShieldedTreasury.compact +1 -1
- package/multisig/ShieldedTreasuryStateless.compact +1 -1
- package/multisig/Signer.compact +1 -1
- package/multisig/UnshieldedTreasury.compact +5 -3
- package/multisig/examples/ForwarderPrivateExample.compact +1 -1
- package/multisig/examples/ForwarderShieldedExample.compact +8 -5
- package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
- package/multisig/examples/NativeShieldedTokenIssuerExample.compact +10 -8
- package/multisig/examples/ShieldedMultiSigV2Example.compact +6 -8
- package/multisig/presets/NativeShieldedTokenIssuer.compact +126 -108
- package/multisig/presets/ShieldedMultiSigV2.compact +46 -45
- package/package.json +2 -2
- package/security/Allowlist.compact +1 -1
- package/security/Blocklist.compact +1 -1
- package/security/Initializable.compact +1 -1
- package/security/Pausable.compact +1 -1
- package/token/ConfidentialFungibleToken.compact +17 -1
- package/token/FungibleToken.compact +1 -1
- package/token/MultiToken.compact +1 -1
- package/token/NativeShieldedToken.compact +2 -2
- package/token/NativeShieldedTokenCore.compact +11 -18
- package/token/NativeShieldedTokenFamily.compact +2 -2
- package/token/NonFungibleToken.compact +1 -1
- package/token/examples/ZOwnableNativeShieldedTokenExample.compact +109 -0
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
- package/token/presets/ZOwnableNativeShieldedToken.compact +314 -0
- package/utils/EvmAbi.compact +1 -1
- package/utils/Utils.compact +1 -1
- package/multisig/EcdsaSignerManager.compact +0 -246
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0-alpha.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
14
|
-
* signers' approvals
|
|
13
|
+
* (a coin this contract holds); each is a single tx carrying `t` registered
|
|
14
|
+
* signers' approvals.
|
|
15
15
|
*
|
|
16
|
-
* Composes `
|
|
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),
|
|
47
|
+
* refundTo.bytes, uint64Word(opNonce), uint128Word(amount)]))`.
|
|
48
48
|
* - BurnFromSelf digest: `keccak(0x1901 ‖ DS ‖ keccak([
|
|
49
|
-
* BURN_FROM_SELF_TYPEHASH, self, uint64Word(opNonce),
|
|
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
|
|
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, "
|
|
59
|
-
* coordinates as `Bytes<32>`. `_calculateSignerId` is the
|
|
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
|
-
* `
|
|
75
|
+
* `Signer` or `Secp256k1EcdsaMultisig` directly.
|
|
73
76
|
*
|
|
74
77
|
* @dev Parameterization:
|
|
75
|
-
*
|
|
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
|
|
81
|
-
*
|
|
82
|
-
* `
|
|
83
|
-
*
|
|
84
|
-
*
|
|
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
|
|
90
|
-
* `Signer_assertSigner`), so WHICH
|
|
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
|
|
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.
|
|
142
|
-
*
|
|
143
|
-
*
|
|
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:
|
|
147
|
-
*
|
|
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
|
|
151
|
-
*
|
|
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
|
|
163
|
-
*
|
|
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
|
|
192
|
+
* `_calculateSignerId(pk, salt)`, pass them as `signerCommitments`.
|
|
182
193
|
* - Per operation: read `getNonce()`, rebuild the digest as
|
|
183
|
-
* `EcdsaTestUtils.ts` does, collect
|
|
184
|
-
* keys and signatures in matching `Vector<
|
|
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:
|
|
199
|
-
*
|
|
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
|
|
220
|
+
* - State: constant-size, two counters and an `n`-entry signer set. Nothing
|
|
204
221
|
* grows per operation.
|
|
205
|
-
* - Amounts:
|
|
206
|
-
*
|
|
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 "../
|
|
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 "../
|
|
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
|
|
255
|
-
* and domain, and fixes the EIP-712 domain separator.
|
|
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<
|
|
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<
|
|
304
|
+
signerCommitments: Vector<n, Bytes<32>>
|
|
289
305
|
): [] {
|
|
290
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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, "
|
|
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
|
|
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=
|
|
431
|
+
* @constraints k=17, rows=89807 (zkir-v3)
|
|
416
432
|
*
|
|
417
433
|
* Requirements:
|
|
418
434
|
*
|
|
419
|
-
* -
|
|
420
|
-
* -
|
|
421
|
-
* -
|
|
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<
|
|
428
|
-
* @param {Vector<
|
|
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<
|
|
435
|
-
signatures: Vector<
|
|
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
|
-
|
|
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=
|
|
479
|
+
* @constraints k=17, rows=85586 (zkir-v3)
|
|
465
480
|
*
|
|
466
481
|
* Requirements:
|
|
467
482
|
*
|
|
468
|
-
* -
|
|
469
|
-
* -
|
|
470
|
-
* -
|
|
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<
|
|
475
|
-
* @param {Vector<
|
|
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<
|
|
481
|
-
signatures: Vector<
|
|
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
|
-
|
|
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=
|
|
523
|
+
* @constraints k=17, rows=119818 (zkir-v3)
|
|
510
524
|
*
|
|
511
525
|
* Requirements:
|
|
512
526
|
*
|
|
513
|
-
* -
|
|
514
|
-
* -
|
|
515
|
-
* -
|
|
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<
|
|
535
|
+
* @param {Uint<128>} amount - The token amount to burn.
|
|
523
536
|
* @param {ZswapCoinPublicKey} refundTo - Receives `coin.value - amount`.
|
|
524
|
-
* @param {Vector<
|
|
525
|
-
* @param {Vector<
|
|
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<
|
|
544
|
+
amount: Uint<128>,
|
|
532
545
|
refundTo: ZswapCoinPublicKey,
|
|
533
|
-
pubkeys: Vector<
|
|
534
|
-
signatures: Vector<
|
|
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
|
-
|
|
560
|
+
EvmAbi_uint128Word(amount)
|
|
548
561
|
]));
|
|
549
562
|
|
|
550
|
-
|
|
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
|
-
*
|
|
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
|
-
* -
|
|
569
|
-
* -
|
|
570
|
-
* -
|
|
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<
|
|
577
|
-
* @param {Vector<
|
|
578
|
-
* @param {Vector<
|
|
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<
|
|
585
|
-
pubkeys: Vector<
|
|
586
|
-
signatures: Vector<
|
|
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<
|
|
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
|
-
|
|
614
|
+
EvmAbi_uint128Word(amount),
|
|
615
|
+
coin.nonce,
|
|
616
|
+
EvmAbi_uint128Word(coin.value)
|
|
599
617
|
]));
|
|
600
618
|
|
|
601
|
-
|
|
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
|
|
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
|
|
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
|
|
711
|
+
return Secp256k1EcdsaMultisig_isSigner(commitment);
|
|
694
712
|
}
|
|
695
713
|
}
|