Commit 02987acc authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-e2e: Fix flaky large preimage test (#9378)

Take the batcher out of the equation and manually send a very large batch transaction.
parent 49c3a85d
package batcher
import (
"context"
"crypto/ecdsa"
"math/big"
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/testutils"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
type Helper struct {
t *testing.T
privKey *ecdsa.PrivateKey
rollupCfg *rollup.Config
l1Client *ethclient.Client
}
func NewHelper(t *testing.T, privKey *ecdsa.PrivateKey, rollupCfg *rollup.Config, l1Client *ethclient.Client) *Helper {
return &Helper{
t: t,
privKey: privKey,
rollupCfg: rollupCfg,
l1Client: l1Client,
}
}
func (h *Helper) SendLargeInvalidBatch(ctx context.Context) {
nonce, err := h.l1Client.PendingNonceAt(ctx, crypto.PubkeyToAddress(h.privKey.PublicKey))
require.NoError(h.t, err, "Should get next batcher nonce")
maxTxDataSize := 131072 // As per the Ethereum spec.
data := testutils.RandomData(rand.New(rand.NewSource(9849248)), maxTxDataSize-200)
tx := gethTypes.MustSignNewTx(h.privKey, h.rollupCfg.L1Signer(), &gethTypes.DynamicFeeTx{
ChainID: h.rollupCfg.L1ChainID,
Nonce: nonce,
GasTipCap: big.NewInt(1 * params.GWei),
GasFeeCap: big.NewInt(10 * params.GWei),
Gas: 5_000_000,
To: &h.rollupCfg.BatchInboxAddress,
Value: big.NewInt(0),
Data: data,
})
err = h.l1Client.SendTransaction(ctx, tx)
require.NoError(h.t, err, "Should send large batch transaction")
_, err = wait.ForReceiptOK(ctx, h.l1Client, tx.Hash())
require.NoError(h.t, err, "Tx should be ok")
}
...@@ -84,11 +84,11 @@ func ForUnsafeBlock(ctx context.Context, rollupCl *sources.RollupClient, n uint6 ...@@ -84,11 +84,11 @@ func ForUnsafeBlock(ctx context.Context, rollupCl *sources.RollupClient, n uint6
return err return err
} }
func ForNextSafeBlock(ctx context.Context, client BlockCaller) error { func ForNextSafeBlock(ctx context.Context, client BlockCaller) (*types.Block, error) {
safeBlockNumber := big.NewInt(rpc.SafeBlockNumber.Int64()) safeBlockNumber := big.NewInt(rpc.SafeBlockNumber.Int64())
current, err := client.BlockByNumber(ctx, safeBlockNumber) current, err := client.BlockByNumber(ctx, safeBlockNumber)
if err != nil { if err != nil {
return err return nil, err
} }
// Long timeout so we don't have to care what the block time is. If the test passes this will complete early anyway. // Long timeout so we don't have to care what the block time is. If the test passes this will complete early anyway.
...@@ -97,14 +97,14 @@ func ForNextSafeBlock(ctx context.Context, client BlockCaller) error { ...@@ -97,14 +97,14 @@ func ForNextSafeBlock(ctx context.Context, client BlockCaller) error {
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return nil, ctx.Err()
default: default:
next, err := client.BlockByNumber(ctx, safeBlockNumber) next, err := client.BlockByNumber(ctx, safeBlockNumber)
if err != nil { if err != nil {
return err return nil, err
} }
if next.NumberU64() > current.NumberU64() { if next.NumberU64() > current.NumberU64() {
return nil return next, nil
} }
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
} }
......
...@@ -216,23 +216,19 @@ func TestOutputCannonStepWithLargePreimage(t *testing.T) { ...@@ -216,23 +216,19 @@ func TestOutputCannonStepWithLargePreimage(t *testing.T) {
op_e2e.InitParallel(t, op_e2e.UsesCannon) op_e2e.InitParallel(t, op_e2e.UsesCannon)
ctx := context.Background() ctx := context.Background()
sys, _ := startFaultDisputeSystem(t, withLargeBatches()) sys, _ := startFaultDisputeSystem(t, withBatcherStopped())
t.Cleanup(sys.Close) t.Cleanup(sys.Close)
// Send a large l2 transaction and use the receipt block number as the l2 block number for the game // Manually send a tx from the correct batcher key to the batcher input with very large (invalid) data
l2Client := sys.NodeClient("sequencer") // This forces op-program to load a large preimage.
sys.BatcherHelper().SendLargeInvalidBatch(ctx)
// Send a large, difficult to compress L2 transaction. This isn't read by op-program but the batcher has to include
// it in a batch which *is* read. require.NoError(t, sys.BatchSubmitter.Start(ctx))
receipt := op_e2e.SendLargeL2Tx(t, sys.Cfg, l2Client, sys.Cfg.Secrets.Alice, func(opts *op_e2e.TxOpts) {
aliceAddr := sys.Cfg.Secrets.Addresses().Alice safeHead, err := wait.ForNextSafeBlock(ctx, sys.NodeClient("sequencer"))
startL2Nonce, err := l2Client.NonceAt(ctx, aliceAddr, nil) require.NoError(t, err, "Batcher should resume submitting valid batches")
require.NoError(t, err)
opts.Nonce = startL2Nonce l2BlockNumber := safeHead.NumberU64()
opts.ToAddr = &common.Address{}
// By default, the receipt status must be successful and is checked in the SendL2Tx function
})
l2BlockNumber := receipt.BlockNumber.Uint64()
disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
// Dispute any block - it will have to read the L1 batches to see if the block is reached // Dispute any block - it will have to read the L1 batches to see if the block is reached
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", l2BlockNumber, common.Hash{0x01, 0xaa}) game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", l2BlockNumber, common.Hash{0x01, 0xaa})
......
...@@ -10,13 +10,9 @@ import ( ...@@ -10,13 +10,9 @@ import (
type faultDisputeConfigOpts func(cfg *op_e2e.SystemConfig) type faultDisputeConfigOpts func(cfg *op_e2e.SystemConfig)
func withLargeBatches() faultDisputeConfigOpts { func withBatcherStopped() faultDisputeConfigOpts {
return func(cfg *op_e2e.SystemConfig) { return func(cfg *op_e2e.SystemConfig) {
maxTxDataSize := uint64(131072) // As per the Ethereum spec. cfg.DisableBatcher = true
// Allow the batcher to produce really huge calldata transactions.
// Make the max deliberately bigger than the target but still with some padding below the actual limit
cfg.BatcherTargetL1TxSizeBytes = maxTxDataSize - 5000
cfg.BatcherMaxL1TxSizeBytes = maxTxDataSize - 1000
} }
} }
......
...@@ -115,8 +115,10 @@ func setupSequencerFailoverTest(t *testing.T) (*System, map[string]*conductor) { ...@@ -115,8 +115,10 @@ func setupSequencerFailoverTest(t *testing.T) (*System, map[string]*conductor) {
// weirdly, batcher does not submit a batch until unsafe block 9. // weirdly, batcher does not submit a batch until unsafe block 9.
// It became normal after that and submits a batch every L1 block (2s) per configuration. // It became normal after that and submits a batch every L1 block (2s) per configuration.
// Since our health monitor checks on safe head progression, wait for batcher to become normal before proceeding. // Since our health monitor checks on safe head progression, wait for batcher to become normal before proceeding.
require.NoError(t, wait.ForNextSafeBlock(ctx, sys.Clients[Sequencer1Name])) _, err = wait.ForNextSafeBlock(ctx, sys.Clients[Sequencer1Name])
require.NoError(t, wait.ForNextSafeBlock(ctx, sys.Clients[Sequencer1Name])) require.NoError(t, err)
_, err = wait.ForNextSafeBlock(ctx, sys.Clients[Sequencer1Name])
require.NoError(t, err)
// make sure conductor reports all sequencers as healthy, this means they're syncing correctly. // make sure conductor reports all sequencers as healthy, this means they're syncing correctly.
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/batcher"
ds "github.com/ipfs/go-datastore" ds "github.com/ipfs/go-datastore"
dsSync "github.com/ipfs/go-datastore/sync" dsSync "github.com/ipfs/go-datastore/sync"
ic "github.com/libp2p/go-libp2p/core/crypto" ic "github.com/libp2p/go-libp2p/core/crypto"
...@@ -853,6 +854,10 @@ func (sys *System) newMockNetPeer() (host.Host, error) { ...@@ -853,6 +854,10 @@ func (sys *System) newMockNetPeer() (host.Host, error) {
return sys.Mocknet.AddPeerWithPeerstore(p, eps) return sys.Mocknet.AddPeerWithPeerstore(p, eps)
} }
func (sys *System) BatcherHelper() *batcher.Helper {
return batcher.NewHelper(sys.t, sys.Cfg.Secrets.Batcher, sys.RollupConfig, sys.NodeClient("l1"))
}
func UseHTTP() bool { func UseHTTP() bool {
return os.Getenv("OP_E2E_USE_HTTP") == "true" return os.Getenv("OP_E2E_USE_HTTP") == "true"
} }
......
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"math/big" "math/big"
"math/rand"
"testing" "testing"
"time" "time"
...@@ -14,7 +13,6 @@ import ( ...@@ -14,7 +13,6 @@ import (
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -81,17 +79,6 @@ func defaultDepositTxOpts(opts *bind.TransactOpts) *DepositTxOpts { ...@@ -81,17 +79,6 @@ func defaultDepositTxOpts(opts *bind.TransactOpts) *DepositTxOpts {
} }
} }
// SendLargeL2Tx creates and sends a transaction with a large amount of txdata.
func SendLargeL2Tx(t *testing.T, cfg SystemConfig, l2Client *ethclient.Client, privKey *ecdsa.PrivateKey, applyTxOpts TxOptsFn) *types.Receipt {
maxTxDataSize := 131072 // As per the Ethereum spec.
data := testutils.RandomData(rand.New(rand.NewSource(12342)), maxTxDataSize-200) // Leave some buffer
return SendL2Tx(t, cfg, l2Client, privKey, func(opts *TxOpts) {
opts.Data = data
opts.Gas = uint64(2_200_000) // Lots but less than the block limit
applyTxOpts(opts)
})
}
// SendL2Tx creates and sends a transaction. // SendL2Tx creates and sends a transaction.
// The supplied privKey is used to specify the account to send from and the transaction is sent to the supplied l2Client // The supplied privKey is used to specify the account to send from and the transaction is sent to the supplied l2Client
// Transaction options and expected status can be configured in the applyTxOpts function by modifying the supplied TxOpts // Transaction options and expected status can be configured in the applyTxOpts function by modifying the supplied TxOpts
......
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