Commit 5a5dd8f4 authored by protolambda's avatar protolambda Committed by GitHub

op-chain-ops: Go forge scripts runner (#11447)

* op-chain-ops: Go forge scripts runner

* fix lint

* op-chain-ops: encapsulate forge script tests in testdata
parent 1e2b19cb
package script
import (
"github.com/ethereum/go-ethereum/common"
)
type CheatCodesPrecompile struct {
h *Host
}
func (c *CheatCodesPrecompile) GetNonce(addr common.Address) uint64 {
return c.h.state.GetNonce(addr)
}
......@@ -7,13 +7,11 @@ import (
type ConsolePrecompile struct {
logger log.Logger
sender common.Address
sender func() common.Address
}
func (c *ConsolePrecompile) log(ctx ...any) {
// frame := c.h.CurrentCall()
// sender := frame.Sender
sender := c.sender
sender := c.sender()
// Log the sender, since the self-address is always equal to the ConsoleAddr
c.logger.With("sender", sender).Info("console", ctx...)
......
......@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
......@@ -26,7 +27,7 @@ func TestConsole(t *testing.T) {
t.Logf("bob: %s", bob)
c := &ConsolePrecompile{
logger: logger,
sender: sender,
sender: func() common.Address { return sender },
}
p, err := NewPrecompile[*ConsolePrecompile](c)
require.NoError(t, err)
......
package script
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
var (
// DefaultSenderAddr is known as DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))))
DefaultSenderAddr = common.HexToAddress("0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38")
// DefaultScriptAddr is the address of the initial executing script, computed from:
// cast compute-address --nonce 1 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38
DefaultScriptAddr = common.HexToAddress("0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496")
// VMAddr is known as VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
VMAddr = common.HexToAddress("0x7109709ECfa91a80626fF3989D68f67F5b1DD12D")
// ConsoleAddr is known as CONSOLE, "console.log" in ascii.
// Utils like console.sol and console2.sol work by executing a staticcall to this address.
ConsoleAddr = common.HexToAddress("0x000000000000000000636F6e736F6c652e6c6f67")
)
const (
// DefaultFoundryGasLimit is set to int64.max in foundry.toml
DefaultFoundryGasLimit = 9223372036854775807
)
type Context struct {
chainID *big.Int
sender common.Address
origin common.Address
feeRecipient common.Address
gasLimit uint64
blockNum uint64
timestamp uint64
prevRandao common.Hash
blobHashes []common.Hash
}
var DefaultContext = Context{
chainID: big.NewInt(1337),
sender: DefaultSenderAddr,
origin: DefaultSenderAddr,
feeRecipient: common.Address{},
gasLimit: DefaultFoundryGasLimit,
blockNum: 0,
timestamp: 0,
prevRandao: common.Hash{},
blobHashes: []common.Hash{},
}
This diff is collapsed.
package script
import (
"testing"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)
//go:generate ./testdata/generate.sh
func TestScript(t *testing.T) {
logger, captLog := testlog.CaptureLogger(t, log.LevelInfo)
af := foundry.OpenArtifactsDir("./testdata/test-artifacts")
scriptContext := DefaultContext
h := NewHost(logger, af, scriptContext)
addr, err := h.LoadContract("ScriptExample.s", "ScriptExample")
require.NoError(t, err)
require.NoError(t, h.EnableCheats())
input := bytes4("run()")
returnData, _, err := h.Call(scriptContext.sender, addr, input[:], DefaultFoundryGasLimit, uint256.NewInt(0))
require.NoError(t, err, "call failed: %x", string(returnData))
require.NotNil(t, captLog.FindLog(
testlog.NewAttributesFilter("p0", "sender nonce"),
testlog.NewAttributesFilter("p1", "1")))
}
################################################################
# PROFILE: DEFAULT (Local) #
################################################################
[profile.default]
# Compilation settings
src = 'src'
out = 'test-artifacts'
script = 'scripts'
optimizer = true
optimizer_runs = 999999
remappings = []
extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout']
bytecode_hash = 'none'
build_info_path = 'artifacts/build-info'
ast = true
evm_version = "cancun"
# 5159 error code is selfdestruct error code
ignored_error_codes = ["transient-storage", "code-size", "init-code-size", 5159]
# We set the gas limit to max int64 to avoid running out of gas during testing, since the default
# gas limit is 1B and some of our tests require more gas than that, such as `test_callWithMinGas_noLeakageLow_succeeds`.
# We use this gas limit since it was the default gas limit prior to https://github.com/foundry-rs/foundry/pull/8274.
# Due to toml-rs limitations, if you increase the gas limit above this value it must be a string.
gas_limit = 9223372036854775807
# Test / Script Runner Settings
ffi = false
fs_permissions = []
libs = ["node_modules", "lib"]
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Vm is a minimal interface to the forge cheatcode precompile
interface Vm {
function getNonce(address account) external view returns (uint64 nonce);
}
// console is a minimal version of the console2 lib.
library console {
address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
function _castLogPayloadViewToPure(
function(bytes memory) internal view fnIn
) internal pure returns (function(bytes memory) internal pure fnOut) {
assembly {
fnOut := fnIn
}
}
function _sendLogPayload(bytes memory payload) internal pure {
_castLogPayloadViewToPure(_sendLogPayloadView)(payload);
}
function _sendLogPayloadView(bytes memory payload) private view {
uint256 payloadLength = payload.length;
address consoleAddress = CONSOLE_ADDRESS;
/// @solidity memory-safe-assembly
assembly {
let payloadStart := add(payload, 32)
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
}
}
function log(string memory p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(string memory p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
}
function log(string memory p0, address p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
}
}
/// @title ScriptExample
/// @notice ScriptExample is an example script. The Go forge script code tests that it can run this.
contract ScriptExample {
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
Vm internal constant vm = Vm(VM_ADDRESS);
/// @notice example function, runs through basic cheat-codes and console logs.
function run() public view {
console.log("contract addr", address(this));
console.log("contract nonce", vm.getNonce(address(this)));
console.log("sender addr", address(msg.sender));
console.log("sender nonce", vm.getNonce(address(msg.sender)));
console.log("done!");
}
}
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