Commit 98b0d311 authored by refcell's avatar refcell Committed by refcell

feat(ctb): statediff script recording

parent 5b0b8cf5
This diff is collapsed.
This diff is collapsed.
...@@ -67,6 +67,9 @@ The spec for the deploy config is defined by the `deployConfigSpec` located insi ...@@ -67,6 +67,9 @@ The spec for the deploy config is defined by the `deployConfigSpec` located insi
### Execution ### Execution
Before deploying the contracts, you can verify the state diff produced by the deploy script using the `runWithStateDiff()` function signature which produces the outputs inside [`snapshots/state-diff/`](./snapshots/state-diff).
Run the deployment with state diffs by executing: `forge script -vvv scripts/Deploy.s.sol:Deploy --sig 'runWithStateDiff()' --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY`.
1. Set the env vars `ETH_RPC_URL`, `PRIVATE_KEY` and `ETHERSCAN_API_KEY` if contract verification is desired 1. Set the env vars `ETH_RPC_URL`, `PRIVATE_KEY` and `ETHERSCAN_API_KEY` if contract verification is desired
1. Deploy the contracts with `forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY` 1. Deploy the contracts with `forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY`
Pass the `--verify` flag to verify the deployments automatically with Etherscan. Pass the `--verify` flag to verify the deployments automatically with Etherscan.
......
...@@ -28,6 +28,7 @@ build_info_path = 'artifacts/build-info' ...@@ -28,6 +28,7 @@ build_info_path = 'artifacts/build-info'
ffi = true ffi = true
fs_permissions = [ fs_permissions = [
{ access='read-write', path='./.resource-metering.csv' }, { access='read-write', path='./.resource-metering.csv' },
{ access='read-write', path='./snapshots/' },
{ access='read-write', path='./deployments/' }, { access='read-write', path='./deployments/' },
{ access='read', path='./deploy-config/' }, { access='read', path='./deploy-config/' },
{ access='read', path='./periphery-deploy-config/' }, { access='read', path='./periphery-deploy-config/' },
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
"coverage:lcov": "pnpm build:go-ffi && forge coverage --report lcov", "coverage:lcov": "pnpm build:go-ffi && forge coverage --report lcov",
"deploy": "./scripts/deploy.sh", "deploy": "./scripts/deploy.sh",
"gas-snapshot:no-build": "forge snapshot --match-contract GasBenchMark", "gas-snapshot:no-build": "forge snapshot --match-contract GasBenchMark",
"statediff": "./scripts/statediff.sh && git diff --exit-code",
"gas-snapshot": "pnpm build:go-ffi && pnpm gas-snapshot:no-build", "gas-snapshot": "pnpm build:go-ffi && pnpm gas-snapshot:no-build",
"storage-snapshot": "./scripts/storage-snapshot.sh", "storage-snapshot": "./scripts/storage-snapshot.sh",
"abi-snapshot": "npx tsx scripts/generate-snapshots.ts", "abi-snapshot": "npx tsx scripts/generate-snapshots.ts",
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { VmSafe } from "forge-std/Vm.sol";
import { Script } from "forge-std/Script.sol"; import { Script } from "forge-std/Script.sol";
import { console2 as console } from "forge-std/console2.sol"; import { console2 as console } from "forge-std/console2.sol";
...@@ -47,6 +48,7 @@ import { AlphabetVM } from "test/mocks/AlphabetVM.sol"; ...@@ -47,6 +48,7 @@ import { AlphabetVM } from "test/mocks/AlphabetVM.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import { ChainAssertions } from "scripts/ChainAssertions.sol"; import { ChainAssertions } from "scripts/ChainAssertions.sol";
import { Types } from "scripts/Types.sol"; import { Types } from "scripts/Types.sol";
import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol";
/// @title Deploy /// @title Deploy
/// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function. /// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function.
...@@ -56,6 +58,8 @@ import { Types } from "scripts/Types.sol"; ...@@ -56,6 +58,8 @@ import { Types } from "scripts/Types.sol";
contract Deploy is Deployer { contract Deploy is Deployer {
DeployConfig public cfg; DeployConfig public cfg;
using stdJson for string;
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Modifiers // // Modifiers //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -87,6 +91,19 @@ contract Deploy is Deployer { ...@@ -87,6 +91,19 @@ contract Deploy is Deployer {
} }
} }
/// @notice Modifier that wraps a function with statediff recording.
/// The returned AccountAccess[] array is then written to
/// the `snapshots/state-diff/<name>.json` output file.
modifier stateDiff() {
vm.startStateDiffRecording();
_;
VmSafe.AccountAccess[] memory accesses = vm.stopAndReturnStateDiff();
console.log("Writing %d state diff account accesses to snapshots/state-diff/%s.json", accesses.length, name());
string memory json = LibStateDiff.encodeAccountAccesses(accesses);
string memory statediffPath = string.concat(vm.projectRoot(), "/snapshots/state-diff/", name(), ".json");
vm.writeJson({ json: json, path: statediffPath });
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Accessors // // Accessors //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -213,7 +230,16 @@ contract Deploy is Deployer { ...@@ -213,7 +230,16 @@ contract Deploy is Deployer {
/// @notice Deploy all of the L1 contracts necessary for a full Superchain with a single Op Chain. /// @notice Deploy all of the L1 contracts necessary for a full Superchain with a single Op Chain.
function run() public { function run() public {
console.log("Deploying a fresh OP Stack including SuperchainConfig"); console.log("Deploying a fresh OP Stack including SuperchainConfig");
_run();
}
/// @notice Deploy all L1 contracts and write the state diff to a file.
function runWithStateDiff() public stateDiff {
_run();
}
/// @notice Internal function containing the deploy logic.
function _run() internal {
deploySafe(); deploySafe();
setupSuperchain(); setupSuperchain();
setupOpChain(); setupOpChain();
......
...@@ -7,7 +7,7 @@ if [ -n "${DEPLOY_VERIFY:-}" ]; then ...@@ -7,7 +7,7 @@ if [ -n "${DEPLOY_VERIFY:-}" ]; then
fi fi
echo "> Deploying contracts" echo "> Deploying contracts"
forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$DEPLOY_ETH_RPC_URL" --broadcast --private-key "$DEPLOY_PRIVATE_KEY" $verify_flag forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url "$DEPLOY_ETH_RPC_URL" --sig 'runWithStateDiff()' --broadcast --private-key "$DEPLOY_PRIVATE_KEY" $verify_flag
if [ -n "${DEPLOY_GENERATE_HARDHAT_ARTIFACTS:-}" ]; then if [ -n "${DEPLOY_GENERATE_HARDHAT_ARTIFACTS:-}" ]; then
echo "> Generating hardhat artifacts" echo "> Generating hardhat artifacts"
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { stdJson } from "forge-std/StdJson.sol";
import { VmSafe } from "forge-std/Vm.sol";
/// @title LibStateDiff
/// @author refcell
/// @notice Library to write StateDiff output to json.
library LibStateDiff {
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
using stdJson for string;
/// @notice Accepts an array of AccountAccess structs from the Vm and encodes them as a json string.
/// @param _accountAccesses Array of AccountAccess structs.
/// @return serialized_ string
function encodeAccountAccesses(VmSafe.AccountAccess[] memory _accountAccesses)
internal
returns (string memory serialized_)
{
string[] memory accountAccesses = new string[](_accountAccesses.length);
for (uint256 i = 0; i < _accountAccesses.length; i++) {
accountAccesses[i] = serializeAccountAccess(_accountAccesses[i]);
}
serialized_ = "";
serialized_.serialize("accountAccesses", accountAccesses);
}
/// @notice Turns an AccountAccess into a json serialized string
/// @param _accountAccess The AccountAccess to serialize
/// @return serialized_ The json serialized string
function serializeAccountAccess(VmSafe.AccountAccess memory _accountAccess)
internal
returns (string memory serialized_)
{
string memory json = "access";
json.serialize("account", _accountAccess.account);
json.serialize("accessor", _accountAccess.accessor);
json.serialize("initialized", _accountAccess.initialized);
json.serialize("oldBalance", _accountAccess.oldBalance);
json.serialize("newBalance", _accountAccess.newBalance);
json.serialize("deployedCode", _accountAccess.deployedCode);
json.serialize("value", _accountAccess.value);
json.serialize("data", _accountAccess.data);
json.serialize("reverted", _accountAccess.reverted);
string memory chainInfo = serializeChainInfo(_accountAccess.chainInfo);
string memory accountAccessKind = serializeAccountAccessKind(_accountAccess.kind);
string memory storageAccesses = serializeStorageAccesses(_accountAccess.storageAccesses);
json = vm.serializeString(json, "chainInfo", chainInfo);
json = vm.serializeString(json, "kind", accountAccessKind);
json = vm.serializeString(json, "storageAccesses", storageAccesses);
serialized_ = json;
}
/// @notice Accepts a VmSafe.ChainInfo struct and encodes it as a json string.
/// @param _chainInfo The ChainInfo struct to serialize
/// @return serialized_ string
function serializeChainInfo(VmSafe.ChainInfo memory _chainInfo) internal returns (string memory serialized_) {
string memory json = "chainInfo";
json.serialize("forkId", _chainInfo.forkId);
json.serialize("chainId", _chainInfo.chainId);
serialized_ = json;
}
/// @notice Turns an AccountAccessKind into a string.
/// @param _kind The AccountAccessKind to serialize
/// @return serialized_ The string representation of the AccountAccessKind
function serializeAccountAccessKind(VmSafe.AccountAccessKind _kind)
internal
pure
returns (string memory serialized_)
{
if (_kind == VmSafe.AccountAccessKind.Call) {
serialized_ = "Call";
} else if (_kind == VmSafe.AccountAccessKind.DelegateCall) {
serialized_ = "DelegateCall";
} else if (_kind == VmSafe.AccountAccessKind.CallCode) {
serialized_ = "CallCode";
} else if (_kind == VmSafe.AccountAccessKind.StaticCall) {
serialized_ = "StaticCall";
} else if (_kind == VmSafe.AccountAccessKind.Create) {
serialized_ = "Create";
} else if (_kind == VmSafe.AccountAccessKind.SelfDestruct) {
serialized_ = "SelfDestruct";
} else {
serialized_ = "Resume";
}
}
/// @notice Accepts an array of StorageAccess structs from the Vm and encodes each as a json string.
/// @param _storageAccesses Array of StorageAccess structs.
/// @return serialized_ json serialized StorageAccess structs.
function serializeStorageAccesses(VmSafe.StorageAccess[] memory _storageAccesses)
internal
returns (string memory serialized_)
{
string[] memory storageAccesses = new string[](_storageAccesses.length);
for (uint256 i = 0; i < _storageAccesses.length; i++) {
storageAccesses[i] = serializeStorageAccess(_storageAccesses[i]);
}
serialized_ = "storageAccesses";
serialized_.serialize("storageAccesses", storageAccesses);
}
/// @notice Turns a StorageAccess into a json serialized string
/// @param _storageAccess The StorageAccess to serialize
/// @return serialized_ The json serialized string
function serializeStorageAccess(VmSafe.StorageAccess memory _storageAccess)
internal
returns (string memory serialized_)
{
string memory json = "storageAccess";
json.serialize("account", _storageAccess.account);
json.serialize("slot", _storageAccess.slot);
json.serialize("isWrite", _storageAccess.isWrite);
json.serialize("previousValue", _storageAccess.previousValue);
json.serialize("newValue", _storageAccess.newValue);
json.serialize("reverted", _storageAccess.reverted);
serialized_ = json;
}
}
#!/usr/bin/env bash
set -euo pipefail
echo "> Deploying contracts to generate state diff (non-broadcast)"
forge script -vvv scripts/Deploy.s.sol:Deploy --sig 'runWithStateDiff()'
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment