Commit 462cf31e authored by Adrian Sutton's avatar Adrian Sutton

op-e2e, op-chain-ops/genesis: Add e2e op-geth tests for Regolith changes

Rework bedrock e2e tests to test when regolith is not scheduled as well as not yet active
Update op-chain-ops genesis creation to support setting a Regolith time via offset from genesis.
parent f5a1a409
......@@ -69,6 +69,9 @@ type DeployConfig struct {
L2GenesisBlockParentHash common.Hash `json:"l2GenesisBlockParentHash"`
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
ProxyAdminOwner common.Address `json:"proxyAdminOwner"`
// Owner of the system on L1
......@@ -284,6 +287,17 @@ func (d *DeployConfig) InitDeveloperDeployedAddresses() error {
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
func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHash common.Hash, l2GenesisBlockNumber uint64) (*rollup.Config, error) {
if d.OptimismPortalProxy == (common.Address{}) {
......@@ -320,6 +334,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas
BatchInboxAddress: d.BatchInboxAddress,
DepositContractAddress: d.OptimismPortalProxy,
L1SystemConfigAddress: d.SystemConfigProxy,
RegolithTime: d.RegolithTime(l1StartBlock.Time()),
}, nil
}
......
......@@ -8,6 +8,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
......@@ -33,3 +34,15 @@ func TestUnmarshalL1StartingBlockTag(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(fmt.Sprintf(`{"l1StartingBlockTag": "%s"}`, h)), decoded))
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))
}
......@@ -54,6 +54,7 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
BedrockBlock: new(big.Int).SetUint64(uint64(config.L2GenesisBlockNumber)),
RegolithTime: config.RegolithTime(block.Time()),
Optimism: &params.OptimismConfig{
EIP1559Denominator: eip1559Denom,
EIP1559Elasticity: eip1559Elasticity,
......
......@@ -214,6 +214,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
BatchInboxAddress: deployConf.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr,
RegolithTime: deployConf.RegolithTime(uint64(deployConf.L1GenesisBlockTimestamp)),
}
deploymentsL1 := DeploymentsL1{
......
......@@ -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.
func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.PayloadAttributes, error) {
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 {
return nil, err
}
......
......@@ -7,8 +7,11 @@ import (
"time"
"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/hexutil"
"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/params"
"github.com/stretchr/testify/require"
......@@ -71,104 +74,486 @@ func TestInvalidDepositInFCU(t *testing.T) {
require.Equal(t, 0, balance.Cmp(common.Big0))
}
func TestBedrockSystemTxUsesZeroGas(t *testing.T) {
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
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.DeployConfig.L2GenesisRegolithTimeOffset = test.regolithTime
block, err := opGeth.AddL2Block(ctx)
require.NoError(t, err)
infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0)
require.NoError(t, err)
require.True(t, infoTx.IsSystemTx())
receipt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash())
require.NoError(t, err)
require.Zero(t, receipt.GasUsed)
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
func TestBedrockDepositTx(t *testing.T) {
cfg := DefaultSystemConfig(t)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
aliceAddr := cfg.Secrets.Addresses().Alice
fromAddr := cfg.Secrets.Addresses().Alice
// Deposit TX with a higher gas limit than required
depositTx := types.NewTx(&types.DepositTx{
From: aliceAddr,
To: &aliceAddr,
Value: big.NewInt(0),
Gas: 50_000, // Simple transfer only requires 21,000
IsSystemTransaction: false,
})
oldBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.NoError(t, err)
// Contract creation deposit tx
contractCreateTx := types.NewTx(&types.DepositTx{
From: aliceAddr,
Value: big.NewInt(params.Ether),
Gas: 1000001,
Data: []byte{},
IsSystemTransaction: false,
})
// 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,
})
_, err = opGeth.AddL2Block(ctx, depositTx, contractCreateTx)
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")
block, err := opGeth.AddL2Block(ctx, depositTx)
require.NoError(t, err)
incorrectContractAddress := crypto.CreateAddress(aliceAddr, uint64(0)) // Expected to be wrong
correctContractAddress := crypto.CreateAddress(aliceAddr, uint64(1))
createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, createRcpt.Status, "create should succeed")
require.Equal(t, incorrectContractAddress, createRcpt.ContractAddress, "should report incorrect contract address")
// L1Info tx should report 0 gas used
infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0)
require.NoError(t, err)
infoRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, infoTx.Hash())
require.NoError(t, err)
require.Zero(t, infoRcpt.GasUsed, "should use 0 gas for system tx")
contractBalance, err := opGeth.L2Client.BalanceAt(ctx, createRcpt.ContractAddress, nil)
require.NoError(t, err)
require.Equal(t, uint64(0), contractBalance.Uint64(), "balance unchanged on incorrect contract address")
// 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")
contractBalance, err = opGeth.L2Client.BalanceAt(ctx, correctContractAddress, nil)
require.NoError(t, err)
require.Equal(t, uint64(params.Ether), contractBalance.Uint64(), "balance changed on correct contract address")
// 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.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
// 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)
expectedNonce := uint64(1)
incorrectContractAddress := crypto.CreateAddress(fromAddr, uint64(0))
correctContractAddress := crypto.CreateAddress(fromAddr, expectedNonce)
createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, createRcpt.Status, "create should succeed")
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, incorrectContractAddress, nil)
require.NoError(t, err)
require.Equal(t, uint64(0), contractBalance.Uint64(), "balance unchanged on incorrect contract address")
contractBalance, err = opGeth.L2Client.BalanceAt(ctx, correctContractAddress, 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.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) {
cfg := DefaultSystemConfig(t)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
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.DeployConfig.L2GenesisRegolithTimeOffset = &test.regolithTime
aliceAddr := cfg.Secrets.Addresses().Alice
origBalance, err := opGeth.L2Client.BalanceAt(ctx, aliceAddr, nil)
require.NoError(t, err)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Deposit TX with a higher gas limit than required
depositTx := types.NewTx(&types.DepositTx{
From: aliceAddr,
To: &aliceAddr,
Value: big.NewInt(0),
Gas: 50_000, // Simple transfer only requires 21,000
IsSystemTransaction: false,
})
opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err)
defer opGeth.Close()
_, err = opGeth.AddL2Block(ctx, depositTx)
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")
test.activateRegolith(ctx, opGeth)
newBalance, err := opGeth.L2Client.BalanceAt(ctx, aliceAddr, nil)
require.NoError(t, err)
require.Equal(t, origBalance, newBalance, "should not refund cost of unused gas")
fromAddr := cfg.Secrets.Addresses().Alice
oldBalance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
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 actual gas used, not 0 or the tx gas limit
infoTx, err := opGeth.L2Client.TransactionInBlock(ctx, block.BlockHash, 0)
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())
require.NoError(t, err)
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)
expectedNonce := uint64(1)
correctContractAddress := crypto.CreateAddress(fromAddr, expectedNonce)
createRcpt, err := opGeth.L2Client.TransactionReceipt(ctx, contractCreateTx.Hash())
require.NoError(t, err)
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) {
BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr,
RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)),
}
}
defaultConfig := makeRollupConfig()
......
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