Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert to upgradeable and cloneable contracts #43

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,21 @@ jobs:
npm install
npm run test:e2e

- name: Run Zeto Tokens hardhat tests
- name: Run Zeto Tokens hardhat tests as upgradeable contracts
env:
PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts
CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts
working-directory: solidity
run: |
npm install
npm t

- name: Run Zeto Tokens hardhat tests as cloned contracts
env:
USE_FACTORY: true
PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts
CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts
working-directory: solidity
run: |
npm install
npm t
3 changes: 2 additions & 1 deletion solidity/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
node_modules
artifacts
cache
typechain-types
typechain-types
ignition/deployments
18 changes: 18 additions & 0 deletions solidity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ This project contains sample implementations of privacy preserving tokens for bo

The Hardhat test cases make use of the `zeto-js` library, which must be built first. Refer to the steps in [the library's README](/zkp/js/README.md#build) to build the proving keys, and verification keys. Make sure you can successfully run the unit tests for the zeto-js library, before returning back here to continue with the hardhat tests for the Solidity implementation.

# Deploy Zeto Token Contracts

Zeto token contracts are all upgradeable contracts. They can be deployed with one of the two hardhat scripts:

- [deploy_upgradeable](/solidity/scripts/deploy_upgradeable.ts): Deploys the target contract, designated by the `ZETO_NAME` environment variable, as a [UUPSUpgradeable contract](https://docs.openzeppelin.com/contracts/4.x/api/proxy#transparent-vs-uups).

```console
export ZETO_NAME=Zeto_AnonEncNullifier
npx hardhat run scripts/deploy_upgradeable.js
```

- [deploy_cloneable](/solidity/scripts/deploy_cloneable.ts): Deploys the target contract, designated by the `ZETO_NAME` environment variable, as a [cloneable contract](https://blog.openzeppelin.com/workshop-recap-cheap-contract-deployment-through-clones).

```console
export ZETO_NAME=Zeto_AnonEncNullifier
npx hardhat run scripts/deploy_cloneable.js
```

# Run The Hardhat Tests

Once the above pre-reqs are complete, you can proceed to run the hardhat tests in this project.
Expand Down
7 changes: 3 additions & 4 deletions solidity/contracts/erc20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ import "hardhat/console.sol";
/// - the hashes in the input and output match the `hash(value, salt, owner public key)` formula
/// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes
contract SampleERC20 is ERC20, Ownable {
constructor()
ERC20("Sample ERC20 token", "SampleERC20")
Ownable(msg.sender)
{
constructor(
address initialOwner
) ERC20("Sample ERC20 token", "SampleERC20") Ownable(initialOwner) {
_mint(msg.sender, 1000000 * 10 ** 18);
}

Expand Down
75 changes: 75 additions & 0 deletions solidity/contracts/factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma solidity ^0.8.20;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {IZetoFungibleInitializable} from "./lib/interfaces/zeto_fungible_initializable.sol";
import {IZetoNonFungibleInitializable} from "./lib/interfaces/zeto_nf_initializable.sol";

contract ZetoTokenFactory {
event ZetoTokenDeployed(address indexed zetoToken);

mapping(string => address) internal implementations;

constructor() {}

function registerImplementation(
string memory name,
address implementation
) public {
implementations[name] = implementation;
}

function deployZetoFungibleToken(
string memory name,
address initialOwner,
address _depositVerifier,
address _withdrawVerifier,
address _verifier
) public returns (address) {
address instance = Clones.clone(implementations[name]);
require(
instance != address(0),
"Factory: failed to find implementation"
);
(IZetoFungibleInitializable(instance)).initialize(
initialOwner,
_depositVerifier,
_withdrawVerifier,
_verifier
);
emit ZetoTokenDeployed(instance);
return instance;
}

function deployZetoNonFungibleToken(
string memory name,
address initialOwner,
address _verifier
) public returns (address) {
address instance = Clones.clone(implementations[name]);
require(
instance != address(0),
"Factory: failed to find implementation"
);
(IZetoNonFungibleInitializable(instance)).initialize(
initialOwner,
_verifier
);
emit ZetoTokenDeployed(instance);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels there are other information that could be useful to emit. But they can be added based on the use case in the future.

return instance;
}
}
25 changes: 25 additions & 0 deletions solidity/contracts/lib/interfaces/zeto_fungible_initializable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma solidity ^0.8.20;

interface IZetoFungibleInitializable {
function initialize(
address initialOwner,
address _depositVerifier,
address _withdrawVerifier,
address _verifier
) external;
}
20 changes: 20 additions & 0 deletions solidity/contracts/lib/interfaces/zeto_nf_initializable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma solidity ^0.8.20;

interface IZetoNonFungibleInitializable {
function initialize(address initialOwner, address _verifier) external;
}
5 changes: 3 additions & 2 deletions solidity/contracts/lib/registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// limitations under the License.
pragma solidity ^0.8.20;

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {SmtLib} from "@iden3/contracts/lib/SmtLib.sol";
import {PoseidonUnit2L, PoseidonUnit3L} from "@iden3/contracts/lib/Poseidon.sol";
import {Commonlib} from "./common.sol";
Expand All @@ -28,15 +29,15 @@ uint256 constant MAX_SMT_DEPTH = 64;
/// submitters can generate proofs of membership for the
/// accounts in a privacy-preserving manner.
/// @author Kaleido, Inc.
abstract contract Registry {
abstract contract Registry is OwnableUpgradeable {
SmtLib.Data internal _publicKeysTree;
using SmtLib for SmtLib.Data;

event IdentityRegistered(uint256[2] publicKey);

error AlreadyRegistered(uint256[2]);

constructor() {
function __Registry_init() internal onlyInitializing {
_publicKeysTree.initialize(MAX_SMT_DEPTH);
}

Expand Down
4 changes: 3 additions & 1 deletion solidity/contracts/lib/zeto_base.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ abstract contract ZetoBase is ZetoCommon {
// maintains all the UTXOs
mapping(uint256 => UTXOStatus) internal _utxos;

constructor() ZetoCommon() {}
function __ZetoBase_init(address initialOwner) internal onlyInitializing {
__ZetoCommon_init(initialOwner);
}

/// @dev query whether a UTXO is currently spent
/// @return bool whether the UTXO is spent
Expand Down
7 changes: 5 additions & 2 deletions solidity/contracts/lib/zeto_common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ pragma solidity ^0.8.20;

import {Commonlib} from "./common.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/// @title A sample base implementation of a Zeto based token contract
/// @author Kaleido, Inc.
/// @dev Implements common functionalities of Zeto based tokens
abstract contract ZetoCommon is Ownable {
abstract contract ZetoCommon is OwnableUpgradeable {
event UTXOMint(uint256[] outputs, address indexed submitter);

event UTXOTransfer(
Expand Down Expand Up @@ -51,7 +52,9 @@ abstract contract ZetoCommon is Ownable {
// that did the locking.
mapping(bytes32 => address) internal lockedProofs;

constructor() Ownable(msg.sender) {}
function __ZetoCommon_init(address initialOwner) internal onlyInitializing {
__Ownable_init(initialOwner);
}

// should be called by escrow contracts that will use uploaded proofs
// to execute transactions, in order to prevent the proof from being used
Expand Down
8 changes: 5 additions & 3 deletions solidity/contracts/lib/zeto_fungible.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ import {Groth16Verifier_CheckHashesValue} from "./verifier_check_hashes_value.so
import {Groth16Verifier_CheckNullifierValue} from "./verifier_check_nullifier_value.sol";
import {Commonlib} from "./common.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/// @title A sample implementation of a base Zeto fungible token contract
/// @author Kaleido, Inc.
/// @dev Defines the verifier library for checking UTXOs against a claimed value.
abstract contract ZetoFungible is Ownable {
abstract contract ZetoFungible is OwnableUpgradeable {
// depositVerifier library for checking UTXOs against a claimed value.
// this can be used in the optional deposit calls to verify that
// the UTXOs match the deposited value
Groth16Verifier_CheckHashesValue internal depositVerifier;

IERC20 internal erc20;

constructor(Groth16Verifier_CheckHashesValue _depositVerifier) {
function __ZetoFungible_init(
Groth16Verifier_CheckHashesValue _depositVerifier
) public onlyInitializing {
depositVerifier = _depositVerifier;
}

Expand Down
6 changes: 3 additions & 3 deletions solidity/contracts/lib/zeto_fungible_withdraw.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {Groth16Verifier_CheckInputsOutputsValue} from "./verifier_check_inputs_o
import {ZetoFungible} from "./zeto_fungible.sol";
import {Commonlib} from "./common.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/// @title A sample implementation of a base Zeto fungible token contract
/// @author Kaleido, Inc.
Expand All @@ -31,10 +30,11 @@ abstract contract ZetoFungibleWithdraw is ZetoFungible {
// match the withdrawn value
Groth16Verifier_CheckInputsOutputsValue internal withdrawVerifier;

constructor(
function __ZetoFungibleWithdraw_init(
Groth16Verifier_CheckHashesValue _depositVerifier,
Groth16Verifier_CheckInputsOutputsValue _withdrawVerifier
) ZetoFungible(_depositVerifier) {
) public onlyInitializing {
__ZetoFungible_init(_depositVerifier);
withdrawVerifier = _withdrawVerifier;
}

Expand Down
5 changes: 3 additions & 2 deletions solidity/contracts/lib/zeto_fungible_withdraw_nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ abstract contract ZetoFungibleWithdrawWithNullifiers is ZetoFungible {
// match the withdrawn value
Groth16Verifier_CheckNullifierValue internal withdrawVerifier;

constructor(
function __ZetoFungibleWithdrawWithNullifiers_init(
Groth16Verifier_CheckHashesValue _depositVerifier,
Groth16Verifier_CheckNullifierValue _withdrawVerifier
) ZetoFungible(_depositVerifier) {
) internal onlyInitializing {
__ZetoFungible_init(_depositVerifier);
withdrawVerifier = _withdrawVerifier;
}

Expand Down
5 changes: 4 additions & 1 deletion solidity/contracts/lib/zeto_nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ abstract contract ZetoNullifier is ZetoCommon {

error UTXORootNotFound(uint256 root);

constructor() ZetoCommon() {
function __ZetoNullifier_init(
address initialOwner
) internal onlyInitializing {
__ZetoCommon_init(initialOwner);
_commitmentsTree.initialize(MAX_SMT_DEPTH);
}

Expand Down
12 changes: 9 additions & 3 deletions solidity/contracts/zeto_anon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {ZetoBase} from "./lib/zeto_base.sol";
import {ZetoFungible} from "./lib/zeto_fungible.sol";
import {ZetoFungibleWithdraw} from "./lib/zeto_fungible_withdraw.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "hardhat/console.sol";

/// @title A sample implementation of a Zeto based fungible token with anonymity and no encryption
Expand All @@ -33,17 +34,22 @@ import "hardhat/console.sol";
/// - the sum of the input values match the sum of output values
/// - the hashes in the input and output match the `hash(value, salt, owner public key)` formula
/// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes
contract Zeto_Anon is ZetoBase, ZetoFungibleWithdraw {
contract Zeto_Anon is ZetoBase, ZetoFungibleWithdraw, UUPSUpgradeable {
Groth16Verifier_Anon internal verifier;

constructor(
function initialize(
address initialOwner,
Groth16Verifier_CheckHashesValue _depositVerifier,
Groth16Verifier_CheckInputsOutputsValue _withdrawVerifier,
Groth16Verifier_Anon _verifier
) ZetoBase() ZetoFungibleWithdraw(_depositVerifier, _withdrawVerifier) {
) public initializer {
__ZetoBase_init(initialOwner);
__ZetoFungibleWithdraw_init(_depositVerifier, _withdrawVerifier);
verifier = _verifier;
}

function _authorizeUpgrade(address) internal override onlyOwner {}

/**
* @dev the main function of the contract.
*
Expand Down
12 changes: 9 additions & 3 deletions solidity/contracts/zeto_anon_enc.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {ZetoFungible} from "./lib/zeto_fungible.sol";
import {Registry} from "./lib/registry.sol";
import {Commonlib} from "./lib/common.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "hardhat/console.sol";

/// @title A sample implementation of a Zeto based fungible token with anonymity, and encryption
Expand All @@ -35,17 +36,22 @@ import "hardhat/console.sol";
/// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes
/// - the encrypted value in the input is derived from the receiver's UTXO value and encrypted with a shared secret using
/// the ECDH protocol between the sender and receiver (this guarantees data availability for the receiver)
contract Zeto_AnonEnc is ZetoBase, ZetoFungibleWithdraw {
contract Zeto_AnonEnc is ZetoBase, ZetoFungibleWithdraw, UUPSUpgradeable {
Groth16Verifier_AnonEnc internal verifier;

constructor(
function initialize(
address initialOwner,
Groth16Verifier_CheckHashesValue _depositVerifier,
Groth16Verifier_CheckInputsOutputsValue _withdrawVerifier,
Groth16Verifier_AnonEnc _verifier
) ZetoBase() ZetoFungibleWithdraw(_depositVerifier, _withdrawVerifier) {
) public initializer {
__ZetoBase_init(initialOwner);
__ZetoFungibleWithdraw_init(_depositVerifier, _withdrawVerifier);
verifier = _verifier;
}

function _authorizeUpgrade(address) internal override onlyOwner {}

/**
* @dev the main function of the contract.
*
Expand Down
Loading
Loading