diff --git a/.github/workflows/_go-tests.yml b/.github/workflows/_go-tests.yml index c4fea57537f..def01ba268d 100644 --- a/.github/workflows/_go-tests.yml +++ b/.github/workflows/_go-tests.yml @@ -46,6 +46,9 @@ on: run-pebble-b: required: false type: boolean + run-experimental: + required: false + type: boolean jobs: go-tests: @@ -191,6 +194,14 @@ jobs: ${{ github.workspace }}/.github/workflows/gotestsum.sh --timeout 90m --test_database_engine pebble --junitfile test-results/junit-pebble-b.xml --skip '^Test[A-N]' --reduce-parallelism + # --------------------- EXPERIMENTAL MODE --------------------- + + - name: run experimental tests + if: inputs.run-experimental + run: >- + ${{ github.workspace }}/.github/workflows/gotestsum.sh + --tags experimental --run '^TestBenchmarkingSequencer' --timeout 60m --cover + # --------------------- PROCESS JUNIT LOGS --------------------- - name: Process JUnit XML logs diff --git a/.github/workflows/_standard-go-test-suite.yml b/.github/workflows/_standard-go-test-suite.yml index eb28b31391f..d0441e97bc4 100644 --- a/.github/workflows/_standard-go-test-suite.yml +++ b/.github/workflows/_standard-go-test-suite.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - test-mode: [defaults-A, defaults-B, flaky, pathdb, challenge, stylus, l3challenge] + test-mode: [defaults-A, defaults-B, flaky, pathdb, challenge, stylus, l3challenge, experimental] uses: ./.github/workflows/_go-tests.yml secrets: inherit with: @@ -20,3 +20,4 @@ jobs: run-challenge: ${{ matrix.test-mode == 'challenge' }} run-stylus: ${{ matrix.test-mode == 'stylus' }} run-l3challenge: ${{ matrix.test-mode == 'l3challenge' }} + run-experimental: ${{ matrix.test-mode == 'experimental' }} diff --git a/Makefile b/Makefile index 9627fa57a32..d6a92ad0797 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,7 @@ all: build build-replay-env test-gen-proofs @touch .make/all .PHONY: build -build: $(patsubst %,$(output_root)/bin/%, nitro deploy relay daprovider anytrustserver autonomous-auctioneer bidder-client anytrusttool blobtool el-proxy mockexternalsigner seq-coordinator-invalidate nitro-val seq-coordinator-manager dbconv genesis-generator transaction-filterer) +build: $(patsubst %,$(output_root)/bin/%, nitro deploy relay daprovider anytrustserver autonomous-auctioneer bidder-client anytrusttool blobtool el-proxy mockexternalsigner seq-coordinator-invalidate nitro-val seq-coordinator-manager dbconv genesis-generator transaction-filterer nitro-experimental) @printf $(done) .PHONY: build-node-deps @@ -250,6 +250,13 @@ test-go-redis: test-go-deps .github/workflows/gotestsum.sh --timeout 120m --run TestRedis --nolog -- --test_redis=redis://localhost:6379/0 @printf $(done) +.PHONY: test-go-experimental +test-go-experimental: test-go-deps + .github/workflows/gotestsum.sh --timeout 120m --run '^TestBenchmarkingSequencer' --tags experimental --nolog + @printf $(done) + + + .PHONY: test-gen-proofs test-gen-proofs: \ $(arbitrator_test_wasms) \ @@ -314,6 +321,10 @@ check-license-headers: $(output_root)/bin/nitro: $(DEP_PREDICATE) build-node-deps go build $(GOLANG_PARAMS) -o $@ "$(CURDIR)/cmd/nitro" +# nitro built with benchmarking sequencer tooling enabled (requires the experimental build tag) +$(output_root)/bin/nitro-experimental: $(DEP_PREDICATE) build-node-deps + go build $(GOLANG_PARAMS) --tags experimental -o $@ "$(CURDIR)/cmd/nitro" + $(output_root)/bin/deploy: $(DEP_PREDICATE) build-node-deps go build $(GOLANG_PARAMS) -o $@ "$(CURDIR)/cmd/deploy" diff --git a/changelog/kolbyml-nit-4481.md b/changelog/kolbyml-nit-4481.md new file mode 100644 index 00000000000..51f20b6974a --- /dev/null +++ b/changelog/kolbyml-nit-4481.md @@ -0,0 +1,5 @@ +### Configuration +- Add `--execution.dangerous.benchmarking-sequencer.enable` to enable the benchmarking sequencer RPC (only available in builds with the `experimental` build tag). + +### Internal +- Add benchmarking sequencer RPC (`benchseq`) and system tests behind the `experimental` build tag. diff --git a/cmd/util/confighelpers/configuration.go b/cmd/util/confighelpers/configuration.go index 6e31088f957..f216a25ebc1 100644 --- a/cmd/util/confighelpers/configuration.go +++ b/cmd/util/confighelpers/configuration.go @@ -209,7 +209,7 @@ func devFlagArgs() []string { "--init.empty=false", "--http.port", "8547", "--http.addr", "127.0.0.1", - "--http.api=net,web3,eth,arb,arbdebug,debug", + "--http.api=net,web3,eth,arb,arbdebug,debug,benchseq", "--node.transaction-streamer.track-block-metadata-from=1", } return args diff --git a/execution/gethexec/bench_sequencer.go b/execution/gethexec/bench_sequencer.go new file mode 100644 index 00000000000..38dfa5ae0cc --- /dev/null +++ b/execution/gethexec/bench_sequencer.go @@ -0,0 +1,93 @@ +// Copyright 2026, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +//go:build experimental + +package gethexec + +import ( + "context" + + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/spf13/pflag" +) + +func BenchmarkingSequencerConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.Bool(prefix+".enable", BenchmarkingSequencerConfigDefault.Enable, "enable benchmarking sequencer RPC (manual block creation; requires experimental build tag)") +} + +func (c *BenchmarkingSequencerConfig) Validate() error { + if c.Enable { + log.Warn("DANGER! benchmarking sequencer enabled (manual block creation); do not use in production") + } + return nil +} + +func NewBenchmarkingSequencer(sequencer *Sequencer) (TransactionPublisher, interface{}) { + benchmarkingSequencer := &BenchmarkingSequencer{ + Sequencer: sequencer, + semaphore: make(chan struct{}, 1), + } + return benchmarkingSequencer, NewBenchmarkingSequencerAPI(benchmarkingSequencer) +} + +type BenchmarkingSequencer struct { + *Sequencer + semaphore chan struct{} +} + +func (s *BenchmarkingSequencer) Start(ctx context.Context) error { + // override Sequencer.Start to not start the inner sequencer + s.StopWaiter.Start(ctx, s) + s.semaphore <- struct{}{} + return nil +} + +func (s *BenchmarkingSequencer) TxQueueLength(includeRetryTxQueue bool) int { + if includeRetryTxQueue { + return len(s.Sequencer.txQueue) + s.Sequencer.txRetryQueue.Len() + } + return len(s.Sequencer.txQueue) +} + +func (s *BenchmarkingSequencer) TxRetryQueueLength() int { + return s.Sequencer.txRetryQueue.Len() +} + +func (s *BenchmarkingSequencer) CreateBlock() containers.PromiseInterface[bool] { + return stopwaiter.LaunchPromiseThread[bool](s, func(ctx context.Context) (bool, error) { + select { + // createBlock can't be run in parallel + case <-s.semaphore: + defer func() { + // release semaphore, also in case of panic + s.semaphore <- struct{}{} + }() + return s.createBlock(ctx), nil + case <-ctx.Done(): + return false, ctx.Err() + } + }) +} + +type BenchmarkingSequencerAPI struct { + benchmarkingSequencer *BenchmarkingSequencer +} + +func (a *BenchmarkingSequencerAPI) TxQueueLength(includeRetryTxQueue bool) int { + return a.benchmarkingSequencer.TxQueueLength(includeRetryTxQueue) +} + +func (a *BenchmarkingSequencerAPI) TxRetryQueueLength() int { + return a.benchmarkingSequencer.TxRetryQueueLength() +} + +func (a *BenchmarkingSequencerAPI) CreateBlock(ctx context.Context) (bool, error) { + return a.benchmarkingSequencer.CreateBlock().Await(ctx) +} + +func NewBenchmarkingSequencerAPI(benchmarkingSequencer *BenchmarkingSequencer) *BenchmarkingSequencerAPI { + return &BenchmarkingSequencerAPI{benchmarkingSequencer: benchmarkingSequencer} +} diff --git a/execution/gethexec/bench_sequencer_config.go b/execution/gethexec/bench_sequencer_config.go new file mode 100644 index 00000000000..360f382f823 --- /dev/null +++ b/execution/gethexec/bench_sequencer_config.go @@ -0,0 +1,15 @@ +// Copyright 2026, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +// DANGER! this file is included in all builds +// DANGER! do not place any experimental tag logic here + +package gethexec + +type BenchmarkingSequencerConfig struct { + Enable bool `koanf:"enable"` +} + +var BenchmarkingSequencerConfigDefault = BenchmarkingSequencerConfig{ + Enable: false, +} diff --git a/execution/gethexec/bench_sequencer_stub.go b/execution/gethexec/bench_sequencer_stub.go new file mode 100644 index 00000000000..5fa1d16bd09 --- /dev/null +++ b/execution/gethexec/bench_sequencer_stub.go @@ -0,0 +1,28 @@ +// Copyright 2026, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +//go:build !experimental + +package gethexec + +import ( + "github.com/spf13/pflag" + + "github.com/ethereum/go-ethereum/log" +) + +func BenchmarkingSequencerConfigAddOptions(_ string, _ *pflag.FlagSet) { + // don't add any options +} + +func (c *BenchmarkingSequencerConfig) Validate() error { + if c.Enable { + log.Warn("benchmarking sequencer requested but not supported in this build (missing experimental build tag)") + } + return nil +} + +func NewBenchmarkingSequencer(sequencer *Sequencer) (TransactionPublisher, interface{}) { + // do nothing + return sequencer, nil +} diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index fbceab19e21..594355b0b13 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -137,6 +137,7 @@ type Config struct { ExposeMultiGas bool `koanf:"expose-multi-gas"` RPCServer rpcserver.Config `koanf:"rpc-server"` ConsensusRPCClient rpcclient.ClientConfig `koanf:"consensus-rpc-client" reload:"hot"` + Dangerous DangerousConfig `koanf:"dangerous"` forwardingTarget string } @@ -168,6 +169,9 @@ func (c *Config) Validate() error { if err := c.ConsensusRPCClient.Validate(); err != nil { return fmt.Errorf("error validating ConsensusRPCClient config: %w", err) } + if err := c.Dangerous.Validate(); err != nil { + return err + } return nil } @@ -191,6 +195,7 @@ func ConfigAddOptions(prefix string, f *pflag.FlagSet) { LiveTracingConfigAddOptions(prefix+".vmtrace", f) rpcserver.ConfigAddOptions(prefix+".rpc-server", "execution", f) rpcclient.RPCClientAddOptions(prefix+".consensus-rpc-client", f, &ConfigDefault.ConsensusRPCClient) + DangerousConfigAddOptions(prefix+".dangerous", f) } type LiveTracingConfig struct { @@ -208,6 +213,25 @@ func LiveTracingConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".json-config", DefaultLiveTracingConfig.JSONConfig, "(experimental) Tracer configuration in JSON format") } +type DangerousConfig struct { + BenchmarkingSequencer BenchmarkingSequencerConfig `koanf:"benchmarking-sequencer"` +} + +var DefaultDangerousConfig = DangerousConfig{ + BenchmarkingSequencer: BenchmarkingSequencerConfigDefault, +} + +func DangerousConfigAddOptions(prefix string, f *pflag.FlagSet) { + BenchmarkingSequencerConfigAddOptions(prefix+".benchmarking-sequencer", f) +} + +func (c *DangerousConfig) Validate() error { + if err := c.BenchmarkingSequencer.Validate(); err != nil { + return err + } + return nil +} + var ConfigDefault = Config{ RPC: arbitrum.DefaultConfig, TxIndexer: DefaultTxIndexerConfig, @@ -237,6 +261,7 @@ var ConfigDefault = Config{ ArgLogLimit: 2048, WebsocketMessageSizeLimit: 256 * 1024 * 1024, }, + Dangerous: DefaultDangerousConfig, } type ConfigFetcher interface { @@ -300,6 +325,7 @@ func CreateExecutionNode( log.Warn("sequencer enabled without l1 client") } + var benchmarkingSequencerService interface{} if config.Sequencer.Enable { seqConfigFetcher := func() *SequencerConfig { return &configFetcher.Get().Sequencer } sequencer, err = NewSequencer(execEngine, parentChainReader, seqConfigFetcher, parentChainID) @@ -307,6 +333,9 @@ func CreateExecutionNode( return nil, err } txPublisher = sequencer + if config.Dangerous.BenchmarkingSequencer.Enable { + txPublisher, benchmarkingSequencerService = NewBenchmarkingSequencer(sequencer) + } } else { if config.Forwarder.RedisUrl != "" { txPublisher = NewRedisTxForwarder(config.forwardingTarget, &config.Forwarder) @@ -438,6 +467,14 @@ func CreateExecutionNode( }) } + if benchmarkingSequencerService != nil { + apis = append(apis, rpc.API{ + Namespace: "benchseq", + Service: benchmarkingSequencerService, + Public: false, + }) + } + stack.RegisterAPIs(apis) return execNode, nil diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index 34ed9716613..f6ae470950d 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -95,12 +95,17 @@ type SequencerConfig struct { ExpectedSurplusHardThreshold string `koanf:"expected-surplus-hard-threshold" reload:"hot"` EnableProfiling bool `koanf:"enable-profiling" reload:"hot"` Timeboost TimeboostConfig `koanf:"timeboost"` - Dangerous DangerousConfig `koanf:"dangerous"` + Dangerous SequencerDangerousConfig `koanf:"dangerous"` TransactionFiltering TransactionFilteringConfig `koanf:"transaction-filtering" reload:"hot"` expectedSurplusSoftThreshold int expectedSurplusHardThreshold int } +type SequencerDangerousConfig struct { + DisableSeqInboxMaxDataSizeCheck bool `koanf:"disable-seq-inbox-max-data-size-check"` + DisableBlobBaseFeeCheck bool `koanf:"disable-blob-base-fee-check"` +} + type TransactionFilteringConfig struct { DisableDelayedSequencingFilter bool `koanf:"disable-delayed-sequencing-filter"` EventFilter eventfilter.EventFilterConfig `koanf:"event-filter"` @@ -135,11 +140,6 @@ func TransactionFilteringConfigAddOptions(prefix string, f *pflag.FlagSet) { rpcclient.RPCClientAddOptions(prefix+".transaction-filterer-rpc-client", f, &DefaultTransactionFilteringConfig.TransactionFiltererRPCClient) } -type DangerousConfig struct { - DisableSeqInboxMaxDataSizeCheck bool `koanf:"disable-seq-inbox-max-data-size-check"` - DisableBlobBaseFeeCheck bool `koanf:"disable-blob-base-fee-check"` -} - type TimeboostConfig struct { Enable bool `koanf:"enable"` AuctionContractAddress string `koanf:"auction-contract-address"` @@ -261,11 +261,11 @@ var DefaultSequencerConfig = SequencerConfig{ ExpectedSurplusHardThreshold: "default", EnableProfiling: false, Timeboost: DefaultTimeboostConfig, - Dangerous: DefaultDangerousConfig, + Dangerous: DefaultSequencerDangerousConfig, TransactionFiltering: DefaultTransactionFilteringConfig, } -var DefaultDangerousConfig = DangerousConfig{ +var DefaultSequencerDangerousConfig = SequencerDangerousConfig{ DisableSeqInboxMaxDataSizeCheck: false, } @@ -279,7 +279,8 @@ func SequencerConfigAddOptions(prefix string, f *pflag.FlagSet) { AddOptionsForSequencerForwarderConfig(prefix+".forwarder", f) TimeboostAddOptions(prefix+".timeboost", f) - DangerousAddOptions(prefix+".dangerous", f) + SequencerDangerousAddOptions(prefix+".dangerous", f) + TransactionFilteringConfigAddOptions(prefix+".transaction-filtering", f) f.Int(prefix+".queue-size", DefaultSequencerConfig.QueueSize, "size of the pending tx queue") f.Duration(prefix+".queue-timeout", DefaultSequencerConfig.QueueTimeout, "maximum amount of time transaction can wait in queue") f.Int(prefix+".nonce-cache-size", DefaultSequencerConfig.NonceCacheSize, "size of the tx sender nonce cache") @@ -290,7 +291,6 @@ func SequencerConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".expected-surplus-soft-threshold", DefaultSequencerConfig.ExpectedSurplusSoftThreshold, "if expected surplus is lower than this value, warnings are posted") f.String(prefix+".expected-surplus-hard-threshold", DefaultSequencerConfig.ExpectedSurplusHardThreshold, "if expected surplus is lower than this value, new incoming transactions will be denied") f.Bool(prefix+".enable-profiling", DefaultSequencerConfig.EnableProfiling, "enable CPU profiling and tracing") - TransactionFilteringConfigAddOptions(prefix+".transaction-filtering", f) } func TimeboostAddOptions(prefix string, f *pflag.FlagSet) { @@ -306,9 +306,9 @@ func TimeboostAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".queue-timeout-in-blocks", DefaultTimeboostConfig.QueueTimeoutInBlocks, "maximum amount of time (measured in blocks) that Express Lane transactions can wait in the sequencer's queue") } -func DangerousAddOptions(prefix string, f *pflag.FlagSet) { - f.Bool(prefix+".disable-seq-inbox-max-data-size-check", DefaultDangerousConfig.DisableSeqInboxMaxDataSizeCheck, "DANGEROUS! disables nitro checks on sequencer MaxTxDataSize against the sequencer inbox MaxDataSize") - f.Bool(prefix+".disable-blob-base-fee-check", DefaultDangerousConfig.DisableBlobBaseFeeCheck, "DANGEROUS! disables nitro checks on sequencer for blob base fee") +func SequencerDangerousAddOptions(prefix string, f *pflag.FlagSet) { + f.Bool(prefix+".disable-seq-inbox-max-data-size-check", DefaultSequencerDangerousConfig.DisableSeqInboxMaxDataSizeCheck, "DANGEROUS! disables nitro checks on sequencer MaxTxDataSize against the sequencer inbox MaxDataSize") + f.Bool(prefix+".disable-blob-base-fee-check", DefaultSequencerDangerousConfig.DisableBlobBaseFeeCheck, "DANGEROUS! disables nitro checks on sequencer for blob base fee") } func EventFilterAddOptions(prefix string, f *pflag.FlagSet) { diff --git a/go-ethereum b/go-ethereum index cdd2f031221..c8de4487fcf 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit cdd2f031221f853f9b521c4fddd7e2217153951c +Subproject commit c8de4487fcf2618d7a3c3b28bc2ab2486b50dbb9 diff --git a/system_tests/bench_sequencer_stub_test.go b/system_tests/bench_sequencer_stub_test.go new file mode 100644 index 00000000000..94d1e1eebad --- /dev/null +++ b/system_tests/bench_sequencer_stub_test.go @@ -0,0 +1,70 @@ +// Copyright 2026, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +//go:build !experimental + +package arbtest + +import ( + "context" + "math/big" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/types" +) + +func TestBenchmarkingSequencerStub(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx).DefaultConfig(t, false) + builder.execConfig.Dangerous.BenchmarkingSequencer.Enable = true + + cleanup := builder.Build(t) + defer cleanup() + + // check benchseq rpc is not available + rpcClient := builder.L2.Client.Client() + var txQueueLen int + err := rpcClient.CallContext(ctx, &txQueueLen, "benchseq_txQueueLength", false) + if err == nil { + Fatal(t, "benchseq_txQueueLength should not have succeeded") + } else if !strings.Contains(err.Error(), "the method benchseq_txQueueLength does not exist") { + Fatal(t, "benchseq_txQueueLength failed with unexpected error:", err) + } + err = rpcClient.CallContext(ctx, &txQueueLen, "benchseq_txRetryQueueLength") + if err == nil { + Fatal(t, "benchseq_txRetryQueueLength should not have succeeded") + } else if !strings.Contains(err.Error(), "the method benchseq_txRetryQueueLength does not exist") { + Fatal(t, "benchseq_txRetryQueueLength failed with unexpected error:", err) + } + var blockCreated bool + // create block with all of the transactions (they should fit) + err = rpcClient.CallContext(ctx, &blockCreated, "benchseq_createBlock") + if err == nil { + Fatal(t, "benchseq_createBlock should not have succeeded") + } else if !strings.Contains(err.Error(), "the method benchseq_createBlock does not exist") { + Fatal(t, "benchseq_createBlock failed with unexpected error:", err) + } + + // check that blocks are created automatically + startBlock, err := builder.L2.Client.BlockNumber(ctx) + Require(t, err) + tx := builder.L2Info.PrepareTx("Owner", "Owner", builder.L2Info.TransferGas, big.NewInt(1), nil) + builder.L2.SendWaitTestTransactions(t, types.Transactions{tx}) + timeout := time.After(5 * time.Second) + for { + block, err := builder.L2.Client.BlockNumber(ctx) + Require(t, err) + if block > startBlock { + break + } + select { + case <-timeout: + Fatal(t, "timeout exceeded while waiting for new block") + case <-time.After(20 * time.Millisecond): + } + } +} diff --git a/system_tests/bench_sequencer_test.go b/system_tests/bench_sequencer_test.go new file mode 100644 index 00000000000..07a9f35b6cf --- /dev/null +++ b/system_tests/bench_sequencer_test.go @@ -0,0 +1,104 @@ +// Copyright 2026, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +//go:build experimental + +package arbtest + +import ( + "context" + "math/big" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/types" +) + +func TestBenchmarkingSequencer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + builder := NewNodeBuilder(ctx).DefaultConfig(t, false) + // We don't want any txes sent during NodeBuilder.Build as they will hang and timeout due to no blocks being created automatically. + builder = builder.WithTakeOwnership(false) + builder.execConfig.Dangerous.BenchmarkingSequencer.Enable = true + + cleanup := builder.Build(t) + defer cleanup() + + startBlock, err := builder.L2.Client.BlockNumber(ctx) + Require(t, err) + + rpcClient := builder.L2.Client.Client() + var txSendersWg sync.WaitGroup + var txes types.Transactions + for i := 0; i < 5; i++ { + // send the transaction in separate thread as the rpc call will wait for it to be accepted by sequencer + tx := builder.L2Info.PrepareTx("Owner", "Owner", builder.L2Info.TransferGas, big.NewInt(1), nil) + txes = append(txes, tx) + txSendersWg.Add(1) + go func() { + defer txSendersWg.Done() + err := builder.L2.Client.SendTransaction(ctx, tx) + Require(t, err) + }() + + // wait for the transaction to be enqueued + timeout := time.After(5 * time.Second) + for { + var txQueueLen int + err := rpcClient.CallContext(ctx, &txQueueLen, "benchseq_txQueueLength", false) + Require(t, err) + if txQueueLen >= i+1 { + break + } + select { + case <-timeout: + Fatal(t, "timeout exceeded while waiting for tx queue to grow") + case <-time.After(10 * time.Millisecond): + } + } + } + + block, err := builder.L2.Client.BlockNumber(ctx) + Require(t, err) + if block != startBlock { + Fatal(t, "block has been created even though benchseq_createBlock hasn't been called") + } + + var blockCreated bool + // create block with all of the transactions (they should fit) + err = rpcClient.CallContext(ctx, &blockCreated, "benchseq_createBlock") + Require(t, err) + if !blockCreated { + Fatal(t, "block should have been created") + } + // check that tx queue is empty + var txQueueLen int + err = rpcClient.CallContext(ctx, &txQueueLen, "benchseq_txQueueLength", false) + Require(t, err) + if txQueueLen != 0 { + Fatal(t, "benchseq_txQueueLength reported non empty queue, want: 0, have:", txQueueLen) + } + + txSendersWg.Wait() + for _, tx := range txes { + _, err := builder.L2.EnsureTxSucceeded(tx) + Require(t, err) + } + + timeout := time.After(5 * time.Second) + for { + block, err := builder.L2.Client.BlockNumber(ctx) + Require(t, err) + if block >= startBlock+1 { + break + } + select { + case <-timeout: + Fatal(t, "timeout exceeded while waiting for new block") + case <-time.After(20 * time.Millisecond): + } + } +}