@lfdecentralizedtrust/zeto-contracts 0.2.2 → 0.5.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/README.md +70 -162
- package/contracts/factory.sol +30 -34
- package/contracts/factory_upgradeable.sol +11 -7
- package/contracts/lib/common/util.sol +40 -0
- package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
- package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
- package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
- package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
- package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
- package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
- package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
- package/contracts/lib/registry.sol +74 -26
- package/contracts/lib/storage/base.sol +210 -0
- package/contracts/lib/storage/nullifier.sol +166 -0
- package/contracts/lib/zeto_common.sol +277 -28
- package/contracts/lib/zeto_fungible.sol +451 -33
- package/contracts/lib/zeto_fungible_base.sol +69 -0
- package/contracts/lib/zeto_fungible_burn.sol +83 -53
- package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
- package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
- package/contracts/lib/zeto_lockable.sol +613 -0
- package/contracts/lib/zeto_non_fungible.sol +236 -0
- package/contracts/lib/zeto_non_fungible_base.sol +61 -0
- package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
- package/contracts/test/escrow1.sol +90 -34
- package/contracts/test/escrow2.sol +64 -29
- package/contracts/test/qurrency.sol +10 -2
- package/contracts/test/smt.sol +6 -1
- package/contracts/test/tendecimals.sol +14 -12
- package/contracts/verifiers/impl/anon.sol +189 -0
- package/contracts/verifiers/impl/anon_batch.sol +301 -0
- package/contracts/verifiers/impl/anon_enc.sol +266 -0
- package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
- package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
- package/contracts/verifiers/impl/burn.sol +182 -0
- package/contracts/verifiers/impl/burn_batch.sol +238 -0
- package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
- package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
- package/contracts/verifiers/impl/deposit.sol +182 -0
- package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
- package/contracts/verifiers/impl/nf_anon.sol +175 -0
- package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
- package/contracts/verifiers/impl/withdraw.sol +189 -0
- package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
- package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
- package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
- package/contracts/verifiers/verifier_anon.sol +31 -186
- package/contracts/verifiers/verifier_anon_batch.sol +31 -298
- package/contracts/verifiers/verifier_anon_enc.sol +31 -263
- package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
- package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
- package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
- package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
- package/contracts/verifiers/verifier_burn.sol +31 -179
- package/contracts/verifiers/verifier_burn_batch.sol +31 -235
- package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
- package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
- package/contracts/verifiers/verifier_deposit.sol +31 -179
- package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
- package/contracts/verifiers/verifier_nf_anon.sol +31 -172
- package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
- package/contracts/verifiers/verifier_withdraw.sol +31 -186
- package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
- package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
- package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
- package/contracts/zeto_anon.sol +77 -231
- package/contracts/zeto_anon_burnable.sol +56 -12
- package/contracts/zeto_anon_enc.sol +93 -190
- package/contracts/zeto_anon_enc_nullifier.sol +249 -195
- package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
- package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
- package/contracts/zeto_anon_nullifier.sol +164 -298
- package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
- package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
- package/contracts/zeto_anon_nullifier_qurrency.sol +217 -361
- package/contracts/zeto_nf_anon.sol +55 -130
- package/contracts/zeto_nf_anon_nullifier.sol +90 -152
- package/hardhat.config.ts +9 -3
- package/ignition/modules/lib/deps.ts +8 -0
- package/ignition/modules/zeto_anon_burnable.ts +8 -7
- package/ignition/modules/zeto_anon_enc_nullifier.ts +31 -0
- package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +2 -2
- package/ignition/modules/zeto_anon_nullifier.ts +28 -4
- package/ignition/modules/zeto_anon_nullifier_burnable.ts +50 -16
- package/ignition/modules/zeto_anon_nullifier_kyc.ts +27 -12
- package/ignition/modules/zeto_nf_anon_nullifier.ts +14 -10
- package/package.json +4 -2
- package/scripts/deploy_cloneable.ts +19 -10
- package/scripts/deploy_upgradeable.ts +9 -5
- package/scripts/tokens/Zeto_Anon.ts +3 -3
- package/scripts/tokens/Zeto_AnonBurnable.ts +1 -1
- package/scripts/tokens/Zeto_AnonEnc.ts +12 -3
- package/scripts/tokens/Zeto_AnonEncNullifier.ts +5 -3
- package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifier.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +1 -1
- package/scripts/tokens/Zeto_NfAnon.ts +2 -2
- package/scripts/tokens/Zeto_NfAnonNullifier.ts +1 -1
- package/test/factory.ts +46 -73
- package/test/lib/anon_nullifier_helpers.ts +89 -0
- package/test/lib/anon_zeto_helpers.ts +76 -0
- package/test/lib/deploy.ts +3 -2
- package/test/lib/utils.ts +74 -35
- package/test/test/escrow1.ts +185 -58
- package/test/test/escrow2.ts +200 -107
- package/test/test/qurrency.ts +13 -33
- package/test/usdc-shielding.ts +39 -26
- package/test/utils.ts +144 -21
- package/test/zeto_anon.ts +956 -465
- package/test/zeto_anon_enc.ts +881 -142
- package/test/zeto_anon_enc_nullifier.ts +847 -38
- package/test/zeto_anon_enc_nullifier_kyc.ts +120 -181
- package/test/zeto_anon_enc_nullifier_non_repudiation.ts +77 -46
- package/test/zeto_anon_nullifier.ts +1212 -953
- package/test/zeto_anon_nullifier_kyc.ts +674 -655
- package/test/zeto_anon_nullifier_qurrency.ts +452 -306
- package/test/zeto_nf_anon.ts +737 -137
- package/test/zeto_nf_anon_nullifier.ts +876 -246
- package/contracts/lib/zeto_base.sol +0 -293
- package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
- package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
- package/contracts/lib/zeto_nullifier.sol +0 -340
- package/contracts/zkDvP.sol_ +0 -273
- package/test/zkDvP.ts_ +0 -455
- /package/contracts/lib/{common.sol → common/common.sol} +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright © 2025 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
pragma solidity ^0.8.27;
|
|
17
|
+
|
|
18
|
+
interface IZetoStorage {
|
|
19
|
+
enum UTXOStatus {
|
|
20
|
+
UNKNOWN, // default value for the empty UTXO slots
|
|
21
|
+
UNSPENT,
|
|
22
|
+
SPENT
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// For the main UTXO storage
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @dev query whether a UTXO is currently spent
|
|
29
|
+
* @param txo The UTXO to check
|
|
30
|
+
* @return bool whether the UTXO is spent
|
|
31
|
+
*/
|
|
32
|
+
function spent(uint256 txo) external view returns (UTXOStatus);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @dev validate the inputs
|
|
36
|
+
* @param inputs The inputs to validate
|
|
37
|
+
* @param inputsLocked Whether the inputs are locked
|
|
38
|
+
*/
|
|
39
|
+
function validateInputs(
|
|
40
|
+
uint256[] calldata inputs,
|
|
41
|
+
bool inputsLocked
|
|
42
|
+
) external view;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @dev validate the outputs
|
|
46
|
+
* @param outputs The outputs to validate
|
|
47
|
+
*/
|
|
48
|
+
function validateOutputs(uint256[] calldata outputs) external view;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @dev validate the root
|
|
52
|
+
* @param root The root to validate
|
|
53
|
+
* @return bool whether the root is valid
|
|
54
|
+
*/
|
|
55
|
+
function validateRoot(uint256 root) external view returns (bool);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @dev get the root
|
|
59
|
+
* @return uint256 the root
|
|
60
|
+
*/
|
|
61
|
+
function getRoot() external view returns (uint256);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @dev process the inputs
|
|
65
|
+
* @param inputs The inputs to process
|
|
66
|
+
* @param inputsLocked Whether the inputs are locked
|
|
67
|
+
*/
|
|
68
|
+
function processInputs(
|
|
69
|
+
uint256[] calldata inputs,
|
|
70
|
+
bool inputsLocked
|
|
71
|
+
) external;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @dev process the outputs
|
|
75
|
+
* @param outputs The outputs to process
|
|
76
|
+
*/
|
|
77
|
+
function processOutputs(uint256[] calldata outputs) external;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @dev Register freshly-minted locked UTXOs in the locked-UTXO ledger.
|
|
81
|
+
*
|
|
82
|
+
* The storage layer is intentionally a thin UTXO substrate and
|
|
83
|
+
* does NOT track who is allowed to spend a locked UTXO; that
|
|
84
|
+
* responsibility belongs to the Zeto contract on top (e.g. the
|
|
85
|
+
* lock-id-keyed `_locks[lockId].spender` in
|
|
86
|
+
* `ZetoFungible`-style lockable-capability contracts). Callers
|
|
87
|
+
* that need delegate/spender semantics must maintain that state
|
|
88
|
+
* themselves above this interface.
|
|
89
|
+
*
|
|
90
|
+
* @param lockedOutputs The locked UTXOs to register as UNSPENT
|
|
91
|
+
*/
|
|
92
|
+
function processLockedOutputs(uint256[] calldata lockedOutputs) external;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @dev Query whether a UTXO is currently in the locked-unspent set.
|
|
96
|
+
*
|
|
97
|
+
* The delegate / spender of the lock is intentionally NOT
|
|
98
|
+
* returned here: the storage layer no longer tracks it. To
|
|
99
|
+
* surface the spender alongside the locked-state, expose the
|
|
100
|
+
* combined view at the Zeto layer instead.
|
|
101
|
+
*
|
|
102
|
+
* @param utxo The UTXO to check
|
|
103
|
+
* @return whether the UTXO is currently locked-unspent
|
|
104
|
+
*/
|
|
105
|
+
function locked(uint256 utxo) external view returns (bool);
|
|
106
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright ©
|
|
1
|
+
// Copyright © 2025 Kaleido, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
//
|
|
@@ -15,24 +15,11 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
pragma solidity ^0.8.27;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
uint256[] inputs,
|
|
26
|
-
uint256[] outputs,
|
|
27
|
-
uint256[] lockedOutputs,
|
|
28
|
-
address indexed delegate,
|
|
29
|
-
address indexed submitter,
|
|
30
|
-
bytes data
|
|
31
|
-
);
|
|
32
|
-
event LockDelegateChanged(
|
|
33
|
-
uint256[] lockedOutputs,
|
|
34
|
-
address indexed oldDelegate,
|
|
35
|
-
address indexed newDelegate,
|
|
36
|
-
bytes data
|
|
37
|
-
);
|
|
18
|
+
interface IGroth16Verifier {
|
|
19
|
+
function verify(
|
|
20
|
+
uint[2] calldata _pA,
|
|
21
|
+
uint[2][2] calldata _pB,
|
|
22
|
+
uint[2] calldata _pC,
|
|
23
|
+
uint[] calldata _pubSignals
|
|
24
|
+
) external view returns (bool);
|
|
38
25
|
}
|
|
@@ -15,81 +15,129 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
pragma solidity ^0.8.27;
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
|
19
19
|
import {SmtLib} from "@iden3/contracts/contracts/lib/SmtLib.sol";
|
|
20
20
|
import {PoseidonHasher} from "@iden3/contracts/contracts/lib/hash/PoseidonHasher.sol";
|
|
21
21
|
import {PoseidonUnit2L, PoseidonUnit3L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {IZetoKyc} from "./interfaces/izeto_kyc.sol";
|
|
25
|
-
|
|
26
|
-
uint256 constant MAX_SMT_DEPTH = 64;
|
|
22
|
+
import {MAX_SMT_DEPTH} from "./interfaces/IZeto.sol";
|
|
23
|
+
import {IZetoKyc} from "./interfaces/IZetoKyc.sol";
|
|
27
24
|
|
|
28
25
|
/// @title A sample on-chain implementation of a KYC registry
|
|
26
|
+
/// @author Kaleido, Inc.
|
|
29
27
|
/// @dev The registry uses a Sparse Merkle Tree to store the
|
|
30
28
|
/// Zeto accounts (Babyjubjub keys), so that transaction
|
|
31
29
|
/// submitters can generate proofs of membership for the
|
|
32
30
|
/// accounts in a privacy-preserving manner.
|
|
33
|
-
///
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
///
|
|
32
|
+
/// Mixed into KYC-aware concrete tokens (e.g. {Zeto_AnonNullifierKyc})
|
|
33
|
+
/// so that those tokens can expose `register`/`isRegistered` and
|
|
34
|
+
/// contribute the identities tree's root as an additional public input
|
|
35
|
+
/// to the transfer/deposit ZK circuits via the inheriting token's
|
|
36
|
+
/// {extraInputs}/{extraInputsForDeposit} overrides.
|
|
37
|
+
///
|
|
38
|
+
/// Inherits {Ownable2StepUpgradeable} (rather than the simpler
|
|
39
|
+
/// {OwnableUpgradeable}) so that the diamond formed when this mixin is
|
|
40
|
+
/// composed with a token that already inherits {ZetoCommon}, which is
|
|
41
|
+
/// itself {Ownable2StepUpgradeable}, resolves cleanly: both branches of
|
|
42
|
+
/// the inheritance graph reach `transferOwnership`/`_transferOwnership`
|
|
43
|
+
/// through the same {Ownable2StepUpgradeable} implementation, so no
|
|
44
|
+
/// leaf-level override is required. The Ownable initialization itself
|
|
45
|
+
/// is performed once by the token's `__ZetoCommon_init`; the Registry
|
|
46
|
+
/// initializer only needs to set up the SMT.
|
|
47
|
+
abstract contract Registry is Ownable2StepUpgradeable, IZetoKyc {
|
|
36
48
|
using SmtLib for SmtLib.Data;
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
/// @dev Thrown by {register} when the supplied public key is already
|
|
51
|
+
/// a member of the identities tree.
|
|
52
|
+
error AlreadyRegistered(uint256[2] publicKey);
|
|
39
53
|
|
|
54
|
+
/// @dev Internal-only so it can only be called from a derived
|
|
55
|
+
/// contract's own `initializer`-guarded entrypoint.
|
|
40
56
|
function __Registry_init() internal onlyInitializing {
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
RegistryStorage.Layout storage $ = RegistryStorage.layout();
|
|
58
|
+
$.publicKeysTree.initialize(MAX_SMT_DEPTH);
|
|
59
|
+
$.publicKeysTree.setHasher(new PoseidonHasher());
|
|
43
60
|
}
|
|
44
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @dev Register a new Zeto account.
|
|
64
|
+
* @param publicKey The public Babyjubjub key to register.
|
|
65
|
+
* @param data Opaque off-chain correlation data appended to the
|
|
66
|
+
* {IdentityRegistered} event.
|
|
67
|
+
*
|
|
68
|
+
* Emits an {IdentityRegistered} event.
|
|
69
|
+
*/
|
|
45
70
|
function register(
|
|
46
|
-
uint256[2]
|
|
71
|
+
uint256[2] calldata publicKey,
|
|
47
72
|
bytes calldata data
|
|
48
73
|
) public onlyOwner {
|
|
49
74
|
_register(publicKey, data);
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
77
|
+
/**
|
|
78
|
+
* @dev Returns whether the given public key is registered.
|
|
79
|
+
* @param publicKey The Babyjubjub public key to check.
|
|
80
|
+
*/
|
|
55
81
|
function isRegistered(
|
|
56
|
-
uint256[2]
|
|
82
|
+
uint256[2] calldata publicKey
|
|
57
83
|
) public view returns (bool) {
|
|
58
84
|
uint256 nodeKey = _getIdentitiesLeafNodeKey(publicKey);
|
|
59
|
-
SmtLib.Node memory node =
|
|
85
|
+
SmtLib.Node memory node = RegistryStorage.layout().publicKeysTree.getNode(
|
|
86
|
+
nodeKey
|
|
87
|
+
);
|
|
60
88
|
return node.nodeType != SmtLib.NodeType.EMPTY;
|
|
61
89
|
}
|
|
62
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @dev Returns the root of the identities tree. This is contributed as
|
|
93
|
+
* an extra public input to the transfer / deposit circuits in
|
|
94
|
+
* KYC-aware token contracts via {extraInputs}.
|
|
95
|
+
*/
|
|
63
96
|
function getIdentitiesRoot() public view returns (uint256) {
|
|
64
|
-
return
|
|
97
|
+
return RegistryStorage.layout().publicKeysTree.getRoot();
|
|
65
98
|
}
|
|
66
99
|
|
|
67
|
-
/// @dev Register a new Zeto account
|
|
68
|
-
/// @param publicKey The public Babyjubjub key to register
|
|
69
100
|
function _register(
|
|
70
|
-
uint256[2]
|
|
101
|
+
uint256[2] calldata publicKey,
|
|
71
102
|
bytes calldata data
|
|
72
103
|
) internal {
|
|
73
104
|
uint256 nodeHash = _getIdentitiesLeafNodeHash(publicKey);
|
|
74
|
-
|
|
105
|
+
RegistryStorage.Layout storage $ = RegistryStorage.layout();
|
|
106
|
+
SmtLib.Node memory node = $.publicKeysTree.getNode(nodeHash);
|
|
75
107
|
if (node.nodeType != SmtLib.NodeType.EMPTY) {
|
|
76
108
|
revert AlreadyRegistered(publicKey);
|
|
77
109
|
}
|
|
78
|
-
|
|
110
|
+
$.publicKeysTree.addLeaf(nodeHash, nodeHash);
|
|
79
111
|
emit IdentityRegistered(publicKey, data);
|
|
80
112
|
}
|
|
81
113
|
|
|
82
114
|
function _getIdentitiesLeafNodeHash(
|
|
83
|
-
uint256[2]
|
|
115
|
+
uint256[2] calldata publicKey
|
|
84
116
|
) internal pure returns (uint256) {
|
|
85
117
|
return PoseidonUnit2L.poseidon(publicKey);
|
|
86
118
|
}
|
|
87
119
|
|
|
88
120
|
function _getIdentitiesLeafNodeKey(
|
|
89
|
-
uint256[2]
|
|
121
|
+
uint256[2] calldata publicKey
|
|
90
122
|
) internal pure returns (uint256) {
|
|
91
123
|
uint256 nodeHash = PoseidonUnit2L.poseidon(publicKey);
|
|
92
124
|
uint256[3] memory params = [nodeHash, nodeHash, uint256(1)];
|
|
93
125
|
return PoseidonUnit3L.poseidon(params);
|
|
94
126
|
}
|
|
95
127
|
}
|
|
128
|
+
|
|
129
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.Registry`).
|
|
130
|
+
library RegistryStorage {
|
|
131
|
+
struct Layout {
|
|
132
|
+
SmtLib.Data publicKeysTree;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
136
|
+
0xf79d6fea6eabe5c80b325fcf2331fe08f18a473c50e27c55890676ff6746fd00;
|
|
137
|
+
|
|
138
|
+
function layout() internal pure returns (Layout storage $) {
|
|
139
|
+
assembly {
|
|
140
|
+
$.slot := STORAGE_LOCATION
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Copyright © 2025 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
pragma solidity ^0.8.27;
|
|
17
|
+
|
|
18
|
+
import {IZetoConstants} from "../interfaces/IZeto.sol";
|
|
19
|
+
import {IZetoLockableCapability} from "../interfaces/IZetoLockableCapability.sol";
|
|
20
|
+
import {IZetoStorage} from "../interfaces/IZetoStorage.sol";
|
|
21
|
+
import {Commonlib} from "../common/common.sol";
|
|
22
|
+
import {Util} from "../common/util.sol";
|
|
23
|
+
|
|
24
|
+
contract BaseStorage is IZetoStorage, IZetoConstants {
|
|
25
|
+
/// @dev Thrown when a state-mutating storage call is made by anyone other
|
|
26
|
+
/// than the Zeto contract that deployed this storage instance.
|
|
27
|
+
error StorageUnauthorized(address caller);
|
|
28
|
+
|
|
29
|
+
/// @dev Thrown by view stubs that only have a meaningful implementation
|
|
30
|
+
/// in nullifier-based storage (e.g. {validateRoot}, {getRoot}).
|
|
31
|
+
/// Replaces the previous `revert("Not implemented")` strings so
|
|
32
|
+
/// callers can match a typed error.
|
|
33
|
+
error NotImplemented();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @dev The Zeto contract that owns this storage instance. Captured at
|
|
37
|
+
* construction time and immutable for the lifetime of the storage
|
|
38
|
+
* contract. All state-mutating entry points are gated to this
|
|
39
|
+
* address; view functions remain unrestricted.
|
|
40
|
+
*
|
|
41
|
+
* This storage contract is created via `new BaseStorage()` (or a
|
|
42
|
+
* subclass) from inside the Zeto contract's initializer, so
|
|
43
|
+
* `msg.sender` at construction is the Zeto proxy address.
|
|
44
|
+
*/
|
|
45
|
+
address internal immutable _zeto;
|
|
46
|
+
|
|
47
|
+
// tracks all the regular (unlocked) UTXOs
|
|
48
|
+
mapping(uint256 => UTXOStatus) internal _utxos;
|
|
49
|
+
// used for tracking locked UTXOs. multi-step transaction flows that require counterparties
|
|
50
|
+
// to upload proofs. To protect the party that uploads their proof first,
|
|
51
|
+
// and prevent any other party from utilizing the uploaded proof to execute
|
|
52
|
+
// a transaction, the input UTXOs or nullifiers can be locked and only usable
|
|
53
|
+
// by the same party that did the locking.
|
|
54
|
+
//
|
|
55
|
+
// The "who is allowed to spend a locked UTXO" question is intentionally
|
|
56
|
+
// NOT answered at this layer; the Zeto contract on top owns that state
|
|
57
|
+
// (e.g. `_locks[lockId].spender` in ZetoFungible-style lockable
|
|
58
|
+
// capabilities). This contract only tracks whether the UTXO is locked.
|
|
59
|
+
mapping(uint256 => UTXOStatus) internal _lockedUtxos;
|
|
60
|
+
|
|
61
|
+
/// @dev Reverts unless the caller is the Zeto contract that deployed this
|
|
62
|
+
/// storage instance. Applied to every state-mutating function so the
|
|
63
|
+
/// storage contract cannot be poked directly by external accounts.
|
|
64
|
+
modifier onlyZeto() {
|
|
65
|
+
if (msg.sender != _zeto) {
|
|
66
|
+
revert StorageUnauthorized(msg.sender);
|
|
67
|
+
}
|
|
68
|
+
_;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
constructor() {
|
|
72
|
+
_zeto = msg.sender;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// @notice Returns the address authorized to call state-mutating
|
|
76
|
+
/// functions on this storage contract.
|
|
77
|
+
function zeto() external view returns (address) {
|
|
78
|
+
return _zeto;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function validateInputs(
|
|
82
|
+
uint256[] calldata inputs,
|
|
83
|
+
bool inputsLocked
|
|
84
|
+
) public view virtual {
|
|
85
|
+
// sort the inputs to detect duplicates
|
|
86
|
+
uint256[] memory sortedInputs = Util.sortCommitments(inputs);
|
|
87
|
+
// Check the inputs are all unspent
|
|
88
|
+
for (uint256 i = 0; i < sortedInputs.length; ++i) {
|
|
89
|
+
if (sortedInputs[i] == 0) {
|
|
90
|
+
// skip the zero inputs
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (i > 0 && sortedInputs[i] == sortedInputs[i - 1]) {
|
|
94
|
+
revert UTXODuplicate(sortedInputs[i]);
|
|
95
|
+
}
|
|
96
|
+
if (
|
|
97
|
+
_lockedUtxos[sortedInputs[i]] == UTXOStatus.UNKNOWN &&
|
|
98
|
+
_utxos[sortedInputs[i]] == UTXOStatus.UNKNOWN
|
|
99
|
+
) {
|
|
100
|
+
revert UTXONotMinted(sortedInputs[i]);
|
|
101
|
+
}
|
|
102
|
+
if (
|
|
103
|
+
_lockedUtxos[sortedInputs[i]] == UTXOStatus.SPENT ||
|
|
104
|
+
_utxos[sortedInputs[i]] == UTXOStatus.SPENT
|
|
105
|
+
) {
|
|
106
|
+
revert UTXOAlreadySpent(sortedInputs[i]);
|
|
107
|
+
}
|
|
108
|
+
if (
|
|
109
|
+
!inputsLocked &&
|
|
110
|
+
_lockedUtxos[sortedInputs[i]] == UTXOStatus.UNSPENT
|
|
111
|
+
) {
|
|
112
|
+
revert IZetoLockableCapability.AlreadyLocked(sortedInputs[i]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateOutputs(uint256[] calldata outputs) public view virtual {
|
|
118
|
+
// sort the outputs to detect duplicates
|
|
119
|
+
uint256[] memory sortedOutputs = Util.sortCommitments(outputs);
|
|
120
|
+
|
|
121
|
+
// Check for duplicate outputs
|
|
122
|
+
for (uint256 i = 0; i < sortedOutputs.length; ++i) {
|
|
123
|
+
uint256 output = sortedOutputs[i];
|
|
124
|
+
if (output == 0) {
|
|
125
|
+
// skip the zero outputs
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (i > 0 && output == sortedOutputs[i - 1]) {
|
|
129
|
+
revert UTXODuplicate(output);
|
|
130
|
+
}
|
|
131
|
+
// check the outputs are all new - looking in the locked and unlocked UTXOs
|
|
132
|
+
if (
|
|
133
|
+
_utxos[output] == UTXOStatus.SPENT ||
|
|
134
|
+
_lockedUtxos[output] == UTXOStatus.SPENT
|
|
135
|
+
) {
|
|
136
|
+
revert UTXOAlreadySpent(output);
|
|
137
|
+
} else if (
|
|
138
|
+
_utxos[output] == UTXOStatus.UNSPENT ||
|
|
139
|
+
_lockedUtxos[output] == UTXOStatus.UNSPENT
|
|
140
|
+
) {
|
|
141
|
+
revert UTXOAlreadyOwned(output);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Only needed for the nullifier based implementation
|
|
147
|
+
function validateRoot(uint256) public view virtual returns (bool) {
|
|
148
|
+
revert NotImplemented();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Only needed for the nullifier based implementation
|
|
152
|
+
function getRoot() public view virtual returns (uint256) {
|
|
153
|
+
revert NotImplemented();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function processInputs(
|
|
157
|
+
uint256[] calldata inputs,
|
|
158
|
+
bool inputsLocked
|
|
159
|
+
) public virtual onlyZeto {
|
|
160
|
+
mapping(uint256 => UTXOStatus) storage utxos = inputsLocked
|
|
161
|
+
? _lockedUtxos
|
|
162
|
+
: _utxos;
|
|
163
|
+
// consume the input UTXOs
|
|
164
|
+
for (uint256 i = 0; i < inputs.length; ++i) {
|
|
165
|
+
if (inputs[i] != 0) {
|
|
166
|
+
utxos[inputs[i]] = UTXOStatus.SPENT;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function processOutputs(
|
|
172
|
+
uint256[] calldata outputs
|
|
173
|
+
) public virtual onlyZeto {
|
|
174
|
+
for (uint256 i = 0; i < outputs.length; ++i) {
|
|
175
|
+
if (outputs[i] != 0) {
|
|
176
|
+
_utxos[outputs[i]] = UTXOStatus.UNSPENT;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function processLockedOutputs(
|
|
182
|
+
uint256[] calldata lockedOutputs
|
|
183
|
+
) public virtual onlyZeto {
|
|
184
|
+
// register the locked UTXOs as UNSPENT in the locked-UTXO ledger.
|
|
185
|
+
for (uint256 i = 0; i < lockedOutputs.length; ++i) {
|
|
186
|
+
if (lockedOutputs[i] != 0) {
|
|
187
|
+
_lockedUtxos[lockedOutputs[i]] = UTXOStatus.UNSPENT;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function locked(uint256 utxo) public view returns (bool) {
|
|
193
|
+
return _lockedUtxos[utxo] == UTXOStatus.UNSPENT;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function spent(uint256 utxo) public view virtual returns (UTXOStatus) {
|
|
197
|
+
if (
|
|
198
|
+
_utxos[utxo] == UTXOStatus.SPENT ||
|
|
199
|
+
_lockedUtxos[utxo] == UTXOStatus.SPENT
|
|
200
|
+
) {
|
|
201
|
+
return UTXOStatus.SPENT;
|
|
202
|
+
} else if (
|
|
203
|
+
_utxos[utxo] == UTXOStatus.UNSPENT ||
|
|
204
|
+
_lockedUtxos[utxo] == UTXOStatus.UNSPENT
|
|
205
|
+
) {
|
|
206
|
+
return UTXOStatus.UNSPENT;
|
|
207
|
+
}
|
|
208
|
+
return UTXOStatus.UNKNOWN;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Copyright © 2025 Kaleido, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
// you may not use this file except in compliance with the License.
|
|
7
|
+
// You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
// See the License for the specific language governing permissions and
|
|
15
|
+
// limitations under the License.
|
|
16
|
+
pragma solidity ^0.8.27;
|
|
17
|
+
|
|
18
|
+
import {IZetoConstants, MAX_SMT_DEPTH} from "../interfaces/IZeto.sol";
|
|
19
|
+
import {IZetoStorage} from "../interfaces/IZetoStorage.sol";
|
|
20
|
+
import {Commonlib} from "../common/common.sol";
|
|
21
|
+
import {Util} from "../common/util.sol";
|
|
22
|
+
import {SmtLib} from "@iden3/contracts/contracts/lib/SmtLib.sol";
|
|
23
|
+
import {IHasher} from "@iden3/contracts/contracts/interfaces/IHasher.sol";
|
|
24
|
+
import {PoseidonUnit3L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
|
|
25
|
+
import {PoseidonHasher} from "@iden3/contracts/contracts/lib/hash/PoseidonHasher.sol";
|
|
26
|
+
import {BaseStorage} from "./base.sol";
|
|
27
|
+
|
|
28
|
+
contract NullifierStorage is BaseStorage {
|
|
29
|
+
// used for tracking regular (unlocked) UTXOs
|
|
30
|
+
// locked UTXOs are tracked in the base storage
|
|
31
|
+
SmtLib.Data internal _commitmentsTree;
|
|
32
|
+
using SmtLib for SmtLib.Data;
|
|
33
|
+
|
|
34
|
+
mapping(uint256 => bool) private _nullifiers;
|
|
35
|
+
|
|
36
|
+
error UTXORootNotFound(uint256 root);
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
_commitmentsTree.initialize(MAX_SMT_DEPTH);
|
|
40
|
+
IHasher hasher = new PoseidonHasher();
|
|
41
|
+
_commitmentsTree.setHasher(hasher);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateInputs(
|
|
45
|
+
uint256[] calldata inputs,
|
|
46
|
+
bool inputsLocked
|
|
47
|
+
) public view override {
|
|
48
|
+
if (inputsLocked) {
|
|
49
|
+
// locked inputs are regular UTXOs (rather than nullifiers)
|
|
50
|
+
// and are validated in the base storage
|
|
51
|
+
super.validateInputs(inputs, inputsLocked);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// sort the nullifiers to detect duplicates
|
|
56
|
+
uint256[] memory sortedInputs = Util.sortCommitments(inputs);
|
|
57
|
+
|
|
58
|
+
// Check the inputs are all unspent
|
|
59
|
+
for (uint256 i = 0; i < sortedInputs.length; ++i) {
|
|
60
|
+
if (sortedInputs[i] == 0) {
|
|
61
|
+
// skip the zero inputs
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (i > 0 && sortedInputs[i] == sortedInputs[i - 1]) {
|
|
65
|
+
revert UTXODuplicate(sortedInputs[i]);
|
|
66
|
+
}
|
|
67
|
+
if (_nullifiers[sortedInputs[i]] == true) {
|
|
68
|
+
revert UTXOAlreadySpent(sortedInputs[i]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function validateOutputs(uint256[] calldata outputs) public view override {
|
|
74
|
+
// sort the outputs to detect duplicates
|
|
75
|
+
uint256[] memory sortedOutputs = Util.sortCommitments(outputs);
|
|
76
|
+
|
|
77
|
+
// Check the outputs are all new UTXOs
|
|
78
|
+
for (uint256 i = 0; i < sortedOutputs.length; ++i) {
|
|
79
|
+
if (sortedOutputs[i] == 0) {
|
|
80
|
+
// skip the zero outputs
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (i > 0 && sortedOutputs[i] == sortedOutputs[i - 1]) {
|
|
84
|
+
revert UTXODuplicate(sortedOutputs[i]);
|
|
85
|
+
}
|
|
86
|
+
// check the unlocked commitments tree
|
|
87
|
+
bool existsInTree = exists(sortedOutputs[i]);
|
|
88
|
+
if (existsInTree) {
|
|
89
|
+
revert UTXOAlreadyOwned(sortedOutputs[i]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateRoot(uint256 root) public view override returns (bool) {
|
|
95
|
+
// Check if the root has existed before. It does not need to be the latest root.
|
|
96
|
+
// Our SMT is append-only, so if the root has existed before, and the merklet proof
|
|
97
|
+
// is valid, then the leaves still exist in the tree.
|
|
98
|
+
if (!_commitmentsTree.rootExists(root)) {
|
|
99
|
+
revert UTXORootNotFound(root);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getRoot() public view override returns (uint256) {
|
|
106
|
+
return _commitmentsTree.getRoot();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function processInputs(
|
|
110
|
+
uint256[] calldata nullifiers,
|
|
111
|
+
bool inputsLocked
|
|
112
|
+
) public override onlyZeto {
|
|
113
|
+
if (inputsLocked) {
|
|
114
|
+
// locked inputs are regular UTXOs (rather than nullifiers)
|
|
115
|
+
// and are processed in the base storage
|
|
116
|
+
super.processInputs(nullifiers, inputsLocked);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (uint256 i = 0; i < nullifiers.length; ++i) {
|
|
121
|
+
if (nullifiers[i] != 0) {
|
|
122
|
+
_nullifiers[nullifiers[i]] = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function processOutputs(
|
|
128
|
+
uint256[] calldata outputs
|
|
129
|
+
) public override onlyZeto {
|
|
130
|
+
for (uint256 i = 0; i < outputs.length; ++i) {
|
|
131
|
+
if (outputs[i] != 0) {
|
|
132
|
+
_commitmentsTree.addLeaf(outputs[i], outputs[i]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function spent(uint256 utxo) public view override returns (UTXOStatus) {
|
|
138
|
+
// by design, the contract does not know this
|
|
139
|
+
return UTXOStatus.UNKNOWN;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// check the existence of a UTXO in either the unlocked or locked commitments storage
|
|
143
|
+
function exists(uint256 utxo) internal view returns (bool) {
|
|
144
|
+
if (everExistedAsUnlocked(utxo)) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return existsAsLocked(utxo);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// check the existence of a UTXO in the commitments tree. we take a shortcut
|
|
151
|
+
// by checking the list of nodes by their node hash, because the commitments
|
|
152
|
+
// tree is append-only, no updates or deletions are allowed. As a result, all
|
|
153
|
+
// nodes in the list are valid leaf nodes, aka there are no orphaned nodes.
|
|
154
|
+
function everExistedAsUnlocked(uint256 utxo) internal view returns (bool) {
|
|
155
|
+
uint256 nodeHash = Util.getLeafNodeHash(utxo, utxo);
|
|
156
|
+
SmtLib.Node memory node = _commitmentsTree.getNode(nodeHash);
|
|
157
|
+
return node.nodeType != SmtLib.NodeType.EMPTY;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function existsAsLocked(uint256 utxo) internal view returns (bool) {
|
|
161
|
+
if (super.locked(utxo)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
return super.spent(utxo) != UTXOStatus.UNKNOWN;
|
|
165
|
+
}
|
|
166
|
+
}
|