Skip to content

Commit f78f019

Browse files
caposselenategraf
andauthored
BM-233: ProofMarket upgrade via UUPS (github#56)
- Add Proof market upgradability via the UUPS pattern - Deploy the `RiscZeroSetVerifier` contract behind the `RiscZeroVerifierRouter` - Add contract management scripts The deployment should look like: ![Screenshot 2024-10-26 at 00 55 39](https://github.com/user-attachments/assets/07dc1bd5-d241-4679-b10f-9db63e949f43) Closes github#52 --------- Co-authored-by: Victor Graf <victor@risczero.com>
1 parent 05e4869 commit f78f019

27 files changed

+1760
-155
lines changed

.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Foundry configuration
2+
FOUNDRY_OUT=contracts/out
3+
14
# URL RPC node
25

36
RPC_URL="http://localhost:8545"
@@ -15,3 +18,4 @@ PROOF_MARKET_ADDRESS="0xb3e579794B6ce24bC7233713289790d9bBEB656c"
1518
### Wallets private keys ###
1619

1720
PRIVATE_KEY="0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6"
21+
ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ out/
88
/broadcast/*/11155111/
99
/broadcast/**/dry-run/
1010

11+
# Ignore the file with RPC and Etherscan API keys for deployment.
12+
deployment_secrets.toml
13+
1114
# Devnet logs
1215
/logs
1316

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
[submodule "lib/openzeppelin-contracts"]
55
path = lib/openzeppelin-contracts
66
url = https://github.com/OpenZeppelin/openzeppelin-contracts
7+
[submodule "lib/openzeppelin-contracts-upgradeable"]
8+
path = lib/openzeppelin-contracts-upgradeable
9+
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
710
[submodule "lib/risc0-ethereum"]
811
path = lib/risc0-ethereum
912
url = https://github.com/risc0/risc0-ethereum
13+
[submodule "lib/openzeppelin-foundry-upgrades"]
14+
path = lib/openzeppelin-foundry-upgrades
15+
url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades

Makefile

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
ANVIL_PORT = 8545
88
ANVIL_BLOCK_TIME = 2
99
RISC0_DEV_MODE = 1
10+
CHAIN_KEY = "anvil"
1011
RUST_LOG = info,broker=debug,boundless_market=debug
12+
DEPLOYER_PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
1113
PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
14+
ADMIN_ADDRESS = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
1215
DEPOSIT_AMOUNT = 10
1316

1417
LOGS_DIR = logs
@@ -28,15 +31,20 @@ devnet-up: check-deps
2831
forge build || { echo "Failed to build contracts"; $(MAKE) devnet-down; exit 1; }
2932
echo "Building Rust project..."
3033
cargo build --bin broker || { echo "Failed to build broker binary"; $(MAKE) devnet-down; exit 1; }
31-
echo "Starting Anvil..."
32-
anvil -b $(ANVIL_BLOCK_TIME) > $(LOGS_DIR)/anvil.txt 2>&1 & echo $$! >> $(PID_FILE)
33-
sleep 5
34+
# Check if Anvil is already running
35+
if nc -z localhost $(ANVIL_PORT); then \
36+
echo "Anvil is already running on port $(ANVIL_PORT). Reusing existing instance."; \
37+
else \
38+
echo "Starting Anvil..."; \
39+
anvil -b $(ANVIL_BLOCK_TIME) > $(LOGS_DIR)/anvil.txt 2>&1 & echo $$! >> $(PID_FILE); \
40+
sleep 5; \
41+
fi
3442
echo "Deploying contracts..."
35-
RISC0_DEV_MODE=$(RISC0_DEV_MODE) forge script contracts/scripts/Deploy.s.sol --rpc-url http://localhost:$(ANVIL_PORT) --broadcast -vv || { echo "Failed to deploy contracts"; $(MAKE) devnet-down; exit 1; }
43+
DEPLOYER_PRIVATE_KEY=$(DEPLOYER_PRIVATE_KEY) CHAIN_KEY=${CHAIN_KEY} RISC0_DEV_MODE=$(RISC0_DEV_MODE) PROOF_MARKET_OWNER=$(ADMIN_ADDRESS) forge script contracts/scripts/Deploy.s.sol --rpc-url http://localhost:$(ANVIL_PORT) --broadcast -vv || { echo "Failed to deploy contracts"; $(MAKE) devnet-down; exit 1; }
3644
echo "Fetching contract addresses..."
3745
{ \
3846
SET_VERIFIER_ADDRESS=$$(jq -re '.transactions[] | select(.contractName == "RiscZeroSetVerifier") | .contractAddress' ./broadcast/Deploy.s.sol/31337/run-latest.json); \
39-
PROOF_MARKET_ADDRESS=$$(jq -re '.transactions[] | select(.contractName == "ProofMarket") | .contractAddress' ./broadcast/Deploy.s.sol/31337/run-latest.json); \
47+
PROOF_MARKET_ADDRESS=$$(jq -re '.transactions[] | select(.contractName == "ERC1967Proxy") | .contractAddress' ./broadcast/Deploy.s.sol/31337/run-latest.json); \
4048
echo "Contract deployed at addresses:"; \
4149
echo "SET_VERIFIER_ADDRESS=$$SET_VERIFIER_ADDRESS"; \
4250
echo "PROOF_MARKET_ADDRESS=$$PROOF_MARKET_ADDRESS"; \

contracts/deployment.toml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[chains.anvil]
2+
name = "Anvil"
3+
id = 31337
4+
etherscan-url = ""
5+
6+
# Accounts
7+
admin = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
8+
9+
# Contracts
10+
router = "0x0000000000000000000000000000000000000000"
11+
set-verifier = "0x0000000000000000000000000000000000000000"
12+
proof-market = "0x0000000000000000000000000000000000000000"
13+
14+
# Guests info
15+
set-builder-image-id = "0xa6d595680f98dddcf6649199cb65a374f2e7d4a234c22cd28799d67f027db065"
16+
set-builder-guest-url = "https://gateway.pinata.cloud/ipfs/QmPobaLheQtpbSLn2QPGjnSM9Yy475hV9xAbMpyUXZ3zZA"
17+
assessor-image-id = "0x4b379fe9f76ae2fda4afa7962f5cf502af02e95022fcc089f1629fb444dbf18e"
18+
assessor-guest-url = "https://gateway.pinata.cloud/ipfs/QmbaAMsfA12WX5xeqjT1k1tzBZKCC11UMM5naQqtEaVw1R"
19+
20+
21+
###
22+
23+
[chains.ethereum-sepolia]
24+
name = "Ethereum Sepolia"
25+
id = 11155111
26+
etherscan-url = "https://sepolia.etherscan.io/"
27+
28+
# Accounts
29+
admin = "0x3a54A45E44a71020Bd0Af42063B9f23e8b9E387D"
30+
31+
# Contracts
32+
router = "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187"
33+
set-verifier = "0x6860e6cC6E4705309b3e946947706b4a346422DB"
34+
proof-market = "0x0000000000000000000000000000000000000000"
35+
36+
# Guests info
37+
set-builder-image-id = "0xa6d595680f98dddcf6649199cb65a374f2e7d4a234c22cd28799d67f027db065"
38+
set-builder-guest-url = "https://gateway.pinata.cloud/ipfs/QmPobaLheQtpbSLn2QPGjnSM9Yy475hV9xAbMpyUXZ3zZA"
39+
assessor-image-id = "0x4b379fe9f76ae2fda4afa7962f5cf502af02e95022fcc089f1629fb444dbf18e"
40+
assessor-guest-url = "https://gateway.pinata.cloud/ipfs/QmbaAMsfA12WX5xeqjT1k1tzBZKCC11UMM5naQqtEaVw1R"

contracts/scripts/Config.s.sol

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright (c) 2024 RISC Zero, Inc.
2+
//
3+
// All rights reserved.
4+
5+
pragma solidity ^0.8.20;
6+
7+
import {Vm} from "forge-std/Vm.sol";
8+
import "forge-std/Test.sol";
9+
10+
struct DeploymentConfig {
11+
string name;
12+
uint256 chainId;
13+
address admin;
14+
address router;
15+
address setVerifier;
16+
address proofMarket;
17+
bytes32 setBuilderImageId;
18+
string setBuilderGuestUrl;
19+
bytes32 assessorImageId;
20+
string assessorGuestUrl;
21+
}
22+
23+
library ConfigLoader {
24+
/// Reference the vm address without needing to inherit from Script.
25+
Vm private constant VM = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
26+
27+
function loadConfig(string memory configFilePath)
28+
internal
29+
view
30+
returns (string memory config, string memory chainKey)
31+
{
32+
// Load the config file
33+
config = VM.readFile(configFilePath);
34+
35+
// Get the config profile from the environment variable, or leave it empty
36+
chainKey = VM.envOr("CHAIN_KEY", string(""));
37+
38+
// If no profile is set, select the default one based on the chainId
39+
if (bytes(chainKey).length == 0) {
40+
string[] memory chainKeys = VM.parseTomlKeys(config, ".chains");
41+
for (uint256 i = 0; i < chainKeys.length; i++) {
42+
if (stdToml.readUint(config, string.concat(".chains.", chainKeys[i], ".id")) == block.chainid) {
43+
chainKey = chainKeys[i];
44+
break;
45+
}
46+
}
47+
}
48+
49+
return (config, chainKey);
50+
}
51+
52+
function loadDeploymentConfig(string memory configFilePath) internal view returns (DeploymentConfig memory) {
53+
(string memory config, string memory chainKey) = loadConfig(configFilePath);
54+
return ConfigParser.parseConfig(config, chainKey);
55+
}
56+
}
57+
58+
library ConfigParser {
59+
function parseConfig(string memory config, string memory chainKey)
60+
internal
61+
pure
62+
returns (DeploymentConfig memory)
63+
{
64+
DeploymentConfig memory deploymentConfig;
65+
66+
string memory chain = string.concat(".chains.", chainKey);
67+
68+
deploymentConfig.name = stdToml.readString(config, string.concat(chain, ".name"));
69+
deploymentConfig.chainId = stdToml.readUint(config, string.concat(chain, ".id"));
70+
deploymentConfig.admin = stdToml.readAddress(config, string.concat(chain, ".admin"));
71+
deploymentConfig.router = stdToml.readAddress(config, string.concat(chain, ".router"));
72+
deploymentConfig.setVerifier = stdToml.readAddress(config, string.concat(chain, ".set-verifier"));
73+
deploymentConfig.proofMarket = stdToml.readAddress(config, string.concat(chain, ".proof-market"));
74+
deploymentConfig.setBuilderImageId = stdToml.readBytes32(config, string.concat(chain, ".set-builder-image-id"));
75+
deploymentConfig.setBuilderGuestUrl = stdToml.readString(config, string.concat(chain, ".set-builder-guest-url"));
76+
deploymentConfig.assessorImageId = stdToml.readBytes32(config, string.concat(chain, ".assessor-image-id"));
77+
deploymentConfig.assessorGuestUrl = stdToml.readString(config, string.concat(chain, ".assessor-guest-url"));
78+
79+
return deploymentConfig;
80+
}
81+
}

contracts/scripts/Deploy.s.sol

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,109 @@
55
pragma solidity ^0.8.20;
66

77
import {Script, console2} from "forge-std/Script.sol";
8+
import "forge-std/Test.sol";
89
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
910
import {ControlID, RiscZeroGroth16Verifier} from "risc0/groth16/RiscZeroGroth16Verifier.sol";
1011
import {RiscZeroCheats} from "risc0/test/RiscZeroCheats.sol";
12+
import {UnsafeUpgrades, Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
13+
import {ConfigLoader, DeploymentConfig} from "./Config.s.sol";
1114

1215
import {ProofMarket} from "../src/ProofMarket.sol";
1316
import {RiscZeroSetVerifier} from "../src/RiscZeroSetVerifier.sol";
1417

1518
// For local testing:
16-
import {ImageID as AssesorImgId} from "../src/AssessorImageID.sol";
17-
import {ImageID as SetBuidlerId} from "../src/SetBuilderImageID.sol";
19+
// TODO: Uncommenting these lines currently creates a chicken-egg problem in that
20+
// `cargo build -Ftest-utils` cannot complete without first running `forge build` and `forge build`
21+
// cannot run without building the guests.
22+
//import {ImageID as AssesorImgId} from "../src/AssessorImageID.sol";
23+
//import {ImageID as SetBuidlerId} from "../src/SetBuilderImageID.sol";
1824

1925
contract Deploy is Script, RiscZeroCheats {
26+
// Path to deployment config file, relative to the project root.
27+
string constant CONFIG_FILE = "contracts/deployment.toml";
28+
29+
IRiscZeroVerifier verifier;
30+
RiscZeroSetVerifier setVerifier;
31+
address proofMarketAddress;
32+
bytes32 setBuilderImageId;
33+
bytes32 assessorImageId;
34+
2035
function run() external {
36+
string memory setBuilderGuestUrl = "";
37+
string memory assessorGuestUrl = "";
38+
2139
// load ENV variables first
22-
uint256 adminKey = vm.envUint("PRIVATE_KEY");
40+
uint256 deployerKey = vm.envOr("DEPLOYER_PRIVATE_KEY", uint256(0));
41+
require(deployerKey != 0, "No deployer key provided. Please set the env var DEPLOYER_PRIVATE_KEY.");
42+
vm.rememberKey(deployerKey);
2343

24-
vm.startBroadcast(adminKey);
44+
address proofMarketOwner = vm.envAddress("PROOF_MARKET_OWNER");
45+
console2.log("ProofMarket Owner:", proofMarketOwner);
2546

26-
IRiscZeroVerifier verifier = deployRiscZeroVerifier();
47+
// Read and log the chainID
48+
uint256 chainId = block.chainid;
49+
console2.log("You are deploying on ChainID %d", chainId);
50+
51+
// Load the deployment config
52+
DeploymentConfig memory deploymentConfig =
53+
ConfigLoader.loadDeploymentConfig(string.concat(vm.projectRoot(), "/", CONFIG_FILE));
54+
55+
// Assign parsed config values to the variables
56+
verifier = IRiscZeroVerifier(deploymentConfig.router);
57+
setVerifier = RiscZeroSetVerifier(deploymentConfig.setVerifier);
58+
setBuilderImageId = deploymentConfig.setBuilderImageId;
59+
setBuilderGuestUrl = deploymentConfig.setBuilderGuestUrl;
60+
assessorImageId = deploymentConfig.assessorImageId;
61+
assessorGuestUrl = deploymentConfig.assessorGuestUrl;
62+
63+
vm.startBroadcast(deployerKey);
64+
65+
// Deploy the verifier, if not already deployed.
66+
if (address(verifier) == address(0)) {
67+
verifier = deployRiscZeroVerifier();
68+
} else {
69+
console2.log("Using IRiscZeroVerifier contract deployed at", address(verifier));
70+
}
71+
72+
// Set the setBuilderImageId and assessorImageId if not set.
73+
if (setBuilderImageId == bytes32(0)) {
74+
// TODO: Currently cannot work. See note in imports.
75+
//setBuilderImageId = SetBuidlerId.SET_BUILDER_GUEST_ID;
76+
revert("set builder image ID must be set in deployment.toml");
77+
}
78+
if (assessorImageId == bytes32(0)) {
79+
// TODO: Currently cannot work. See note in imports.
80+
//assessorImageId = AssesorImgId.ASSESSOR_GUEST_ID;
81+
revert("assessor image ID must be set in deployment.toml");
82+
}
2783

28-
string memory setBuilderGuestUrl = "";
29-
string memory assessorGuestUrl = "";
3084
if (bytes(vm.envOr("RISC0_DEV_MODE", string(""))).length > 0) {
31-
// TODO: Create a more robust way of getting a URI for guests.
85+
// TODO: Create a more robust way of getting a URI for guests, and ensure that it is
86+
// in-sync with the configured image ID.
3287
string memory cwd = vm.envString("PWD");
33-
setBuilderGuestUrl = string.concat(
34-
"file://",
35-
cwd,
36-
"/target/riscv-guest/riscv32im-risc0-zkvm-elf/release/set-builder-guest"
37-
);
88+
setBuilderGuestUrl =
89+
string.concat("file://", cwd, "/target/riscv-guest/riscv32im-risc0-zkvm-elf/release/set-builder-guest");
3890
console2.log("Set builder URI", setBuilderGuestUrl);
39-
assessorGuestUrl = string.concat(
40-
"file://",
41-
cwd,
42-
"/target/riscv-guest/riscv32im-risc0-zkvm-elf/release/assessor-guest"
43-
);
91+
assessorGuestUrl =
92+
string.concat("file://", cwd, "/target/riscv-guest/riscv32im-risc0-zkvm-elf/release/assessor-guest");
4493
console2.log("Assessor URI", assessorGuestUrl);
4594
}
4695

47-
RiscZeroSetVerifier setVerifier = new RiscZeroSetVerifier(verifier, SetBuidlerId.SET_BUILDER_GUEST_ID, setBuilderGuestUrl);
48-
console2.log("Deployed SetVerifier to,", address(setVerifier));
96+
// Deploy the setVerifier, if not already deployed.
97+
if (address(setVerifier) == address(0)) {
98+
setVerifier = new RiscZeroSetVerifier(verifier, setBuilderImageId, setBuilderGuestUrl);
99+
console2.log("Deployed RiscZeroSetVerifier to", address(setVerifier));
100+
} else {
101+
console2.log("Using RiscZeroSetVerifier contract deployed at", address(setVerifier));
102+
}
49103

50-
ProofMarket market = new ProofMarket(setVerifier, AssesorImgId.ASSESSOR_GUEST_ID, assessorGuestUrl);
51-
console2.log("Deployed ProofMarket to", address(market));
104+
// Deploy the proof market
105+
address newImplementation = address(new ProofMarket(setVerifier, assessorImageId));
106+
console2.log("Deployed new ProofMarket implementation at", newImplementation);
107+
proofMarketAddress = UnsafeUpgrades.deployUUPSProxy(
108+
newImplementation, abi.encodeCall(ProofMarket.initialize, (proofMarketOwner, assessorGuestUrl))
109+
);
110+
console2.log("Deployed ProofMarket (proxy) to", proofMarketAddress);
52111

53112
vm.stopBroadcast();
54113
}

0 commit comments

Comments
 (0)