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

op-e2e: interop cross L2 inbox system test (#12754)

* op-e2e: interop cross L2 inbox system test
Co-authored-by: default avatarAxel Kingsley <axel.kingsley@gmail.com>

* point at latest interop op-geth commit

* lint

* test fixes

* update to latest geth commit

* op-supervisor: uint32 log index, improve chainid test

* correct uint64 to uint32 everywhere ; point at latest geth commit

* use tagged geth version

---------
Co-authored-by: default avatarAxel Kingsley <axel.kingsley@gmail.com>
parent 2c5f6ed3
...@@ -250,9 +250,9 @@ require ( ...@@ -250,9 +250,9 @@ require (
rsc.io/tmplfunc v0.0.3 // indirect rsc.io/tmplfunc v0.0.3 // indirect
) )
replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.2 replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.5
//replace github.com/ethereum/go-ethereum => ../op-geth //replace github.com/ethereum/go-ethereum => ../go-ethereum
// replace github.com/ethereum-optimism/superchain-registry/superchain => ../superchain-registry/superchain // replace github.com/ethereum-optimism/superchain-registry/superchain => ../superchain-registry/superchain
......
...@@ -187,8 +187,8 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u ...@@ -187,8 +187,8 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u
github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v1.101411.1-rc.2 h1:nOeSTzcFWUCvJO1MJ5AyI26dqR1F7vYgz2jNNKuEtoE= github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 h1:LDSP85xTczjDYMBK0mOF5mzpZifLGz1TTW/6NgQsytc=
github.com/ethereum-optimism/op-geth v1.101411.1-rc.2/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= github.com/ethereum-optimism/op-geth v1.101411.1-rc.5/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
......
...@@ -109,6 +109,7 @@ func prefundL2Accounts(l1Cfg *L1Config, l2Cfg *L2Config, addrs devkeys.Addresses ...@@ -109,6 +109,7 @@ func prefundL2Accounts(l1Cfg *L1Config, l2Cfg *L2Config, addrs devkeys.Addresses
l1Cfg.Prefund[l2Cfg.BatchSenderAddress] = Ether(10_000_000) l1Cfg.Prefund[l2Cfg.BatchSenderAddress] = Ether(10_000_000)
l1Cfg.Prefund[l2Cfg.Deployer] = Ether(10_000_000) l1Cfg.Prefund[l2Cfg.Deployer] = Ether(10_000_000)
l1Cfg.Prefund[l2Cfg.FinalSystemOwner] = Ether(10_000_000) l1Cfg.Prefund[l2Cfg.FinalSystemOwner] = Ether(10_000_000)
l1Cfg.Prefund[l2Cfg.SystemConfigOwner] = Ether(10_000_000)
proposer, err := addrs.Address(devkeys.ChainOperatorKey{ proposer, err := addrs.Address(devkeys.ChainOperatorKey{
ChainID: new(big.Int).SetUint64(l2Cfg.L2ChainID), ChainID: new(big.Int).SetUint64(l2Cfg.L2ChainID),
Role: devkeys.ProposerRole, Role: devkeys.ProposerRole,
......
...@@ -400,7 +400,7 @@ func TestInteropDeployment(t *testing.T) { ...@@ -400,7 +400,7 @@ func TestInteropDeployment(t *testing.T) {
chainState := st.Chains[0] chainState := st.Chains[0]
depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c") depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c")
checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot) checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot)
proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes()) proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.SystemConfigOwner.Bytes())
checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash) checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash)
} }
......
...@@ -26,7 +26,6 @@ var ( ...@@ -26,7 +26,6 @@ var (
_ = common.Big1 _ = common.Big1
_ = types.BloomLookup _ = types.BloomLookup
_ = event.NewSubscription _ = event.NewSubscription
_ = abi.ConvertType
) )
// EmitMetaData contains all meta data concerning the Emit contract. // EmitMetaData contains all meta data concerning the Emit contract.
...@@ -157,11 +156,11 @@ func NewEmitFilterer(address common.Address, filterer bind.ContractFilterer) (*E ...@@ -157,11 +156,11 @@ func NewEmitFilterer(address common.Address, filterer bind.ContractFilterer) (*E
// bindEmit binds a generic wrapper to an already deployed contract. // bindEmit binds a generic wrapper to an already deployed contract.
func bindEmit(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { func bindEmit(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := EmitMetaData.GetAbi() parsed, err := abi.JSON(strings.NewReader(EmitABI))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
......
This diff is collapsed.
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package systemconfig
import (
"errors"
"math/big"
"strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
// Reference imports to suppress errors if they are not otherwise used.
var (
_ = errors.New
_ = big.NewInt
_ = strings.NewReader
_ = ethereum.NotFound
_ = bind.Bind
_ = common.Big1
_ = types.BloomLookup
_ = event.NewSubscription
)
// SystemconfigMetaData contains all meta data concerning the Systemconfig contract.
var SystemconfigMetaData = &bind.MetaData{
ABI: "[{\"type\":\"function\",\"name\":\"addDependency\",\"inputs\":[{\"name\":\"_chainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"}]",
}
// SystemconfigABI is the input ABI used to generate the binding from.
// Deprecated: Use SystemconfigMetaData.ABI instead.
var SystemconfigABI = SystemconfigMetaData.ABI
// Systemconfig is an auto generated Go binding around an Ethereum contract.
type Systemconfig struct {
SystemconfigCaller // Read-only binding to the contract
SystemconfigTransactor // Write-only binding to the contract
SystemconfigFilterer // Log filterer for contract events
}
// SystemconfigCaller is an auto generated read-only Go binding around an Ethereum contract.
type SystemconfigCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SystemconfigTransactor is an auto generated write-only Go binding around an Ethereum contract.
type SystemconfigTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SystemconfigFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type SystemconfigFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SystemconfigSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type SystemconfigSession struct {
Contract *Systemconfig // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SystemconfigCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type SystemconfigCallerSession struct {
Contract *SystemconfigCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// SystemconfigTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type SystemconfigTransactorSession struct {
Contract *SystemconfigTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SystemconfigRaw is an auto generated low-level Go binding around an Ethereum contract.
type SystemconfigRaw struct {
Contract *Systemconfig // Generic contract binding to access the raw methods on
}
// SystemconfigCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type SystemconfigCallerRaw struct {
Contract *SystemconfigCaller // Generic read-only contract binding to access the raw methods on
}
// SystemconfigTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type SystemconfigTransactorRaw struct {
Contract *SystemconfigTransactor // Generic write-only contract binding to access the raw methods on
}
// NewSystemconfig creates a new instance of Systemconfig, bound to a specific deployed contract.
func NewSystemconfig(address common.Address, backend bind.ContractBackend) (*Systemconfig, error) {
contract, err := bindSystemconfig(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &Systemconfig{SystemconfigCaller: SystemconfigCaller{contract: contract}, SystemconfigTransactor: SystemconfigTransactor{contract: contract}, SystemconfigFilterer: SystemconfigFilterer{contract: contract}}, nil
}
// NewSystemconfigCaller creates a new read-only instance of Systemconfig, bound to a specific deployed contract.
func NewSystemconfigCaller(address common.Address, caller bind.ContractCaller) (*SystemconfigCaller, error) {
contract, err := bindSystemconfig(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &SystemconfigCaller{contract: contract}, nil
}
// NewSystemconfigTransactor creates a new write-only instance of Systemconfig, bound to a specific deployed contract.
func NewSystemconfigTransactor(address common.Address, transactor bind.ContractTransactor) (*SystemconfigTransactor, error) {
contract, err := bindSystemconfig(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &SystemconfigTransactor{contract: contract}, nil
}
// NewSystemconfigFilterer creates a new log filterer instance of Systemconfig, bound to a specific deployed contract.
func NewSystemconfigFilterer(address common.Address, filterer bind.ContractFilterer) (*SystemconfigFilterer, error) {
contract, err := bindSystemconfig(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &SystemconfigFilterer{contract: contract}, nil
}
// bindSystemconfig binds a generic wrapper to an already deployed contract.
func bindSystemconfig(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(SystemconfigABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Systemconfig *SystemconfigRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Systemconfig.Contract.SystemconfigCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Systemconfig *SystemconfigRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Systemconfig.Contract.SystemconfigTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Systemconfig *SystemconfigRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Systemconfig.Contract.SystemconfigTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_Systemconfig *SystemconfigCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _Systemconfig.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_Systemconfig *SystemconfigTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _Systemconfig.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_Systemconfig *SystemconfigTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _Systemconfig.Contract.contract.Transact(opts, method, params...)
}
// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c.
//
// Solidity: function addDependency(uint256 _chainId) returns()
func (_Systemconfig *SystemconfigTransactor) AddDependency(opts *bind.TransactOpts, _chainId *big.Int) (*types.Transaction, error) {
return _Systemconfig.contract.Transact(opts, "addDependency", _chainId)
}
// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c.
//
// Solidity: function addDependency(uint256 _chainId) returns()
func (_Systemconfig *SystemconfigSession) AddDependency(_chainId *big.Int) (*types.Transaction, error) {
return _Systemconfig.Contract.AddDependency(&_Systemconfig.TransactOpts, _chainId)
}
// AddDependency is a paid mutator transaction binding the contract method 0xa89c793c.
//
// Solidity: function addDependency(uint256 _chainId) returns()
func (_Systemconfig *SystemconfigTransactorSession) AddDependency(_chainId *big.Int) (*types.Transaction, error) {
return _Systemconfig.Contract.AddDependency(&_Systemconfig.TransactOpts, _chainId)
}
[
{
"type": "function",
"name": "executeMessage",
"inputs": [
{
"name": "_id",
"type": "tuple",
"internalType": "struct Identifier",
"components": [
{
"name": "origin",
"type": "address",
"internalType": "address"
},
{
"name": "blockNumber",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "logIndex",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "chainId",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "_target",
"type": "address",
"internalType": "address"
},
{
"name": "_message",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"type": "function",
"name": "validateMessage",
"inputs": [
{
"name": "_id",
"type": "tuple",
"internalType": "struct Identifier",
"components": [
{
"name": "origin",
"type": "address",
"internalType": "address"
},
{
"name": "blockNumber",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "logIndex",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "chainId",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "_msgHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]
[
{
"type": "function",
"name": "addDependency",
"inputs": [
{
"name": "_chainId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
}
]
{"abi":[{"type":"function","name":"addDependency","inputs":[{"name":"_chainId","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"addDependency(uint256)":"a89c793c"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_chainId\",\"type\":\"uint256\"}],\"name\":\"addDependency\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addDependency(uint256)\":{\"params\":{\"_chainId\":\"Chain ID of chain to add.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"addDependency(uint256)\":{\"notice\":\"Adds a chain to the interop dependency set. Can only be called by the dependency manager.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ISystemConfig.sol\":\"ISystemConfig\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"src/ISystemConfig.sol\":{\"keccak256\":\"0x764ea7e528e9e5ab907515b4da22738a49a3d5d8ffb948def86d0cbb9e7765be\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9307f5fd1e826f8d059f4aa9141c360aef54612ba8562ec996e7f078ef1d1cf2\",\"dweb:/ipfs/QmbzFSWWLoQUyeuD6U7detgjPy36L3DHJp9BPpaUZqVs68\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"addDependency"}],"devdoc":{"kind":"dev","methods":{"addDependency(uint256)":{"params":{"_chainId":"Chain ID of chain to add."}}},"version":1},"userdoc":{"kind":"user","methods":{"addDependency(uint256)":{"notice":"Adds a chain to the interop dependency set. Can only be called by the dependency manager."}},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/ISystemConfig.sol":"ISystemConfig"},"evmVersion":"cancun","libraries":{}},"sources":{"src/ISystemConfig.sol":{"keccak256":"0x764ea7e528e9e5ab907515b4da22738a49a3d5d8ffb948def86d0cbb9e7765be","urls":["bzz-raw://9307f5fd1e826f8d059f4aa9141c360aef54612ba8562ec996e7f078ef1d1cf2","dweb:/ipfs/QmbzFSWWLoQUyeuD6U7detgjPy36L3DHJp9BPpaUZqVs68"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user","methods":{"addDependency(uint256)":{"notice":"Adds a chain to the interop dependency set. Can only be called by the dependency manager."}}},"devdoc":{"version":1,"kind":"dev","methods":{"addDependency(uint256)":{"params":{"_chainId":"Chain ID of chain to add."}}}},"ast":{"absolutePath":"src/ISystemConfig.sol","id":9,"exportedSymbols":{"ISystemConfig":[8]},"nodeType":"SourceUnit","src":"32:264:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","^","0.8",".0"]},{"id":8,"nodeType":"ContractDefinition","src":"57:238:0","nodes":[{"id":7,"nodeType":"FunctionDefinition","src":"243:50:0","nodes":[],"documentation":{"id":2,"nodeType":"StructuredDocumentation","src":"87:151:0","text":"@notice Adds a chain to the interop dependency set. Can only be called by the dependency manager.\n @param _chainId Chain ID of chain to add."},"functionSelector":"a89c793c","implemented":false,"kind":"function","modifiers":[],"name":"addDependency","nameLocation":"252:13:0","parameters":{"id":5,"nodeType":"ParameterList","parameters":[{"constant":false,"id":4,"mutability":"mutable","name":"_chainId","nameLocation":"274:8:0","nodeType":"VariableDeclaration","scope":7,"src":"266:16:0","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":3,"name":"uint256","nodeType":"ElementaryTypeName","src":"266:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"265:18:0"},"returnParameters":{"id":6,"nodeType":"ParameterList","parameters":[],"src":"292:0:0"},"scope":8,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"ISystemConfig","contractDependencies":[],"contractKind":"interface","fullyImplemented":false,"linearizedBaseContracts":[8],"name":"ISystemConfig","nameLocation":"67:13:0","scope":9,"usedErrors":[],"usedEvents":[]}],"license":"MIT"},"id":0}
\ No newline at end of file
[
{
"type": "function",
"name": "emitData",
"inputs": [
{
"name": "_data",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "DataEmitted",
"inputs": [
{
"name": "_data",
"type": "bytes",
"indexed": true,
"internalType": "bytes"
}
],
"anonymous": false
}
]
0x6080604052348015600e575f80fd5b5060ff8061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a
{"abi":[{"type":"function","name":"emitData","inputs":[{"name":"_data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"DataEmitted","inputs":[{"name":"_data","type":"bytes","indexed":true,"internalType":"bytes"}],"anonymous":false}],"bytecode":{"object":"0x6080604052348015600e575f80fd5b5060ff8061001b5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a","sourceMap":"58:278:1:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052348015600e575f80fd5b50600436106026575f3560e01c8063d836083e14602a575b5f80fd5b60396035366004607c565b603b565b005b8181604051604992919060e3565b604051908190038120907fe00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c905f90a25050565b5f8060208385031215608c575f80fd5b823567ffffffffffffffff8082111560a2575f80fd5b818501915085601f83011260b4575f80fd5b81358181111560c1575f80fd5b86602082850101111560d1575f80fd5b60209290920196919550909350505050565b818382375f910190815291905056fea164736f6c6343000819000a","sourceMap":"58:278:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;245:89;;;;;;:::i;:::-;;:::i;:::-;;;321:5;;309:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;245:89;;:::o;14:591:2:-;84:6;92;145:2;133:9;124:7;120:23;116:32;113:52;;;161:1;158;151:12;113:52;201:9;188:23;230:18;271:2;263:6;260:14;257:34;;;287:1;284;277:12;257:34;325:6;314:9;310:22;300:32;;370:7;363:4;359:2;355:13;351:27;341:55;;392:1;389;382:12;341:55;432:2;419:16;458:2;450:6;447:14;444:34;;;474:1;471;464:12;444:34;519:7;514:2;505:6;501:2;497:15;493:24;490:37;487:57;;;540:1;537;530:12;487:57;571:2;563:11;;;;;593:6;;-1:-1:-1;14:591:2;;-1:-1:-1;;;;14:591:2:o;610:271::-;793:6;785;780:3;767:33;749:3;819:16;;844:13;;;819:16;610:271;-1:-1:-1;610:271:2:o","linkReferences":{}},"methodIdentifiers":{"emitData(bytes)":"d836083e"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"DataEmitted\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"emitData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/emit.sol\":\"EmitEvent\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":999999},\"remappings\":[]},\"sources\":{\"src/emit.sol\":{\"keccak256\":\"0xe078378fd445ed0cbbf1087c5013110412ab6a44850af24a15bcde945467accc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://66c218de0059688c75903c2ba6d4066661dc6f5fa17a329dd7c385d151f3993a\",\"dweb:/ipfs/Qmby4S4N44naZ2miw3Tgdpq5Qbj4DwzJbcGapgqtd7qd26\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes","indexed":true}],"type":"event","name":"DataEmitted","anonymous":false},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"emitData"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":[],"optimizer":{"enabled":true,"runs":999999},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/emit.sol":"EmitEvent"},"evmVersion":"cancun","libraries":{}},"sources":{"src/emit.sol":{"keccak256":"0xe078378fd445ed0cbbf1087c5013110412ab6a44850af24a15bcde945467accc","urls":["bzz-raw://66c218de0059688c75903c2ba6d4066661dc6f5fa17a329dd7c385d151f3993a","dweb:/ipfs/Qmby4S4N44naZ2miw3Tgdpq5Qbj4DwzJbcGapgqtd7qd26"],"license":"MIT"}},"version":1},"storageLayout":{"storage":[],"types":{}},"userdoc":{"version":1,"kind":"user"},"devdoc":{"version":1,"kind":"dev"},"ast":{"absolutePath":"src/emit.sol","id":52,"exportedSymbols":{"EmitEvent":[51]},"nodeType":"SourceUnit","src":"32:305:1","nodes":[{"id":36,"nodeType":"PragmaDirective","src":"32:24:1","nodes":[],"literals":["solidity","^","0.8",".15"]},{"id":51,"nodeType":"ContractDefinition","src":"58:278:1","nodes":[{"id":40,"nodeType":"EventDefinition","src":"133:39:1","nodes":[],"anonymous":false,"eventSelector":"e00bbfe6f6f8f1bbed2da38e3f5a139c6f9da594ab248a3cf8b44fc73627772c","name":"DataEmitted","nameLocation":"139:11:1","parameters":{"id":39,"nodeType":"ParameterList","parameters":[{"constant":false,"id":38,"indexed":true,"mutability":"mutable","name":"_data","nameLocation":"165:5:1","nodeType":"VariableDeclaration","scope":40,"src":"151:19:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_bytes_memory_ptr","typeString":"bytes"},"typeName":{"id":37,"name":"bytes","nodeType":"ElementaryTypeName","src":"151:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"150:21:1"}},{"id":50,"nodeType":"FunctionDefinition","src":"245:89:1","nodes":[],"body":{"id":49,"nodeType":"Block","src":"294:40:1","nodes":[],"statements":[{"eventCall":{"arguments":[{"id":46,"name":"_data","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":42,"src":"321:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes calldata"}}],"expression":{"argumentTypes":[{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes calldata"}],"id":45,"name":"DataEmitted","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":40,"src":"309:11:1","typeDescriptions":{"typeIdentifier":"t_function_event_nonpayable$_t_bytes_memory_ptr_$returns$__$","typeString":"function (bytes memory)"}},"id":47,"isConstant":false,"isLValue":false,"isPure":false,"kind":"functionCall","lValueRequested":false,"nameLocations":[],"names":[],"nodeType":"FunctionCall","src":"309:18:1","tryCall":false,"typeDescriptions":{"typeIdentifier":"t_tuple$__$","typeString":"tuple()"}},"id":48,"nodeType":"EmitStatement","src":"304:23:1"}]},"functionSelector":"d836083e","implemented":true,"kind":"function","modifiers":[],"name":"emitData","nameLocation":"254:8:1","parameters":{"id":43,"nodeType":"ParameterList","parameters":[{"constant":false,"id":42,"mutability":"mutable","name":"_data","nameLocation":"278:5:1","nodeType":"VariableDeclaration","scope":50,"src":"263:20:1","stateVariable":false,"storageLocation":"calldata","typeDescriptions":{"typeIdentifier":"t_bytes_calldata_ptr","typeString":"bytes"},"typeName":{"id":41,"name":"bytes","nodeType":"ElementaryTypeName","src":"263:5:1","typeDescriptions":{"typeIdentifier":"t_bytes_storage_ptr","typeString":"bytes"}},"visibility":"internal"}],"src":"262:22:1"},"returnParameters":{"id":44,"nodeType":"ParameterList","parameters":[],"src":"294:0:1"},"scope":51,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"EmitEvent","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[51],"name":"EmitEvent","nameLocation":"67:9:1","scope":52,"usedErrors":[],"usedEvents":[40]}],"license":"MIT"},"id":1}
\ No newline at end of file
...@@ -9,4 +9,21 @@ cat EmitEvent.json | jq -r '.bytecode.object' > EmitEvent.bin ...@@ -9,4 +9,21 @@ cat EmitEvent.json | jq -r '.bytecode.object' > EmitEvent.bin
cat EmitEvent.json | jq '.abi' > EmitEvent.abi cat EmitEvent.json | jq '.abi' > EmitEvent.abi
cd ../.. cd ../..
abigen --abi ./build/emit.sol/EmitEvent.abi --bin ./build/emit.sol/EmitEvent.bin --pkg emit --out ./emit.go mkdir -p bindings/emit
abigen --abi ./build/emit.sol/EmitEvent.abi --bin ./build/emit.sol/EmitEvent.bin --pkg emit --out ./bindings/emit/emit.go
cd build/ICrossL2Inbox.sol
cat ICrossL2Inbox.json | jq -r '.bytecode.object' > ICrossL2Inbox.bin
cat ICrossL2Inbox.json | jq '.abi' > ICrossL2Inbox.abi
cd ../..
mkdir -p bindings/inbox
abigen --abi ./build/ICrossL2Inbox.sol/ICrossL2Inbox.abi --bin ./build/ICrossL2Inbox.sol/ICrossL2Inbox.bin --pkg inbox --out ./bindings/inbox/inbox.go
cd build/ISystemConfig.sol
cat ISystemConfig.json | jq -r '.bytecode.object' > ISystemConfig.bin
cat ISystemConfig.json | jq '.abi' > ISystemConfig.abi
cd ../..
mkdir -p bindings/systemconfig
abigen --abi ./build/ISystemConfig.sol/ISystemConfig.abi --bin ./build/ISystemConfig.sol/ISystemConfig.bin --pkg systemconfig --out ./bindings/systemconfig/systemconfig.go
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct Identifier {
address origin;
uint256 blockNumber;
uint256 logIndex;
uint256 timestamp;
uint256 chainId;
}
/// @title ICrossL2Inbox
/// @notice Interface for the CrossL2Inbox contract.
interface ICrossL2Inbox {
/// @notice Executes a cross chain message on the destination chain.
/// @param _id An Identifier pointing to the initiating message.
/// @param _target Account that is called with _msg.
/// @param _message The message payload, matching the initiating message.
function executeMessage(Identifier calldata _id, address _target, bytes calldata _message) external payable;
/// @notice Validates a cross chain message on the destination chain
/// and emits an ExecutingMessage event. This function is useful
/// for applications that understand the schema of the _message payload and want to
/// process it in a custom way.
/// @param _id Identifier of the message.
/// @param _msgHash Hash of the message payload to call target with.
function validateMessage(Identifier calldata _id, bytes32 _msgHash) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISystemConfig {
/// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager.
/// @param _chainId Chain ID of chain to add.
function addDependency(uint256 _chainId) external;
}
...@@ -7,6 +7,11 @@ import ( ...@@ -7,6 +7,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-service/dial"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -157,8 +162,6 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { ...@@ -157,8 +162,6 @@ func TestInteropTrivial_EmitLogs(t *testing.T) {
} }
msgPayload = append(msgPayload, log.Data...) msgPayload = append(msgPayload, log.Data...)
expectedHash := common.BytesToHash(crypto.Keccak256(msgPayload)) expectedHash := common.BytesToHash(crypto.Keccak256(msgPayload))
// convert payload hash to log hash
logHash := types.PayloadHashToLogHash(expectedHash, log.Address)
// get block for the log (for timestamp) // get block for the log (for timestamp)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
...@@ -170,16 +173,16 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { ...@@ -170,16 +173,16 @@ func TestInteropTrivial_EmitLogs(t *testing.T) {
identifier := types.Identifier{ identifier := types.Identifier{
Origin: log.Address, Origin: log.Address,
BlockNumber: log.BlockNumber, BlockNumber: log.BlockNumber,
LogIndex: uint64(log.Index), LogIndex: uint32(log.Index),
Timestamp: block.Time(), Timestamp: block.Time(),
ChainID: types.ChainIDFromBig(s2.ChainID(chainID)), ChainID: types.ChainIDFromBig(s2.ChainID(chainID)),
} }
safety, error := supervisor.CheckMessage(context.Background(), safety, err := supervisor.CheckMessage(context.Background(),
identifier, identifier,
logHash, expectedHash,
) )
require.ErrorIs(t, error, expectedError) require.ErrorIs(t, err, expectedError)
// the supervisor could progress the safety level more quickly than we expect, // the supervisor could progress the safety level more quickly than we expect,
// which is why we check for a minimum safety level // which is why we check for a minimum safety level
require.True(t, safety.AtLeastAsSafe(expectedSafety), "log: %v should be at least %s, but is %s", log, expectedSafety.String(), safety.String()) require.True(t, safety.AtLeastAsSafe(expectedSafety), "log: %v should be at least %s, but is %s", log, expectedSafety.String(), safety.String())
...@@ -194,3 +197,98 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { ...@@ -194,3 +197,98 @@ func TestInteropTrivial_EmitLogs(t *testing.T) {
} }
setupAndRun(t, test) setupAndRun(t, test)
} }
func TestInteropBlockBuilding(t *testing.T) {
logger := testlog.Logger(t, log.LevelInfo)
oplog.SetGlobalLogHandler(logger.Handler())
test := func(t *testing.T, s2 SuperSystem) {
ids := s2.L2IDs()
chainA := ids[0]
chainB := ids[1]
// We will initiate on chain A, and execute on chain B
s2.DeployEmitterContract(chainA, "Alice")
// Add chain A as dependency to chain B,
// such that we can execute a message on B that was initiated on A.
depRec := s2.AddDependency(chainB, s2.ChainID(chainA))
t.Logf("Dependency set in L1 block %d", depRec.BlockNumber)
rollupClA, err := dial.DialRollupClientWithTimeout(context.Background(), time.Second*15, logger, s2.OpNode(chainA).UserRPC().RPC())
require.NoError(t, err)
// Now wait for the dependency to be visible in the L2 (receipt needs to be picked up)
require.Eventually(t, func() bool {
status, err := rollupClA.SyncStatus(context.Background())
require.NoError(t, err)
return status.CrossUnsafeL2.L1Origin.Number >= depRec.BlockNumber.Uint64()
}, time.Second*30, time.Second, "wait for L1 origin to match dependency L1 block")
t.Log("Dependency information has been processed in L2 block")
// emit log on chain A
emitRec := s2.EmitData(chainA, "Alice", "hello world")
t.Logf("Emitted a log event in block %d", emitRec.BlockNumber.Uint64())
// Wait for initiating side to become cross-unsafe
require.Eventually(t, func() bool {
status, err := rollupClA.SyncStatus(context.Background())
require.NoError(t, err)
return status.CrossUnsafeL2.Number >= emitRec.BlockNumber.Uint64()
}, time.Second*60, time.Second, "wait for emitted data to become cross-unsafe")
t.Logf("Reached cross-unsafe block %d", emitRec.BlockNumber.Uint64())
// Identify the log
require.Len(t, emitRec.Logs, 1)
ev := emitRec.Logs[0]
ethCl := s2.L2GethClient(chainA)
header, err := ethCl.HeaderByHash(context.Background(), emitRec.BlockHash)
require.NoError(t, err)
identifier := types.Identifier{
Origin: ev.Address,
BlockNumber: ev.BlockNumber,
LogIndex: uint32(ev.Index),
Timestamp: header.Time,
ChainID: types.ChainIDFromBig(s2.ChainID(chainA)),
}
msgPayload := types.LogToMessagePayload(ev)
payloadHash := crypto.Keccak256Hash(msgPayload)
logHash := types.PayloadHashToLogHash(payloadHash, identifier.Origin)
t.Logf("expected payload hash: %s", payloadHash)
t.Logf("expected log hash: %s", logHash)
invalidPayload := []byte("test invalid message")
invalidPayloadHash := crypto.Keccak256Hash(invalidPayload)
invalidLogHash := types.PayloadHashToLogHash(invalidPayloadHash, identifier.Origin)
t.Logf("invalid payload hash: %s", invalidPayloadHash)
t.Logf("invalid log hash: %s", invalidLogHash)
// submit executing txs on B
t.Log("Testing invalid message")
{
bobAddr := s2.Address(chainA, "Bob") // direct it to a random account without code
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
// Send an executing message, but with different payload.
// We expect the miner to be unable to include this tx, and confirmation to thus time out.
_, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload)
require.NotNil(t, err)
require.ErrorIs(t, err, ctx.Err())
require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded)
}
t.Log("Testing valid message now")
{
bobAddr := s2.Address(chainA, "Bob") // direct it to a random account without code
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
// Send an executing message with the correct identifier / payload
rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload)
require.NoError(t, err, "expecting tx to be confirmed")
t.Logf("confirmed executing msg in block %s", rec.BlockNumber)
}
t.Log("Done")
}
setupAndRun(t, test)
}
...@@ -11,6 +11,12 @@ import ( ...@@ -11,6 +11,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/systemconfig"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum/eth/ethconfig"
gn "github.com/ethereum/go-ethereum/node"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
...@@ -33,7 +39,8 @@ import ( ...@@ -33,7 +39,8 @@ import (
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/opnode" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/opnode"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/services" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/services"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils"
emit "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts" "github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/emit"
"github.com/ethereum-optimism/optimism/op-e2e/interop/contracts/bindings/inbox"
"github.com/ethereum-optimism/optimism/op-e2e/system/helpers" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers"
"github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/node"
"github.com/ethereum-optimism/optimism/op-node/p2p" "github.com/ethereum-optimism/optimism/op-node/p2p"
...@@ -96,6 +103,17 @@ type SuperSystem interface { ...@@ -96,6 +103,17 @@ type SuperSystem interface {
DeployEmitterContract(network string, username string) common.Address DeployEmitterContract(network string, username string) common.Address
// Use the Emitter Contract to emit an Event Log // Use the Emitter Contract to emit an Event Log
EmitData(network string, username string, data string) *types.Receipt EmitData(network string, username string, data string) *types.Receipt
// AddDependency adds a dependency (by chain ID) to the given chain
AddDependency(network string, dep *big.Int) *types.Receipt
// ExecuteMessage calls the CrossL2Inbox executeMessage function
ExecuteMessage(
ctx context.Context,
id string,
sender string,
msgIdentifier supervisortypes.Identifier,
target common.Address,
message []byte,
) (*types.Receipt, error)
// Access a contract on a network by name // Access a contract on a network by name
Contract(network string, contractName string) interface{} Contract(network string, contractName string) interface{}
} }
...@@ -121,6 +139,7 @@ type interopE2ESystem struct { ...@@ -121,6 +139,7 @@ type interopE2ESystem struct {
beacon *fakebeacon.FakeBeacon beacon *fakebeacon.FakeBeacon
l1 *geth.GethInstance l1 *geth.GethInstance
l2s map[string]l2Set l2s map[string]l2Set
l1GethClient *ethclient.Client
l2GethClients map[string]*ethclient.Client l2GethClients map[string]*ethclient.Client
supervisor *supervisor.SupervisorService supervisor *supervisor.SupervisorService
superClient *sources.SupervisorClient superClient *sources.SupervisorClient
...@@ -200,6 +219,7 @@ func (s *interopE2ESystem) prepareL1() (*fakebeacon.FakeBeacon, *geth.GethInstan ...@@ -200,6 +219,7 @@ func (s *interopE2ESystem) prepareL1() (*fakebeacon.FakeBeacon, *geth.GethInstan
require.NoError(s.t, err) require.NoError(s.t, err)
require.NoError(s.t, l1Geth.Node.Start()) require.NoError(s.t, l1Geth.Node.Start())
s.t.Cleanup(func() { s.t.Cleanup(func() {
s.t.Logf("Closing L1 geth")
_ = l1Geth.Close() _ = l1Geth.Close()
}) })
return bcn, l1Geth return bcn, l1Geth
...@@ -239,11 +259,17 @@ func (s *interopE2ESystem) newOperatorKeysForL2(l2Out *interopgen.L2Output) map[ ...@@ -239,11 +259,17 @@ func (s *interopE2ESystem) newOperatorKeysForL2(l2Out *interopgen.L2Output) map[
func (s *interopE2ESystem) newGethForL2(id string, l2Out *interopgen.L2Output) *geth.GethInstance { func (s *interopE2ESystem) newGethForL2(id string, l2Out *interopgen.L2Output) *geth.GethInstance {
jwtPath := writeDefaultJWT(s.t) jwtPath := writeDefaultJWT(s.t)
name := "l2-" + id name := "l2-" + id
l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath) l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath,
func(ethCfg *ethconfig.Config, nodeCfg *gn.Config) error {
ethCfg.InteropMessageRPC = s.supervisor.RPC()
return nil
})
require.NoError(s.t, err) require.NoError(s.t, err)
require.NoError(s.t, l2Geth.Node.Start()) require.NoError(s.t, l2Geth.Node.Start())
s.t.Cleanup(func() { s.t.Cleanup(func() {
_ = l2Geth.Close() s.t.Logf("Closing L2 geth of chain %s", id)
closeErr := l2Geth.Close()
s.t.Logf("Closed L2 geth of chain %s: %v", id, closeErr)
}) })
return l2Geth return l2Geth
} }
...@@ -306,7 +332,9 @@ func (s *interopE2ESystem) newNodeForL2( ...@@ -306,7 +332,9 @@ func (s *interopE2ESystem) newNodeForL2(
s.t.Cleanup(func() { s.t.Cleanup(func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() // force-quit cancel() // force-quit
s.t.Logf("Closing op-node of chain %s", id)
_ = opNode.Stop(ctx) _ = opNode.Stop(ctx)
s.t.Logf("Closed op-node of chain %s", id)
}) })
return opNode return opNode
} }
...@@ -390,7 +418,9 @@ func (s *interopE2ESystem) newBatcherForL2( ...@@ -390,7 +418,9 @@ func (s *interopE2ESystem) newBatcherForL2(
s.t.Cleanup(func() { s.t.Cleanup(func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() // force-quit cancel() // force-quit
s.t.Logf("Closing batcher of chain %s", id)
_ = batcher.Stop(ctx) _ = batcher.Stop(ctx)
s.t.Logf("Closed batcher of chain %s", id)
}) })
return batcher return batcher
} }
...@@ -471,7 +501,9 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService { ...@@ -471,7 +501,9 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService {
s.t.Cleanup(func() { s.t.Cleanup(func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() // force-quit cancel() // force-quit
_ = super.Stop(ctx) s.t.Logf("Closing supervisor")
closeErr := super.Stop(ctx)
s.t.Logf("Closed supervisor: %v", closeErr)
}) })
return super return super
} }
...@@ -503,12 +535,23 @@ func (s *interopE2ESystem) prepare(t *testing.T, w worldResourcePaths) { ...@@ -503,12 +535,23 @@ func (s *interopE2ESystem) prepare(t *testing.T, w worldResourcePaths) {
s.beacon, s.l1 = s.prepareL1() s.beacon, s.l1 = s.prepareL1()
s.l2s = s.prepareL2s() s.l2s = s.prepareL2s()
s.prepareContracts()
// add the L2 RPCs to the supervisor now that the L2s are created // add the L2 RPCs to the supervisor now that the L2s are created
ctx := context.Background() ctx := context.Background()
for _, l2 := range s.l2s { for _, l2 := range s.l2s {
err := s.SupervisorClient().AddL2RPC(ctx, l2.l2Geth.UserRPC().RPC()) err := s.SupervisorClient().AddL2RPC(ctx, l2.l2Geth.UserRPC().RPC())
require.NoError(s.t, err, "failed to add L2 RPC to supervisor") require.NoError(s.t, err, "failed to add L2 RPC to supervisor")
} }
// Try to close the op-supervisor first
s.t.Cleanup(func() {
ctx, cancel := context.WithCancel(context.Background())
cancel() // force-quit
s.t.Logf("Closing supervisor")
closeErr := s.supervisor.Stop(ctx)
s.t.Logf("Closed supervisor: %v", closeErr)
})
} }
// AddUser adds a user to the system by creating a user key for each L2. // AddUser adds a user to the system by creating a user key for each L2.
...@@ -552,6 +595,44 @@ func (s *interopE2ESystem) prepareL2s() map[string]l2Set { ...@@ -552,6 +595,44 @@ func (s *interopE2ESystem) prepareL2s() map[string]l2Set {
return l2s return l2s
} }
// prepareContracts prepares contract-bindings for the L2s
func (s *interopE2ESystem) prepareContracts() {
// Add bindings to common contracts for each L2
l1GethClient := s.L1GethClient()
for id, l2Dep := range s.worldDeployment.L2s {
{
contract, err := inbox.NewInbox(predeploys.CrossL2InboxAddr, s.L2GethClient(id))
require.NoError(s.t, err)
s.l2s[id].contracts["inbox"] = contract
}
{
contract, err := systemconfig.NewSystemconfig(l2Dep.SystemConfigProxy, l1GethClient)
require.NoError(s.t, err)
s.l2s[id].contracts["systemconfig"] = contract
}
}
}
func (s *interopE2ESystem) L1GethClient() *ethclient.Client {
if s.l1GethClient != nil {
return s.l1GethClient
}
rpcEndpoint := s.l1.UserRPC()
rpcCl := endpoint.DialRPC(
endpoint.PreferAnyRPC,
rpcEndpoint,
func(v string) *rpc.Client {
logger := testlog.Logger(s.t, log.LevelInfo)
cl, err := dial.DialRPCClientWithTimeout(context.Background(), 30*time.Second, logger, v)
require.NoError(s.t, err, "failed to dial L1 eth node instance")
return cl
})
nodeClient := ethclient.NewClient(rpcCl)
// register the client so it can be reused
s.l1GethClient = nodeClient
return nodeClient
}
func (s *interopE2ESystem) L2GethClient(id string) *ethclient.Client { func (s *interopE2ESystem) L2GethClient(id string) *ethclient.Client {
// guard: check if the client already exists and return it in that case // guard: check if the client already exists and return it in that case
nodeClient, ok := s.l2GethClients[id] nodeClient, ok := s.l2GethClients[id]
...@@ -639,6 +720,64 @@ func (s *interopE2ESystem) SendL2Tx( ...@@ -639,6 +720,64 @@ func (s *interopE2ESystem) SendL2Tx(
newApply) newApply)
} }
func (s *interopE2ESystem) ExecuteMessage(
ctx context.Context,
id string,
sender string,
msgIdentifier supervisortypes.Identifier,
target common.Address,
message []byte,
) (*types.Receipt, error) {
secret := s.UserKey(id, sender)
auth, err := bind.NewKeyedTransactorWithChainID(&secret, s.l2s[id].chainID)
require.NoError(s.t, err)
auth.GasLimit = uint64(3000_000)
auth.GasPrice = big.NewInt(20_000_000_000)
contract := s.Contract(id, "inbox").(*inbox.Inbox)
identifier := inbox.Identifier{
Origin: msgIdentifier.Origin,
BlockNumber: new(big.Int).SetUint64(msgIdentifier.BlockNumber),
LogIndex: new(big.Int).SetUint64(uint64(msgIdentifier.LogIndex)),
Timestamp: new(big.Int).SetUint64(msgIdentifier.Timestamp),
ChainId: msgIdentifier.ChainID.ToBig(),
}
tx, err := contract.InboxTransactor.ExecuteMessage(auth, identifier, target, message)
require.NoError(s.t, err)
s.logger.Info("Executing message", "tx", tx.Hash(), "to", tx.To(), "target", target, "data", hexutil.Bytes(tx.Data()))
return bind.WaitMined(ctx, s.L2GethClient(id), tx)
}
func (s *interopE2ESystem) AddDependency(id string, dep *big.Int) *types.Receipt {
// There is a note in OPContractsManagerInterop that the proxy-admin is used for now,
// even though it should be a separate dependency-set-manager address.
secret, err := s.hdWallet.Secret(devkeys.ChainOperatorKey{
ChainID: s.l2s[id].chainID,
Role: devkeys.SystemConfigOwner,
})
require.NoError(s.t, err)
auth, err := bind.NewKeyedTransactorWithChainID(secret, s.worldOutput.L1.Genesis.Config.ChainID)
require.NoError(s.t, err)
balance, err := s.l1GethClient.BalanceAt(context.Background(), crypto.PubkeyToAddress(secret.PublicKey), nil)
require.NoError(s.t, err)
require.False(s.t, balance.Sign() == 0, "system config owner needs a balance")
auth.GasLimit = uint64(3000000)
auth.GasPrice = big.NewInt(20000000000)
contract := s.Contract(id, "systemconfig").(*systemconfig.Systemconfig)
tx, err := contract.SystemconfigTransactor.AddDependency(auth, dep)
require.NoError(s.t, err)
receipt, err := wait.ForReceiptOK(context.Background(), s.L1GethClient(), tx.Hash())
require.NoError(s.t, err)
return receipt
}
func (s *interopE2ESystem) DeployEmitterContract( func (s *interopE2ESystem) DeployEmitterContract(
id string, id string,
sender string, sender string,
......
...@@ -267,6 +267,7 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error { ...@@ -267,6 +267,7 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error {
if !su.started.CompareAndSwap(true, false) { if !su.started.CompareAndSwap(true, false) {
return errAlreadyStopped return errAlreadyStopped
} }
su.logger.Info("Closing supervisor backend")
// close all processors // close all processors
for id, processor := range su.chainProcessors { for id, processor := range su.chainProcessors {
su.logger.Info("stopping chain processor", "chainID", id) su.logger.Info("stopping chain processor", "chainID", id)
...@@ -308,24 +309,27 @@ func (su *SupervisorBackend) DependencySet() depset.DependencySet { ...@@ -308,24 +309,27 @@ func (su *SupervisorBackend) DependencySet() depset.DependencySet {
// Query methods // Query methods
// ---------------------------- // ----------------------------
func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, logHash common.Hash) (types.SafetyLevel, error) { func (su *SupervisorBackend) CheckMessage(identifier types.Identifier, payloadHash common.Hash) (types.SafetyLevel, error) {
su.mu.RLock() su.mu.RLock()
defer su.mu.RUnlock() defer su.mu.RUnlock()
logHash := types.PayloadHashToLogHash(payloadHash, identifier.Origin)
chainID := identifier.ChainID chainID := identifier.ChainID
blockNum := identifier.BlockNumber blockNum := identifier.BlockNumber
logIdx := identifier.LogIndex logIdx := identifier.LogIndex
_, err := su.chainDBs.Check(chainID, blockNum, uint32(logIdx), logHash) _, err := su.chainDBs.Check(chainID, blockNum, logIdx, logHash)
if errors.Is(err, types.ErrFuture) { if errors.Is(err, types.ErrFuture) {
su.logger.Debug("Future message", "identifier", identifier, "payloadHash", payloadHash, "err", err)
return types.LocalUnsafe, nil return types.LocalUnsafe, nil
} }
if errors.Is(err, types.ErrConflict) { if errors.Is(err, types.ErrConflict) {
su.logger.Debug("Conflicting message", "identifier", identifier, "payloadHash", payloadHash, "err", err)
return types.Invalid, nil return types.Invalid, nil
} }
if err != nil { if err != nil {
return types.Invalid, fmt.Errorf("failed to check log: %w", err) return types.Invalid, fmt.Errorf("failed to check log: %w", err)
} }
return su.chainDBs.Safest(chainID, blockNum, uint32(logIdx)) return su.chainDBs.Safest(chainID, blockNum, logIdx)
} }
func (su *SupervisorBackend) CheckMessages( func (su *SupervisorBackend) CheckMessages(
...@@ -334,12 +338,21 @@ func (su *SupervisorBackend) CheckMessages( ...@@ -334,12 +338,21 @@ func (su *SupervisorBackend) CheckMessages(
su.mu.RLock() su.mu.RLock()
defer su.mu.RUnlock() defer su.mu.RUnlock()
su.logger.Debug("Checking messages", "count", len(messages), "minSafety", minSafety)
for _, msg := range messages { for _, msg := range messages {
su.logger.Debug("Checking message",
"identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String())
safety, err := su.CheckMessage(msg.Identifier, msg.PayloadHash) safety, err := su.CheckMessage(msg.Identifier, msg.PayloadHash)
if err != nil { if err != nil {
su.logger.Error("Check message failed", "err", err,
"identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String())
return fmt.Errorf("failed to check message: %w", err) return fmt.Errorf("failed to check message: %w", err)
} }
if !safety.AtLeastAsSafe(minSafety) { if !safety.AtLeastAsSafe(minSafety) {
su.logger.Error("Message is not sufficiently safe",
"safety", safety, "minSafety", minSafety,
"identifier", msg.Identifier, "payloadHash", msg.PayloadHash.String())
return fmt.Errorf("message %v (safety level: %v) does not meet the minimum safety %v", return fmt.Errorf("message %v (safety level: %v) does not meet the minimum safety %v",
msg.Identifier, msg.Identifier,
safety, safety,
......
...@@ -287,7 +287,7 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ ...@@ -287,7 +287,7 @@ func (db *DB) Contains(blockNum uint64, logIdx uint32, logHash common.Hash) (typ
db.log.Trace("Found initiatingEvent", "blockNum", blockNum, "logIdx", logIdx, "hash", evtHash) db.log.Trace("Found initiatingEvent", "blockNum", blockNum, "logIdx", logIdx, "hash", evtHash)
// Found the requested block and log index, check if the hash matches // Found the requested block and log index, check if the hash matches
if evtHash != logHash { if evtHash != logHash {
return types.BlockSeal{}, fmt.Errorf("payload hash mismatch: expected %s, got %s", logHash, evtHash) return types.BlockSeal{}, fmt.Errorf("payload hash mismatch: expected %s, got %s %w", logHash, evtHash, types.ErrConflict)
} }
// Now find the block seal after the log, to identify where the log was included in. // Now find the block seal after the log, to identify where the log was included in.
err = iter.TraverseConditional(func(state IteratorState) error { err = iter.TraverseConditional(func(state IteratorState) error {
......
...@@ -77,18 +77,6 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts ...@@ -77,18 +77,6 @@ func (p *logProcessor) ProcessLogs(_ context.Context, block eth.BlockRef, rcpts
// The address is hashed into the payload hash to save space in the log storage, // The address is hashed into the payload hash to save space in the log storage,
// and because they represent paired data. // and because they represent paired data.
func logToLogHash(l *ethTypes.Log) common.Hash { func logToLogHash(l *ethTypes.Log) common.Hash {
payloadHash := crypto.Keccak256(logToMessagePayload(l)) payloadHash := crypto.Keccak256(types.LogToMessagePayload(l))
return types.PayloadHashToLogHash(common.Hash(payloadHash), l.Address) return types.PayloadHashToLogHash(common.Hash(payloadHash), l.Address)
} }
// logToMessagePayload is the data that is hashed to get the logHash
// it is the concatenation of the log's topics and data
// the implementation is based on the interop messaging spec
func logToMessagePayload(l *ethTypes.Log) []byte {
msg := make([]byte, 0)
for _, topic := range l.Topics {
msg = append(msg, topic.Bytes()...)
}
msg = append(msg, l.Data...)
return msg
}
...@@ -184,6 +184,7 @@ func (su *SupervisorService) Start(ctx context.Context) error { ...@@ -184,6 +184,7 @@ func (su *SupervisorService) Start(ctx context.Context) error {
func (su *SupervisorService) Stop(ctx context.Context) error { func (su *SupervisorService) Stop(ctx context.Context) error {
if !su.closing.CompareAndSwap(false, true) { if !su.closing.CompareAndSwap(false, true) {
su.log.Warn("Supervisor is already closing")
return nil // already closing return nil // already closing
} }
su.log.Info("Stopping JSON-RPC server") su.log.Info("Stopping JSON-RPC server")
......
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"math/big" "math/big"
"strconv" "strconv"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -58,7 +60,7 @@ type Message struct { ...@@ -58,7 +60,7 @@ type Message struct {
type Identifier struct { type Identifier struct {
Origin common.Address Origin common.Address
BlockNumber uint64 BlockNumber uint64
LogIndex uint64 LogIndex uint32
Timestamp uint64 Timestamp uint64
ChainID ChainID // flat, not a pointer, to make Identifier safe as map key ChainID ChainID // flat, not a pointer, to make Identifier safe as map key
} }
...@@ -88,7 +90,10 @@ func (id *Identifier) UnmarshalJSON(input []byte) error { ...@@ -88,7 +90,10 @@ func (id *Identifier) UnmarshalJSON(input []byte) error {
} }
id.Origin = dec.Origin id.Origin = dec.Origin
id.BlockNumber = uint64(dec.BlockNumber) id.BlockNumber = uint64(dec.BlockNumber)
id.LogIndex = uint64(dec.LogIndex) if dec.LogIndex > math.MaxUint32 {
return fmt.Errorf("log index too large: %d", dec.LogIndex)
}
id.LogIndex = uint32(dec.LogIndex)
id.Timestamp = uint64(dec.Timestamp) id.Timestamp = uint64(dec.Timestamp)
id.ChainID = (ChainID)(dec.ChainID) id.ChainID = (ChainID)(dec.ChainID)
return nil return nil
...@@ -190,6 +195,10 @@ func (id ChainID) ToUInt32() (uint32, error) { ...@@ -190,6 +195,10 @@ func (id ChainID) ToUInt32() (uint32, error) {
return uint32(v64), nil return uint32(v64), nil
} }
func (id *ChainID) ToBig() *big.Int {
return (*uint256.Int)(id).ToBig()
}
func (id ChainID) MarshalText() ([]byte, error) { func (id ChainID) MarshalText() ([]byte, error) {
return []byte(id.String()), nil return []byte(id.String()), nil
} }
...@@ -264,3 +273,15 @@ func PayloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.H ...@@ -264,3 +273,15 @@ func PayloadHashToLogHash(payloadHash common.Hash, addr common.Address) common.H
msg = append(msg, payloadHash.Bytes()...) msg = append(msg, payloadHash.Bytes()...)
return crypto.Keccak256Hash(msg) return crypto.Keccak256Hash(msg)
} }
// LogToMessagePayload is the data that is hashed to get the payloadHash
// it is the concatenation of the log's topics and data
// the implementation is based on the interop messaging spec
func LogToMessagePayload(l *ethTypes.Log) []byte {
msg := make([]byte, 0)
for _, topic := range l.Topics {
msg = append(msg, topic.Bytes()...)
}
msg = append(msg, l.Data...)
return msg
}
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
) )
func FuzzRoundtripIdentifierJSONMarshal(f *testing.F) { func FuzzRoundtripIdentifierJSONMarshal(f *testing.F) {
f.Fuzz(func(t *testing.T, origin []byte, blockNumber uint64, logIndex uint64, timestamp uint64, chainID []byte) { f.Fuzz(func(t *testing.T, origin []byte, blockNumber uint64, logIndex uint32, timestamp uint64, chainID []byte) {
if len(chainID) > 32 { if len(chainID) > 32 {
chainID = chainID[:32] chainID = chainID[:32]
} }
...@@ -55,7 +55,19 @@ func TestChainID_String(t *testing.T) { ...@@ -55,7 +55,19 @@ func TestChainID_String(t *testing.T) {
for _, test := range tests { for _, test := range tests {
test := test test := test
t.Run(test.expected, func(t *testing.T) { t.Run(test.expected, func(t *testing.T) {
require.Equal(t, test.expected, test.input.String()) t.Run("String", func(t *testing.T) {
require.Equal(t, test.expected, test.input.String())
})
t.Run("MarshalText", func(t *testing.T) {
data, err := test.input.MarshalText()
require.NoError(t, err)
require.Equal(t, test.expected, string(data))
})
t.Run("UnmarshalText", func(t *testing.T) {
var id ChainID
require.NoError(t, id.UnmarshalText([]byte(test.expected)))
require.Equal(t, test.input, id)
})
}) })
} }
} }
...@@ -32,12 +32,12 @@ contract OPContractsManagerInterop is OPContractsManager { ...@@ -32,12 +32,12 @@ contract OPContractsManagerInterop is OPContractsManager {
(IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) =
defaultSystemConfigParams(_selector, _input, _output); defaultSystemConfigParams(_selector, _input, _output);
// TODO For now we assume that the dependency manager is the same as the proxy admin owner. // TODO For now we assume that the dependency manager is the same as system config owner.
// This is currently undefined since it's not part of the standard config, so we may need // This is currently undefined since it's not part of the standard config, so we may need
// to update where this value is pulled from in the future. To support a different dependency // to update where this value is pulled from in the future. To support a different dependency
// manager in this contract without an invasive change of redefining the `Roles` struct, // manager in this contract without an invasive change of redefining the `Roles` struct,
// we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783. // we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783.
address dependencyManager = address(_input.roles.opChainProxyAdminOwner); address dependencyManager = address(_input.roles.systemConfigOwner);
return abi.encodeWithSelector( return abi.encodeWithSelector(
_selector, _selector,
......
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