From 0eefc997348182d952b469b3b12937b4a38093ce Mon Sep 17 00:00:00 2001 From: Angelo Capossele Date: Wed, 26 Jun 2024 10:10:51 +0100 Subject: [PATCH] Add mainnet deployment (#119) * add mainnet deployment * fix typos * add note on RISC Zero managed RiscZeroVerifierRouter * fmt * use config files * ignore mainnet broadcast * fix typo * fmt * fix typo * use profile config * fmt * fix typo * use riscZeroVerifierAddress * rename config * simplify gitignore * Suggestions for PR #119 (#122) * refactor to make defaults based on the chainId and support deployment with ledger * add note about the lack of support for hardware wallets * run forge fmt * remove warning tag --------- Co-authored-by: Victor Graf --- .gitignore | 5 +-- README.md | 2 +- deployment-guide.md | 102 +++++++++++++++++++++++++++++++++++++++++--- foundry.toml | 1 + script/Deploy.s.sol | 70 +++++++++++++++++++++++++++--- script/config.toml | 14 ++++++ 6 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 script/config.toml diff --git a/.gitignore b/.gitignore index 44ee06f1..f8d7cf56 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ cache/ out/ # Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/*/11155111/ -/broadcast/**/dry-run/ +broadcast/ # Autogenerated contracts contracts/ImageID.sol diff --git a/README.md b/README.md index 5a35d9fd..edacd0d3 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ RISC0_USE_DOCKER=1 cargo build ## Deploy Your Application -When you're ready, follow the [deployment guide] to get your application running on [Sepolia]. +When you're ready, follow the [deployment guide] to get your application running on [Sepolia] or Ethereum Mainnet. ## Project Structure diff --git a/deployment-guide.md b/deployment-guide.md index b212b361..09da9764 100644 --- a/deployment-guide.md +++ b/deployment-guide.md @@ -8,6 +8,7 @@ You can either: - [Deploy your project to a local network] - [Deploy to a testnet] +- [Deploy to Ethereum Mainnet] ## Deploy your project on a local network @@ -49,6 +50,7 @@ You can deploy your contracts and run an end-to-end test or demo as follows: ```bash ... == Logs == + You are deploying on ChainID 31337 Deployed RiscZeroGroth16Verifier to 0x5FbDB2315678afecb367f032d93F642f64180aa3 Deployed EvenNumber to 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 ... @@ -90,11 +92,11 @@ You can deploy your contracts and run an end-to-end test or demo as follows: cast call --rpc-url http://localhost:8545 ${EVEN_NUMBER_ADDRESS:?} 'get()(uint256)' ``` -## Deploy your project on a testnet +## Deploy your project on Sepolia testnet -You can deploy your contracts on a testnet such as `Sepolia` and run an end-to-end test or demo as follows: +You can deploy your contracts on the `Sepolia` testnet and run an end-to-end test or demo as follows: -1. Get access to Bonsai and an Ethereum node running on a given testnet, e.g., Sepolia (in this example, we will be using [Alchemy](https://www.alchemy.com/) as our Ethereum node provider) and export the following environment variables: +1. Get access to Bonsai and an Ethereum node running on Sepolia testnet (in this example, we will be using [Alchemy](https://www.alchemy.com/) as our Ethereum node provider) and export the following environment variables: > ***Note:*** *This requires having access to a Bonsai API Key. To request an API key [complete the form here](https://bonsai.xyz/apply).* > Alternatively you can generate your proofs locally, assuming you have a machine with an x86 architecture and [Docker] installed. In this case do not export Bonsai related env variables. @@ -117,12 +119,14 @@ You can deploy your contracts on a testnet such as `Sepolia` and run an end-to-e forge script script/Deploy.s.sol --rpc-url https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} --broadcast ``` - This command should output something similar to: + This command uses the `sepolia` profile defined in the [config][config] file, and should output something similar to: ```bash ... == Logs == - Deployed RiscZeroGroth16Verifier to 0x5FbDB2315678afecb367f032d93F642f64180aa3 + You are deploying on ChainID 11155111 + Deploying using config profile: sepolia + Using IRiscZeroVerifier contract deployed at 0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 Deployed EvenNumber to 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 ... ``` @@ -139,7 +143,7 @@ You can deploy your contracts on a testnet such as `Sepolia` and run an end-to-e > export EVEN_NUMBER_ADDRESS=$(jq -re '.transactions[] | select(.contractName == "EvenNumber") | .contractAddress' ./broadcast/Deploy.s.sol/11155111/run-latest.json) > ``` -### Interact with your testnet deployment +### Interact with your Sepolia testnet deployment 1. Query the state: @@ -163,10 +167,94 @@ You can deploy your contracts on a testnet such as `Sepolia` and run an end-to-e cast call --rpc-url https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} ${EVEN_NUMBER_ADDRESS:?} 'get()(uint256)' ``` -[Deploy to a testnet]: #deploy-your-project-on-a-testnet +## Deploy your project on Ethereum mainnet + +You can deploy your contract on Ethereum Mainnet as follows: + +1. Get access to Bonsai and an Ethereum node running on Mainnet (in this example, we will be using [Alchemy](https://www.alchemy.com/) as our Ethereum node provider) and export the following environment variables: + > ***Note:*** *This requires having access to a Bonsai API Key. To request an API key [complete the form here](https://bonsai.xyz/apply).* + > Alternatively you can generate your proofs locally, assuming you have a machine with an x86 architecture and [Docker] installed. In this case do not export Bonsai related env variables. + + ```bash + export BONSAI_API_KEY="YOUR_API_KEY" # see form linked in the previous section + export BONSAI_API_URL="BONSAI_API_URL" # provided with your api key + export ALCHEMY_API_KEY="YOUR_ALCHEMY_API_KEY" # the API_KEY provided with an alchemy account + export ETH_WALLET_ADDRESS="YOUR_WALLET_ADDRESS" # the account address you want to use for deployment + ``` + +2. Build your project: + + ```bash + cargo build + ``` + +3. Deploy your contract by running: + + You'll need to pass options to forge script to connect to your deployer wallet. See the [Foundry documentation][forge-script-wallet-docs]. + The example command below configures Forge to use a Ledger hardware wallet. + + ```bash + forge script script/Deploy.s.sol --rpc-url https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} --broadcast --ledger + ``` + + This command uses the `mainnet` profile defined in the [config][config] file, and should output something similar to: + + ```bash + ... + == Logs == + You are deploying on ChainID 1 + Deploying using config profile: mainnet + Using IRiscZeroVerifier contract deployed at 0x8EaB2D97Dfce405A1692a21b3ff3A172d593D319 + Deployed EvenNumber to 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + ... + ``` + + Save the `EvenNumber` contract address to an env variable: + + ```bash + export EVEN_NUMBER_ADDRESS=#COPY EVEN NUMBER ADDRESS FROM DEPLOY LOGS + ``` + + > You can also use the following command to set the contract address if you have [`jq`][jq] installed: + > + > ```bash + > export EVEN_NUMBER_ADDRESS=$(jq -re '.transactions[] | select(.contractName == "EvenNumber") | .contractAddress' ./broadcast/Deploy.s.sol/1/run-latest.json) + > ``` + +### Interact with your Ethereum Mainnet deployment + +1. Query the state: + + ```bash + cast call --rpc-url https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} ${EVEN_NUMBER_ADDRESS:?} 'get()(uint256)' + ``` + +2. Publish a new state + + > NOTE: Currently only a local wallet, provided by the `ETH_WALLET_PRIVATE_KEY` env var is implemented in the example publisher app. + > Please see https://github.com/risc0/risc0-foundry-template/issues/121 for more details. + + ```bash + cargo run --bin publisher -- \ + --chain-id=1 \ + --rpc-url=https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} \ + --contract=${EVEN_NUMBER_ADDRESS:?} \ + --input=12345678 + ``` + +3. Query the state again to see the change: + + ```bash + cast call --rpc-url https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY:?} ${EVEN_NUMBER_ADDRESS:?} 'get()(uint256)' + ``` + +[Deploy to Ethereum Mainnet]: #deploy-your-project-on-ethereum-mainnet [Deploy your project to a local network]: #deploy-your-project-on-a-local-network [RISC Zero]: https://www.risczero.com/ +[Docker]: https://docs.docker.com/engine/install/ [contracts]: ./contracts/ [jq]: https://jqlang.github.io/jq/ [methods]: ./methods/ [tested]: ./README.md#run-the-tests +[config]: ./script/config.toml +[forge-script-wallet-docs]: https://book.getfoundry.sh/reference/forge/forge-script#wallet-options---raw diff --git a/foundry.toml b/foundry.toml index 0612bcc3..d9d9d6f0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,5 +4,6 @@ out = "out" libs = ["lib"] test = "tests" ffi = true +fs_permissions = [{ access = "read-write", path = "./"}] # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index cf7c0a6a..0d76455b 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -17,7 +17,7 @@ pragma solidity ^0.8.20; import {Script} from "forge-std/Script.sol"; -import {console2} from "forge-std/console2.sol"; +import "forge-std/Test.sol"; import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol"; import {RiscZeroGroth16Verifier} from "risc0/groth16/RiscZeroGroth16Verifier.sol"; import {ControlID} from "risc0/groth16/ControlID.sol"; @@ -26,19 +26,75 @@ import {EvenNumber} from "../contracts/EvenNumber.sol"; /// @notice Deployment script for the RISC Zero starter project. /// @dev Use the following environment variable to control the deployment: -/// * ETH_WALLET_PRIVATE_KEY private key of the wallet to be used for deployment. +/// * Set one of these two environment variables to control the deployment wallet: +/// * ETH_WALLET_PRIVATE_KEY private key of the wallet account. +/// * ETH_WALLET_ADDRESS address of the wallet account. +/// +/// See the Foundry documentation for more information about Solidity scripts, +/// including information about wallet options. /// -/// See the Foundry documentation for more information about Solidity scripts. /// https://book.getfoundry.sh/tutorials/solidity-scripting +/// https://book.getfoundry.sh/reference/forge/forge-script contract EvenNumberDeploy is Script { + // Path to deployment config file, relative to the project root. + string constant CONFIG_FILE = "script/config.toml"; + + IRiscZeroVerifier verifier; + function run() external { - uint256 deployerKey = uint256(vm.envBytes32("ETH_WALLET_PRIVATE_KEY")); + // Read and log the chainID + uint256 chainId = block.chainid; + console2.log("You are deploying on ChainID %d", chainId); + + // Read the config profile from the environment variable, or use the default for the chainId. + // Default is the first profile with a matching chainId field. + string memory config = vm.readFile(string.concat(vm.projectRoot(), "/", CONFIG_FILE)); + string memory configProfile = vm.envOr("CONFIG_PROFILE", string("")); + if (bytes(configProfile).length == 0) { + string[] memory profileKeys = vm.parseTomlKeys(config, ".profile"); + for (uint256 i = 0; i < profileKeys.length; i++) { + if (stdToml.readUint(config, string.concat(".profile.", profileKeys[i], ".chainId")) == chainId) { + configProfile = profileKeys[i]; + break; + } + } + } + + if (bytes(configProfile).length != 0) { + console2.log("Deploying using config profile:", configProfile); + string memory configProfileKey = string.concat(".profile.", configProfile); + address riscZeroVerifierAddress = + stdToml.readAddress(config, string.concat(configProfileKey, ".riscZeroVerifierAddress")); + // If set, use the predeployed verifier address found in the config. + verifier = IRiscZeroVerifier(riscZeroVerifierAddress); + } + + // Determine the wallet to send transactions from. + uint256 deployerKey = uint256(vm.envOr("ETH_WALLET_PRIVATE_KEY", bytes32(0))); + address deployerAddr = address(0); + if (deployerKey != 0) { + // Check for conflicts in how the two environment variables are set. + address envAddr = vm.envOr("ETH_WALLET_ADDRESS", address(0)); + require( + envAddr == address(0) || envAddr == vm.addr(deployerKey), + "conflicting settings from ETH_WALLET_PRIVATE_KEY and ETH_WALLET_ADDRESS" + ); - vm.startBroadcast(deployerKey); + vm.startBroadcast(deployerKey); + } else { + deployerAddr = vm.envAddress("ETH_WALLET_ADDRESS"); + vm.startBroadcast(deployerAddr); + } - IRiscZeroVerifier verifier = new RiscZeroGroth16Verifier(ControlID.CONTROL_ROOT, ControlID.BN254_CONTROL_ID); - console2.log("Deployed RiscZeroGroth16Verifier to", address(verifier)); + // Deploy the verifier, if not already deployed. + if (address(verifier) == address(0)) { + verifier = new RiscZeroGroth16Verifier(ControlID.CONTROL_ROOT, ControlID.BN254_CONTROL_ID); + console2.log("Deployed RiscZeroGroth16Verifier to", address(verifier)); + } else { + console2.log("Using IRiscZeroVerifier contract deployed at", address(verifier)); + } + // Deploy the application contract. EvenNumber evenNumber = new EvenNumber(verifier); console2.log("Deployed EvenNumber to", address(evenNumber)); diff --git a/script/config.toml b/script/config.toml new file mode 100644 index 00000000..9c97f149 --- /dev/null +++ b/script/config.toml @@ -0,0 +1,14 @@ +[profile.mainnet] +# RISC Zero Verifier contract deployed on mainnet (see https://dev.risczero.com/api/blockchain-integration/contracts/verifier#deployed-verifiers) +chainId = 1 +riscZeroVerifierAddress = "0x8EaB2D97Dfce405A1692a21b3ff3A172d593D319" + +[profile.sepolia] +# RISC Zero Verifier contract deployed on sepolia (see https://dev.risczero.com/api/blockchain-integration/contracts/verifier#deployed-verifiers) +chainId = 11155111 +riscZeroVerifierAddress = "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" + +# You can add additional profiles here +# [profile.custom] +# chainId = 11155111 +# riscZeroVerifierAddress =