diff --git a/simulators/optimism/devnet/ethclient.go b/simulators/optimism/devnet/ethclient.go index 9a9e829fbe..1d74b8f637 100644 --- a/simulators/optimism/devnet/ethclient.go +++ b/simulators/optimism/devnet/ethclient.go @@ -83,12 +83,22 @@ func genesisBlockByNumberTest(t *TestEnv) { } } +// CodeAtTest tests the code for the deployed contract. +func CodeAtTest(t *TestEnv) { + code, err := t.Eth.CodeAt(t.Ctx(), t.Config.DeployedContractAddr, nil) + if err != nil { + t.Fatalf("Could not fetch code for deployed contract: %v", err) + } + if bytes.Compare(runtimeCode, code) != 0 { + t.Fatalf("Unexpected code, want %x, got %x", runtimeCode, code) + } +} // deployContractTest deploys `contractSrc` and tests if the code and state // on the contract address contain the expected values (as set in the ctor). func deployContractTest(t *TestEnv) { var ( - address = t.Vault.createAccount(t, big.NewInt(params.Ether)) + address = t.Config.Vault.createAccount(t, big.NewInt(params.Ether)) nonce = uint64(0) expectedContractAddress = crypto.CreateAddress(address, nonce) @@ -96,7 +106,7 @@ func deployContractTest(t *TestEnv) { ) rawTx := types.NewContractCreation(nonce, big0, gasLimit, gasPrice, deployCode) - deployTx, err := t.Vault.signTransaction(address, rawTx) + deployTx, err := t.Config.Vault.signTransaction(address, rawTx) if err != nil { t.Fatalf("Unable to sign deploy tx: %v", err) } @@ -162,7 +172,7 @@ func deployContractTest(t *TestEnv) { // the contract address. func deployContractOutOfGasTest(t *TestEnv) { var ( - address = t.Vault.createAccount(t, big.NewInt(params.Ether)) + address = t.Config.Vault.createAccount(t, big.NewInt(params.Ether)) nonce = uint64(0) contractAddress = crypto.CreateAddress(address, nonce) gasLimit = uint64(240000) // insufficient gas @@ -171,7 +181,7 @@ func deployContractOutOfGasTest(t *TestEnv) { // Deploy the contract. rawTx := types.NewContractCreation(nonce, big0, gasLimit, gasPrice, deployCode) - deployTx, err := t.Vault.signTransaction(address, rawTx) + deployTx, err := t.Config.Vault.signTransaction(address, rawTx) if err != nil { t.Fatalf("unable to sign deploy tx: %v", err) } @@ -249,9 +259,9 @@ func newHeadSubscriptionTest(t *TestEnv) { // address are updated correct. func balanceAndNonceAtTest(t *TestEnv) { var ( - sourceAddr = t.Vault.createAccount(t, big.NewInt(params.Ether)) + sourceAddr = t.Config.Vault.createAccount(t, big.NewInt(params.Ether)) sourceNonce = uint64(0) - targetAddr = t.Vault.createAccount(t, nil) + targetAddr = t.Config.Vault.createAccount(t, nil) ) // Get current balance @@ -279,7 +289,7 @@ func balanceAndNonceAtTest(t *TestEnv) { gasLimit = uint64(50000) ) rawTx := types.NewTransaction(sourceNonce, targetAddr, amount, gasLimit, gasPrice, nil) - valueTx, err := t.Vault.signTransaction(sourceAddr, rawTx) + valueTx, err := t.Config.Vault.signTransaction(sourceAddr, rawTx) if err != nil { t.Fatalf("Unable to sign value tx: %v", err) } diff --git a/simulators/optimism/devnet/helper.go b/simulators/optimism/devnet/helper.go index 17cc043833..5b795b743f 100644 --- a/simulators/optimism/devnet/helper.go +++ b/simulators/optimism/devnet/helper.go @@ -22,14 +22,18 @@ import ( // default timeout for RPC calls var rpcTimeout = 10 * time.Second +type TestConfig struct { + Vault *vault + L1GenesisBlock []byte + DeployedContractAddr common.Address +} + // TestClient is the environment of a single test. type TestEnv struct { *hivesim.T - RPC *rpc.Client - Eth *ethclient.Client - Vault *vault - - genesis []byte + RPC *rpc.Client + Eth *ethclient.Client + Config *TestConfig // This holds most recent context created by the Ctx method. // Every time Ctx is called, it creates a new context with the default @@ -39,7 +43,7 @@ type TestEnv struct { } // runHTTP runs the given test function using the HTTP RPC client. -func runHTTP(t *hivesim.T, c *hivesim.Client, v *vault, g []byte, fn func(*TestEnv)) { +func runHTTP(t *hivesim.T, c *hivesim.Client, config *TestConfig, fn func(*TestEnv)) { // This sets up debug logging of the requests and responses. client := &http.Client{ Transport: &loggingRoundTrip{ @@ -51,11 +55,10 @@ func runHTTP(t *hivesim.T, c *hivesim.Client, v *vault, g []byte, fn func(*TestE rpcClient, _ := rpc.DialHTTPWithClient(fmt.Sprintf("http://%v:9545/", c.IP), client) defer rpcClient.Close() env := &TestEnv{ - T: t, - RPC: rpcClient, - Eth: ethclient.NewClient(rpcClient), - Vault: v, - genesis: g, + T: t, + RPC: rpcClient, + Eth: ethclient.NewClient(rpcClient), + Config: config, } fn(env) if env.lastCtx != nil { @@ -64,7 +67,7 @@ func runHTTP(t *hivesim.T, c *hivesim.Client, v *vault, g []byte, fn func(*TestE } // runWS runs the given test function using the WebSocket RPC client. -func runWS(t *hivesim.T, c *hivesim.Client, v *vault, g []byte, fn func(*TestEnv)) { +func runWS(t *hivesim.T, c *hivesim.Client, config *TestConfig, fn func(*TestEnv)) { ctx, done := context.WithTimeout(context.Background(), 5*time.Second) rpcClient, err := rpc.DialWebsocket(ctx, fmt.Sprintf("ws://%v:9546/", c.IP), "") done() @@ -74,11 +77,10 @@ func runWS(t *hivesim.T, c *hivesim.Client, v *vault, g []byte, fn func(*TestEnv defer rpcClient.Close() env := &TestEnv{ - T: t, - RPC: rpcClient, - Eth: ethclient.NewClient(rpcClient), - Vault: v, - genesis: g, + T: t, + RPC: rpcClient, + Eth: ethclient.NewClient(rpcClient), + Config: config, } fn(env) if env.lastCtx != nil { @@ -93,10 +95,10 @@ func (t *TestEnv) CallContext(ctx context.Context, result interface{}, method st return t.RPC.CallContext(ctx, result, method, args...) } -// LoadGenesis returns the genesis block. +// LoadGenesis returns the L1 genesis block. func (t *TestEnv) LoadGenesis() *types.Block { var genesis core.Genesis - if err := json.Unmarshal(t.genesis, &genesis); err != nil { + if err := json.Unmarshal(t.Config.L1GenesisBlock, &genesis); err != nil { panic(fmt.Errorf("can't parse genesis JSON: %v", err)) } return genesis.ToBlock(nil) diff --git a/simulators/optimism/devnet/main.go b/simulators/optimism/devnet/main.go index cfd707fcb1..cca5001e25 100644 --- a/simulators/optimism/devnet/main.go +++ b/simulators/optimism/devnet/main.go @@ -2,11 +2,15 @@ package main import ( "context" + "math/big" "time" "fmt" "strings" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/hive/hivesim" ) @@ -16,9 +20,46 @@ type testSpec struct { Run func(*TestEnv) } +func setup(t *TestEnv) { + var ( + address = t.Config.Vault.createAccount(t, big.NewInt(params.Ether)) + nonce = uint64(0) + + expectedContractAddress = crypto.CreateAddress(address, nonce) + gasLimit = uint64(1200000) + ) + + rawTx := types.NewContractCreation(nonce, big0, gasLimit, gasPrice, deployCode) + deployTx, err := t.Config.Vault.signTransaction(address, rawTx) + if err != nil { + t.Fatalf("Unable to sign deploy tx: %v", err) + } + + // deploy contract + if err := t.Eth.SendTransaction(t.Ctx(), deployTx); err != nil { + t.Fatalf("Unable to send transaction: %v", err) + } + + t.Logf("Deploy transaction: 0x%x", deployTx.Hash()) + + // fetch transaction receipt for contract address + receipt, err := waitForTxConfirmations(t, deployTx.Hash(), 5) + if err != nil { + t.Fatalf("Unable to retrieve receipt: %v", err) + } + + // ensure receipt has the expected address + if expectedContractAddress != receipt.ContractAddress { + t.Fatalf("Contract deploy on different address, expected %x, got %x", expectedContractAddress, receipt.ContractAddress) + } + + t.Config.DeployedContractAddr = receipt.ContractAddress +} + var tests = []testSpec{ // HTTP RPC tests. {Name: "http/BalanceAndNonceAt", Run: balanceAndNonceAtTest}, + {Name: "http/CodeAt", Run: CodeAtTest}, {Name: "http/ContractDeployment", Run: deployContractTest}, {Name: "http/ContractDeploymentOutOfGas", Run: deployContractOutOfGasTest}, {Name: "http/GenesisBlockByHash", Run: genesisBlockByHashTest}, @@ -82,8 +123,12 @@ func runAllTests(t *hivesim.T) { c := d.l2.Client - vault := newVault() - genesis := []byte(d.l2Genesis) + // Setup deployed contract + config := &TestConfig{ + Vault: newVault(), + L1GenesisBlock: []byte(d.l2Genesis), + } + runHTTP(t, c, config, setup) s := newSemaphore(16) for _, test := range tests { @@ -97,9 +142,9 @@ func runAllTests(t *hivesim.T) { Run: func(t *hivesim.T) { switch test.Name[:strings.IndexByte(test.Name, '/')] { case "http": - runHTTP(t, c, vault, genesis, test.Run) + runHTTP(t, c, config, test.Run) case "ws": - runWS(t, c, vault, genesis, test.Run) + runWS(t, c, config, test.Run) default: panic("bad test prefix in name " + test.Name) }