From 42f54f9a7a1886a51433c6b382e6af9f5261bdde Mon Sep 17 00:00:00 2001 From: Tei Im Date: Mon, 11 Mar 2024 21:28:38 +0900 Subject: [PATCH 01/11] Implement RISCV deploy script --- rvsol/foundry.toml | 11 ++- rvsol/lib/optimism | 2 +- rvsol/scripts/Artifacts.s.sol | 163 ++++++++++++++++++++++++++++++++++ rvsol/scripts/Chains.sol | 16 ++++ rvsol/scripts/Config.sol | 79 ++++++++++++++++ rvsol/scripts/Deploy.s.sol | 117 ++++++++++++++++++++++++ rvsol/scripts/Deployer.sol | 41 +++++++++ rvsol/test/RISCV.t.sol | 2 +- 8 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 rvsol/scripts/Artifacts.s.sol create mode 100644 rvsol/scripts/Chains.sol create mode 100644 rvsol/scripts/Config.sol create mode 100644 rvsol/scripts/Deploy.s.sol create mode 100644 rvsol/scripts/Deployer.sol diff --git a/rvsol/foundry.toml b/rvsol/foundry.toml index 2e6162d9..a6caa854 100644 --- a/rvsol/foundry.toml +++ b/rvsol/foundry.toml @@ -1,6 +1,7 @@ [profile.default] src = 'src' out = 'out' +script = 'scripts' libs = ['lib'] optimizer = true optimizer_runs = 999999 @@ -13,10 +14,18 @@ remappings = [ '@lib-keccak/=lib/optimism/packages/contracts-bedrock/lib/lib-keccak/contracts/lib', 'safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts', 'kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src', - 'solady/=lib/optimism/packages/contracts-bedrock/lib/solady/src', + 'solady/=lib/optimism/packages/contracts-bedrock/lib/solady/src', + 'src/dispute=lib/optimism/packages/contracts-bedrock/src/dispute', + 'src/libraries=lib/optimism/packages/contracts-bedrock/src/libraries', 'forge-std/=lib/forge-std/src', 'ds-test/=lib/forge-std/lib/ds-test/src', ] +ffi = true +fs_permissions = [ + { access='read-write', path='./deployments' }, + { access='read', path='./' } +] + # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/rvsol/lib/optimism b/rvsol/lib/optimism index c9340197..27d578e0 160000 --- a/rvsol/lib/optimism +++ b/rvsol/lib/optimism @@ -1 +1 @@ -Subproject commit c934019745c98c749af986e75b2ec72c9c406723 +Subproject commit 27d578e0faf31fcf160148b1bdd568c43013b301 diff --git a/rvsol/scripts/Artifacts.s.sol b/rvsol/scripts/Artifacts.s.sol new file mode 100644 index 00000000..96384487 --- /dev/null +++ b/rvsol/scripts/Artifacts.s.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { console2 as console } from "forge-std/console2.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { Config } from "scripts/Config.sol"; + +/// @notice Represents a deployment. Is serialized to JSON as a key/value +/// pair. Can be accessed from within scripts. + struct Deployment { + string name; + address payable addr; + } + +/// @title Artifacts +abstract contract Artifacts { + /// @notice Foundry cheatcode VM. + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + /// @notice Error for when attempting to fetch a deployment and it does not exist + error DeploymentDoesNotExist(string); + /// @notice Error for when trying to save an invalid deployment + error InvalidDeployment(string); + + /// @notice The set of chain deployments that have been already deployed. + mapping(string => Deployment) internal _chainDeployments; + /// @notice The set of deployments that have been done during execution. + mapping(string => Deployment) internal _namedDeployments; + /// @notice The path to the deployment artifact that is being written to. + string internal deploymentOutfile; + + /// @notice Accepts a filepath and then ensures that the directory + /// exists for the file to live in. + function ensurePath(string memory _path) internal { + (, bytes memory returndata) = + address(vm).call(abi.encodeWithSignature("split(string,string)", _path, string("/"))); + string[] memory outputs = abi.decode(returndata, (string[])); + + string memory path = ""; + for (uint256 i = 0; i < outputs.length - 1; i++) { + path = string.concat(path, outputs[i], "/"); + } + vm.createDir(path, true); + } + + /// @notice Setup function. The arguments here + function setUp() public virtual { + deploymentOutfile = Config.deploymentOutfile(); + console.log("Writing artifact to %s", deploymentOutfile); + ensurePath(deploymentOutfile); + + // Load addresses from a JSON file if the CHAIN_DEPLOYMENT_FILE environment variable + // is set. Great for loading addresses from `superchain-registry`. + string memory addresses = Config.chainDeploymentFile(); + if (bytes(addresses).length > 0) { + console.log("Loading chain addresses from %s", addresses); + _loadChainAddresses(addresses); + } + } + + /// @notice Populates the addresses to be used in a script based on a JSON file. + /// The JSON key is the name of the contract and the value is an address. + function _loadChainAddresses(string memory _path) internal { + string[] memory commands = new string[](3); + commands[0] = "bash"; + commands[1] = "-c"; + commands[2] = string.concat("jq -cr < ", _path); + string memory json = string(vm.ffi(commands)); + string[] memory keys = vm.parseJsonKeys(json, ""); + for (uint256 i; i < keys.length; i++) { + string memory key = keys[i]; + address addr = stdJson.readAddress(json, string.concat("$.", key)); + Deployment memory deployment = Deployment({ name: key, addr: payable(addr) }); + _chainDeployments[key] = deployment; + console.log("Loading %s: %s", key, addr); + } + } + + /// @notice Appends a deployment to disk as a JSON deploy artifact. + /// @param _name The name of the deployment. + /// @param _deployed The address of the deployment. + function save(string memory _name, address _deployed) public { + if (bytes(_name).length == 0) { + revert InvalidDeployment("EmptyName"); + } + if (bytes(_namedDeployments[_name].name).length > 0) { + revert InvalidDeployment("AlreadyExists"); + } + + console.log("Saving %s: %s", _name, _deployed); + Deployment memory deployment = Deployment({ name: _name, addr: payable(_deployed) }); + _namedDeployments[_name] = deployment; + _appendDeployment(_name, _deployed); + } + + /// @notice Adds a deployment to the temp deployments file + function _appendDeployment(string memory _name, address _deployed) internal { + vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: deploymentOutfile }); + } + + /// @notice Returns a deployment that is suitable to be used to interact with contracts. + /// @param _name The name of the deployment. + /// @return The deployment. + function getChainDeployment(string memory _name) public view returns (Deployment memory) { + return _chainDeployments[_name]; + } + + /// @notice Returns the address of a deployment. Also handles the predeploys. + /// @param _name The name of the deployment. + /// @return The address of the deployment. May be `address(0)` if the deployment does not + /// exist. + function getChainAddress(string memory _name) public view returns (address payable) { + Deployment memory existing = _chainDeployments[_name]; + if (existing.addr != address(0)) { + if (bytes(existing.name).length == 0) { + return payable(address(0)); + } + return existing.addr; + } + + return payable(address(0)); + } + + /// @notice Returns the address of a deployment and reverts if the deployment + /// does not exist. + /// @return The address of the deployment. + function mustGetChainAddress(string memory _name) public view returns (address payable) { + address addr = getChainAddress(_name); + if (addr == address(0)) { + revert DeploymentDoesNotExist(_name); + } + return payable(addr); + } + + /// @notice Returns the address of a deployment. Also handles the predeploys. + /// @param _name The name of the deployment. + /// @return The address of the deployment. May be `address(0)` if the deployment does not + /// exist. + function getAddress(string memory _name) public view returns (address payable) { + Deployment memory existing = _namedDeployments[_name]; + if (existing.addr != address(0)) { + if (bytes(existing.name).length == 0) { + return payable(address(0)); + } + return existing.addr; + } + + return payable(address(0)); + } + + /// @notice Returns the address of a deployment and reverts if the deployment + /// does not exist. + /// @return The address of the deployment. + function mustGetAddress(string memory _name) public view returns (address payable) { + address addr = getAddress(_name); + if (addr == address(0)) { + revert DeploymentDoesNotExist(_name); + } + return payable(addr); + } + +} diff --git a/rvsol/scripts/Chains.sol b/rvsol/scripts/Chains.sol new file mode 100644 index 00000000..fcc86d1b --- /dev/null +++ b/rvsol/scripts/Chains.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @notice Chain IDs for the various networks. +library Chains { + uint256 internal constant Mainnet = 1; + uint256 internal constant OPMainnet = 10; + uint256 internal constant Goerli = 5; + uint256 internal constant OPGoerli = 420; + uint256 internal constant Sepolia = 11155111; + uint256 internal constant OPSepolia = 11155420; + uint256 internal constant LocalDevnet = 900; + uint256 internal constant OPLocalDevnet = 901; + uint256 internal constant GethDevnet = 1337; + uint256 internal constant Hardhat = 31337; +} \ No newline at end of file diff --git a/rvsol/scripts/Config.sol b/rvsol/scripts/Config.sol new file mode 100644 index 00000000..2c4b25c1 --- /dev/null +++ b/rvsol/scripts/Config.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { Vm } from "forge-std/Vm.sol"; + +import { Chains } from "scripts/Chains.sol"; + +/// @title Config +library Config { + /// @notice Foundry cheatcode VM. + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + /// @notice Returns the path on the local filesystem where the deployment artifact is + /// written to disk after doing a deployment. + function deploymentOutfile() internal view returns (string memory _env) { + _env = vm.envOr( + "DEPLOYMENT_OUTFILE", string.concat(vm.projectRoot(), "/deployments/", _getDeploymentContext(), "/.deploy") + ); + } + + /// @notice Returns the path on the local filesystem where the deploy config is + function deployConfigPath() internal view returns (string memory _env) { + _env = vm.envOr( + "DEPLOY_CONFIG_PATH", string.concat(vm.projectRoot(), "/deploy-config/", _getDeploymentContext(), ".json") + ); + } + + function chainDeploymentFile() internal view returns (string memory _env) { + _env = vm.envOr("CHAIN_DEPLOYMENT_FILE", string("./.deploy")); + } + + /// @notice Returns the chainid from the EVM context or the value of the CHAIN_ID env var as + /// an override. + function chainID() internal view returns (uint256 _env) { + _env = vm.envOr("CHAIN_ID", block.chainid); + } + + /// @notice Returns the deployment context which was only useful in the hardhat deploy style + /// of deployments. It is now DEPRECATED and will be removed in the future. + function deploymentContext() internal view returns (string memory _env) { + _env = vm.envOr("DEPLOYMENT_CONTEXT", string("")); + } + + /// @notice The CREATE2 salt to be used when deploying the implementations. + function implSalt() internal view returns (string memory _env) { + _env = vm.envOr("IMPL_SALT", string("ethers phoenix")); + } + + /// @notice The context of the deployment is used to namespace the artifacts. + /// An unknown context will use the chainid as the context name. + /// This is legacy code and should be removed in the future. + function _getDeploymentContext() private view returns (string memory) { + string memory context = deploymentContext(); + if (bytes(context).length > 0) { + return context; + } + + uint256 chainid = Config.chainID(); + if (chainid == Chains.Mainnet) { + return "mainnet"; + } else if (chainid == Chains.Goerli) { + return "goerli"; + } else if (chainid == Chains.OPGoerli) { + return "optimism-goerli"; + } else if (chainid == Chains.OPMainnet) { + return "optimism-mainnet"; + } else if (chainid == Chains.LocalDevnet || chainid == Chains.GethDevnet) { + return "devnetL1"; + } else if (chainid == Chains.Hardhat) { + return "hardhat"; + } else if (chainid == Chains.Sepolia) { + return "sepolia"; + } else if (chainid == Chains.OPSepolia) { + return "optimism-sepolia"; + } else { + return vm.toString(chainid); + } + } +} diff --git a/rvsol/scripts/Deploy.s.sol b/rvsol/scripts/Deploy.s.sol new file mode 100644 index 00000000..93626440 --- /dev/null +++ b/rvsol/scripts/Deploy.s.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { Config } from "scripts/Config.sol"; +import { Deployer } from "scripts/Deployer.sol"; +import { RISCV } from "../src/RISCV.sol"; + +import { IBigStepper } from "@optimism/src/dispute/interfaces/IBigStepper.sol"; +import { IPreimageOracle } from "@optimism/src/cannon/interfaces/IPreimageOracle.sol"; +import { DisputeGameFactory } from "@optimism/src/dispute/DisputeGameFactory.sol"; +import { DelayedWETH } from "@optimism/src/dispute/weth/DelayedWETH.sol"; +import { FaultDisputeGame } from "@optimism/src/dispute/FaultDisputeGame.sol"; +import { Safe } from "safe-contracts/Safe.sol"; +import { Enum as SafeOps } from "safe-contracts/common/Enum.sol"; +import "@optimism/src/libraries/DisputeTypes.sol"; + +contract Deploy is Deployer { + /// @notice Modifier that wraps a function in broadcasting. + modifier broadcast() { + vm.startBroadcast(msg.sender); + _; + vm.stopBroadcast(); + } + + /// @inheritdoc Deployer + function name() public pure override returns (string memory name_) { + name_ = "Deploy"; + } + + /// @notice The create2 salt used for deployment of the contract implementations. + /// Using this helps to reduce config across networks as the implementation + /// addresses will be the same across networks when deployed with create2. + function _implSalt() internal view returns (bytes32) { + return keccak256(bytes(Config.implSalt())); + } + + function run() public { + deployRiscv(); + setAsteriscFaultGameImplementation(false); + } + + /// @notice Deploy RISCV + function deployRiscv() public broadcast returns (address addr_) { + console.log("Deploying RISCV implementation"); + RISCV riscv = new RISCV{ salt: _implSalt() }(IPreimageOracle(mustGetChainAddress("PreimageOracle"))); + save("RISCV", address(riscv)); + console.log("RISCV deployed at %s", address(riscv)); + addr_ = address(riscv); + } + + /// @notice Loads the riscv absolute prestate from the prestate-proof for devnets otherwise + /// from the config. + function loadRiscvAbsolutePrestate() internal returns (Claim riscVAbsolutePrestate_) { + // TODO: Implement this function + return Claim.wrap(bytes32(0)); + } + + /// @notice Make a call from the Safe contract to an arbitrary address with arbitrary data + function _callViaSafe(address _target, bytes memory _data) internal { + Safe safe = Safe(mustGetChainAddress("SystemOwnerSafe")); + + // This is the signature format used the caller is also the signer. + bytes memory signature = abi.encodePacked(uint256(uint160(msg.sender)), bytes32(0), uint8(1)); + + safe.execTransaction({ + to: _target, + value: 0, + data: _data, + operation: SafeOps.Operation.Call, + safeTxGas: 0, + baseGas: 0, + gasPrice: 0, + gasToken: address(0), + refundReceiver: payable(address(0)), + signatures: signature + }); + } + + /// @notice Sets the implementation for the given fault game type in the `DisputeGameFactory`. + function setAsteriscFaultGameImplementation(bool _allowUpgrade) public broadcast { + console.log("Setting Asterisc FaultDisputeGame implementation"); + DisputeGameFactory factory = DisputeGameFactory(mustGetChainAddress("DisputeGameFactoryProxy")); + DelayedWETH weth = DelayedWETH(mustGetChainAddress("DelayedWETHProxy")); + + if (address(factory.gameImpls(GameTypes.ASTERISC)) != address(0) && !_allowUpgrade) { + console.log( + "[WARN] DisputeGameFactoryProxy: `FaultDisputeGame` implementation already set for game type: ASTERISC" + ); + return; + } + + FaultDisputeGame fdg = new FaultDisputeGame({ + _gameType: GameTypes.ASTERISC, + _absolutePrestate: loadRiscvAbsolutePrestate(), + _genesisBlockNumber: cfg.faultGameGenesisBlock(), + _genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()), + _maxGameDepth :cfg.faultGameMaxDepth(), + _splitDepth: cfg.faultGameSplitDepth(), + _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), + _vm: IBigStepper(mustGetAddress("RISCV")), + _weth: weth, + _l2ChainId: cfg.l2ChainID() + }); + + bytes memory data = abi.encodeCall(DisputeGameFactory.setImplementation, (GameTypes.ASTERISC, fdg)); + _callViaSafe(address(factory), data); + + console.log( + "DisputeGameFactoryProxy: set `FaultDisputeGame` implementation (Backend: ASTERISC | GameType: %s)", + vm.toString(GameType.unwrap(GameTypes.ASTERISC)) + ); + } + +} \ No newline at end of file diff --git a/rvsol/scripts/Deployer.sol b/rvsol/scripts/Deployer.sol new file mode 100644 index 00000000..fffe9210 --- /dev/null +++ b/rvsol/scripts/Deployer.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Artifacts } from "scripts/Artifacts.s.sol"; +import { Config } from "scripts/Config.sol"; + +import { DeployConfig } from "@optimism/scripts/DeployConfig.s.sol"; +import { USE_FAULT_PROOFS_SLOT } from "@optimism/scripts/DeployConfig.s.sol"; + +/// @title Deployer +/// @author tynes +/// @notice A contract that can make deploying and interacting with deployments easy. +abstract contract Deployer is Script, Artifacts { + DeployConfig public constant cfg = + DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + + /// @notice Sets up the artifacts contract. + function setUp() public virtual override { + Artifacts.setUp(); + + // Load the `useFaultProofs` slot value prior to etching the DeployConfig's bytecode and reading the deploy + // config file. If this slot has already been set, it will override the preference in the deploy config. + bytes32 useFaultProofsOverride = vm.load(address(cfg), USE_FAULT_PROOFS_SLOT); + + vm.etch(address(cfg), vm.getDeployedCode("DeployConfig.s.sol:DeployConfig")); + vm.label(address(cfg), "DeployConfig"); + vm.allowCheatcodes(address(cfg)); + cfg.read(Config.deployConfigPath()); + + if (useFaultProofsOverride != 0) { + vm.store(address(cfg), USE_FAULT_PROOFS_SLOT, useFaultProofsOverride); + } + } + + /// @notice Returns the name of the deployment script. Children contracts + /// must implement this to ensure that the deploy artifacts can be found. + /// This should be the same as the name of the script and is used as the file + /// name inside of the `broadcast` directory when looking up deployment artifacts. + function name() public pure virtual returns (string memory); +} diff --git a/rvsol/test/RISCV.t.sol b/rvsol/test/RISCV.t.sol index d943c53e..f630959e 100644 --- a/rvsol/test/RISCV.t.sol +++ b/rvsol/test/RISCV.t.sol @@ -31,7 +31,7 @@ contract RISCV_Test is CommonTest { function setUp() public virtual override { super.setUp(); - oracle = new PreimageOracle(0, 0, 0); + oracle = new PreimageOracle(0, 0); riscv = new RISCV(oracle); vm.store(address(riscv), 0x0, bytes32(abi.encode(address(oracle)))); vm.label(address(oracle), "PreimageOracle"); From b0559ac7da3721fb92842218811c2c3e3d300a41 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 12 Mar 2024 11:57:39 +0900 Subject: [PATCH 02/11] Add salt for fdg deployment --- rvsol/scripts/Deploy.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rvsol/scripts/Deploy.s.sol b/rvsol/scripts/Deploy.s.sol index 93626440..215a85a3 100644 --- a/rvsol/scripts/Deploy.s.sol +++ b/rvsol/scripts/Deploy.s.sol @@ -92,7 +92,7 @@ contract Deploy is Deployer { return; } - FaultDisputeGame fdg = new FaultDisputeGame({ + FaultDisputeGame fdg = new FaultDisputeGame{ salt: _implSalt() }({ _gameType: GameTypes.ASTERISC, _absolutePrestate: loadRiscvAbsolutePrestate(), _genesisBlockNumber: cfg.faultGameGenesisBlock(), From 234d5bf8e2dfbc8582ced8ad3bd978539fee5f6a Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 12 Mar 2024 14:55:50 +0900 Subject: [PATCH 03/11] Implement loadRiscvAbsolutePrestate() --- .gitignore | 2 ++ Makefile | 8 ++++++++ rvsol/scripts/Deploy.s.sol | 25 ++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8a4fd5d0..35ab6e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ rvgo/test/testdata rvsol/cache rvsol/out +rvsol/broadcast +rvsol/deployments/devnetL1 tests/go-tests/bin diff --git a/Makefile b/Makefile index 5c42c43c..89d8daa3 100644 --- a/Makefile +++ b/Makefile @@ -44,3 +44,11 @@ fuzz-mac: .PHONY: \ fuzz \ fuzz-mac + +OP_PROGRAM_PATH ?= ./op-program-client-riscv.elf + +prestate: build-rvgo + ./rvgo/bin/asterisc load-elf --path $(OP_PROGRAM_PATH) --out ./rvgo/bin/prestate.json --meta ./rvgo/bin/meta.json + ./rvgo/bin/asterisc run --proof-at '=0' --stop-at '=1' --input ./rvgo/bin/prestate.json --meta ./rvgo/bin/meta.json --proof-fmt './rvgo/bin/%d.json' --output "" + mv ./rvgo/bin/0.json ./rvgo/bin/prestate-proof.json +.PHONY: prestate diff --git a/rvsol/scripts/Deploy.s.sol b/rvsol/scripts/Deploy.s.sol index 215a85a3..e012d2f5 100644 --- a/rvsol/scripts/Deploy.s.sol +++ b/rvsol/scripts/Deploy.s.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.15; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; +import { Chains } from "scripts/Chains.sol"; import { Config } from "scripts/Config.sol"; import { Deployer } from "scripts/Deployer.sol"; import { RISCV } from "../src/RISCV.sol"; @@ -53,9 +54,27 @@ contract Deploy is Deployer { /// @notice Loads the riscv absolute prestate from the prestate-proof for devnets otherwise /// from the config. - function loadRiscvAbsolutePrestate() internal returns (Claim riscVAbsolutePrestate_) { - // TODO: Implement this function - return Claim.wrap(bytes32(0)); + function loadRiscvAbsolutePrestate() internal returns (Claim riscvAbsolutePrestate_) { + if (block.chainid == Chains.LocalDevnet || block.chainid == Chains.GethDevnet) { + // Fetch the absolute prestate dump + string memory filePath = string.concat(vm.projectRoot(), "/../rvgo/bin/prestate-proof.json"); + string[] memory commands = new string[](3); + commands[0] = "bash"; + commands[1] = "-c"; + commands[2] = string.concat("[[ -f ", filePath, " ]] && echo \"present\""); + if (vm.ffi(commands).length == 0) { + revert("Asterisc prestate dump not found, generate it with `make prestate` in the Asterisc root."); + } + commands[2] = string.concat("cat ", filePath, " | jq -r .pre"); + riscvAbsolutePrestate_ = Claim.wrap(abi.decode(vm.ffi(commands), (bytes32))); + console.log( + "[Asterisc Dispute Game] Using devnet RISCV Absolute prestate: %s", + vm.toString(Claim.unwrap(riscvAbsolutePrestate_)) + ); + } else { + revert("Currently Asterisc only supports local devnet"); + // TODO: Add Asterisc absolute prestate into OP stack deploy config + } } /// @notice Make a call from the Safe contract to an arbitrary address with arbitrary data From 7c431aa4c35ebd9a5d004323d9135cacbb3e4333 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 12 Mar 2024 15:19:57 +0900 Subject: [PATCH 04/11] Add deploy shell script --- rvsol/scripts/Config.sol | 1 + rvsol/scripts/deploy.sh | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100755 rvsol/scripts/deploy.sh diff --git a/rvsol/scripts/Config.sol b/rvsol/scripts/Config.sol index 2c4b25c1..9c533165 100644 --- a/rvsol/scripts/Config.sol +++ b/rvsol/scripts/Config.sol @@ -25,6 +25,7 @@ library Config { ); } + /// @notice Returns the path on the local filesystem where the target chain deployment artifact is written. function chainDeploymentFile() internal view returns (string memory _env) { _env = vm.envOr("CHAIN_DEPLOYMENT_FILE", string("./.deploy")); } diff --git a/rvsol/scripts/deploy.sh b/rvsol/scripts/deploy.sh new file mode 100755 index 00000000..b76ebb4a --- /dev/null +++ b/rvsol/scripts/deploy.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail + +DEPLOY_ETH_RPC_URL="${DEPLOY_ETH_RPC_URL:-"http://localhost:8545"}" +DEPLOY_PRIVATE_KEY="${DEPLOY_PRIVATE_KEY:-"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}" # foundry pre-funded account #1 + +if [ -z "${CHAIN_DEPLOYMENT_FILE}" ]; then + echo "CHAIN_DEPLOYMENT_FILE is not set. Must point target chain deployment file (optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy)" + exit 1 +fi + +if [ -z "${DEPLOY_CONFIG_PATH}" ]; then + echo "DEPLOY_CONFIG_PATH is not set. Must point target chain deploy config file (optimism/packages/contracts-bedrock/deploy-config/devnetL1.json)" + exit 1 +fi +LOCAL_DEPLOY_CONFIG_PATH="$(dirname "$(dirname "$(realpath "$0")")")/deploy-config.json" +cp "$DEPLOY_CONFIG_PATH" "$LOCAL_DEPLOY_CONFIG_PATH" + + +echo "> Deploying contracts" +DEPLOY_CONFIG_PATH=$LOCAL_DEPLOY_CONFIG_PATH forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$DEPLOY_ETH_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" From e63d302d13b46ea3acce78da1d3480763361d564 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 12 Mar 2024 15:38:21 +0900 Subject: [PATCH 05/11] Add deployment README --- rvsol/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 rvsol/README.md diff --git a/rvsol/README.md b/rvsol/README.md new file mode 100644 index 00000000..d434bd0d --- /dev/null +++ b/rvsol/README.md @@ -0,0 +1,22 @@ +# Asterisc VM contract + +## Deployment +Currently, Asterisc only supports the local devnet launched from the Optimism monorepo. + +### Prerequisites +1. Running local devnet launched from the Optimism monorepo + - Run ```make devnet-up``` in the monorepo root. + - Set the env var `DEPLOY_ETH_RPC_URL` to the L1 RPC endpoint + - Set the env var `CHAIN_DEPLOYMENT_FILE` to the path of devnet deployment file (`optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy`) + - Set the env var `DEPLOY_CONFIG_PATH` to the path of devnet deploy config file (`optimism/packages/contracts-bedrock/deploy-config/devnetL1.json`) +2. Asterisc absolute prestate of op-program + - Run ```make op-program-client-riscv``` in `optimism/op-program` + - Set the built elf file path to the env var `OP_PROGRAM_PATH` + +### How to deploy +1. Build Asterisc binary and contracts + - Run ```make build``` in the project root +2. Generate prestate proof + - Run ```make prestate``` in the project root +3. Run deploy script + - Run ```./rvsol/scripts/deploy.sh``` in the project root From 87454c02667627f037c241960d0441dc103d019f Mon Sep 17 00:00:00 2001 From: Tei Im Date: Thu, 14 Mar 2024 17:04:42 +0900 Subject: [PATCH 06/11] Change env vars name --- rvsol/README.md | 6 +++--- rvsol/scripts/Artifacts.s.sol | 2 +- rvsol/scripts/Config.sol | 4 ++-- rvsol/scripts/deploy.sh | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/rvsol/README.md b/rvsol/README.md index d434bd0d..fbd51e7c 100644 --- a/rvsol/README.md +++ b/rvsol/README.md @@ -6,9 +6,9 @@ Currently, Asterisc only supports the local devnet launched from the Optimism mo ### Prerequisites 1. Running local devnet launched from the Optimism monorepo - Run ```make devnet-up``` in the monorepo root. - - Set the env var `DEPLOY_ETH_RPC_URL` to the L1 RPC endpoint - - Set the env var `CHAIN_DEPLOYMENT_FILE` to the path of devnet deployment file (`optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy`) - - Set the env var `DEPLOY_CONFIG_PATH` to the path of devnet deploy config file (`optimism/packages/contracts-bedrock/deploy-config/devnetL1.json`) + - Set the env var `TARGET_L1_RPC_URL` to the L1 RPC endpoint + - Set the env var `TARGET_L2_DEPLOYMENT_FILE` to the path of devnet deployment file (`optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy`) + - Set the env var `TARGET_L2_DEPLOY_CONFIG` to the path of devnet deploy config file (`optimism/packages/contracts-bedrock/deploy-config/devnetL1.json`) 2. Asterisc absolute prestate of op-program - Run ```make op-program-client-riscv``` in `optimism/op-program` - Set the built elf file path to the env var `OP_PROGRAM_PATH` diff --git a/rvsol/scripts/Artifacts.s.sol b/rvsol/scripts/Artifacts.s.sol index 96384487..15dcea5b 100644 --- a/rvsol/scripts/Artifacts.s.sol +++ b/rvsol/scripts/Artifacts.s.sol @@ -50,7 +50,7 @@ abstract contract Artifacts { console.log("Writing artifact to %s", deploymentOutfile); ensurePath(deploymentOutfile); - // Load addresses from a JSON file if the CHAIN_DEPLOYMENT_FILE environment variable + // Load addresses from a JSON file if the TARGET_L2_DEPLOYMENT_FILE environment variable // is set. Great for loading addresses from `superchain-registry`. string memory addresses = Config.chainDeploymentFile(); if (bytes(addresses).length > 0) { diff --git a/rvsol/scripts/Config.sol b/rvsol/scripts/Config.sol index 9c533165..6b435037 100644 --- a/rvsol/scripts/Config.sol +++ b/rvsol/scripts/Config.sol @@ -21,13 +21,13 @@ library Config { /// @notice Returns the path on the local filesystem where the deploy config is function deployConfigPath() internal view returns (string memory _env) { _env = vm.envOr( - "DEPLOY_CONFIG_PATH", string.concat(vm.projectRoot(), "/deploy-config/", _getDeploymentContext(), ".json") + "TARGET_L2_DEPLOY_CONFIG", string.concat(vm.projectRoot(), "/deploy-config/", _getDeploymentContext(), ".json") ); } /// @notice Returns the path on the local filesystem where the target chain deployment artifact is written. function chainDeploymentFile() internal view returns (string memory _env) { - _env = vm.envOr("CHAIN_DEPLOYMENT_FILE", string("./.deploy")); + _env = vm.envOr("TARGET_L2_DEPLOYMENT_FILE", string("./.deploy")); } /// @notice Returns the chainid from the EVM context or the value of the CHAIN_ID env var as diff --git a/rvsol/scripts/deploy.sh b/rvsol/scripts/deploy.sh index b76ebb4a..0909b040 100755 --- a/rvsol/scripts/deploy.sh +++ b/rvsol/scripts/deploy.sh @@ -1,21 +1,21 @@ #!/usr/bin/env bash set -eo pipefail -DEPLOY_ETH_RPC_URL="${DEPLOY_ETH_RPC_URL:-"http://localhost:8545"}" +TARGET_L1_RPC_URL="${TARGET_L1_RPC_URL:-"http://localhost:8545"}" DEPLOY_PRIVATE_KEY="${DEPLOY_PRIVATE_KEY:-"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}" # foundry pre-funded account #1 -if [ -z "${CHAIN_DEPLOYMENT_FILE}" ]; then - echo "CHAIN_DEPLOYMENT_FILE is not set. Must point target chain deployment file (optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy)" +if [ -z "${TARGET_L2_DEPLOYMENT_FILE}" ]; then + echo "TARGET_L2_DEPLOYMENT_FILE is not set. Must point target chain deployment file (optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy)" exit 1 fi -if [ -z "${DEPLOY_CONFIG_PATH}" ]; then - echo "DEPLOY_CONFIG_PATH is not set. Must point target chain deploy config file (optimism/packages/contracts-bedrock/deploy-config/devnetL1.json)" +if [ -z "${TARGET_L2_DEPLOY_CONFIG}" ]; then + echo "TARGET_L2_DEPLOY_CONFIG is not set. Must point target chain deploy config file (optimism/packages/contracts-bedrock/deploy-config/devnetL1.json)" exit 1 fi LOCAL_DEPLOY_CONFIG_PATH="$(dirname "$(dirname "$(realpath "$0")")")/deploy-config.json" -cp "$DEPLOY_CONFIG_PATH" "$LOCAL_DEPLOY_CONFIG_PATH" +cp "$TARGET_L2_DEPLOY_CONFIG" "$LOCAL_DEPLOY_CONFIG_PATH" echo "> Deploying contracts" -DEPLOY_CONFIG_PATH=$LOCAL_DEPLOY_CONFIG_PATH forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$DEPLOY_ETH_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" +TARGET_L2_DEPLOY_CONFIG=$LOCAL_DEPLOY_CONFIG_PATH forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$TARGET_L1_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" From fb9f806c24c0f42a0ba79fbcef7e7fadb9210b81 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Thu, 14 Mar 2024 17:06:40 +0900 Subject: [PATCH 07/11] Add more examples into rvsol README --- rvsol/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rvsol/README.md b/rvsol/README.md index fbd51e7c..5bc7552c 100644 --- a/rvsol/README.md +++ b/rvsol/README.md @@ -6,12 +6,12 @@ Currently, Asterisc only supports the local devnet launched from the Optimism mo ### Prerequisites 1. Running local devnet launched from the Optimism monorepo - Run ```make devnet-up``` in the monorepo root. - - Set the env var `TARGET_L1_RPC_URL` to the L1 RPC endpoint + - Set the env var `TARGET_L1_RPC_URL` to the L1 RPC endpoint (Local devnet L1: `http://localhost:8545`) - Set the env var `TARGET_L2_DEPLOYMENT_FILE` to the path of devnet deployment file (`optimism/packages/contracts-bedrock/deployments/devnetL1/.deploy`) - Set the env var `TARGET_L2_DEPLOY_CONFIG` to the path of devnet deploy config file (`optimism/packages/contracts-bedrock/deploy-config/devnetL1.json`) 2. Asterisc absolute prestate of op-program - - Run ```make op-program-client-riscv``` in `optimism/op-program` - - Set the built elf file path to the env var `OP_PROGRAM_PATH` + - Run ```make op-program-client-riscv``` in `optimism/op-program + - Set the built elf file path to the env var `OP_PROGRAM_PATH` (`optimism/op-program/bin/op-program-client-riscv.elf`) ### How to deploy 1. Build Asterisc binary and contracts From 602528d7a943b1772dc0406011132ae3f30b6815 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Thu, 14 Mar 2024 17:16:37 +0900 Subject: [PATCH 08/11] Fix deploy.sh path error --- rvsol/README.md | 2 +- rvsol/scripts/deploy.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rvsol/README.md b/rvsol/README.md index 5bc7552c..c7eec421 100644 --- a/rvsol/README.md +++ b/rvsol/README.md @@ -19,4 +19,4 @@ Currently, Asterisc only supports the local devnet launched from the Optimism mo 2. Generate prestate proof - Run ```make prestate``` in the project root 3. Run deploy script - - Run ```./rvsol/scripts/deploy.sh``` in the project root + - Run ```./scripts/deploy.sh``` in `rvsol` diff --git a/rvsol/scripts/deploy.sh b/rvsol/scripts/deploy.sh index 0909b040..764db32d 100755 --- a/rvsol/scripts/deploy.sh +++ b/rvsol/scripts/deploy.sh @@ -13,9 +13,10 @@ if [ -z "${TARGET_L2_DEPLOY_CONFIG}" ]; then echo "TARGET_L2_DEPLOY_CONFIG is not set. Must point target chain deploy config file (optimism/packages/contracts-bedrock/deploy-config/devnetL1.json)" exit 1 fi -LOCAL_DEPLOY_CONFIG_PATH="$(dirname "$(dirname "$(realpath "$0")")")/deploy-config.json" +SCRIPTS_DIR="$(dirname "$(realpath "$0")")" +LOCAL_DEPLOY_CONFIG_PATH="$(dirname "${SCRIPTS_DIR}")/deploy-config.json" cp "$TARGET_L2_DEPLOY_CONFIG" "$LOCAL_DEPLOY_CONFIG_PATH" echo "> Deploying contracts" -TARGET_L2_DEPLOY_CONFIG=$LOCAL_DEPLOY_CONFIG_PATH forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$TARGET_L1_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" +TARGET_L2_DEPLOY_CONFIG=$LOCAL_DEPLOY_CONFIG_PATH forge script -vvv "${SCRIPTS_DIR}"/Deploy.s.sol:Deploy --rpc-url "$TARGET_L1_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" From 2b4d3bf562ffd3251df73d5ef04d8cc4497aa466 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 19 Mar 2024 16:38:27 +0900 Subject: [PATCH 09/11] Add notes for known issues --- rvsol/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rvsol/README.md b/rvsol/README.md index c7eec421..d05eaf25 100644 --- a/rvsol/README.md +++ b/rvsol/README.md @@ -20,3 +20,12 @@ Currently, Asterisc only supports the local devnet launched from the Optimism mo - Run ```make prestate``` in the project root 3. Run deploy script - Run ```./scripts/deploy.sh``` in `rvsol` + +### Notes +- There are few issues with Foundry. + - Run script directly without manual build does not work with the current version of Foundry (2024-03-15 `3fa0270`). + You **must run** `make build` **before** running the deploy script. ([issue](https://github.com/foundry-rs/foundry/issues/6572)) + - Some older version(2024-02-01 `2f4b5db`) of Foundry makes a dependency error reproted above issue. + Use the **latest version** of Foundry! +- The deploy script can be run only once on the devnet because of the `create2` salt. + To rerun the script for dev purpose, you must restart the devnet with `make devnet-clean && make devnet-up` command on the monorepo. \ No newline at end of file From e1333dd977a1671b33c272e863481909ca07e8ef Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 19 Mar 2024 16:41:36 +0900 Subject: [PATCH 10/11] Fix formatting --- rvsol/scripts/Artifacts.s.sol | 11 +++++------ rvsol/scripts/Chains.sol | 2 +- rvsol/scripts/Config.sol | 3 ++- rvsol/scripts/Deploy.s.sol | 5 ++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/rvsol/scripts/Artifacts.s.sol b/rvsol/scripts/Artifacts.s.sol index 15dcea5b..263b95b8 100644 --- a/rvsol/scripts/Artifacts.s.sol +++ b/rvsol/scripts/Artifacts.s.sol @@ -8,10 +8,10 @@ import { Config } from "scripts/Config.sol"; /// @notice Represents a deployment. Is serialized to JSON as a key/value /// pair. Can be accessed from within scripts. - struct Deployment { - string name; - address payable addr; - } +struct Deployment { + string name; + address payable addr; +} /// @title Artifacts abstract contract Artifacts { @@ -34,7 +34,7 @@ abstract contract Artifacts { /// exists for the file to live in. function ensurePath(string memory _path) internal { (, bytes memory returndata) = - address(vm).call(abi.encodeWithSignature("split(string,string)", _path, string("/"))); + address(vm).call(abi.encodeWithSignature("split(string,string)", _path, string("/"))); string[] memory outputs = abi.decode(returndata, (string[])); string memory path = ""; @@ -159,5 +159,4 @@ abstract contract Artifacts { } return payable(addr); } - } diff --git a/rvsol/scripts/Chains.sol b/rvsol/scripts/Chains.sol index fcc86d1b..2edadb78 100644 --- a/rvsol/scripts/Chains.sol +++ b/rvsol/scripts/Chains.sol @@ -13,4 +13,4 @@ library Chains { uint256 internal constant OPLocalDevnet = 901; uint256 internal constant GethDevnet = 1337; uint256 internal constant Hardhat = 31337; -} \ No newline at end of file +} diff --git a/rvsol/scripts/Config.sol b/rvsol/scripts/Config.sol index 6b435037..e344d3ce 100644 --- a/rvsol/scripts/Config.sol +++ b/rvsol/scripts/Config.sol @@ -21,7 +21,8 @@ library Config { /// @notice Returns the path on the local filesystem where the deploy config is function deployConfigPath() internal view returns (string memory _env) { _env = vm.envOr( - "TARGET_L2_DEPLOY_CONFIG", string.concat(vm.projectRoot(), "/deploy-config/", _getDeploymentContext(), ".json") + "TARGET_L2_DEPLOY_CONFIG", + string.concat(vm.projectRoot(), "/deploy-config/", _getDeploymentContext(), ".json") ); } diff --git a/rvsol/scripts/Deploy.s.sol b/rvsol/scripts/Deploy.s.sol index e012d2f5..0073d362 100644 --- a/rvsol/scripts/Deploy.s.sol +++ b/rvsol/scripts/Deploy.s.sol @@ -116,7 +116,7 @@ contract Deploy is Deployer { _absolutePrestate: loadRiscvAbsolutePrestate(), _genesisBlockNumber: cfg.faultGameGenesisBlock(), _genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()), - _maxGameDepth :cfg.faultGameMaxDepth(), + _maxGameDepth: cfg.faultGameMaxDepth(), _splitDepth: cfg.faultGameSplitDepth(), _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), _vm: IBigStepper(mustGetAddress("RISCV")), @@ -132,5 +132,4 @@ contract Deploy is Deployer { vm.toString(GameType.unwrap(GameTypes.ASTERISC)) ); } - -} \ No newline at end of file +} From 88f24f96f924fbacef9930fdd3e9233d99882bba Mon Sep 17 00:00:00 2001 From: Tei Im Date: Tue, 19 Mar 2024 16:54:50 +0900 Subject: [PATCH 11/11] Add comment about remapping --- rvsol/foundry.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rvsol/foundry.toml b/rvsol/foundry.toml index a6caa854..c347e965 100644 --- a/rvsol/foundry.toml +++ b/rvsol/foundry.toml @@ -15,6 +15,8 @@ remappings = [ 'safe-contracts/=lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts', 'kontrol-cheatcodes/=lib/optimism/packages/contracts-bedrock/lib/kontrol-cheatcodes/src', 'solady/=lib/optimism/packages/contracts-bedrock/lib/solady/src', + + # We need these remappings to use the Optimism monorepo contracts as a library. 'src/dispute=lib/optimism/packages/contracts-bedrock/src/dispute', 'src/libraries=lib/optimism/packages/contracts-bedrock/src/libraries',