Commit de4279b2 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge pull request #4963 from ethereum-optimism/aj/update-op-geth

op-geth: Update to latest op-geth and add e2e tests
parents 33e63455 ad330fcc
...@@ -191,6 +191,6 @@ require ( ...@@ -191,6 +191,6 @@ require (
nhooyr.io/websocket v1.8.7 // indirect nhooyr.io/websocket v1.8.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.11.2 => github.com/ethereum-optimism/op-geth v0.0.0-20230222154945-12d211246519 replace github.com/ethereum/go-ethereum v1.11.2 => github.com/ethereum-optimism/op-geth v1.11.2-aea0402.0.20230227230209-0705cf1b7df9
//replace github.com/ethereum/go-ethereum v1.11.2 => ../go-ethereum //replace github.com/ethereum/go-ethereum v1.11.2 => ../go-ethereum
...@@ -217,8 +217,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 ...@@ -217,8 +217,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
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 v0.0.0-20230222154945-12d211246519 h1:6ha+V+b2sHGSgJHLvrrrGjJM27Vr51g381Vq4Jj0Qfc= github.com/ethereum-optimism/op-geth v1.11.2-aea0402.0.20230227230209-0705cf1b7df9 h1:O13fqCZYW+HiGVs+UFKtMUHnCMpWR7XcyTPijm9IAiY=
github.com/ethereum-optimism/op-geth v0.0.0-20230222154945-12d211246519/go.mod h1:/tjlXxOaovIyuF0l6+wCzr6AtDb3lYWTymmpQAQcqu8= github.com/ethereum-optimism/op-geth v1.11.2-aea0402.0.20230227230209-0705cf1b7df9/go.mod h1:/tjlXxOaovIyuF0l6+wCzr6AtDb3lYWTymmpQAQcqu8=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
......
...@@ -2,7 +2,10 @@ module github.com/ethereum-optimism/optimism/indexer ...@@ -2,7 +2,10 @@ module github.com/ethereum-optimism/optimism/indexer
go 1.17 go 1.17
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b replace (
github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20230214215134-401b7fd3309b
github.com/ethereum/go-ethereum v1.11.2 => github.com/ethereum-optimism/op-geth v1.11.2-aea0402.0.20230227230209-0705cf1b7df9
)
require ( require (
github.com/ethereum-optimism/optimism/op-bindings v0.10.14 github.com/ethereum-optimism/optimism/op-bindings v0.10.14
......
...@@ -69,6 +69,9 @@ type DeployConfig struct { ...@@ -69,6 +69,9 @@ type DeployConfig struct {
L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"` L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
L2GenesisBlockBaseFeePerGas *hexutil.Big `json:"l2GenesisBlockBaseFeePerGas"` L2GenesisBlockBaseFeePerGas *hexutil.Big `json:"l2GenesisBlockBaseFeePerGas"`
// Seconds after genesis block that Regolith hard fork activates. 0 to activate at genesis. Nil to disable regolith
L2GenesisRegolithTimeOffset *hexutil.Uint64 `json:"l2GenesisRegolithTimeOffset,omitempty"`
// Owner of the ProxyAdmin predeploy // Owner of the ProxyAdmin predeploy
ProxyAdminOwner common.Address `json:"proxyAdminOwner"` ProxyAdminOwner common.Address `json:"proxyAdminOwner"`
// Owner of the system on L1 // Owner of the system on L1
...@@ -284,6 +287,17 @@ func (d *DeployConfig) InitDeveloperDeployedAddresses() error { ...@@ -284,6 +287,17 @@ func (d *DeployConfig) InitDeveloperDeployedAddresses() error {
return nil return nil
} }
func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 {
if d.L2GenesisRegolithTimeOffset == nil {
return nil
}
v := uint64(0)
if offset := *d.L2GenesisRegolithTimeOffset; offset > 0 {
v = genesisTime + uint64(offset)
}
return &v
}
// RollupConfig converts a DeployConfig to a rollup.Config // RollupConfig converts a DeployConfig to a rollup.Config
func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHash common.Hash, l2GenesisBlockNumber uint64) (*rollup.Config, error) { func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHash common.Hash, l2GenesisBlockNumber uint64) (*rollup.Config, error) {
if d.OptimismPortalProxy == (common.Address{}) { if d.OptimismPortalProxy == (common.Address{}) {
...@@ -320,6 +334,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas ...@@ -320,6 +334,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas
BatchInboxAddress: d.BatchInboxAddress, BatchInboxAddress: d.BatchInboxAddress,
DepositContractAddress: d.OptimismPortalProxy, DepositContractAddress: d.OptimismPortalProxy,
L1SystemConfigAddress: d.SystemConfigProxy, L1SystemConfigAddress: d.SystemConfigProxy,
RegolithTime: d.RegolithTime(l1StartBlock.Time()),
}, nil }, nil
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -33,3 +34,15 @@ func TestUnmarshalL1StartingBlockTag(t *testing.T) { ...@@ -33,3 +34,15 @@ func TestUnmarshalL1StartingBlockTag(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded)) require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded))
require.EqualValues(t, common.HexToHash(h), *decoded.L1StartingBlockTag.BlockHash) require.EqualValues(t, common.HexToHash(h), *decoded.L1StartingBlockTag.BlockHash)
} }
func TestRegolithTimeZero(t *testing.T) {
regolithOffset := hexutil.Uint64(0)
config := &DeployConfig{L2GenesisRegolithTimeOffset: &regolithOffset}
require.Equal(t, uint64(0), *config.RegolithTime(1234))
}
func TestRegolithTimeAsOffset(t *testing.T) {
regolithOffset := hexutil.Uint64(1500)
config := &DeployConfig{L2GenesisRegolithTimeOffset: &regolithOffset}
require.Equal(t, uint64(1500+5000), *config.RegolithTime(5000))
}
...@@ -289,6 +289,8 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m ...@@ -289,6 +289,8 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
// Set the Optimism options. // Set the Optimism options.
cfg.BedrockBlock = bedrockBlock.Number() cfg.BedrockBlock = bedrockBlock.Number()
// Enable Regolith from the start of Bedrock
cfg.RegolithTime = new(uint64)
cfg.Optimism = &params.OptimismConfig{ cfg.Optimism = &params.OptimismConfig{
EIP1559Denominator: config.EIP1559Denominator, EIP1559Denominator: config.EIP1559Denominator,
EIP1559Elasticity: config.EIP1559Elasticity, EIP1559Elasticity: config.EIP1559Elasticity,
......
...@@ -54,6 +54,7 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro ...@@ -54,6 +54,7 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro
TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true, TerminalTotalDifficultyPassed: true,
BedrockBlock: new(big.Int).SetUint64(uint64(config.L2GenesisBlockNumber)), BedrockBlock: new(big.Int).SetUint64(uint64(config.L2GenesisBlockNumber)),
RegolithTime: config.RegolithTime(block.Time()),
Optimism: &params.OptimismConfig{ Optimism: &params.OptimismConfig{
EIP1559Denominator: eip1559Denom, EIP1559Denominator: eip1559Denom,
EIP1559Elasticity: eip1559Elasticity, EIP1559Elasticity: eip1559Elasticity,
......
...@@ -214,6 +214,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * ...@@ -214,6 +214,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
BatchInboxAddress: deployConf.BatchInboxAddress, BatchInboxAddress: deployConf.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr, DepositContractAddress: predeploys.DevOptimismPortalAddr,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr, L1SystemConfigAddress: predeploys.DevSystemConfigAddr,
RegolithTime: deployConf.RegolithTime(uint64(deployConf.L1GenesisBlockTimestamp)),
} }
deploymentsL1 := DeploymentsL1{ deploymentsL1 := DeploymentsL1{
......
...@@ -179,7 +179,8 @@ func (d *OpGeth) StartBlockBuilding(ctx context.Context, attrs *eth.PayloadAttri ...@@ -179,7 +179,8 @@ func (d *OpGeth) StartBlockBuilding(ctx context.Context, attrs *eth.PayloadAttri
// CreatePayloadAttributes creates a valid PayloadAttributes containing a L1Info deposit transaction followed by the supplied transactions. // CreatePayloadAttributes creates a valid PayloadAttributes containing a L1Info deposit transaction followed by the supplied transactions.
func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.PayloadAttributes, error) { func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.PayloadAttributes, error) {
timestamp := d.L2Head.Timestamp + 2 timestamp := d.L2Head.Timestamp + 2
l1Info, err := derive.L1InfoDepositBytes(d.sequenceNum, d.L1Head, d.SystemConfig, false) regolith := d.L2ChainConfig.IsRegolith(uint64(timestamp))
l1Info, err := derive.L1InfoDepositBytes(d.sequenceNum, d.L1Head, d.SystemConfig, regolith)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -7,8 +7,11 @@ import ( ...@@ -7,8 +7,11 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -71,104 +74,486 @@ func TestInvalidDepositInFCU(t *testing.T) { ...@@ -71,104 +74,486 @@ func TestInvalidDepositInFCU(t *testing.T) {
require.Equal(t, 0, balance.Cmp(common.Big0)) require.Equal(t, 0, balance.Cmp(common.Big0))
} }
func TestBedrockSystemTxUsesZeroGas(t *testing.T) { func TestPreregolith(t *testing.T) {
futureTimestamp := hexutil.Uint64(4)
tests := []struct {
name string
regolithTime *hexutil.Uint64
}{
{name: "RegolithNotScheduled"},
{name: "RegolithNotYetActive", regolithTime: &futureTimestamp},
}
for _, test := range tests {
test := test
t.Run("GasUsed_"+test.name, func(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false cfg.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err) require.NoError(t, err)
defer opGeth.Close() defer opGeth.Close()
block, err := opGeth.AddL2Block(ctx) fromAddr := cfg.Secrets.Addresses().Alice
oldBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.NoError(t, err) require.NoError(t, err)
// Simple transfer deposit tx
depositTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &fromAddr, // send it to ourselves
Value: big.NewInt(params.Ether),
Gas: 25000,
IsSystemTransaction: false,
})
block, err := opGeth.AddL2Block(ctx, depositTx)
require.NoError(t, err)
// L1Info tx should report 0 gas used
infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0) infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0)
require.NoError(t, err) require.NoError(t, err)
require.True(t, infoTx.IsSystemTx()) infoRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash())
receipt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Zero(t, receipt.GasUsed) require.Zero(t, infoRcpt.GasUsed, "should use 0 gas for system tx")
}
func TestBedrockDepositTx(t *testing.T) { // Deposit tx should report all gas used
receipt, err := opGeth.L2Client.TransactionReceipt(ctx, depositTx.Hash())
require.NoError(t, err)
require.Equal(t, depositTx.Gas(), receipt.GasUsed, "should report all gas used")
// Should not refund ETH for unused gas
newBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.NoError(t, err)
require.Equal(t, oldBalance, newBalance, "should not repay sender for unused gas")
})
t.Run("DepositNonce_"+test.name, func(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err) require.NoError(t, err)
defer opGeth.Close() defer opGeth.Close()
aliceAddr := cfg.Secrets.Addresses().Alice fromAddr := cfg.Secrets.Addresses().Alice
// Include a tx just to ensure Alice's nonce isn't 0
// Deposit TX with a higher gas limit than required incrementNonceTx := types.NewTx(&types.DepositTx{
depositTx := types.NewTx(&types.DepositTx{ From: fromAddr,
From: aliceAddr, To: &fromAddr,
To: &aliceAddr,
Value: big.NewInt(0), Value: big.NewInt(0),
Gas: 50_000, // Simple transfer only requires 21,000 Gas: 21_000,
IsSystemTransaction: false, IsSystemTransaction: false,
}) })
// Contract creation deposit tx // Contract creation deposit tx
contractCreateTx := types.NewTx(&types.DepositTx{ contractCreateTx := types.NewTx(&types.DepositTx{
From: aliceAddr, From: fromAddr,
Value: big.NewInt(params.Ether), Value: big.NewInt(params.Ether),
Gas: 1000001, Gas: 1000001,
Data: []byte{}, Data: []byte{},
IsSystemTransaction: false, IsSystemTransaction: false,
}) })
_, err = opGeth.AddL2Block(ctx, depositTx, contractCreateTx) _, err = opGeth.AddL2Block(ctx, incrementNonceTx, contractCreateTx)
require.NoError(t, err) require.NoError(t, err)
receipt, err := opGeth.L2Client.TransactionReceipt(ctx, depositTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "tx should succeed")
require.Equal(t, depositTx.Gas(), receipt.GasUsed, "should use all gas")
incorrectContractAddress := crypto.CreateAddress(aliceAddr, uint64(0)) // Expected to be wrong expectedNonce := uint64(1)
correctContractAddress := crypto.CreateAddress(aliceAddr, uint64(1)) incorrectContractAddress := crypto.CreateAddress(fromAddr, uint64(0))
correctContractAddress := crypto.CreateAddress(fromAddr, expectedNonce)
createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash()) createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, createRcpt.Status, "create should succeed") require.Equal(t, types.ReceiptStatusSuccessful, createRcpt.Status, "create should succeed")
require.Equal(t, incorrectContractAddress, createRcpt.ContractAddress, "should report incorrect contract address") require.Nil(t, createRcpt.DepositNonce, "should not report deposit nonce")
require.Equal(t, incorrectContractAddress, createRcpt.ContractAddress, "should report correct contract address")
contractBalance, err := opGeth.L2Client.BalanceAt(ctx, createRcpt.ContractAddress, nil) contractBalance, err := opGeth.L2Client.BalanceAt(ctx, incorrectContractAddress, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, uint64(0), contractBalance.Uint64(), "balance unchanged on incorrect contract address") require.Equal(t, uint64(0), contractBalance.Uint64(), "balance unchanged on incorrect contract address")
contractBalance, err = opGeth.L2Client.BalanceAt(ctx, correctContractAddress, nil) contractBalance, err = opGeth.L2Client.BalanceAt(ctx, correctContractAddress, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, uint64(params.Ether), contractBalance.Uint64(), "balance changed on correct contract address") require.Equal(t, uint64(params.Ether), contractBalance.Uint64(), "balance changed on correct contract address")
// Check the actual transaction nonce is reported correctly when retrieving the tx from the API.
tx, _, err := opGeth.L2Client.TransactionByHash(ctx, contractCreateTx.Hash())
require.NoError(t, err)
require.Zero(t, *tx.EffectiveNonce(), "should report 0 as tx nonce")
})
t.Run("UnusedGasConsumed_"+test.name, func(t *testing.T) {
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
fromAddr := cfg.Secrets.Addresses().Alice
// Deposit TX with a high gas limit but using very little actual gas
depositTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &fromAddr, // send it to ourselves
Value: big.NewInt(params.Ether),
// SystemTx is assigned 1M gas limit
Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000,
IsSystemTransaction: false,
})
signer := types.LatestSigner(opGeth.L2ChainConfig)
// Second tx with a gas limit that will fit in regolith but not bedrock
tx := types.MustSignNewTx(cfg.Secrets.Bob, signer, &types.DynamicFeeTx{
ChainID: big.NewInt(int64(cfg.DeployConfig.L2ChainID)),
Nonce: 0,
GasTipCap: big.NewInt(100),
GasFeeCap: big.NewInt(100000),
Gas: 1_000_001,
To: &cfg.Secrets.Addresses().Alice,
Value: big.NewInt(0),
Data: nil,
})
_, err = opGeth.AddL2Block(ctx, depositTx, tx)
// Geth checks the gas limit usage of transactions as part of validating the payload attributes and refuses to even start building the block
require.ErrorContains(t, err, "Invalid payload attributes", "block should be invalid due to using too much gas")
})
t.Run("AllowSystemTx_"+test.name, func(t *testing.T) {
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
systemTx, err := derive.L1InfoDeposit(1, opGeth.L1Head, opGeth.SystemConfig, false)
systemTx.IsSystemTransaction = true
require.NoError(t, err)
_, err = opGeth.AddL2Block(ctx, types.NewTx(systemTx))
require.NoError(t, err, "should allow blocks containing system tx")
})
}
} }
func TestBedrockShouldNotRefundDepositTxUnusedGas(t *testing.T) { func TestRegolith(t *testing.T) {
tests := []struct {
name string
regolithTime hexutil.Uint64
activateRegolith func(ctx context.Context, opGeth *OpGeth)
}{
{name: "ActivateAtGenesis", regolithTime: 0, activateRegolith: func(ctx context.Context, opGeth *OpGeth) {}},
{name: "ActivateAfterGenesis", regolithTime: 2, activateRegolith: func(ctx context.Context, opGeth *OpGeth) {
_, err := opGeth.AddL2Block(ctx)
require.NoError(t, err)
}},
}
for _, test := range tests {
test := test
t.Run("GasUsedIsAccurate_"+test.name, func(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err) require.NoError(t, err)
defer opGeth.Close() defer opGeth.Close()
aliceAddr := cfg.Secrets.Addresses().Alice test.activateRegolith(ctx, opGeth)
origBalance, err := opGeth.L2Client.BalanceAt(ctx, aliceAddr, nil)
fromAddr := cfg.Secrets.Addresses().Alice
oldBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.NoError(t, err) require.NoError(t, err)
// Deposit TX with a higher gas limit than required // Simple transfer deposit tx
depositTx := types.NewTx(&types.DepositTx{ depositTx := types.NewTx(&types.DepositTx{
From: aliceAddr, From: fromAddr,
To: &aliceAddr, To: &fromAddr, // send it to ourselves
Value: big.NewInt(0), Value: big.NewInt(params.Ether),
Gas: 50_000, // Simple transfer only requires 21,000 Gas: 25000,
IsSystemTransaction: false, IsSystemTransaction: false,
}) })
_, err = opGeth.AddL2Block(ctx, depositTx) block, err := opGeth.AddL2Block(ctx, depositTx)
require.NoError(t, err)
// L1Info tx should report actual gas used, not 0 or the tx gas limit
infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0)
require.NoError(t, err) require.NoError(t, err)
infoRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash())
require.NoError(t, err)
require.NotZero(t, infoRcpt.GasUsed)
require.NotEqual(t, infoTx.Gas(), infoRcpt.GasUsed)
// Deposit tx should report actual gas used (21,000 for a normal transfer)
receipt, err := opGeth.L2Client.TransactionReceipt(ctx, depositTx.Hash()) receipt, err := opGeth.L2Client.TransactionReceipt(ctx, depositTx.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "tx should succeed") require.Equal(t, uint64(21_000), receipt.GasUsed, "should report actual gas used")
// Should not refund ETH for unused gas
newBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.NoError(t, err)
require.Equal(t, oldBalance, newBalance, "should not repay sender for unused gas")
})
t.Run("DepositNonceCorrect_"+test.name, func(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
test.activateRegolith(ctx, opGeth)
fromAddr := cfg.Secrets.Addresses().Alice
// Include a tx just to ensure Alice's nonce isn't 0
incrementNonceTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &fromAddr,
Value: big.NewInt(0),
Gas: 21_000,
IsSystemTransaction: false,
})
// Contract creation deposit tx
contractCreateTx := types.NewTx(&types.DepositTx{
From: fromAddr,
Value: big.NewInt(params.Ether),
Gas: 1000001,
Data: []byte{},
IsSystemTransaction: false,
})
_, err = opGeth.AddL2Block(ctx, incrementNonceTx, contractCreateTx)
require.NoError(t, err)
newBalance, err := opGeth.L2Client.BalanceAt(ctx, aliceAddr, nil) expectedNonce := uint64(1)
correctContractAddress := crypto.CreateAddress(fromAddr, expectedNonce)
createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, origBalance, newBalance, "should not refund cost of unused gas") require.Equal(t, types.ReceiptStatusSuccessful, createRcpt.Status, "create should succeed")
require.Equal(t, &expectedNonce, createRcpt.DepositNonce, "should report correct deposit nonce")
require.Equal(t, correctContractAddress, createRcpt.ContractAddress, "should report correct contract address")
contractBalance, err := opGeth.L2Client.BalanceAt(ctx, createRcpt.ContractAddress, nil)
require.NoError(t, err)
require.Equal(t, uint64(params.Ether), contractBalance.Uint64(), "balance changed on correct contract address")
// Check the actual transaction nonce is reported correctly when retrieving the tx from the API.
tx, _, err := opGeth.L2Client.TransactionByHash(ctx, contractCreateTx.Hash())
require.NoError(t, err)
require.Equal(t, expectedNonce, *tx.EffectiveNonce(), "should report actual tx nonce")
})
t.Run("ReturnUnusedGasToPool_"+test.name, func(t *testing.T) {
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
test.activateRegolith(ctx, opGeth)
fromAddr := cfg.Secrets.Addresses().Alice
// Deposit TX with a high gas limit but using very little actual gas
depositTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &fromAddr, // send it to ourselves
Value: big.NewInt(params.Ether),
// SystemTx is assigned 1M gas limit
Gas: uint64(cfg.DeployConfig.L2GenesisBlockGasLimit) - 1_000_000,
IsSystemTransaction: false,
})
signer := types.LatestSigner(opGeth.L2ChainConfig)
// Second tx with a gas limit that will fit in regolith but not bedrock
tx := types.MustSignNewTx(cfg.Secrets.Bob, signer, &types.DynamicFeeTx{
ChainID: big.NewInt(int64(cfg.DeployConfig.L2ChainID)),
Nonce: 0,
GasTipCap: big.NewInt(100),
GasFeeCap: big.NewInt(100000),
Gas: 1_000_001,
To: &cfg.Secrets.Addresses().Alice,
Value: big.NewInt(0),
Data: nil,
})
_, err = opGeth.AddL2Block(ctx, depositTx, tx)
require.NoError(t, err, "block should be valid as cumulativeGasUsed only tracks actual usage now")
})
t.Run("RejectSystemTx_"+test.name, func(t *testing.T) {
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
test.activateRegolith(ctx, opGeth)
systemTx, err := derive.L1InfoDeposit(1, opGeth.L1Head, opGeth.SystemConfig, false)
systemTx.IsSystemTransaction = true
require.NoError(t, err)
_, err = opGeth.AddL2Block(ctx, types.NewTx(systemTx))
require.ErrorIs(t, err, ErrNewPayloadNotValid, "should reject blocks containing system tx")
})
t.Run("IncludeGasRefunds_"+test.name, func(t *testing.T) {
// Simple constructor that is prefixed to the actual contract code
// Results in the contract code being returned as the code for the new contract
deployPrefixSize := byte(16)
deployPrefix := []byte{
// Copy input data after this prefix into memory starting at address 0x00
// CODECOPY arg size
byte(vm.PUSH1), deployPrefixSize,
byte(vm.CODESIZE),
byte(vm.SUB),
// CODECOPY arg offset
byte(vm.PUSH1), deployPrefixSize,
// CODECOPY arg destOffset
byte(vm.PUSH1), 0x00,
byte(vm.CODECOPY),
// Return code from memory
// RETURN arg size
byte(vm.PUSH1), deployPrefixSize,
byte(vm.CODESIZE),
byte(vm.SUB),
// RETURN arg offset
byte(vm.PUSH1), 0x00,
byte(vm.RETURN),
}
// Stores the first word from call data code to storage slot 0
sstoreContract := []byte{
// Load first word from call data
byte(vm.PUSH1), 0x00,
byte(vm.CALLDATALOAD),
// Store it to slot 0
byte(vm.PUSH1), 0x00,
byte(vm.SSTORE),
}
deployData := append(deployPrefix, sstoreContract...)
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
test.activateRegolith(ctx, opGeth)
fromAddr := cfg.Secrets.Addresses().Alice
storeContractAddr := crypto.CreateAddress(fromAddr, 0)
// Deposit TX to deploy a contract that lets us store an arbitrary value
deployTx := types.NewTx(&types.DepositTx{
From: fromAddr,
Value: common.Big0,
Data: deployData,
Gas: 1_000_000,
IsSystemTransaction: false,
})
// Store a non-zero value
storeTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &storeContractAddr,
Value: common.Big0,
Data: []byte{0x06},
Gas: 1_000_000,
IsSystemTransaction: false,
})
// Store a non-zero value
zeroTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &storeContractAddr,
Value: common.Big0,
Data: []byte{0x00},
Gas: 1_000_000,
IsSystemTransaction: false,
})
// Store a non-zero value again
// Has same gas cost as zeroTx, except the first tx gets a gas refund for clearing the storage slot
rezeroTx := types.NewTx(&types.DepositTx{
From: fromAddr,
To: &storeContractAddr,
Value: common.Big0,
Data: []byte{0x00},
Gas: 1_000_001,
IsSystemTransaction: false,
})
_, err = opGeth.AddL2Block(ctx, deployTx, storeTx, zeroTx, rezeroTx)
require.NoError(t, err)
// Sanity check the contract code deployed correctly
code, err := opGeth.L2Client.CodeAt(ctx, storeContractAddr, nil)
require.NoError(t, err)
require.Equal(t, sstoreContract, code, "should create contract with expected code")
deployReceipt, err := opGeth.L2Client.TransactionReceipt(ctx, deployTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, deployReceipt.Status)
require.Equal(t, storeContractAddr, deployReceipt.ContractAddress, "should create contract at expected address")
storeReceipt, err := opGeth.L2Client.TransactionReceipt(ctx, storeTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, storeReceipt.Status, "setting storage value should succeed")
zeroReceipt, err := opGeth.L2Client.TransactionReceipt(ctx, zeroTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, zeroReceipt.Status, "zeroing storage value should succeed")
rezeroReceipt, err := opGeth.L2Client.TransactionReceipt(ctx, rezeroTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, rezeroReceipt.Status, "rezeroing storage value should succeed")
require.Greater(t, rezeroReceipt.GasUsed, zeroReceipt.GasUsed, "rezero should use more gas due to not getting gas refund for clearing slot")
})
}
} }
...@@ -309,6 +309,7 @@ func (cfg SystemConfig) Start() (*System, error) { ...@@ -309,6 +309,7 @@ func (cfg SystemConfig) Start() (*System, error) {
BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress, BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr, DepositContractAddress: predeploys.DevOptimismPortalAddr,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr, L1SystemConfigAddress: predeploys.DevSystemConfigAddr,
RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)),
} }
} }
defaultConfig := makeRollupConfig() defaultConfig := makeRollupConfig()
......
...@@ -6,11 +6,11 @@ import subprocess ...@@ -6,11 +6,11 @@ import subprocess
import os import os
GETH_VERSION='v1.10.26' GETH_VERSION='v1.11.2'
def main(): def main():
for project in ('.', 'op-wheel', 'indexer'): for project in ('.', 'indexer'):
print(f'Updating {project}...') print(f'Updating {project}...')
update_mod(project) update_mod(project)
...@@ -22,7 +22,7 @@ def update_mod(project): ...@@ -22,7 +22,7 @@ def update_mod(project):
'mod', 'mod',
'edit', 'edit',
'-replace', '-replace',
f'github.com/ethereum/go-ethereum@{GETH_VERSION}=github.com/ethereum-optimism/op-geth@optimism-history' f'github.com/ethereum/go-ethereum@{GETH_VERSION}=github.com/ethereum-optimism/op-geth@optimism'
], cwd=os.path.join(project), check=True) ], cwd=os.path.join(project), check=True)
print('Tidying...') print('Tidying...')
subprocess.run([ subprocess.run([
......
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