@openzeppelin/compact-contracts 0.4.0 → 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.
- package/README.md +9 -4
- package/access/AccessControl.compact +8 -9
- package/access/Ownable.compact +21 -20
- package/access/ShieldedAccessControl.compact +20 -19
- package/access/ZOwnablePK.compact +5 -3
- package/crypto/Ecdh.compact +153 -0
- package/crypto/EcdhMask.compact +146 -84
- package/crypto/Ecdsa.compact +1 -1
- package/crypto/Eip712.compact +1 -1
- package/crypto/ElGamal.compact +13 -13
- package/math/UintN.compact +82 -0
- package/multisig/ForwarderPrivate.compact +6 -17
- package/multisig/ForwarderShielded.compact +1 -1
- package/multisig/ForwarderUnshielded.compact +1 -1
- package/multisig/ProposalManager.compact +127 -65
- package/multisig/Secp256k1EcdsaMultisig.compact +1 -1
- package/multisig/ShieldedTreasury.compact +8 -20
- package/multisig/ShieldedTreasuryStateless.compact +9 -21
- package/multisig/Signer.compact +1 -1
- package/multisig/UnshieldedTreasury.compact +11 -24
- package/multisig/examples/ForwarderPrivateExample.compact +2 -3
- package/multisig/examples/ForwarderShieldedExample.compact +1 -1
- package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
- package/multisig/examples/{ShieldedMultiSigV2Example.compact → NativeShieldedStatelessTreasuryExample.compact} +23 -23
- package/multisig/examples/NativeShieldedTokenIssuerExample.compact +1 -1
- package/multisig/presets/NativeShieldedStatelessTreasury.compact +443 -0
- package/multisig/presets/NativeShieldedTokenIssuer.compact +2 -5
- package/package.json +11 -11
- package/security/Allowlist.compact +1 -1
- package/security/Blocklist.compact +1 -1
- package/security/Initializable.compact +3 -1
- package/security/Pausable.compact +1 -1
- package/token/ConfidentialFungibleToken.compact +22 -70
- package/token/ConfidentialNoteFungibleToken.compact +741 -0
- package/token/FungibleToken.compact +36 -30
- package/token/MultiToken.compact +27 -21
- package/token/NativeShieldedToken.compact +4 -12
- package/token/NativeShieldedTokenCore.compact +1 -1
- package/token/NativeShieldedTokenFamily.compact +4 -4
- package/token/NonFungibleToken.compact +28 -22
- package/token/examples/ZOwnableNativeShieldedTokenExample.compact +1 -1
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
- package/token/extensions/ConfidentialNoteFungibleTokenDelivery.compact +274 -0
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
- package/token/extensions/NativeShieldedTokenFamilyPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenPublicSupplyCore.compact +1 -1
- package/token/presets/ZOwnableNativeShieldedToken.compact +1 -1
- package/utils/EvmAbi.compact +1 -1
- package/utils/Utils.compact +1 -1
- package/multisig/RecipientTypes.compact +0 -48
- package/multisig/presets/ShieldedMultiSigV2.compact +0 -295
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ProposalManager.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -8,15 +8,14 @@ pragma language_version >= 0.26.0;
|
|
|
8
8
|
* @description Token-agnostic proposal lifecycle management for multisig
|
|
9
9
|
* governance contracts.
|
|
10
10
|
*
|
|
11
|
-
* Supports shielded and unshielded proposals through
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Supports shielded and unshielded proposals through a unified
|
|
12
|
+
* Recipient type with a RecipientKind tag. Typed helper circuits
|
|
13
|
+
* provide safe construction of recipients without exposing the
|
|
14
|
+
* internal Bytes<32> representation to consumers.
|
|
15
15
|
*
|
|
16
16
|
* @warning PROPOSALS ARE PUBLIC. `_proposals` is exported ledger state, so
|
|
17
|
-
* every proposal's recipient address, token color, amount,
|
|
18
|
-
*
|
|
19
|
-
* created.
|
|
17
|
+
* every proposal's recipient address, token color, amount, and state is
|
|
18
|
+
* readable off-chain, and `_nextProposalId` reveals how many have been created.
|
|
20
19
|
* This holds regardless of which treasury a consumer composes: pairing this
|
|
21
20
|
* module with `ShieldedTreasuryStateless` still publishes the full intent of
|
|
22
21
|
* every transfer, even though the transfer itself is shielded. The transfer is
|
|
@@ -31,23 +30,18 @@ pragma language_version >= 0.26.0;
|
|
|
31
30
|
*/
|
|
32
31
|
module ProposalManager {
|
|
33
32
|
import CompactStandardLibrary;
|
|
34
|
-
import "./RecipientTypes";
|
|
35
|
-
|
|
36
|
-
export { Recipient, RecipientKind };
|
|
37
33
|
|
|
38
34
|
// ─── Types ──────────────────────────────────────────────────────
|
|
39
35
|
|
|
40
36
|
/**
|
|
41
|
-
* @description Proposal lifecycle
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
44
|
-
* - `Inactive`: no proposal with that id exists.
|
|
45
|
-
* - `Active`:
|
|
46
|
-
*
|
|
47
|
-
* - `
|
|
48
|
-
* - `
|
|
49
|
-
* - `Expired`: stored `Active` with an `expiry` at or before the current
|
|
50
|
-
* block time. Never stored.
|
|
37
|
+
* @description Proposal lifecycle states, derived from `Proposal.state` by
|
|
38
|
+
* `getProposalStatus`. Not stored on the ledger.
|
|
39
|
+
*
|
|
40
|
+
* - `Inactive`: no proposal with that id exists.
|
|
41
|
+
* - `Active`: `state` is an expiry in the future.
|
|
42
|
+
* - `Executed`: `state` is `executedState()`.
|
|
43
|
+
* - `Cancelled`: `state` is `cancelledState()`.
|
|
44
|
+
* - `Expired`: `state` is an expiry at or before the current block time.
|
|
51
45
|
*/
|
|
52
46
|
export enum ProposalStatus {
|
|
53
47
|
Inactive,
|
|
@@ -58,30 +52,69 @@ module ProposalManager {
|
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
/**
|
|
61
|
-
* @description
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
55
|
+
* @description What a `Recipient`'s bytes are and which rail accepts them.
|
|
56
|
+
*
|
|
57
|
+
* - `ShieldedUser`: a Zswap coin public key. `toShieldedRecipient` only.
|
|
58
|
+
* - `UnshieldedUser`: a user address. `toUnshieldedRecipient` only.
|
|
59
|
+
* - `Contract`: a contract address. Accepted by both.
|
|
60
|
+
*
|
|
61
|
+
* @warning `toShieldedRecipient` rejects `UnshieldedUser` and
|
|
62
|
+
* `toUnshieldedRecipient` rejects `ShieldedUser`, both at execution rather
|
|
63
|
+
* than at creation. That mismatch is the hazard noted in `_createProposal`.
|
|
64
|
+
*/
|
|
65
|
+
export enum RecipientKind {
|
|
66
|
+
ShieldedUser,
|
|
67
|
+
UnshieldedUser,
|
|
68
|
+
Contract
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @description A transfer target.
|
|
73
|
+
*
|
|
74
|
+
* @warning `address` is raw 32 bytes. `kind` alone decides whether they are
|
|
75
|
+
* read as a Zswap coin public key, a user address, or a contract address.
|
|
76
|
+
*/
|
|
77
|
+
export struct Recipient {
|
|
78
|
+
kind: RecipientKind,
|
|
79
|
+
address: Bytes<32>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @description A proposal. `state` holds both the lifecycle stage and the
|
|
84
|
+
* expiry in a single field:
|
|
65
85
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
86
|
+
* - `executedState()` (1): executed.
|
|
87
|
+
* - `cancelledState()` (2): cancelled.
|
|
88
|
+
* - any greater value: the expiry, as seconds since the Unix epoch. The
|
|
89
|
+
* proposal is active while the block time is below it and expired
|
|
90
|
+
* afterwards.
|
|
91
|
+
*
|
|
92
|
+
* `_createProposal` rejects an `expiry` in the reserved range, so a proposal
|
|
93
|
+
* that has not reached a terminal state always has `state` greater than
|
|
94
|
+
* `cancelledState()`. Note that `Expired` is NOT terminal: a lapsed proposal
|
|
95
|
+
* still carries its original deadline, which is what tells a reader when it
|
|
96
|
+
* lapsed.
|
|
97
|
+
*
|
|
98
|
+
* @warning In the deadline range, `state` marks when a proposal STOPS being
|
|
99
|
+
* executable, not when it starts: it is executable while `blockTime < state`
|
|
100
|
+
* and inert once `blockTime >= state`.
|
|
69
101
|
*
|
|
70
102
|
* @notice This module implements no execution delay. `expiry` bounds how long
|
|
71
103
|
* a proposal stays valid; it does not impose a minimum wait before it may be
|
|
72
104
|
* executed. A consumer that needs a delay must compose one.
|
|
73
105
|
*
|
|
74
|
-
* @notice
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
106
|
+
* @notice Off-chain readers decode `state` themselves, applying the sentinel
|
|
107
|
+
* checks and the deadline comparison above. `_proposals` is exported ledger
|
|
108
|
+
* state and readable without a proof. `executedState` and
|
|
109
|
+
* `cancelledState` are pure, so a reader can obtain the sentinel values without
|
|
110
|
+
* hardcoding them. A state-reading accessor will be revisited once this library migrates to
|
|
111
|
+
* contract-to-contract calls.
|
|
78
112
|
*/
|
|
79
113
|
export struct Proposal {
|
|
80
114
|
to: Recipient,
|
|
81
115
|
color: Bytes<32>,
|
|
82
116
|
amount: Uint<128>,
|
|
83
|
-
|
|
84
|
-
status: ProposalStatus
|
|
117
|
+
state: Uint<64>
|
|
85
118
|
}
|
|
86
119
|
|
|
87
120
|
// ─── State ──────────────────────────────────────────────────────
|
|
@@ -103,23 +136,43 @@ module ProposalManager {
|
|
|
103
136
|
|
|
104
137
|
/**
|
|
105
138
|
* @description Proposals by id, keyed by the value `_nextProposalId` produced
|
|
106
|
-
* at creation. Each entry carries the recipient, token color, amount,
|
|
107
|
-
*
|
|
139
|
+
* at creation. Each entry carries the recipient, token color, amount, and the
|
|
140
|
+
* overlaid lifecycle/expiry encoding documented on `Proposal`.
|
|
108
141
|
* @type {Uint<64>} id - The proposal identifier, issued by `_nextProposalId`.
|
|
109
142
|
* @type {Proposal} proposal - The proposal record.
|
|
110
143
|
* @type {Map<id, proposal>}
|
|
111
144
|
* @type {Map<Uint<64>, Proposal>} _proposals
|
|
112
145
|
*
|
|
113
|
-
* @notice Entries are never deleted. Reaching a terminal
|
|
114
|
-
* `
|
|
146
|
+
* @notice Entries are never deleted. Reaching a terminal state overwrites
|
|
147
|
+
* `state` in place so the map only grows, and no circuit here prunes it.
|
|
115
148
|
* A consumer that needs cleanup must compose its own.
|
|
116
149
|
*
|
|
117
150
|
* @notice Readable off-chain without a proof so integrators read this map
|
|
118
|
-
* directly rather than calling a circuit.
|
|
119
|
-
*
|
|
151
|
+
* directly rather than calling a circuit. Decoding `state` is the reader's
|
|
152
|
+
* job. See `Proposal`
|
|
120
153
|
*/
|
|
121
154
|
export ledger _proposals: Map<Uint<64>, Proposal>;
|
|
122
155
|
|
|
156
|
+
// ─── State Sentinels ────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @description The `Proposal.state` value marking a proposal as executed.
|
|
160
|
+
*
|
|
161
|
+
* @returns {Uint<64>} 1.
|
|
162
|
+
*/
|
|
163
|
+
export pure circuit executedState(): Uint<64> {
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @description The `Proposal.state` value marking a proposal as cancelled.
|
|
169
|
+
*
|
|
170
|
+
* @returns {Uint<64>} 2.
|
|
171
|
+
*/
|
|
172
|
+
export pure circuit cancelledState(): Uint<64> {
|
|
173
|
+
return 2;
|
|
174
|
+
}
|
|
175
|
+
|
|
123
176
|
// ─── Recipient Helpers ──────────────────────────────────────────
|
|
124
177
|
|
|
125
178
|
/**
|
|
@@ -225,8 +278,8 @@ module ProposalManager {
|
|
|
225
278
|
|
|
226
279
|
/**
|
|
227
280
|
* @description Asserts that a proposal exists and is still executable. The two
|
|
228
|
-
* checks are separate: a `
|
|
229
|
-
* deadline that has passed fails the second, so the error message
|
|
281
|
+
* checks are separate: a terminal `state` (executed or cancelled) fails the
|
|
282
|
+
* first, and a deadline that has passed fails the second, so the error message
|
|
230
283
|
* distinguishes the two.
|
|
231
284
|
*
|
|
232
285
|
* @notice Expiry is terminal, unlike a conflicting read. A tx rejected
|
|
@@ -237,10 +290,10 @@ module ProposalManager {
|
|
|
237
290
|
* Requirements:
|
|
238
291
|
*
|
|
239
292
|
* - Proposal must exist.
|
|
240
|
-
* - Proposal
|
|
293
|
+
* - Proposal must not be executed or cancelled.
|
|
241
294
|
* - Proposal must not have expired.
|
|
242
295
|
*
|
|
243
|
-
* @constraints k=
|
|
296
|
+
* @constraints k=9, rows=129
|
|
244
297
|
*
|
|
245
298
|
* @param {Uint<64>} id - The proposal ID.
|
|
246
299
|
*
|
|
@@ -249,13 +302,13 @@ module ProposalManager {
|
|
|
249
302
|
export circuit assertProposalActive(id: Uint<64>): [] {
|
|
250
303
|
assertProposalExists(id);
|
|
251
304
|
|
|
252
|
-
const
|
|
305
|
+
const state = _proposals.lookup(disclose(id)).state;
|
|
253
306
|
assert(
|
|
254
|
-
|
|
307
|
+
state > cancelledState(),
|
|
255
308
|
"ProposalManager: proposal not active"
|
|
256
309
|
);
|
|
257
310
|
assert(
|
|
258
|
-
blockTimeLt(
|
|
311
|
+
blockTimeLt(state),
|
|
259
312
|
"ProposalManager: proposal expired"
|
|
260
313
|
);
|
|
261
314
|
}
|
|
@@ -288,6 +341,11 @@ module ProposalManager {
|
|
|
288
341
|
* reintroducing the immortal proposal this field exists to prevent. Consuming
|
|
289
342
|
* contracts that need a bounded lifetime must impose their own cap.
|
|
290
343
|
*
|
|
344
|
+
* @notice `expiry` is also rejected if it falls in the sentinel range, keeping
|
|
345
|
+
* the two domains `state` overlays disjoint. That check is enforced here rather
|
|
346
|
+
* than left to the future check, so disjointness holds for whatever block time
|
|
347
|
+
* the executing environment reports.
|
|
348
|
+
*
|
|
291
349
|
* @notice Access control is NOT enforced here.
|
|
292
350
|
* The consuming contract must gate this behind its own
|
|
293
351
|
* authorization policy.
|
|
@@ -295,9 +353,10 @@ module ProposalManager {
|
|
|
295
353
|
* Requirements:
|
|
296
354
|
*
|
|
297
355
|
* - `amount` must be greater than 0.
|
|
356
|
+
* - `expiry` must be greater than `cancelledState()`.
|
|
298
357
|
* - `expiry` must be in the future.
|
|
299
358
|
*
|
|
300
|
-
* @constraints k=10, rows=
|
|
359
|
+
* @constraints k=10, rows=913
|
|
301
360
|
*
|
|
302
361
|
* @param {Recipient} to - The recipient (constructed via helper circuits).
|
|
303
362
|
* @param {Bytes<32>} color - The token color.
|
|
@@ -314,6 +373,10 @@ module ProposalManager {
|
|
|
314
373
|
expiry: Uint<64>
|
|
315
374
|
): Uint<64> {
|
|
316
375
|
assert(amount > 0, "ProposalManager: zero amount");
|
|
376
|
+
assert(
|
|
377
|
+
expiry > cancelledState(),
|
|
378
|
+
"ProposalManager: expiry in reserved range"
|
|
379
|
+
);
|
|
317
380
|
assert(
|
|
318
381
|
blockTimeLt(disclose(expiry)),
|
|
319
382
|
"ProposalManager: expiry not in the future"
|
|
@@ -328,8 +391,7 @@ module ProposalManager {
|
|
|
328
391
|
to: to,
|
|
329
392
|
color: color,
|
|
330
393
|
amount: amount,
|
|
331
|
-
|
|
332
|
-
status: ProposalStatus.Active
|
|
394
|
+
state: expiry
|
|
333
395
|
}));
|
|
334
396
|
|
|
335
397
|
return id;
|
|
@@ -358,8 +420,7 @@ module ProposalManager {
|
|
|
358
420
|
to: proposal.to,
|
|
359
421
|
color: proposal.color,
|
|
360
422
|
amount: proposal.amount,
|
|
361
|
-
|
|
362
|
-
status: ProposalStatus.Cancelled
|
|
423
|
+
state: cancelledState()
|
|
363
424
|
});
|
|
364
425
|
}
|
|
365
426
|
|
|
@@ -386,17 +447,16 @@ module ProposalManager {
|
|
|
386
447
|
to: proposal.to,
|
|
387
448
|
color: proposal.color,
|
|
388
449
|
amount: proposal.amount,
|
|
389
|
-
|
|
390
|
-
status: ProposalStatus.Executed
|
|
450
|
+
state: executedState()
|
|
391
451
|
});
|
|
392
452
|
}
|
|
393
453
|
|
|
394
454
|
// ─── View ───────────────────────────────────────────────────────
|
|
395
455
|
|
|
396
456
|
/**
|
|
397
|
-
* @description Returns the full proposal data. The returned `
|
|
398
|
-
*
|
|
399
|
-
*
|
|
457
|
+
* @description Returns the full proposal data. The returned `state` is the
|
|
458
|
+
* raw encoded field. See `Proposal` for how to read it, and prefer
|
|
459
|
+
* `getProposalStatus` when the lifecycle stage is what is wanted.
|
|
400
460
|
*
|
|
401
461
|
* @param {Uint<64>} id - The proposal ID.
|
|
402
462
|
*
|
|
@@ -444,10 +504,9 @@ module ProposalManager {
|
|
|
444
504
|
}
|
|
445
505
|
|
|
446
506
|
/**
|
|
447
|
-
* @description Derives the
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* `Inactive` for an id that was never created rather than failing.
|
|
507
|
+
* @description Derives the lifecycle status of a proposal from its `state`
|
|
508
|
+
* field and the current block time. Returns `Inactive` for an id that was
|
|
509
|
+
* never created rather than failing.
|
|
451
510
|
*
|
|
452
511
|
* @constraints k=9, rows=252
|
|
453
512
|
*
|
|
@@ -460,11 +519,14 @@ module ProposalManager {
|
|
|
460
519
|
return ProposalStatus.Inactive;
|
|
461
520
|
}
|
|
462
521
|
|
|
463
|
-
const
|
|
464
|
-
if (
|
|
465
|
-
return
|
|
522
|
+
const state = _proposals.lookup(disclose(id)).state;
|
|
523
|
+
if (state == executedState()) {
|
|
524
|
+
return ProposalStatus.Executed;
|
|
525
|
+
}
|
|
526
|
+
if (state == cancelledState()) {
|
|
527
|
+
return ProposalStatus.Cancelled;
|
|
466
528
|
}
|
|
467
|
-
if (blockTimeLt(
|
|
529
|
+
if (blockTimeLt(state)) {
|
|
468
530
|
return ProposalStatus.Active;
|
|
469
531
|
}
|
|
470
532
|
return ProposalStatus.Expired;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ShieldedTreasury.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -82,13 +82,13 @@ module ShieldedTreasury {
|
|
|
82
82
|
* The coin is then merged with any existing coin of the same color,
|
|
83
83
|
* or inserted as a new entry if no coin of that color exists.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
85
|
+
* Zero-value deposits are permitted and are not always a no-op. On a color the
|
|
86
|
+
* treasury does not yet hold, a zero-value coin is inserted and occupies that
|
|
87
|
+
* color's single slot until a later deposit merges into it; if a coin of that
|
|
88
|
+
* color already exists, the zero merges into it via `mergeCoinImmediate` and
|
|
89
|
+
* the stored value is unchanged. They may serve as signaling mechanisms when
|
|
90
|
+
* events are supported. They are not useful as privacy decoys, this
|
|
91
|
+
* treasury's holdings are public (see the module header).
|
|
92
92
|
*
|
|
93
93
|
* @notice Access control is NOT enforced here.
|
|
94
94
|
* The consuming contract must gate this behind its own
|
|
@@ -96,7 +96,6 @@ module ShieldedTreasury {
|
|
|
96
96
|
*
|
|
97
97
|
* Requirements:
|
|
98
98
|
*
|
|
99
|
-
* - The coin's value must be non-zero.
|
|
100
99
|
* - Deposit must not cause received total overflow.
|
|
101
100
|
*
|
|
102
101
|
* @param {ShieldedCoinInfo} coin - The incoming shielded coin descriptor.
|
|
@@ -104,8 +103,6 @@ module ShieldedTreasury {
|
|
|
104
103
|
* @returns {[]} Empty tuple.
|
|
105
104
|
*/
|
|
106
105
|
export circuit _deposit(coin: ShieldedCoinInfo): [] {
|
|
107
|
-
assert(coin.value != 0, "ShieldedTreasury: zero value");
|
|
108
|
-
|
|
109
106
|
const currentReceived = getReceivedTotal(coin.color);
|
|
110
107
|
assert(currentReceived <= Utils_UINT128_MAX() - coin.value, "ShieldedTreasury: overflow");
|
|
111
108
|
|
|
@@ -145,16 +142,12 @@ module ShieldedTreasury {
|
|
|
145
142
|
* the retained change coin, and nothing at all when the send leaves no change,
|
|
146
143
|
* so the sent coin's plaintext is disclosed beyond anything in public state.
|
|
147
144
|
*
|
|
148
|
-
* @dev A self-addressed coin is auto-received by `sendShielded` but never recorded
|
|
149
|
-
* in _coins so it would be stranded. Therefore, this circuit rejects sends to self.
|
|
150
|
-
*
|
|
151
145
|
* @notice Access control is NOT enforced here.
|
|
152
146
|
* The consuming contract must gate this behind its own
|
|
153
147
|
* authorization policy.
|
|
154
148
|
*
|
|
155
149
|
* Requirements:
|
|
156
150
|
*
|
|
157
|
-
* - `recipient` must not be this contract's own address.
|
|
158
151
|
* - A coin of the given `color` must exist in the treasury.
|
|
159
152
|
* - The coin's value must be >= `amount`.
|
|
160
153
|
* - Send must not cause sent total overflow.
|
|
@@ -170,11 +163,6 @@ module ShieldedTreasury {
|
|
|
170
163
|
color: Bytes<32>,
|
|
171
164
|
amount: Uint<128>
|
|
172
165
|
): ShieldedSendResult {
|
|
173
|
-
// `kernel.self()` stays outside the `||`: the compiler treats a ledger
|
|
174
|
-
// read behind a witness-conditioned branch as an undeclared disclosure
|
|
175
|
-
const self = kernel.self();
|
|
176
|
-
assert(recipient.is_left || recipient.right != self,
|
|
177
|
-
"ShieldedTreasury: self-send");
|
|
178
166
|
assert(_coins.member(disclose(color)), "ShieldedTreasury: no balance");
|
|
179
167
|
|
|
180
168
|
const coin = _coins.lookup(disclose(color));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/ShieldedTreasuryStateless.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -22,16 +22,10 @@ pragma language_version >= 0.26.0;
|
|
|
22
22
|
* application time. Sufficiency is likewise enforced by `sendShielded` rather
|
|
23
23
|
* than by an assert here.
|
|
24
24
|
*
|
|
25
|
-
* @notice "No ledger state" is not the same as "unobservable". A coin
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* therefore find the spend that consumes it, recover the amount sent (on a
|
|
30
|
-
* partial spend the change commitment has the value as its only unknown),
|
|
31
|
-
* test candidate recipients against the sent coin's commitment, and repeat
|
|
32
|
-
* from the change coin for every later spend descending from it. `_send`
|
|
33
|
-
* declares the spent coin's disclosure on the consumer's behalf. Consumers
|
|
34
|
-
* that need to make the trade-off themselves
|
|
25
|
+
* @notice "No ledger state" is not the same as "unobservable". A coin
|
|
26
|
+
* commitment is a hash of the coin's data, so `sendShielded` can link a coin
|
|
27
|
+
* receive to its later spend; `_send` declares that disclosure on the
|
|
28
|
+
* consumer's behalf. Consumers that need to make the trade-off themselves
|
|
35
29
|
* should note it is only declared here for convenience — it could be moved to
|
|
36
30
|
* the caller. Separately, see `_send`'s warning about its return value, which
|
|
37
31
|
* carries coin plaintext.
|
|
@@ -69,16 +63,10 @@ module ShieldedTreasuryStateless {
|
|
|
69
63
|
* self — `sendShielded` already routes it to self.) No balance or
|
|
70
64
|
* received/sent accounting is tracked on the ledger.
|
|
71
65
|
*
|
|
72
|
-
* @warning The
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* contract publishes those coin details, including the amount left over from
|
|
77
|
-
* the coin that was spent. Note the compiler will NOT stop you: the
|
|
78
|
-
* `disclose` calls below clear the witness taint, so such a return compiles
|
|
79
|
-
* with no `disclose` of your own and no marker at the disclosure site. If you
|
|
80
|
-
* do not need in-circuit change handling, ignore the result. The change is
|
|
81
|
-
* 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`.
|
|
82
70
|
*
|
|
83
71
|
* @notice Access control is NOT enforced here.
|
|
84
72
|
* The consuming contract must gate this behind its own
|
package/multisig/Signer.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/UnshieldedTreasury.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -61,14 +61,6 @@ module UnshieldedTreasury {
|
|
|
61
61
|
* operation creates a zero-valued `_balances` entry, which
|
|
62
62
|
* `getTokenBalance` reports the same as absence.
|
|
63
63
|
*
|
|
64
|
-
* @warning Every non-zero deposit of a color rewrites the `_balances` entry
|
|
65
|
-
* that `_send` reads for that color, so a deposit that lands first
|
|
66
|
-
* invalidates every `_send` of the same color still waiting to be applied.
|
|
67
|
-
* A consumer that exposes `_deposit` without access control lets any
|
|
68
|
-
* depositor delay authorized sends for as long as they keep depositing; the
|
|
69
|
-
* sends must then be re-proven. No funds are lost. A zero-value deposit
|
|
70
|
-
* rewrites the entry with the same value and does not have this effect.
|
|
71
|
-
*
|
|
72
64
|
* @notice Access control is NOT enforced here.
|
|
73
65
|
* The consuming contract must gate this behind its own
|
|
74
66
|
* authorization policy.
|
|
@@ -100,16 +92,17 @@ module UnshieldedTreasury {
|
|
|
100
92
|
/**
|
|
101
93
|
* @description Sends unshielded tokens from the treasury.
|
|
102
94
|
*
|
|
103
|
-
* @warning Recipients
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* contract's responsibility.
|
|
95
|
+
* @warning Recipients are not validated. An unspendable recipient (such as a
|
|
96
|
+
* zero address) is accepted and permanently removes the funds from the
|
|
97
|
+
* contract. This is not a sanctioned burn — no unshielded equivalent of
|
|
98
|
+
* `shieldedBurnAddress` is exposed to contracts — so treat recipient
|
|
99
|
+
* validation as the consuming contract's responsibility.
|
|
109
100
|
*
|
|
110
|
-
* @
|
|
111
|
-
*
|
|
112
|
-
*
|
|
101
|
+
* @warning Sending to this contract's own address zeroes the tracked balance
|
|
102
|
+
* while the funds stay held and spendable at the protocol level, after which
|
|
103
|
+
* `_send` will refuse to touch them. Recovery is only possible outside this
|
|
104
|
+
* module: a consumer may reconcile the exported `_balances`, or spend the
|
|
105
|
+
* funds through its own `sendUnshielded` call.
|
|
113
106
|
*
|
|
114
107
|
* @notice Access control is NOT enforced here.
|
|
115
108
|
* The consuming contract must gate this behind its own
|
|
@@ -117,7 +110,6 @@ module UnshieldedTreasury {
|
|
|
117
110
|
*
|
|
118
111
|
* Requirements:
|
|
119
112
|
*
|
|
120
|
-
* - `recipient` must not be this contract's own address.
|
|
121
113
|
* - The amount must be non-zero. The ledger rejects zero-value outputs.
|
|
122
114
|
* - The treasury must hold sufficient balance for the given token color.
|
|
123
115
|
*
|
|
@@ -132,11 +124,6 @@ module UnshieldedTreasury {
|
|
|
132
124
|
color: Bytes<32>,
|
|
133
125
|
amount: Uint<128>
|
|
134
126
|
): [] {
|
|
135
|
-
// `kernel.self()` stays outside the `||`: the compiler treats a ledger
|
|
136
|
-
// read behind a witness-conditioned branch as an undeclared disclosure
|
|
137
|
-
const self = kernel.self();
|
|
138
|
-
assert(!recipient.is_left || recipient.left != self,
|
|
139
|
-
"UnshieldedTreasury: self-send");
|
|
140
127
|
assert(amount != 0, "UnshieldedTreasury: zero amount");
|
|
141
128
|
|
|
142
129
|
const bal = getTokenBalance(color);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/examples/ForwarderPrivateExample.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -54,8 +54,7 @@ export circuit deposit(coin: ShieldedCoinInfo): [] {
|
|
|
54
54
|
* @description Spends a previously-deposited shielded coin to the parent,
|
|
55
55
|
* a coin public key. The caller proves knowledge of `(parent, opSecret)`
|
|
56
56
|
* matching the stored commitment; the coin is sent as a shielded note, so the
|
|
57
|
-
* parent stays hidden
|
|
58
|
-
* `ForwarderPrivate` module notice). If the input coin's value exceeds `value`,
|
|
57
|
+
* parent stays hidden on-chain. If the input coin's value exceeds `value`,
|
|
59
58
|
* change is re-emitted back to the contract for future drains.
|
|
60
59
|
*
|
|
61
60
|
* The parent is a `ZswapCoinPublicKey`, never a `ContractAddress`: a shielded
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/examples/ForwarderShieldedExample.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.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.5.0-alpha.2 (multisig/examples/ForwarderUnshieldedExample.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|