From 5352b1d65c71543f900ca1917ca9788581c04cde Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Fri, 13 Sep 2024 16:38:18 +0200 Subject: [PATCH] feat: interop logging & code cleanup (#140) * changes * lint --- README.md | 60 ++++++---- anvil/anvil.go | 8 +- config/chain.go | 14 ++- config/cli.go | 33 ++++-- interop/applier.go | 179 ++++++++++++++++++++++++++++++ interop/indexer.go | 9 +- opsimulator/applier.go | 129 --------------------- opsimulator/derive.go | 38 ------- opsimulator/opsimulator.go | 91 +-------------- orchestrator/fork.go | 17 ++- orchestrator/orchestrator.go | 150 +++++++++++++------------ orchestrator/orchestrator_test.go | 2 +- supersim.go | 41 +++++-- supersim_test.go | 17 ++- testutils/mockchain.go | 5 +- 15 files changed, 386 insertions(+), 407 deletions(-) create mode 100644 interop/applier.go delete mode 100644 opsimulator/applier.go diff --git a/README.md b/README.md index 2c209e2e..520e2691 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,23 @@ supersim Vanilla mode will start 3 chains, with the OP Stack contracts already deployed. ``` -L1: - Name: L1 Chain ID: 900 RPC: http://127.0.0.1:8545 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-900 -L2: - Name: OPChainA Chain ID: 901 RPC: http://127.0.0.1:9545 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-901 - Name: OPChainB Chain ID: 902 RPC: http://127.0.0.1:9546 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-902 +Chain Configuration +----------------------- +L1: Name: Local ChainID: 900 RPC: http://127.0.0.1:8545 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-900-3719464405 + +L2s: Predeploy Contracts Spec ( https://specs.optimism.io/protocol/predeploys.html ) + + * Name: OPChainA ChainID: 901 RPC: http://127.0.0.1:9545 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-901-1956365912 + L1 Contracts: + - OptimismPortal: 0xF5fe61a258CeBb54CCe428F76cdeD04Cbc12F53d + - L1CrossDomainMessenger: 0xe5bda89cd85cE0DfB80E053281cA070D65B738e6 + - L1StandardBridge: 0xa01ae68902e205B420FD164435F299E07b0C778b + + * Name: OPChainB ChainID: 902 RPC: http://127.0.0.1:9546 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-902-1214175152 + L1 Contracts: + - OptimismPortal: 0xdfC9DEAbEEbDaa7620C71e2E76AEda32919DE5f2 + - L1CrossDomainMessenger: 0xCB9768921831677Ae15cE4B64A10B94F49cD88E2 + - L1StandardBridge: 0x2D8543c236a4d626f54B51Fa8bc229a257C5143E ``` ### 4. Start testing multichain features 🚀 @@ -137,23 +149,29 @@ supersim fork --chains=op,base,zora The fork height is determined by L1 block height (default `latest`), which determines the maximum timestamp for the forked L2 state of each chain. ``` -Available Accounts ------------------------ -(0): 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 ---- truncated for brevity --- - -Private Keys +Chain Configuration ----------------------- -(0): 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 ---- truncated for brevity --- - -Orchestrator Config: -L1: - Name: mainnet Chain ID: 1 RPC: http://127.0.0.1:8545 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-1-1521250718 -L2: - Name: op Chain ID: 10 RPC: http://127.0.0.1:9545 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-10 - Name: base Chain ID: 8453 RPC: http://127.0.0.1:9546 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-8453 - Name: zora Chain ID: 7777777 RPC: http://127.0.0.1:9547 LogPath: /var/folders/0w/ethers-phoenix/T/anvil-chain-7777777 +L1: Name: mainnet ChainID: 1 RPC: http://127.0.0.1:8545 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-1-832151416 + +L2s: Predeploy Contracts Spec ( https://specs.optimism.io/protocol/predeploys.html ) + + * Name: op ChainID: 10 RPC: http://127.0.0.1:9545 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-10-2710239022 + L1 Contracts: + - OptimismPortal: 0xbEb5Fc579115071764c7423A4f12eDde41f106Ed + - L1CrossDomainMessenger: 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 + - L1StandardBridge: 0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1 + + * Name: base ChainID: 8453 RPC: http://127.0.0.1:9546 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-8453-1054019892 + L1 Contracts: + - OptimismPortal: 0x49048044D57e1C92A77f79988d21Fa8fAF74E97e + - L1CrossDomainMessenger: 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa + - L1StandardBridge: 0x3154Cf16ccdb4C6d922629664174b904d80F2C35 + + * Name: zora ChainID: 7777777 RPC: http://127.0.0.1:9547 LogPath: /var/folders/y6/bkjdghqx1sn_3ypk1n0zy3040000gn/T/anvil-chain-7777777-1949580962 + L1 Contracts: + - OptimismPortal: 0x1a0ad011913A150f69f6A19DF447A0CfD9551054 + - L1CrossDomainMessenger: 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 + - L1StandardBridge: 0x3e2Ea9B92B7E48A52296fD261dc26fd995284631 ``` ### Note diff --git a/anvil/anvil.go b/anvil/anvil.go index 10bfebd2..682eccf4 100644 --- a/anvil/anvil.go +++ b/anvil/anvil.go @@ -240,12 +240,12 @@ func (a *Anvil) EthClient() *ethclient.Client { return a.ethClient } -func (a *Anvil) SetCode(ctx context.Context, result interface{}, address string, code string) error { - return a.rpcClient.CallContext(ctx, result, "anvil_setCode", address, code) +func (a *Anvil) SetCode(ctx context.Context, result interface{}, address common.Address, code string) error { + return a.rpcClient.CallContext(ctx, result, "anvil_setCode", address.Hex(), code) } -func (a *Anvil) SetStorageAt(ctx context.Context, result interface{}, address string, storageSlot string, storageValue string) error { - return a.rpcClient.CallContext(ctx, result, "anvil_setStorageAt", address, storageSlot, storageValue) +func (a *Anvil) SetStorageAt(ctx context.Context, result interface{}, address common.Address, storageSlot string, storageValue string) error { + return a.rpcClient.CallContext(ctx, result, "anvil_setStorageAt", address.Hex(), storageSlot, storageValue) } func (a *Anvil) SetIntervalMining(ctx context.Context, result interface{}, interval int64) error { diff --git a/config/chain.go b/config/chain.go index 2e6301c6..caab94ed 100644 --- a/config/chain.go +++ b/config/chain.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/supersim/hdaccount" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ) @@ -25,7 +26,6 @@ var ( type ForkConfig struct { RPCUrl string BlockNumber uint64 - UseInterop bool } type SecretsConfig struct { @@ -63,6 +63,11 @@ type NetworkConfig struct { L2StartingPort uint64 L2Configs []ChainConfig + + // Signaled higher up as a way to generally + // check if Interop is enabled + InteropEnabled bool + InteropAutoRelay bool } type Chain interface { @@ -74,8 +79,8 @@ type Chain interface { // Additional methods SimulatedLogs(ctx context.Context, tx *types.Transaction) ([]types.Log, error) - SetCode(ctx context.Context, result interface{}, address string, code string) error - SetStorageAt(ctx context.Context, result interface{}, address string, storageSlot string, storageValue string) error + SetCode(ctx context.Context, result interface{}, address common.Address, code string) error + SetStorageAt(ctx context.Context, result interface{}, address common.Address, storageSlot string, storageValue string) error SetIntervalMining(ctx context.Context, result interface{}, interval int64) error // Lifecycle @@ -85,6 +90,9 @@ type Chain interface { func GetDefaultNetworkConfig(startingTimestamp uint64) NetworkConfig { return NetworkConfig{ + // Enabled by default as it is included in genesis + InteropEnabled: true, + L1Config: ChainConfig{ Name: "Local", ChainID: genesis.GeneratedGenesisDeployment.L1.ChainID, diff --git a/config/cli.go b/config/cli.go index 5e94082f..bb5c8eee 100644 --- a/config/cli.go +++ b/config/cli.go @@ -21,8 +21,8 @@ const ( NetworkFlagName = "network" L2StartingPortFlagName = "l2.starting.port" - InteropEnabledInForkModeFlagName = "interop.enabled" - InteropAutoRelayFlagName = "interop.autorelay" + InteropEnabledFlagName = "interop.enabled" + InteropAutoRelayFlagName = "interop.autorelay" ) func BaseCLIFlags(envPrefix string) []cli.Flag { @@ -71,10 +71,10 @@ func ForkCLIFlags(envPrefix string) []cli.Flag { EnvVars: opservice.PrefixEnvVar(envPrefix, "NETWORK"), }, &cli.BoolFlag{ - Name: InteropEnabledInForkModeFlagName, - Value: false, - Usage: "Enable interop in fork mode", - EnvVars: opservice.PrefixEnvVar(envPrefix, "FORK_WITH_INTEROP"), + Name: InteropEnabledFlagName, + Value: true, // enabled by default + Usage: "enable interop predeploy and functionality", + EnvVars: opservice.PrefixEnvVar(envPrefix, "INTEROP_ENABLED"), }, } } @@ -83,12 +83,14 @@ type ForkCLIConfig struct { L1ForkHeight uint64 Network string Chains []string - UseInterop bool + + InteropEnabled bool } type CLIConfig struct { - L1Port uint64 - L2StartingPort uint64 + L1Port uint64 + L2StartingPort uint64 + InteropAutoRelay bool ForkConfig *ForkCLIConfig @@ -96,8 +98,9 @@ type CLIConfig struct { func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) { cfg := &CLIConfig{ - L1Port: ctx.Uint64(L1PortFlagName), - L2StartingPort: ctx.Uint64(L2StartingPortFlagName), + L1Port: ctx.Uint64(L1PortFlagName), + L2StartingPort: ctx.Uint64(L2StartingPortFlagName), + InteropAutoRelay: ctx.Bool(InteropAutoRelayFlagName), } @@ -106,7 +109,8 @@ func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) { L1ForkHeight: ctx.Uint64(L1ForkHeightFlagName), Network: ctx.String(NetworkFlagName), Chains: ctx.StringSlice(ChainsFlagName), - UseInterop: ctx.Bool(InteropEnabledInForkModeFlagName), + + InteropEnabled: ctx.Bool(InteropEnabledFlagName), } } @@ -117,12 +121,17 @@ func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) { func (c *CLIConfig) Check() error { if c.ForkConfig != nil { forkCfg := c.ForkConfig + superchain, ok := registry.Superchains[forkCfg.Network] if !ok { return fmt.Errorf("unrecognized superchain network `%s`, available networks: [%s]", forkCfg.Network, strings.Join(superchainNetworks(), ", ")) } + if len(forkCfg.Chains) == 0 { + return fmt.Errorf("no chains specified! available chains: [%s]", strings.Join(superchainMemberChains(superchain), ",")) + } + // ensure every chain is apart of the network for _, chain := range forkCfg.Chains { if !isInSuperchain(chain, superchain) { diff --git a/interop/applier.go b/interop/applier.go new file mode 100644 index 00000000..180aaeb2 --- /dev/null +++ b/interop/applier.go @@ -0,0 +1,179 @@ +package interop + +import ( + "context" + "errors" + "fmt" + "math/big" + "strings" + "time" + + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + "github.com/ethereum-optimism/supersim/bindings" + "github.com/ethereum-optimism/supersim/config" + "github.com/ethereum-optimism/supersim/genesis" + + "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/log" +) + +const emptyCode = "0x" + +var interopPredeploys = []common.Address{ + predeploys.CrossL2InboxAddr, + predeploys.L2toL2CrossDomainMessengerAddr, + predeploys.L1BlockAddr, +} + +type predeploy struct { + proxy common.Address + impl common.Address +} + +func Configure(ctx context.Context, chain config.Chain) error { + cfg := chain.Config() + if cfg.L2Config == nil { + return nil // should only be called on L2s + } + + // Interop contracts are included in L2 genesis. So we only need to + // apply them manually in a forked configuration. + if cfg.ForkConfig != nil { + var predeploys []predeploy + for _, proxy := range interopPredeploys { + impl := predeployToCodeNamespace(proxy) + predeploys = append(predeploys, predeploy{proxy, impl}) + } + + l2Genesis, err := genesis.UnMarshaledL2GenesisJSON() + if err != nil { + return err + } + + // Setup predeploys + for _, predeploy := range predeploys { + if err := applyAllocForPredeploy(ctx, chain, predeploy, l2Genesis); err != nil { + return fmt.Errorf("failed to apply predeploy %s", predeploy.proxy) + } + } + } + + // Setup The Dependency Set (only on L2) + for _, chainId := range cfg.L2Config.DependencySet { + if err := addChainDependency(ctx, chain, chainId); err != nil { + return fmt.Errorf("failed to update chain dep set: %s: %w", cfg.Name, err) + } + } + + return nil +} + +// Uses same logic as `predeployToCodeNamespace` in Predeploy.sol to determine impl address. +func predeployToCodeNamespace(addr common.Address) common.Address { + addrBigInt := new(big.Int).SetBytes(addr.Bytes()) + namespaceBigInt := new(big.Int).SetBytes(common.FromHex("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")) + resultBigInt := new(big.Int).Or(new(big.Int).And(addrBigInt, big.NewInt(0xffff)), namespaceBigInt) + return common.BytesToAddress(resultBigInt.Bytes()) +} + +func applyAllocForPredeploy(ctx context.Context, chain config.Chain, predeploy predeploy, genesisJSON *genesis.GenesisJson) error { + implAlloc, ok := genesisJSON.Alloc[strings.ToLower(predeploy.impl.Hex()[2:])] + if !ok { + return fmt.Errorf("alloc not found %s:", predeploy.impl) + } + if err := applyAllocToAddress(ctx, chain, &implAlloc, predeploy.impl); err != nil { + return fmt.Errorf("failed to apply alloc for %s: %w", predeploy.impl, err) + } + + proxyAlloc, ok := genesisJSON.Alloc[strings.ToLower(predeploy.proxy.Hex()[2:])] + if !ok { + return fmt.Errorf("alloc not found %s:", predeploy.proxy) + } + if err := applyAllocToAddress(ctx, chain, &proxyAlloc, predeploy.proxy); err != nil { + return fmt.Errorf("failed to apply alloc for %s: %w", predeploy.proxy, err) + } + + return nil +} + +func applyAllocToAddress(ctx context.Context, chain config.Chain, alloc *genesis.Alloc, address common.Address) error { + if alloc.Code != emptyCode { + if err := chain.SetCode(ctx, nil, address, alloc.Code); err != nil { + return fmt.Errorf("failed to set code for %s: %w", address, err) + } + } + if alloc.Storage != nil { + for storageSlot, storageValue := range alloc.Storage { + if err := chain.SetStorageAt(ctx, nil, address, storageSlot, storageValue); err != nil { + return fmt.Errorf("failed to set storage for %s: %w", address, err) + } + } + } + return nil +} + +func addChainDependency(ctx context.Context, chain config.Chain, chainId uint64) error { + // TODO: when these are available in the monorepo import the constants directly from there + // L1BlockInterop contract/bindings are not available in the monorepo yet + l1BlockAddress := common.HexToAddress(predeploys.L1Block) + l1BlockABI, err := abi.JSON(strings.NewReader(bindings.L1BlockInteropMetaData.ABI)) + if err != nil { + return fmt.Errorf("failed to read l1 block abi: %w", err) + } + + cfgTypeAddDep := uint8(1) + data, err := l1BlockABI.Pack("setConfig", cfgTypeAddDep, big.NewInt(int64(chainId)).FillBytes(make([]byte, 32))) + if err != nil { + return fmt.Errorf("failed to construct l1Block dep update calldata: %w", err) + } + + // Since we're not depositing from the L1, SourceHash is empty + // - TODO: Update when we initiate the system config tx from the L1 + dep := &types.DepositTx{From: derive.L1InfoDepositerAddress, To: &l1BlockAddress, Value: big.NewInt(0), Gas: derive.RegolithSystemTxGas, Data: data} + tx, clnt := types.NewTx(dep), chain.EthClient() + if err := clnt.SendTransaction(ctx, tx); err != nil { + return fmt.Errorf("failed to send setConfig deposit tx: %w", err) + } + + txReceipt, err := waitMinedWithTicker(ctx, clnt, tx, time.Millisecond*20) + if err != nil { + return fmt.Errorf("failed to get tx receipt for deposit tx: %w", err) + } + if txReceipt.Status == 0 { + return fmt.Errorf("setConfig deposit tx failed") + } + + return nil +} + +// From bind.WaitMined with additional ticker param +func waitMinedWithTicker(ctx context.Context, b bind.DeployBackend, tx *types.Transaction, tickerDuration time.Duration) (*types.Receipt, error) { + queryTicker := time.NewTicker(tickerDuration) + defer queryTicker.Stop() + + logger := log.New("hash", tx.Hash()) + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if err == nil { + return receipt, nil + } + + if errors.Is(err, ethereum.NotFound) { + logger.Trace("Transaction not yet mined") + } else { + logger.Trace("Receipt retrieval failed", "err", err) + } + + // Wait for the next round. + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} diff --git a/interop/indexer.go b/interop/indexer.go index 87df15f2..21a218fe 100644 --- a/interop/indexer.go +++ b/interop/indexer.go @@ -48,15 +48,10 @@ func (i *L2ToL2MessageIndexer) Start(ctx context.Context, clients map[uint64]*et i.clients = clients for chainID, client := range i.clients { - client := client - i.tasks.Go(func() error { - logCh := make(chan types.Log) - - sub, err := client.SubscribeFilterLogs(i.tasksCtx, ethereum.FilterQuery{ - Addresses: []common.Address{predeploys.L2toL2CrossDomainMessengerAddr}, - }, logCh) + fq := ethereum.FilterQuery{Addresses: []common.Address{predeploys.L2toL2CrossDomainMessengerAddr}} + sub, err := client.SubscribeFilterLogs(i.tasksCtx, fq, logCh) if err != nil { return fmt.Errorf("failed to subscribe to L2ToL2CrossDomainMessenger events: %w", err) } diff --git a/opsimulator/applier.go b/opsimulator/applier.go deleted file mode 100644 index 4c75bdea..00000000 --- a/opsimulator/applier.go +++ /dev/null @@ -1,129 +0,0 @@ -package opsimulator - -import ( - "context" - "fmt" - "math/big" - "strings" - "sync" - - "github.com/ethereum-optimism/optimism/op-service/predeploys" - "github.com/ethereum-optimism/supersim/config" - "github.com/ethereum-optimism/supersim/genesis" - - "github.com/ethereum/go-ethereum/common" -) - -type predeploy struct { - proxy common.Address - impl common.Address -} - -var interopProxyPredeploys = []common.Address{ - predeploys.CrossL2InboxAddr, - predeploys.L2toL2CrossDomainMessengerAddr, - predeploys.L1BlockAddr, -} - -const ( - emptyCode = "0x" -) - -// Uses same logic as `predeployToCodeNamespace` in Predeploy.sol to determine impl address. -func predeployToCodeNamespace(addr common.Address) common.Address { - addrBigInt := new(big.Int).SetBytes(addr.Bytes()) - - namespaceBigInt := new(big.Int).SetBytes(common.FromHex("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")) - resultBigInt := new(big.Int).Or(new(big.Int).And(addrBigInt, big.NewInt(0xffff)), namespaceBigInt) - - return common.BytesToAddress(resultBigInt.Bytes()) -} - -func fetchAllocForAddr(addr common.Address, genesisJSON *genesis.GenesisJson) (*genesis.Alloc, error) { - alloc, exists := genesisJSON.Alloc[strings.ToLower(addr.Hex()[2:])] - if !exists { - return nil, fmt.Errorf("alloc not found: %s", addr) - } - - return &alloc, nil -} - -func applyAllocToAddress(ctx context.Context, chain config.Chain, alloc *genesis.Alloc, address common.Address) error { - if alloc.Code != emptyCode { - err := chain.SetCode(ctx, nil, address.Hex(), alloc.Code) - if err != nil { - return fmt.Errorf("failed to set code for %s: %w", address, err) - } - } - if alloc.Storage != nil { - for storageSlot, storageValue := range alloc.Storage { - err := chain.SetStorageAt(ctx, nil, address.Hex(), storageSlot, storageValue) - if err != nil { - return fmt.Errorf("failed to set storage for %s: %w", address, err) - } - } - } - return nil -} - -func applyAllocForPredeploy(ctx context.Context, chain config.Chain, predeploy predeploy, genesisJSON *genesis.GenesisJson) error { - implAlloc, err := fetchAllocForAddr(predeploy.impl, genesisJSON) - if err != nil { - return fmt.Errorf("failed to fetch alloc for %s: %w", predeploy.impl, err) - } - if err := applyAllocToAddress(ctx, chain, implAlloc, predeploy.impl); err != nil { - return fmt.Errorf("failed to apply alloc for %s: %w", predeploy.impl, err) - } - - proxyAlloc, err := fetchAllocForAddr(predeploy.proxy, genesisJSON) - if err != nil { - return fmt.Errorf("failed to fetch alloc for %s: %w", predeploy.proxy, err) - } - if err := applyAllocToAddress(ctx, chain, proxyAlloc, predeploy.proxy); err != nil { - return fmt.Errorf("failed to apply alloc for %s: %w", predeploy.proxy, err) - } - - return nil -} - -func configureInteropForChain(ctx context.Context, chain config.Chain) error { - var interopPredeploys []predeploy - for _, proxy := range interopProxyPredeploys { - implContractAddress := predeployToCodeNamespace(proxy) - interopPredeploys = append(interopPredeploys, predeploy{proxy: proxy, impl: implContractAddress}) - } - l2Genesis, unMarshaledErr := genesis.UnMarshaledL2GenesisJSON() - if unMarshaledErr != nil { - return unMarshaledErr - } - - var once sync.Once - var handledError error - ctx, cancel := context.WithCancel(ctx) - - handleErr := func(e error) { - if e == nil { - return - } - - once.Do(func() { - handledError = e - cancel() - }) - } - - var wg sync.WaitGroup - wg.Add(len(interopPredeploys)) - for _, predeploy := range interopPredeploys { - predeploy := predeploy - go func() { - defer wg.Done() - handleErr(applyAllocForPredeploy(ctx, chain, predeploy, l2Genesis)) - }() - - } - - wg.Wait() - - return handledError -} diff --git a/opsimulator/derive.go b/opsimulator/derive.go index 249883a8..1e3a3924 100644 --- a/opsimulator/derive.go +++ b/opsimulator/derive.go @@ -1,48 +1,10 @@ package opsimulator import ( - "math/big" - "strings" - - "github.com/ethereum-optimism/optimism/op-node/rollup/derive" - "github.com/ethereum-optimism/optimism/op-service/predeploys" "github.com/ethereum-optimism/supersim/bindings" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) -// TODO: when these are available in the monorepo import the constants directly from there -// L1BlockInterop contract/bindings are not available in the monorepo yet -const ( - ConfigTypeSetGasPayingToken uint8 = 0 - ConfigTypeAddDependency uint8 = 1 - ConfigTypeRemoveDependency uint8 = 2 -) - -var L1BlockInteropABI, _ = abi.JSON(strings.NewReader(bindings.L1BlockInteropMetaData.ABI)) -var L1BlockAddress = common.HexToAddress(predeploys.L1Block) - -func NewAddDependencyDepositTx(chainID *big.Int) (*types.DepositTx, error) { - data, err := L1BlockInteropABI.Pack("setConfig", ConfigTypeAddDependency, chainID.FillBytes(make([]byte, 32))) - if err != nil { - return nil, err - } - - return &types.DepositTx{ - // Since we're not depositing from the L1, SourceHash is empty - // Update when we initiate the system config tx from the L1 - SourceHash: common.BytesToHash(make([]byte, 0)), - From: derive.L1InfoDepositerAddress, - To: &L1BlockAddress, - Mint: nil, - Value: big.NewInt(0), - Gas: derive.RegolithSystemTxGas, - IsSystemTransaction: false, // Deprecated post-Regolith - Data: data, - }, nil -} - func executingMessagePayloadBytes(log *types.Log) []byte { msg := []byte{} for _, topic := range log.Topics { diff --git a/opsimulator/opsimulator.go b/opsimulator/opsimulator.go index 5e7c2e09..265fca92 100644 --- a/opsimulator/opsimulator.go +++ b/opsimulator/opsimulator.go @@ -13,7 +13,6 @@ import ( "strconv" "strings" "sync/atomic" - "time" ophttp "github.com/ethereum-optimism/optimism/op-service/httputil" "github.com/ethereum-optimism/optimism/op-service/predeploys" @@ -50,11 +49,6 @@ type OpSimulator struct { bgTasksCancel context.CancelFunc peers map[uint64]config.Chain - // One time tasks at startup - startupTasks tasks.Group - startupTasksCtx context.Context - startupTasksCancel context.CancelFunc - port uint64 httpServer *ophttp.HTTPServer crossL2Inbox *bindings.CrossL2Inbox @@ -67,7 +61,6 @@ type OpSimulator struct { // OpSimulator wraps around the l2 chain. By embedding `Chain`, it also implements the same inteface func New(log log.Logger, closeApp context.CancelCauseFunc, port uint64, l1Chain, l2Chain config.Chain, peers map[uint64]config.Chain) *OpSimulator { bgTasksCtx, bgTasksCancel := context.WithCancel(context.Background()) - startupTasksCtx, startupTasksCancel := context.WithCancel(context.Background()) crossL2Inbox, err := bindings.NewCrossL2Inbox(predeploys.CrossL2InboxAddr, l2Chain.EthClient()) if err != nil { @@ -91,15 +84,6 @@ func New(log log.Logger, closeApp context.CancelCauseFunc, port uint64, l1Chain, }, }, - startupTasksCtx: startupTasksCtx, - startupTasksCancel: startupTasksCancel, - startupTasks: tasks.Group{ - HandleCrit: func(err error) { - log.Error("opsim startup task failed", err) - closeApp(err) - }, - }, - peers: peers, } } @@ -124,17 +108,12 @@ func (opSim *OpSimulator) Start(ctx context.Context) error { } } - opSim.startStartupTasks(ctx) - if err := opSim.startupTasks.Wait(); err != nil { - return fmt.Errorf("failed to start opsimulator: %w", err) - } - ethClient, err := ethclient.Dial(opSim.Endpoint()) if err != nil { return fmt.Errorf("failed to create eth client: %w", err) } - opSim.ethClient = ethClient + opSim.ethClient = ethClient opSim.startBackgroundTasks() return nil } @@ -155,24 +134,6 @@ func (opSim *OpSimulator) EthClient() *ethclient.Client { return opSim.ethClient } -func (opSim *OpSimulator) startStartupTasks(ctx context.Context) { - opSim.startupTasks.Go(func() error { - if opSim.Config().ForkConfig != nil && opSim.Config().ForkConfig.UseInterop { - if err := configureInteropForChain(ctx, opSim.Chain); err != nil { - return fmt.Errorf("failed to configure interop: %w", err) - } - } - - for _, chainID := range opSim.Config().L2Config.DependencySet { - if err := opSim.addDependency(chainID); err != nil { - return fmt.Errorf("failed to configure dependency set: %w", err) - } - } - - return nil - }) -} - func (opSim *OpSimulator) startBackgroundTasks() { // Relay deposit tx from L1 to L2 opSim.bgTasks.Go(func() error { @@ -335,29 +296,6 @@ func forwardRPCRequest(ctx context.Context, rpcClient *rpc.Client, req *jsonRpcM return &jsonRpcMessage{Version: vsn, Result: result, ID: req.ID}, nil } -// Update dependency set on the L2#L1BlockInterop using a deposit tx -func (opSim *OpSimulator) addDependency(chainID uint64) error { - dep, err := NewAddDependencyDepositTx(big.NewInt(int64(chainID))) - if err != nil { - return fmt.Errorf("failed to create setConfig deposit tx: %w", err) - } - - tx, clnt := types.NewTx(dep), opSim.Chain.EthClient() - if err := clnt.SendTransaction(opSim.startupTasksCtx, tx); err != nil { - return fmt.Errorf("failed to send setConfig deposit tx: %w", err) - } - - txReceipt, err := waitMinedWithTicker(opSim.startupTasksCtx, clnt, tx, time.Millisecond*20) - if err != nil { - return fmt.Errorf("failed to get tx receipt for deposit tx: %w", err) - } - if txReceipt.Status == 0 { - return fmt.Errorf("setConfig deposit tx failed") - } - - return nil -} - func (opSim *OpSimulator) checkInteropInvariants(ctx context.Context, tx *types.Transaction) error { logs, err := opSim.SimulatedLogs(ctx, tx) if err != nil { @@ -456,30 +394,3 @@ func corsHandler(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } - -// From bind.WaitMined with additional ticker param -func waitMinedWithTicker(ctx context.Context, b bind.DeployBackend, tx *types.Transaction, tickerDuration time.Duration) (*types.Receipt, error) { - queryTicker := time.NewTicker(tickerDuration) - defer queryTicker.Stop() - - logger := log.New("hash", tx.Hash()) - for { - receipt, err := b.TransactionReceipt(ctx, tx.Hash()) - if err == nil { - return receipt, nil - } - - if errors.Is(err, ethereum.NotFound) { - logger.Trace("Transaction not yet mined") - } else { - logger.Trace("Receipt retrieval failed", "err", err) - } - - // Wait for the next round. - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-queryTicker.C: - } - } -} diff --git a/orchestrator/fork.go b/orchestrator/fork.go index 81932d54..32062e1f 100644 --- a/orchestrator/fork.go +++ b/orchestrator/fork.go @@ -19,7 +19,7 @@ const blockTime = 2 func NetworkConfigFromForkCLIConfig(log log.Logger, envPrefix string, forkConfig *config.ForkCLIConfig) (config.NetworkConfig, error) { superchain := registry.Superchains[forkConfig.Network] - networkConfig := config.NetworkConfig{} + networkConfig := config.NetworkConfig{InteropEnabled: forkConfig.InteropEnabled} // L1 l1RpcUrl := superchain.Config.L1.PublicRPC @@ -98,7 +98,6 @@ func NetworkConfigFromForkCLIConfig(log log.Logger, envPrefix string, forkConfig ForkConfig: &config.ForkConfig{ RPCUrl: l2RpcUrl, BlockNumber: l2ForkHeight, - UseInterop: forkConfig.UseInterop, }, L2Config: &config.L2Config{ L1ChainID: superchain.Config.L1.ChainID, @@ -106,17 +105,15 @@ func NetworkConfigFromForkCLIConfig(log log.Logger, envPrefix string, forkConfig }, } - if l2ChainConfig.ForkConfig.UseInterop { + if forkConfig.InteropEnabled { var dependencySet []uint64 - for _, chainToAddToDepSet := range forkConfig.Chains { - chainToAddToDepSetCfg := config.OPChainByName(superchain, chainToAddToDepSet) - if chainToAddToDepSetCfg == nil { - return networkConfig, fmt.Errorf("unrecognized chain %s. superchain %s", chainToAddToDepSet, superchain.Superchain) - } - if chainToAddToDepSetCfg.ChainID != l2ChainConfig.ChainID { - dependencySet = append(dependencySet, chainToAddToDepSetCfg.ChainID) + for _, chain := range forkConfig.Chains { + chainCfg := config.OPChainByName(superchain, chain) + if chainCfg.ChainID != l2ChainConfig.ChainID { + dependencySet = append(dependencySet, chainCfg.ChainID) } } + l2ChainConfig.L2Config.DependencySet = dependencySet } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index fc2002d2..e980c21f 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -19,7 +19,8 @@ import ( ) type Orchestrator struct { - log log.Logger + log log.Logger + config *config.NetworkConfig l1Chain config.Chain @@ -30,11 +31,11 @@ type Orchestrator struct { l2ToL2MsgRelayer *interop.L2ToL2MessageRelayer } -func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkConfig *config.NetworkConfig, enableAutoRelay bool) (*Orchestrator, error) { +func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkConfig *config.NetworkConfig) (*Orchestrator, error) { // Spin up L1 anvil instance l1Anvil := anvil.New(log, closeApp, &networkConfig.L1Config) - // Spin up L2 anvil instances fronted by opsim + // Spin up L2 anvil instances nextL2Port := networkConfig.L2StartingPort l2Anvils, l2OpSims := make(map[uint64]config.Chain), make(map[uint64]*opsimulator.OpSimulator) for i := range networkConfig.L2Configs { @@ -44,8 +45,8 @@ func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkCo l2Anvil := anvil.New(log, closeApp, &cfg) l2Anvils[cfg.ChainID] = l2Anvil } - l2ToL2MsgIndexer := interop.NewL2ToL2MessageIndexer(log) + // Sping up OpSim to fornt the L2 instances for i := range networkConfig.L2Configs { cfg := networkConfig.L2Configs[i] l2OpSims[cfg.ChainID] = opsimulator.New(log, closeApp, nextL2Port, l1Anvil, l2Anvils[cfg.ChainID], l2Anvils) @@ -56,21 +57,26 @@ func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkCo } } - var l2ToL2MessageRelayer *interop.L2ToL2MessageRelayer - if enableAutoRelay { - l2ToL2MessageRelayer = interop.NewL2ToL2MessageRelayer() + o := Orchestrator{log: log, config: networkConfig, l1Chain: l1Anvil, l2Chains: l2Anvils, l2OpSims: l2OpSims} + + // Interop Setup + if networkConfig.InteropEnabled { + o.l2ToL2MsgIndexer = interop.NewL2ToL2MessageIndexer(log) + if networkConfig.InteropAutoRelay { + o.l2ToL2MsgRelayer = interop.NewL2ToL2MessageRelayer() + } } - return &Orchestrator{log, l1Anvil, l2Anvils, l2OpSims, l2ToL2MsgIndexer, l2ToL2MessageRelayer}, nil + return &o, nil } func (o *Orchestrator) Start(ctx context.Context) error { + o.log.Debug("starting orchestrator") - o.log.Info("starting orchestrator") + // Start Chains if err := o.l1Chain.Start(ctx); err != nil { return fmt.Errorf("l1 chain %s failed to start: %w", o.l1Chain.Config().Name, err) } - for _, chain := range o.l2Chains { if err := chain.Start(ctx); err != nil { return fmt.Errorf("l2 chain %s failed to start: %w", chain.Config().Name, err) @@ -82,6 +88,7 @@ func (o *Orchestrator) Start(ctx context.Context) error { } } + // Start Mining Blocks if err := o.kickOffMining(ctx); err != nil { return fmt.Errorf("unable to start mining: %w", err) } @@ -91,20 +98,38 @@ func (o *Orchestrator) Start(ctx context.Context) error { // We should try to use make RPC through opsim not directly to the underlying chain l2ChainClientByChainId := make(map[uint64]*ethclient.Client) l2OpSimClientByChainId := make(map[uint64]*ethclient.Client) - for chainID, opSim := range o.l2OpSims { l2ChainClientByChainId[chainID] = opSim.Chain.EthClient() l2OpSimClientByChainId[chainID] = opSim.EthClient() } - if err := o.l2ToL2MsgIndexer.Start(ctx, l2ChainClientByChainId); err != nil { - return fmt.Errorf("l2 to l2 message indexer failed to start: %w", err) - } + // Configure Interop (if applicable) + if o.config.InteropEnabled { + o.log.Info("configuring interop contracts") + + var wg sync.WaitGroup + wg.Add(len(o.l2Chains)) + errs := make([]error, len(o.l2Chains)) + for i, chain := range o.L2Chains() { + go func(i int) { + if err := interop.Configure(ctx, chain); err != nil { + errs[i] = fmt.Errorf("failed to configure interop for chain %s: %w", chain.Config().Name, err) + } + }(i) + } + if err := errors.Join(errs...); err != nil { + return err + } + + if err := o.l2ToL2MsgIndexer.Start(ctx, l2ChainClientByChainId); err != nil { + return fmt.Errorf("l2 to l2 message indexer failed to start: %w", err) + } - if o.l2ToL2MsgRelayer != nil { - o.log.Info("starting L2ToL2CrossDomainMessenger autorelayer") - if err := o.l2ToL2MsgRelayer.Start(o.l2ToL2MsgIndexer, l2OpSimClientByChainId); err != nil { - return fmt.Errorf("l2 to l2 message relayer failed to start: %w", err) + if o.l2ToL2MsgRelayer != nil { + o.log.Info("starting L2ToL2CrossDomainMessenger autorelayer") // `info` since it's explictily enabled + if err := o.l2ToL2MsgRelayer.Start(o.l2ToL2MsgIndexer, l2OpSimClientByChainId); err != nil { + return fmt.Errorf("l2 to l2 message relayer failed to start: %w", err) + } } } @@ -114,18 +139,20 @@ func (o *Orchestrator) Start(ctx context.Context) error { func (o *Orchestrator) Stop(ctx context.Context) error { var errs []error + o.log.Debug("stopping orchestrator") - o.log.Info("stopping orchestrator") + if o.config.InteropEnabled { + if o.l2ToL2MsgRelayer != nil { + o.log.Info("stopping L2ToL2CrossDomainMessenger autorelayer") + o.l2ToL2MsgRelayer.Stop(ctx) + } - if o.l2ToL2MsgRelayer != nil { - o.log.Info("stopping L2ToL2CrossDomainMessenger autorelayer") - o.l2ToL2MsgRelayer.Stop(ctx) + o.log.Debug("stopping L2ToL2CrossDomainMessenger indexer") + if err := o.l2ToL2MsgIndexer.Stop(ctx); err != nil { + errs = append(errs, fmt.Errorf("l2 to l2 message indexer failed to stop: %w", err)) + } } - o.log.Info("stopping L2ToL2CrossDomainMessenger indexer") - if err := o.l2ToL2MsgIndexer.Stop(ctx); err != nil { - errs = append(errs, fmt.Errorf("l2 to l2 message indexer failed to stop: %w", err)) - } for _, opSim := range o.l2OpSims { o.log.Debug("stopping op simulator", "chain.id", opSim.Config().ChainID) if err := opSim.Stop(ctx); err != nil { @@ -148,37 +175,25 @@ func (o *Orchestrator) Stop(ctx context.Context) error { } func (o *Orchestrator) kickOffMining(ctx context.Context) error { - var once sync.Once - var err error - ctx, cancel := context.WithCancel(ctx) - - handleErr := func(e error) { - if e == nil { - return - } - - once.Do(func() { - err = e - cancel() - }) + if err := o.l1Chain.SetIntervalMining(ctx, nil, 2); err != nil { + return errors.New("failed to start interval mining on l1") } var wg sync.WaitGroup - wg.Add(len(o.l2Chains) + 1) + wg.Add(len(o.l2Chains)) - handleErr(o.l1Chain.SetIntervalMining(ctx, nil, 2)) - wg.Done() + errs := make([]error, len(o.l2Chains)) + for i, chain := range o.L2Chains() { + go func(i int) { + if err := chain.SetIntervalMining(ctx, nil, 2); err != nil { + errs[i] = fmt.Errorf("failed to start interval mining for chain %s", chain.Config().Name) + } - for _, chain := range o.l2Chains { - go func() { - defer wg.Done() - handleErr(chain.SetIntervalMining(ctx, nil, 2)) - }() + wg.Done() + }(i) } - wg.Wait() - - return err + return errors.Join(errs...) } func (o *Orchestrator) L1Chain() config.Chain { @@ -205,25 +220,22 @@ func (o *Orchestrator) ConfigAsString() string { l1Cfg := o.l1Chain.Config() fmt.Fprintf(&b, "L1: Name: %s ChainID: %d RPC: %s LogPath: %s\n", l1Cfg.Name, l1Cfg.ChainID, o.l1Chain.Endpoint(), o.l1Chain.LogPath()) - if len(o.l2OpSims) > 0 { - fmt.Fprintf(&b, "\nL2s: Predeploy Contracts Spec ( %s )\n", "https://specs.optimism.io/protocol/predeploys.html") - - opSims := make([]*opsimulator.OpSimulator, 0, len(o.l2OpSims)) - for _, chain := range o.l2OpSims { - opSims = append(opSims, chain) - } - - // sort by port number (retain ordering of chain flags) - sort.Slice(opSims, func(i, j int) bool { return opSims[i].Config().Port < opSims[j].Config().Port }) - for _, opSim := range opSims { - cfg := opSim.Config() - fmt.Fprintf(&b, "\n") - fmt.Fprintf(&b, " * Name: %s ChainID: %d RPC: %s LogPath: %s\n", cfg.Name, cfg.ChainID, opSim.Endpoint(), opSim.LogPath()) - fmt.Fprintf(&b, " L1 Contracts:\n") - fmt.Fprintf(&b, " - OptimismPortal: %s\n", cfg.L2Config.L1Addresses.OptimismPortalProxy) - fmt.Fprintf(&b, " - L1CrossDomainMessenger: %s\n", cfg.L2Config.L1Addresses.L1CrossDomainMessengerProxy) - fmt.Fprintf(&b, " - L1StandardBridge: %s\n", cfg.L2Config.L1Addresses.L1StandardBridgeProxy) - } + fmt.Fprintf(&b, "\nL2s: Predeploy Contracts Spec ( %s )\n", "https://specs.optimism.io/protocol/predeploys.html") + opSims := make([]*opsimulator.OpSimulator, 0, len(o.l2OpSims)) + for _, chain := range o.l2OpSims { + opSims = append(opSims, chain) + } + + // sort by port number (retain ordering of chain flags) + sort.Slice(opSims, func(i, j int) bool { return opSims[i].Config().Port < opSims[j].Config().Port }) + for _, opSim := range opSims { + cfg := opSim.Config() + fmt.Fprintf(&b, "\n") + fmt.Fprintf(&b, " * Name: %s ChainID: %d RPC: %s LogPath: %s\n", cfg.Name, cfg.ChainID, opSim.Endpoint(), opSim.LogPath()) + fmt.Fprintf(&b, " L1 Contracts:\n") + fmt.Fprintf(&b, " - OptimismPortal: %s\n", cfg.L2Config.L1Addresses.OptimismPortalProxy) + fmt.Fprintf(&b, " - L1CrossDomainMessenger: %s\n", cfg.L2Config.L1Addresses.L1CrossDomainMessengerProxy) + fmt.Fprintf(&b, " - L1StandardBridge: %s\n", cfg.L2Config.L1Addresses.L1StandardBridgeProxy) } return b.String() diff --git a/orchestrator/orchestrator_test.go b/orchestrator/orchestrator_test.go index b85d1bbe..f1787cea 100644 --- a/orchestrator/orchestrator_test.go +++ b/orchestrator/orchestrator_test.go @@ -25,7 +25,7 @@ func createTestSuite(t *testing.T) *TestSuite { testlog := testlog.Logger(t, log.LevelInfo) ctx, closeApp := context.WithCancelCause(context.Background()) - orchestrator, _ := NewOrchestrator(testlog, closeApp, &networkConfig, false) + orchestrator, _ := NewOrchestrator(testlog, closeApp, &networkConfig) t.Cleanup(func() { closeApp(nil) if err := orchestrator.Stop(context.Background()); err != nil { diff --git a/supersim.go b/supersim.go index cb478b17..baf8c25e 100644 --- a/supersim.go +++ b/supersim.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/ethereum-optimism/optimism/op-service/predeploys" registry "github.com/ethereum-optimism/superchain-registry/superchain" "github.com/ethereum-optimism/supersim/config" "github.com/ethereum-optimism/supersim/orchestrator" @@ -16,18 +17,22 @@ import ( type Supersim struct { log log.Logger - Cfg config.NetworkConfig + CLIConfig *config.CLIConfig + NetworkConfig *config.NetworkConfig + Orchestrator *orchestrator.Orchestrator } func NewSupersim(log log.Logger, envPrefix string, closeApp context.CancelCauseFunc, cliConfig *config.CLIConfig) (*Supersim, error) { - cfg := config.GetDefaultNetworkConfig(uint64(time.Now().Unix())) + networkConfig := config.GetDefaultNetworkConfig(uint64(time.Now().Unix())) + + // If Forking, override the network config with the generated fork config if cliConfig.ForkConfig != nil { superchain := registry.Superchains[cliConfig.ForkConfig.Network] log.Info("generating fork configuration", "superchain", superchain.Superchain) var err error - cfg, err = orchestrator.NetworkConfigFromForkCLIConfig(log, envPrefix, cliConfig.ForkConfig) + networkConfig, err = orchestrator.NetworkConfigFromForkCLIConfig(log, envPrefix, cliConfig.ForkConfig) if err != nil { return nil, fmt.Errorf("failed to construct fork configuration: %w", err) } @@ -36,28 +41,31 @@ func NewSupersim(log log.Logger, envPrefix string, closeApp context.CancelCauseF if cliConfig.ForkConfig.L1ForkHeight > 0 { l1ForkHeightStr = fmt.Sprintf("%d", cliConfig.ForkConfig.L1ForkHeight) } - log.Info("forked l1 chain config", "name", superchain.Superchain, "chain.id", cfg.L1Config.ChainID, "fork.height", l1ForkHeightStr) - for _, chainCfg := range cfg.L2Configs { + + log.Info("forked l1 chain config", "name", superchain.Superchain, "chain.id", networkConfig.L1Config.ChainID, "fork.height", l1ForkHeightStr) + for _, chainCfg := range networkConfig.L2Configs { name := registry.OPChains[chainCfg.ChainID].Chain log.Info("forked l2 chain config", "name", name, "chain.id", chainCfg.ChainID, "fork.height", chainCfg.ForkConfig.BlockNumber) } } // Forward set ports. Setting `0` will work to allocate a random port - cfg.L1Config.Port = cliConfig.L1Port - cfg.L2StartingPort = cliConfig.L2StartingPort + networkConfig.L1Config.Port = cliConfig.L1Port + networkConfig.L2StartingPort = cliConfig.L2StartingPort + + // Forward interop config + networkConfig.InteropAutoRelay = cliConfig.InteropAutoRelay - o, err := orchestrator.NewOrchestrator(log, closeApp, &cfg, cliConfig.InteropAutoRelay) + o, err := orchestrator.NewOrchestrator(log, closeApp, &networkConfig) if err != nil { return nil, fmt.Errorf("failed to create orchestrator") } - return &Supersim{log, cfg, o}, nil + return &Supersim{log, cliConfig, &networkConfig, o}, nil } func (s *Supersim) Start(ctx context.Context) error { s.log.Info("starting supersim") - if err := s.Orchestrator.Start(ctx); err != nil { return fmt.Errorf("orchestrator failed to start: %w", err) } @@ -85,10 +93,19 @@ func (s *Supersim) Stopped() bool { func (s *Supersim) ConfigAsString() string { var b strings.Builder fmt.Fprintln(&b, config.DefaultSecretsConfigAsString()) - - fmt.Fprintln(&b, "Orchestrator Config") + fmt.Fprintln(&b, "Chain Configuration") fmt.Fprintln(&b, "-----------------------") fmt.Fprintln(&b, s.Orchestrator.ConfigAsString()) + // Vanilla mode or enabled in fork mode + if s.NetworkConfig.InteropEnabled { + fmt.Fprintln(&b, "(EXPERIMENTAL) Interop!") + fmt.Fprintln(&b, "-----------------------") + fmt.Fprintln(&b, "For more information see the explainer! ( https://docs.optimism.io/stack/protocol/interop/explainer )") + fmt.Fprintln(&b, "\nAdded Predeploy Contracts:") + fmt.Fprintf(&b, " - L2ToL2CrossDomainMessenger: %s\n", predeploys.L2CrossDomainMessenger) + fmt.Fprintf(&b, " - CrossL2Inbox: %s\n", predeploys.CrossL2Inbox) + } + return b.String() } diff --git a/supersim_test.go b/supersim_test.go index 00887372..c43899e2 100644 --- a/supersim_test.go +++ b/supersim_test.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum-optimism/supersim/bindings" "github.com/ethereum-optimism/supersim/config" "github.com/ethereum-optimism/supersim/hdaccount" - "github.com/ethereum-optimism/supersim/opsimulator" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi" @@ -113,9 +112,9 @@ func createForkedInteropTestSuite(t *testing.T) *InteropTestSuite { destChain := "base" cliConfig := &config.CLIConfig{ ForkConfig: &config.ForkCLIConfig{ - Chains: []string{srcChain, destChain}, - UseInterop: true, - Network: "mainnet", + Chains: []string{srcChain, destChain}, + Network: "mainnet", + InteropEnabled: true, }, } superchain := registry.Superchains[cliConfig.ForkConfig.Network] @@ -152,11 +151,11 @@ func createForkedInteropTestSuite(t *testing.T) *InteropTestSuite { func createInteropTestSuite(t *testing.T, cliConfig config.CLIConfig) *InteropTestSuite { testSuite := createTestSuite(t, &cliConfig) - sourceURL := testSuite.Supersim.Orchestrator.Endpoint(testSuite.Supersim.Cfg.L2Configs[0].ChainID) + sourceURL := testSuite.Supersim.Orchestrator.Endpoint(testSuite.Supersim.NetworkConfig.L2Configs[0].ChainID) sourceEthClient, _ := ethclient.Dial(sourceURL) defer sourceEthClient.Close() - destURL := testSuite.Supersim.Orchestrator.Endpoint(testSuite.Supersim.Cfg.L2Configs[1].ChainID) + destURL := testSuite.Supersim.Orchestrator.Endpoint(testSuite.Supersim.NetworkConfig.L2Configs[1].ChainID) destEthClient, _ := ethclient.Dial(destURL) defer destEthClient.Close() @@ -170,8 +169,8 @@ func createInteropTestSuite(t *testing.T, cliConfig config.CLIConfig) *InteropTe HdAccountStore: testSuite.HdAccountStore, SourceEthClient: sourceEthClient, DestEthClient: destEthClient, - SourceChainID: new(big.Int).SetUint64(testSuite.Supersim.Cfg.L2Configs[0].ChainID), - DestChainID: new(big.Int).SetUint64(testSuite.Supersim.Cfg.L2Configs[1].ChainID), + SourceChainID: new(big.Int).SetUint64(testSuite.Supersim.NetworkConfig.L2Configs[0].ChainID), + DestChainID: new(big.Int).SetUint64(testSuite.Supersim.NetworkConfig.L2Configs[1].ChainID), } } @@ -317,7 +316,7 @@ func TestDependencySet(t *testing.T) { require.NoError(t, err) defer l2Client.Close() - l1BlockInterop, err := bindings.NewL1BlockInterop(opsimulator.L1BlockAddress, l2Client) + l1BlockInterop, err := bindings.NewL1BlockInterop(predeploys.L1BlockAddr, l2Client) require.NoError(t, err) // TODO: fix when we add a wait for ready on the opsim diff --git a/testutils/mockchain.go b/testutils/mockchain.go index 96fcb536..cea4623f 100644 --- a/testutils/mockchain.go +++ b/testutils/mockchain.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum-optimism/supersim/config" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" ) @@ -61,11 +62,11 @@ func (c *MockChain) SimulatedLogs(ctx context.Context, tx *types.Transaction) ([ return nil, nil } -func (c *MockChain) SetCode(ctx context.Context, result interface{}, address string, code string) error { +func (c *MockChain) SetCode(ctx context.Context, result interface{}, address common.Address, code string) error { return nil } -func (c *MockChain) SetStorageAt(ctx context.Context, result interface{}, address string, storageSlot string, storageValue string) error { +func (c *MockChain) SetStorageAt(ctx context.Context, result interface{}, address common.Address, storageSlot string, storageValue string) error { return nil }