Skip to content

Commit

Permalink
simulator (optimism): fix l1 genesis timestamp (#3)
Browse files Browse the repository at this point in the history
* simulator (optimism): restore balance test

* simulator (optimism): restore contract deployment test
  • Loading branch information
tuxcanfly authored and mslipper committed Sep 8, 2022
1 parent d99a265 commit c690e2d
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 2 deletions.
2 changes: 0 additions & 2 deletions clients/ops-l1/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ COPY --from=foundry /usr/local/bin/forge /usr/local/bin/forge
# Regenerate the L1 genesis file
RUN mkdir /hive
ADD genesis.json /genesis.json
RUN date +%s | xargs printf "0x%x" > /hive/genesis_timestamp
RUN GENESIS_TIMESTAMP=$(cat /hive/genesis_timestamp); jq ". | .timestamp = \"$GENESIS_TIMESTAMP\" " < ./genesis.json > /hive/genesis-l1.json

# Inject the startup script
ADD entrypoint.sh /entrypoint.sh
Expand Down
4 changes: 4 additions & 0 deletions clients/ops-l1/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/bin/sh
set -exu

# Regenerate the L1 genesis file
date +%s | xargs printf "0x%x" > /hive/genesis_timestamp
GENESIS_TIMESTAMP=$(cat /hive/genesis_timestamp); jq ". | .timestamp = \"$GENESIS_TIMESTAMP\" " < ./genesis.json > /hive/genesis-l1.json

VERBOSITY=${GETH_VERBOSITY:-3}
GETH_DATA_DIR=/db
GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
Expand Down
229 changes: 229 additions & 0 deletions simulators/optimism/devnet/ethclient.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package main

import (
"bytes"
"math/big"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)

var (
// test contract deploy code, will deploy the contract with 1234 as argument
deployCode = common.Hex2Bytes("6060604052346100005760405160208061048c833981016040528080519060200190919050505b8060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b505b610409806100836000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc117002900000000000000000000000000000000000000000000000000000000000004d2")
// test contract code as deployed
runtimeCode = common.Hex2Bytes("60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029")
)

var (
Expand Down Expand Up @@ -70,6 +83,131 @@ func genesisBlockByNumberTest(t *TestEnv) {
}
}


// 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))
nonce = uint64(0)

expectedContractAddress = crypto.CreateAddress(address, nonce)
gasLimit = uint64(1200000)
)

rawTx := types.NewContractCreation(nonce, big0, gasLimit, gasPrice, deployCode)
deployTx, err := t.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
var contractAddress common.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, contractAddress)
}

// test deployed code matches runtime code
code, err := t.Eth.CodeAt(t.Ctx(), receipt.ContractAddress, nil)
if err != nil {
t.Fatalf("Unable to fetch contract code: %v", err)
}
if bytes.Compare(runtimeCode, code) != 0 {
t.Errorf("Deployed code doesn't match, expected %x, got %x", runtimeCode, code)
}

// test contract state, pos 0 must be 1234
value, err := t.Eth.StorageAt(t.Ctx(), receipt.ContractAddress, common.Hash{}, nil)
if err == nil {
v := new(big.Int).SetBytes(value)
if v.Uint64() != 1234 {
t.Errorf("Unexpected value on %x:0x01, expected 1234, got %d", receipt.ContractAddress, v)
}
} else {
t.Errorf("Unable to retrieve storage pos 0x01 on address %x: %v", contractAddress, err)
}

// test contract state, map on pos 1 with key myAccount must be 1234
storageKey := make([]byte, 64)
copy(storageKey[12:32], address.Bytes())
storageKey[63] = 1
storageKey = crypto.Keccak256(storageKey)

value, err = t.Eth.StorageAt(t.Ctx(), receipt.ContractAddress, common.BytesToHash(storageKey), nil)
if err == nil {
v := new(big.Int).SetBytes(value)
if v.Uint64() != 1234 {
t.Errorf("Unexpected value in map, expected 1234, got %d", v)
}
} else {
t.Fatalf("Unable to retrieve value in map: %v", err)
}
}

// deployContractOutOfGasTest tries to deploy `contractSrc` with insufficient gas. It
// checks the receipts reflects the "out of gas" event and code / state isn't created in
// the contract address.
func deployContractOutOfGasTest(t *TestEnv) {
var (
address = t.Vault.createAccount(t, big.NewInt(params.Ether))
nonce = uint64(0)
contractAddress = crypto.CreateAddress(address, nonce)
gasLimit = uint64(240000) // insufficient gas
)
t.Logf("calculated contract address: %x", contractAddress)

// Deploy the contract.
rawTx := types.NewContractCreation(nonce, big0, gasLimit, gasPrice, deployCode)
deployTx, err := t.Vault.signTransaction(address, rawTx)
if err != nil {
t.Fatalf("unable to sign deploy tx: %v", err)
}
t.Logf("out of gas tx: %x", deployTx.Hash())
if err := t.Eth.SendTransaction(t.Ctx(), deployTx); err != nil {
t.Fatalf("unable to send transaction: %v", err)
}

// Wait for the transaction receipt.
receipt, err := waitForTxConfirmations(t, deployTx.Hash(), 5)
if err != nil {
t.Fatalf("unable to fetch tx receipt: %v", err)
}
// Check receipt fields.
if receipt.Status != types.ReceiptStatusFailed {
t.Errorf("receipt has status %d, want %d", receipt.Status, types.ReceiptStatusFailed)
}
if receipt.GasUsed != gasLimit {
t.Errorf("receipt has gasUsed %d, want %d", receipt.GasUsed, gasLimit)
}
if receipt.ContractAddress != contractAddress {
t.Errorf("receipt has contract address %x, want %x", receipt.ContractAddress, contractAddress)
}
if receipt.BlockHash == (common.Hash{}) {
t.Errorf("receipt has empty block hash", receipt.BlockHash)
}
// Check that nothing is deployed at the contract address.
code, err := t.Eth.CodeAt(t.Ctx(), contractAddress, nil)
if err != nil {
t.Fatalf("unable to fetch code: %v", err)
}
if len(code) != 0 {
t.Errorf("expected no code deployed but got %x", code)
}
}

// syncProgressTest only tests if this function is supported by the node.
func syncProgressTest(t *TestEnv) {
_, err := t.Eth.SyncProgress(t.Ctx())
Expand Down Expand Up @@ -105,3 +243,94 @@ func newHeadSubscriptionTest(t *TestEnv) {
}
}
}

// balanceAndNonceAtTest creates a new account and transfers funds to it.
// It then tests if the balance and nonce of the sender and receiver
// address are updated correct.
func balanceAndNonceAtTest(t *TestEnv) {
var (
sourceAddr = t.Vault.createAccount(t, big.NewInt(params.Ether))
sourceNonce = uint64(0)
targetAddr = t.Vault.createAccount(t, nil)
)

// Get current balance
sourceAddressBalanceBefore, err := t.Eth.BalanceAt(t.Ctx(), sourceAddr, nil)
if err != nil {
t.Fatalf("Unable to retrieve balance: %v", err)
}

expected := big.NewInt(params.Ether)
if sourceAddressBalanceBefore.Cmp(expected) != 0 {
t.Errorf("Expected balance %d, got %d", expected, sourceAddressBalanceBefore)
}

nonceBefore, err := t.Eth.NonceAt(t.Ctx(), sourceAddr, nil)
if err != nil {
t.Fatalf("Unable to determine nonce: %v", err)
}
if nonceBefore != sourceNonce {
t.Fatalf("Invalid nonce, want %d, got %d", sourceNonce, nonceBefore)
}

// send 1234 wei to target account and verify balances and nonces are updated
var (
amount = big.NewInt(1234)
gasLimit = uint64(50000)
)
rawTx := types.NewTransaction(sourceNonce, targetAddr, amount, gasLimit, gasPrice, nil)
valueTx, err := t.Vault.signTransaction(sourceAddr, rawTx)
if err != nil {
t.Fatalf("Unable to sign value tx: %v", err)
}
sourceNonce++

t.Logf("BalanceAt: send %d wei from 0x%x to 0x%x in 0x%x", valueTx.Value(), sourceAddr, targetAddr, valueTx.Hash())
if err := t.Eth.SendTransaction(t.Ctx(), valueTx); err != nil {
t.Fatalf("Unable to send transaction: %v", err)
}

var receipt *types.Receipt
for {
receipt, err = t.Eth.TransactionReceipt(t.Ctx(), valueTx.Hash())
if receipt != nil {
break
}
if err != ethereum.NotFound {
t.Fatalf("Could not fetch receipt for 0x%x: %v", valueTx.Hash(), err)
}
time.Sleep(time.Second)
}

// ensure balances have been updated
accountBalanceAfter, err := t.Eth.BalanceAt(t.Ctx(), sourceAddr, nil)
if err != nil {
t.Fatalf("Unable to retrieve balance: %v", err)
}
balanceTargetAccountAfter, err := t.Eth.BalanceAt(t.Ctx(), targetAddr, nil)
if err != nil {
t.Fatalf("Unable to retrieve balance: %v", err)
}

// expected balance is previous balance - tx amount - tx fee (gasUsed * gasPrice)
exp := new(big.Int).Set(sourceAddressBalanceBefore)
exp.Sub(exp, amount)
exp.Sub(exp, new(big.Int).Mul(big.NewInt(int64(receipt.GasUsed)), valueTx.GasPrice()))

if exp.Cmp(accountBalanceAfter) != 0 {
t.Errorf("Expected sender account to have a balance of %d, got %d", exp, accountBalanceAfter)
}
if balanceTargetAccountAfter.Cmp(amount) != 0 {
t.Errorf("Expected new account to have a balance of %d, got %d", valueTx.Value(), balanceTargetAccountAfter)
}

// ensure nonce is incremented by 1
nonceAfter, err := t.Eth.NonceAt(t.Ctx(), sourceAddr, nil)
if err != nil {
t.Fatalf("Unable to determine nonce: %v", err)
}
expectedNonce := nonceBefore + 1
if expectedNonce != nonceAfter {
t.Fatalf("Invalid nonce, want %d, got %d", expectedNonce, nonceAfter)
}
}
6 changes: 6 additions & 0 deletions simulators/optimism/devnet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ type testSpec struct {

var tests = []testSpec{
// HTTP RPC tests.
{Name: "http/BalanceAndNonceAt", Run: balanceAndNonceAtTest},
{Name: "http/ContractDeployment", Run: deployContractTest},
{Name: "http/ContractDeploymentOutOfGas", Run: deployContractOutOfGasTest},
{Name: "http/GenesisBlockByHash", Run: genesisBlockByHashTest},
{Name: "http/GenesisBlockByNumber", Run: genesisBlockByNumberTest},
{Name: "http/GenesisHeaderByHash", Run: genesisHeaderByHashTest},
{Name: "http/GenesisHeaderByNumber", Run: genesisHeaderByNumberTest},
{Name: "http/SyncProgress", Run: syncProgressTest},

// WebSocket RPC tests.
{Name: "ws/BalanceAndNonceAt", Run: balanceAndNonceAtTest},
{Name: "ws/ContractDeployment", Run: deployContractTest},
{Name: "ws/ContractDeploymentOutOfGas", Run: deployContractOutOfGasTest},
{Name: "ws/GenesisBlockByHash", Run: genesisBlockByHashTest},
{Name: "ws/GenesisBlockByNumber", Run: genesisBlockByNumberTest},
{Name: "ws/GenesisHeaderByHash", Run: genesisHeaderByHashTest},
Expand Down

0 comments on commit c690e2d

Please sign in to comment.