@openzeppelin/compact-contracts 0.4.0-alpha.6 → 0.4.0
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/access/AccessControl.compact +1 -1
- package/access/Ownable.compact +1 -3
- package/access/ShieldedAccessControl.compact +44 -41
- package/access/ZOwnablePK.compact +1 -3
- package/crypto/EcdhMask.compact +11 -1
- package/crypto/Ecdsa.compact +1 -1
- package/crypto/Eip712.compact +1 -1
- package/crypto/ElGamal.compact +1 -1
- package/multisig/ForwarderPrivate.compact +17 -6
- package/multisig/ForwarderShielded.compact +1 -1
- package/multisig/ForwarderUnshielded.compact +1 -1
- package/multisig/ProposalManager.compact +65 -127
- package/multisig/RecipientTypes.compact +48 -0
- package/multisig/Secp256k1EcdsaMultisig.compact +1 -1
- package/multisig/ShieldedTreasury.compact +20 -8
- package/multisig/ShieldedTreasuryStateless.compact +11 -5
- package/multisig/Signer.compact +1 -1
- package/multisig/UnshieldedTreasury.compact +24 -11
- package/multisig/examples/ForwarderPrivateExample.compact +3 -2
- package/multisig/examples/ForwarderShieldedExample.compact +1 -1
- package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
- package/multisig/examples/NativeShieldedTokenIssuerExample.compact +1 -1
- package/multisig/examples/ShieldedMultiSigV2Example.compact +2 -2
- package/multisig/presets/NativeShieldedTokenIssuer.compact +5 -2
- package/multisig/presets/ShieldedMultiSigV2.compact +16 -10
- package/package.json +1 -1
- package/security/Allowlist.compact +1 -1
- package/security/Blocklist.compact +1 -1
- package/security/Initializable.compact +1 -3
- package/security/Pausable.compact +1 -1
- package/token/ConfidentialFungibleToken.compact +70 -22
- package/token/FungibleToken.compact +1 -3
- package/token/MultiToken.compact +1 -3
- package/token/NativeShieldedToken.compact +11 -3
- package/token/NativeShieldedTokenCore.compact +1 -1
- package/token/NativeShieldedTokenFamily.compact +1 -1
- package/token/NonFungibleToken.compact +1 -3
- package/token/examples/ZOwnableNativeShieldedTokenExample.compact +1 -1
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
- package/utils/EvmAbi.compact +1 -1
- package/utils/Utils.compact +1 -1
package/access/Ownable.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.4.0 (access/Ownable.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -117,8 +117,6 @@ module Ownable {
|
|
|
117
117
|
* @description Initializes the contract by setting the `initialOwner`.
|
|
118
118
|
* This must be called in the contract's constructor.
|
|
119
119
|
*
|
|
120
|
-
* @constraints k=10, rows=626
|
|
121
|
-
*
|
|
122
120
|
* Requirements:
|
|
123
121
|
*
|
|
124
122
|
* - Contract is not already initialized.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.4.0 (access/ShieldedAccessControl.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -108,8 +108,8 @@ pragma language_version >= 0.26.0;
|
|
|
108
108
|
* depends on the number of plausible `(role, accountId)` pairings. Since role identifiers
|
|
109
109
|
* are public, the anonymity set is effectively the number of plausible account IDs per role.
|
|
110
110
|
* - Outside observers will know when roles are granted and revoked.
|
|
111
|
-
* - Outside observers can infer the total number of role
|
|
112
|
-
*
|
|
111
|
+
* - Outside observers can infer the total number of distinct `(role, accountId)` pairings granted
|
|
112
|
+
* across all roles, not per-role counts.
|
|
113
113
|
* - Outside observers can link calls made by the same role instance across time.
|
|
114
114
|
* - Users can be retroactively deanonymized if their secret key is exposed.
|
|
115
115
|
* - Outside observers will NOT be able to identify the holder of any role
|
|
@@ -133,29 +133,20 @@ pragma language_version >= 0.26.0;
|
|
|
133
133
|
* - It's strongly recommended to use cryptographically secure random values for the `_instanceSalt`.
|
|
134
134
|
* Failure to do so may weaken privacy guarantees.
|
|
135
135
|
* - The `_instanceSalt` is immutable and used to differentiate deployments.
|
|
136
|
-
* - The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots.
|
|
137
|
-
*
|
|
138
|
-
* capacity through repeated grants of the same active (role, accountId) pairing.
|
|
139
|
-
* Duplicate grants are benign from a security perspective. They produce identical
|
|
140
|
-
* commitments and a single nullifier invalidates all copies; however, they waste tree capacity.
|
|
141
|
-
* Implementing contracts are responsible for mitigating tree exhaustion risk.
|
|
136
|
+
* - The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots. Each distinct
|
|
137
|
+
* `(role, accountId)` pairing consumes one slot; re-granting an active pairing is a no-op.
|
|
142
138
|
* - A single `secretKey` compromise exposes all roles held by that user within the contract
|
|
143
139
|
* instance, since all roles share the same `accountId`. Users requiring compartmentalization
|
|
144
140
|
* between roles should use separate keys per role and accept the additional key management overhead.
|
|
145
141
|
* In the event of key compromise, the user should coordinate with admins to revoke all roles
|
|
146
142
|
* under the compromised `accountId` and re-grant under a new identity.
|
|
147
|
-
* -
|
|
148
|
-
*
|
|
149
|
-
* nullifier for that pairing, blocking any future grant. Admin trust is a fundamental
|
|
150
|
-
* assumption of this system. Off-chain validation should confirm that a pairing was actually
|
|
151
|
-
* granted before submitting a revocation transaction.
|
|
152
|
-
* - `renounceRole` allows burning nullifiers for roles the caller does not currently hold,
|
|
153
|
-
* provided they possess the correct `secretKey`. This requires knowledge of the secret key,
|
|
154
|
-
* at which point the `accountId` should be considered fully compromised regardless.
|
|
143
|
+
* - A `(role, accountId)` pairing can only be revoked after it was granted, so the nullifier
|
|
144
|
+
* set holds at most one entry per granted pairing.
|
|
155
145
|
* - The Merkle tree capacity of 2^20 leaf slots is a permanent, irrecoverable resource.
|
|
156
146
|
* There is no mechanism to reclaim slots after revocation, no upgrade path, and no emergency
|
|
157
147
|
* recovery. Once exhausted, the contract is permanently unable to grant new roles. Deployers
|
|
158
|
-
* must monitor slot consumption off-chain and plan capacity accordingly.
|
|
148
|
+
* must monitor slot consumption off-chain and plan capacity accordingly. The exported tree
|
|
149
|
+
* reports occupancy via `firstFree()` (TypeScript) and `isFull()` (circuit).
|
|
159
150
|
*
|
|
160
151
|
* @notice Using the SHA256 hashing function comes at a significant performance cost. In the future, we
|
|
161
152
|
* plan on migrating to a ZK-friendly hashing function when an implementation is available.
|
|
@@ -213,6 +204,14 @@ module ShieldedAccessControl {
|
|
|
213
204
|
*/
|
|
214
205
|
export ledger _roleCommitmentNullifiers: Set<RoleNullifier>;
|
|
215
206
|
|
|
207
|
+
/**
|
|
208
|
+
* @description Nullifiers of every pairing ever granted. Makes a repeated grant a no-op so
|
|
209
|
+
* one pairing can never consume more than one `_operatorRoles` slot, and gates revocation
|
|
210
|
+
* so a nullifier can only exist for a granted pairing.
|
|
211
|
+
* @type {Set<RoleNullifier>} _grantedRoleNullifiers
|
|
212
|
+
*/
|
|
213
|
+
export ledger _grantedRoleNullifiers: Set<RoleNullifier>;
|
|
214
|
+
|
|
216
215
|
/**
|
|
217
216
|
* @sealed @ledger _instanceSalt
|
|
218
217
|
* @description A per-instance value provided at initialization used to namespace
|
|
@@ -395,12 +394,11 @@ module ShieldedAccessControl {
|
|
|
395
394
|
|
|
396
395
|
/**
|
|
397
396
|
* @description Grants `role` to `accountId` by inserting a role commitment unique to the
|
|
398
|
-
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree.
|
|
399
|
-
*
|
|
400
|
-
* limited Merkle tree storage slots. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
397
|
+
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree. Granting an already active
|
|
398
|
+
* pairing is a no-op. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
401
399
|
* `accountId` must be generated to be re-authorized for a revoked `role`.
|
|
402
400
|
*
|
|
403
|
-
* @constraints k=15, rows=
|
|
401
|
+
* @constraints k=15, rows=29071
|
|
404
402
|
*
|
|
405
403
|
* Requirements:
|
|
406
404
|
*
|
|
@@ -429,9 +427,8 @@ module ShieldedAccessControl {
|
|
|
429
427
|
|
|
430
428
|
/**
|
|
431
429
|
* @description Grants `role` to `accountId` by inserting a role commitment unique to the
|
|
432
|
-
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree.
|
|
433
|
-
*
|
|
434
|
-
* limited Merkle tree storage slots. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
430
|
+
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree. Granting an already active
|
|
431
|
+
* pairing is a no-op. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
435
432
|
* `accountId` must be generated to be re-authorized for a revoked `role`.
|
|
436
433
|
*
|
|
437
434
|
* Internal circuit without access restriction.
|
|
@@ -439,12 +436,10 @@ module ShieldedAccessControl {
|
|
|
439
436
|
* @warning Exposing this circuit directly in an implementing contract would allow anyone to grant
|
|
440
437
|
* roles without authorization. It must be wrapped with appropriate access control.
|
|
441
438
|
*
|
|
442
|
-
* @warning The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots
|
|
443
|
-
* Deployers should monitor slot consumption off-chain.
|
|
444
|
-
* but are otherwise benign. A single nullifier invalidates all duplicate commitments.
|
|
445
|
-
* Implementing contracts are responsible for mitigating tree exhaustion risk.
|
|
439
|
+
* @warning The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots, one per
|
|
440
|
+
* distinct pairing. Deployers should monitor slot consumption off-chain.
|
|
446
441
|
*
|
|
447
|
-
* @constraints k=14, rows=
|
|
442
|
+
* @constraints k=14, rows=12326
|
|
448
443
|
*
|
|
449
444
|
* Requirements:
|
|
450
445
|
*
|
|
@@ -474,15 +469,17 @@ module ShieldedAccessControl {
|
|
|
474
469
|
* @notice Roles are often managed via {grantRole} and {revokeRole}: this circuit's
|
|
475
470
|
* purpose is to provide a mechanism for accounts to lose their privileges
|
|
476
471
|
* if they are compromised (such as when a trusted device is misplaced).
|
|
472
|
+
* Consumers with no need for self-revocation should not export this circuit.
|
|
477
473
|
*
|
|
478
474
|
* @warning Outside observers may be able to use timing and pattern analysis to weaken pseudonymity
|
|
479
475
|
* guarantees if renounceRole is used in tandem with other on-chain actions.
|
|
480
476
|
*
|
|
481
|
-
* @constraints k=14, rows=
|
|
477
|
+
* @constraints k=14, rows=14442
|
|
482
478
|
*
|
|
483
479
|
* Requirements:
|
|
484
480
|
*
|
|
485
481
|
* - The caller must provide a valid `accountId` for the role.
|
|
482
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
486
483
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
487
484
|
* - Contract is initialized.
|
|
488
485
|
*
|
|
@@ -508,15 +505,15 @@ module ShieldedAccessControl {
|
|
|
508
505
|
/**
|
|
509
506
|
* @description Permanently revokes `role` from `accountId` by inserting a role nullifier into the
|
|
510
507
|
* `_roleCommitmentNullifiers` set. Once revoked, a new `accountId` must be generated to be re-authorized for
|
|
511
|
-
* `role`.
|
|
512
|
-
* so a `(role, accountId)` pairing that does not exist can still be revoked.
|
|
508
|
+
* `role`.
|
|
513
509
|
*
|
|
514
|
-
* @constraints k=15, rows=
|
|
510
|
+
* @constraints k=15, rows=27007
|
|
515
511
|
*
|
|
516
512
|
* Requirements:
|
|
517
513
|
*
|
|
518
514
|
* - caller must prove they're an admin for `role`.
|
|
519
515
|
* - caller must not provide valid Merkle tree path for a different `(role, accountId)` pairing.
|
|
516
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
520
517
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
521
518
|
* - Contract is initialized.
|
|
522
519
|
*
|
|
@@ -541,18 +538,18 @@ module ShieldedAccessControl {
|
|
|
541
538
|
/**
|
|
542
539
|
* @description Permanently revokes `role` from `accountId` by inserting a role nullifier into the
|
|
543
540
|
* `_roleCommitmentNullifiers` set. Once revoked, a new `accountId` must be generated to be re-authorized for
|
|
544
|
-
* `role`.
|
|
545
|
-
* so a `(role, accountId)` pairing that does not exist can still be revoked.
|
|
541
|
+
* `role`.
|
|
546
542
|
*
|
|
547
543
|
* Internal circuit without access restriction.
|
|
548
544
|
*
|
|
549
545
|
* @warning Exposing this circuit directly in an implementing contract would allow anyone to revoke
|
|
550
546
|
* roles without authorization. It must be wrapped with appropriate access control.
|
|
551
547
|
*
|
|
552
|
-
* @constraints k=14, rows=
|
|
548
|
+
* @constraints k=14, rows=10262
|
|
553
549
|
*
|
|
554
550
|
* Requirements:
|
|
555
551
|
*
|
|
552
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
556
553
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
557
554
|
* - Contract is initialized.
|
|
558
555
|
*
|
|
@@ -573,13 +570,14 @@ module ShieldedAccessControl {
|
|
|
573
570
|
/**
|
|
574
571
|
* @description Core business logic for the grant/revoke role circuits. Asserts that the
|
|
575
572
|
* `(role, accountId)` pairing has not already been revoked. On success, dispatches on
|
|
576
|
-
* `updateType`: a `Grant` inserts the role commitment into `_operatorRoles
|
|
577
|
-
* a `Revoke`
|
|
573
|
+
* `updateType`: a `Grant` inserts the role commitment into `_operatorRoles` unless the
|
|
574
|
+
* nullifier is already in `_grantedRoleNullifiers`, and a `Revoke` asserts the nullifier
|
|
575
|
+
* is in `_grantedRoleNullifiers` before inserting it into `_roleCommitmentNullifiers`.
|
|
578
576
|
*
|
|
579
577
|
* Disclosures:
|
|
580
578
|
*
|
|
581
579
|
* - A nullifier for the respective role commitment.
|
|
582
|
-
* - A role commitment (on Grant only).
|
|
580
|
+
* - A role commitment (on first Grant only).
|
|
583
581
|
*
|
|
584
582
|
* @notice The nullifier is disclosed via `_roleCommitmentNullifiers.member` on every call,
|
|
585
583
|
* regardless of the update type. Since `Set.member` publicly reveals its argument, the
|
|
@@ -602,9 +600,14 @@ module ShieldedAccessControl {
|
|
|
602
600
|
const isRevoked = _roleCommitmentNullifiers.member(disclose(roleNullifier));
|
|
603
601
|
assert(!isRevoked, "ShieldedAccessControl: role is already revoked");
|
|
604
602
|
|
|
603
|
+
const isGranted = _grantedRoleNullifiers.member(disclose(roleNullifier));
|
|
605
604
|
if (updateType == UpdateType.Grant) {
|
|
606
|
-
|
|
605
|
+
if (!isGranted) {
|
|
606
|
+
_grantedRoleNullifiers.insert(disclose(roleNullifier));
|
|
607
|
+
_operatorRoles.insert(disclose(roleCommitment));
|
|
608
|
+
}
|
|
607
609
|
} else {
|
|
610
|
+
assert(isGranted, "ShieldedAccessControl: role was never granted");
|
|
608
611
|
_roleCommitmentNullifiers.insert(disclose(roleNullifier));
|
|
609
612
|
}
|
|
610
613
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.4.0 (access/ZOwnablePK.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -106,8 +106,6 @@ module ZOwnablePK {
|
|
|
106
106
|
* @warning The `ownerId` must be calculated prior to contract deployment using the SHA256 hashing algorithm.
|
|
107
107
|
* Using any other algorithm will result in a permanent loss of contract access.
|
|
108
108
|
*
|
|
109
|
-
* @constraints k=13, rows=6765
|
|
110
|
-
*
|
|
111
109
|
* Requirements:
|
|
112
110
|
*
|
|
113
111
|
* - Contract is not initialized.
|
package/crypto/EcdhMask.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.4.0 (crypto/EcdhMask.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -49,6 +49,8 @@ pragma language_version >= 0.26.0;
|
|
|
49
49
|
* an out-of-range scalar: `g^e = identity` captures both `e = 0` and any
|
|
50
50
|
* `e ≡ 0 (mod ℓ)`. (Guarding `e != 0` directly would instead lean on the runtime
|
|
51
51
|
* faulting `ecMul` for scalars `≥ ℓ`; the point guard removes that dependency.)
|
|
52
|
+
* `decrypt` guards the same point, refusing an identity ephemeral rather than
|
|
53
|
+
* unmasking against that public constant.
|
|
52
54
|
* These are the only weak inputs (for a subgroup key, `S = identity` iff the
|
|
53
55
|
* ephemeral point is the identity).
|
|
54
56
|
*
|
|
@@ -162,6 +164,10 @@ module EcdhMask {
|
|
|
162
164
|
* @description Recovers the value from a ciphertext using the recipient's
|
|
163
165
|
* secret scalar. Pure and off-chain (no proof needed to read a received value).
|
|
164
166
|
*
|
|
167
|
+
* Requirements:
|
|
168
|
+
*
|
|
169
|
+
* - `ciphertext.ephemeralPk` is not the identity point.
|
|
170
|
+
*
|
|
165
171
|
* @param ciphertext - The ciphertext to decrypt.
|
|
166
172
|
* @param ekScalar - The recipient's secret scalar (`crypto/ElGamal`'s
|
|
167
173
|
* `secretToScalar(EK)`).
|
|
@@ -170,6 +176,10 @@ module EcdhMask {
|
|
|
170
176
|
* which is `< 2^128`).
|
|
171
177
|
*/
|
|
172
178
|
export pure circuit decrypt(ciphertext: Ciphertext, ekScalar: JubjubScalar, domain: Bytes<32>): Field {
|
|
179
|
+
// `encrypt` never emits an identity ephemeral; one here is a malformed
|
|
180
|
+
// ciphertext whose mask is a public constant, so refuse rather than decrypt.
|
|
181
|
+
assert(ciphertext.ephemeralPk != ecMulGenerator(0 as JubjubScalar),
|
|
182
|
+
"EcdhMask: identity ephemeral");
|
|
173
183
|
const sShared = ecMul(ciphertext.ephemeralPk, ekScalar);
|
|
174
184
|
const mask = kdf(sShared, domain);
|
|
175
185
|
return ciphertext.ct - mask;
|
package/crypto/Ecdsa.compact
CHANGED
package/crypto/Eip712.compact
CHANGED
package/crypto/ElGamal.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.4.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.4.0 (multisig/ForwarderPrivate.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.26.0;
|
|
5
5
|
|
|
@@ -10,15 +10,25 @@ pragma language_version >= 0.26.0;
|
|
|
10
10
|
* Deposits accumulate at the contract (no atomic forward); the operator
|
|
11
11
|
* drains coins by presenting the `(parent, opSecret)` preimage at drain
|
|
12
12
|
* time. The drained coin is sent as a shielded note to the parent key, so
|
|
13
|
-
* the parent
|
|
14
|
-
* key and not a contract address: a shielded send to a contract
|
|
15
|
-
* that contract's address in cleartext, which would defeat the
|
|
13
|
+
* the parent never appears on the ledger. The parent is deliberately a coin
|
|
14
|
+
* public key and not a contract address: a shielded send to a contract
|
|
15
|
+
* publishes that contract's address in cleartext, which would defeat the
|
|
16
|
+
* guarantee.
|
|
17
|
+
*
|
|
18
|
+
* @notice The commitment hides the parent from the public, not from a
|
|
19
|
+
* depositor. A contract-owned coin's nullifier is a hash of the coin and the
|
|
20
|
+
* contract address, and `sendShielded` derives the nonces of the sent coin
|
|
21
|
+
* and of the change from a public hash of the spent coin's nonce. Whoever
|
|
22
|
+
* knows a deposited coin can therefore find the drain that spends it, recover
|
|
23
|
+
* the drained amount (on a partial drain the change commitment has the value
|
|
24
|
+
* as its only unknown), and test candidate parent keys against the sent
|
|
25
|
+
* coin's commitment. A depositor who can guess the parent can confirm it.
|
|
16
26
|
*
|
|
17
27
|
* Knowledge of the preimage is the sole authorization gate — there is
|
|
18
28
|
* no signer set and no nullifier scheme. The operational secret is
|
|
19
29
|
* held off-chain; the contract never sees it except during a drain.
|
|
20
30
|
* Two forwarders bound to the same parent with different operational
|
|
21
|
-
* secrets produce different commitments
|
|
31
|
+
* secrets produce different commitments, so the ledger does not link them.
|
|
22
32
|
*
|
|
23
33
|
* Underscore-prefixed circuits have no access control beyond the
|
|
24
34
|
* preimage check inside `_drain`. State access is gated by the
|
|
@@ -103,7 +113,8 @@ module ForwarderPrivate {
|
|
|
103
113
|
* @description Spends a previously-deposited shielded coin to the parent,
|
|
104
114
|
* a coin public key. The caller proves knowledge of `(parent, opSecret)`
|
|
105
115
|
* matching the stored commitment; the coin is sent as a plain shielded note,
|
|
106
|
-
* so the parent stays hidden
|
|
116
|
+
* so the parent stays hidden from anyone who holds no deposited coin (see the
|
|
117
|
+
* module notice). If `coin.value` exceeds `value`,
|
|
107
118
|
* `sendShielded` routes the change back to this contract as a self-owned
|
|
108
119
|
* output; `_drain` returns it untouched via `result.change`. The
|
|
109
120
|
* implementing contract chooses what to do with it: leave it dwelling at
|