Skip to content

Commit 35db44b

Browse files
committed
feat: add load-test payload worker for base-load-test binary integration
Add a new payload worker type 'load-test' that integrates the Rust base-load-test binary as an external transaction generator. The worker follows the same proxy-based pattern as the tx-fuzz integration: - New LoadTestPayloadWorker spawns the binary with a generated YAML config pointing to a proxy server that captures transactions - CLI flag --load-test-bin (env: BASE_BENCH_LOAD_TEST_BIN) for binary path - Config interface extended with LoadTestBinary() method - Factory routes 'load-test' payload type to the new worker - Generated config supports configurable sender_count, target_gps, and duration via YAML payload params
1 parent 6906314 commit 35db44b

4 files changed

Lines changed: 245 additions & 35 deletions

File tree

benchmark/flags/flags.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,24 @@ func prefixEnvVars(name string) []string {
1515
}
1616

1717
const (
18-
ConfigFlagName = "config"
19-
RootDirFlagName = "root-dir"
20-
OutputDirFlagName = "output-dir"
21-
TxFuzzBinFlagName = "tx-fuzz-bin"
22-
ProxyPortFlagName = "proxy-port"
23-
BenchmarkRunIDFlagName = "benchmark-run-id"
24-
MachineTypeFlagName = "machine-type"
25-
MachineProviderFlagName = "machine-provider"
26-
MachineRegionFlagName = "machine-region"
27-
FileSystemFlagName = "file-system"
18+
ConfigFlagName = "config"
19+
RootDirFlagName = "root-dir"
20+
OutputDirFlagName = "output-dir"
21+
TxFuzzBinFlagName = "tx-fuzz-bin"
22+
LoadTestBinFlagName = "load-test-bin"
23+
ProxyPortFlagName = "proxy-port"
24+
BenchmarkRunIDFlagName = "benchmark-run-id"
25+
MachineTypeFlagName = "machine-type"
26+
MachineProviderFlagName = "machine-provider"
27+
MachineRegionFlagName = "machine-region"
28+
FileSystemFlagName = "file-system"
2829
ParallelTxBatchesFlagName = "parallel-tx-batches"
2930
)
3031

3132
// TxFuzz defaults
3233
const (
33-
DefaultTxFuzzBin = "../tx-fuzz/cmd/livefuzzer/livefuzzer"
34+
DefaultTxFuzzBin = "../tx-fuzz/cmd/livefuzzer/livefuzzer"
35+
DefaultLoadTestBin = "./base-load-test"
3436
)
3537

3638
var (
@@ -62,6 +64,13 @@ var (
6264
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "TX_FUZZ_BIN"),
6365
}
6466

67+
LoadTestBinFlag = &cli.StringFlag{
68+
Name: LoadTestBinFlagName,
69+
Usage: "Load test binary path",
70+
Value: DefaultLoadTestBin,
71+
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "LOAD_TEST_BIN"),
72+
}
73+
6574
ProxyPortFlag = &cli.IntFlag{
6675
Name: "proxy-port",
6776
Usage: "Proxy port",
@@ -116,6 +125,7 @@ var RunFlags = []cli.Flag{
116125
RootDirFlag,
117126
OutputDirFlag,
118127
TxFuzzBinFlag,
128+
LoadTestBinFlag,
119129
ProxyPortFlag,
120130
BenchmarkRunIDFlag,
121131
MachineTypeFlag,

runner/config/config.go

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Config interface {
2020
DataDir() string
2121
OutputDir() string
2222
TxFuzzBinary() string
23+
LoadTestBinary() string
2324
ProxyPort() int
2425
BenchmarkRunID() string
2526
MachineType() string
@@ -30,36 +31,38 @@ type Config interface {
3031
}
3132

3233
type config struct {
33-
logConfig oplog.CLIConfig
34-
configPath string
35-
dataDir string
36-
outputDir string
37-
clientOptions ClientOptions
38-
txFuzzBinary string
39-
proxyPort int
40-
benchmarkRunID string
41-
machineType string
42-
machineProvider string
43-
machineRegion string
44-
fileSystem string
34+
logConfig oplog.CLIConfig
35+
configPath string
36+
dataDir string
37+
outputDir string
38+
clientOptions ClientOptions
39+
txFuzzBinary string
40+
loadTestBinary string
41+
proxyPort int
42+
benchmarkRunID string
43+
machineType string
44+
machineProvider string
45+
machineRegion string
46+
fileSystem string
4547
parallelTxBatches int
4648
}
4749

4850
func NewConfig(ctx *cli.Context) Config {
4951
return &config{
50-
logConfig: oplog.ReadCLIConfig(ctx),
51-
configPath: ctx.String(appFlags.ConfigFlagName),
52-
dataDir: ctx.String(appFlags.RootDirFlagName),
53-
outputDir: ctx.String(appFlags.OutputDirFlagName),
54-
txFuzzBinary: ctx.String(appFlags.TxFuzzBinFlagName),
55-
proxyPort: ctx.Int(appFlags.ProxyPortFlagName),
56-
benchmarkRunID: ctx.String(appFlags.BenchmarkRunIDFlagName),
57-
machineType: ctx.String(appFlags.MachineTypeFlagName),
58-
machineProvider: ctx.String(appFlags.MachineProviderFlagName),
59-
machineRegion: ctx.String(appFlags.MachineRegionFlagName),
60-
fileSystem: ctx.String(appFlags.FileSystemFlagName),
52+
logConfig: oplog.ReadCLIConfig(ctx),
53+
configPath: ctx.String(appFlags.ConfigFlagName),
54+
dataDir: ctx.String(appFlags.RootDirFlagName),
55+
outputDir: ctx.String(appFlags.OutputDirFlagName),
56+
txFuzzBinary: ctx.String(appFlags.TxFuzzBinFlagName),
57+
loadTestBinary: ctx.String(appFlags.LoadTestBinFlagName),
58+
proxyPort: ctx.Int(appFlags.ProxyPortFlagName),
59+
benchmarkRunID: ctx.String(appFlags.BenchmarkRunIDFlagName),
60+
machineType: ctx.String(appFlags.MachineTypeFlagName),
61+
machineProvider: ctx.String(appFlags.MachineProviderFlagName),
62+
machineRegion: ctx.String(appFlags.MachineRegionFlagName),
63+
fileSystem: ctx.String(appFlags.FileSystemFlagName),
6164
parallelTxBatches: ctx.Int(appFlags.ParallelTxBatchesFlagName),
62-
clientOptions: ReadClientOptions(ctx),
65+
clientOptions: ReadClientOptions(ctx),
6366
}
6467
}
6568

@@ -112,6 +115,10 @@ func (c *config) TxFuzzBinary() string {
112115
return c.txFuzzBinary
113116
}
114117

118+
func (c *config) LoadTestBinary() string {
119+
return c.loadTestBinary
120+
}
121+
115122
func (c *config) BenchmarkRunID() string {
116123
return c.benchmarkRunID
117124
}

runner/payload/factory.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
clienttypes "github.com/base/base-bench/runner/clients/types"
88
benchtypes "github.com/base/base-bench/runner/network/types"
99
"github.com/base/base-bench/runner/payload/contract"
10+
"github.com/base/base-bench/runner/payload/loadtest"
1011
"github.com/base/base-bench/runner/payload/simulator"
1112
"github.com/base/base-bench/runner/payload/transferonly"
1213
"github.com/base/base-bench/runner/payload/txfuzz"
@@ -31,6 +32,13 @@ func NewPayloadWorker(ctx context.Context, log log.Logger, testConfig *benchtype
3132
case "tx-fuzz":
3233
worker, err = txfuzz.NewTxFuzzPayloadWorker(
3334
log, sequencerClient.ClientURL(), params, privateKey, amount, config.TxFuzzBinary(), genesis.Config.ChainID)
35+
case "load-test":
36+
def, _ := definition.Params.(*loadtest.LoadTestPayloadDefinition)
37+
if def == nil {
38+
def = &loadtest.LoadTestPayloadDefinition{}
39+
}
40+
worker, err = loadtest.NewLoadTestPayloadWorker(
41+
log, sequencerClient.ClientURL(), params, privateKey, amount, config.LoadTestBinary(), genesis.Config.ChainID, *def)
3442
case "transfer-only":
3543
worker, err = transferonly.NewTransferPayloadWorker(
3644
ctx, log, sequencerClient.ClientURL(), params, privateKey, amount, &genesis, definition.Params)
@@ -77,6 +85,8 @@ func (t *Definition) UnmarshalYAML(node *yaml.Node) error {
7785
params = &transferonly.TransferOnlyPayloadDefinition{}
7886
case "tx-fuzz":
7987
params = &txfuzz.TxFuzzPayloadDefinition{}
88+
case "load-test":
89+
params = &loadtest.LoadTestPayloadDefinition{}
8090
case "contract":
8191
params = &contract.ContractPayloadDefinition{}
8292
case "simulator":
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package loadtest
2+
3+
import (
4+
"context"
5+
"crypto/ecdsa"
6+
"encoding/hex"
7+
"fmt"
8+
"math/big"
9+
"os"
10+
"os/exec"
11+
12+
"github.com/base/base-bench/runner/clients/common/proxy"
13+
"github.com/base/base-bench/runner/network/mempool"
14+
"github.com/base/base-bench/runner/network/types"
15+
"github.com/base/base-bench/runner/payload/worker"
16+
"github.com/ethereum/go-ethereum/log"
17+
"github.com/pkg/errors"
18+
"gopkg.in/yaml.v3"
19+
)
20+
21+
const proxyPort = 8545
22+
23+
// LoadTestPayloadDefinition is the YAML payload params for the load-test type.
24+
type LoadTestPayloadDefinition struct {
25+
SenderCount uint64 `yaml:"sender_count"`
26+
TargetGPS uint64 `yaml:"target_gps"`
27+
Duration string `yaml:"duration"`
28+
}
29+
30+
// loadTestConfig is the YAML config written to a temp file for the load-test binary.
31+
type loadTestConfig struct {
32+
RPC string `yaml:"rpc"`
33+
SenderCount uint64 `yaml:"sender_count"`
34+
TargetGPS uint64 `yaml:"target_gps"`
35+
Duration string `yaml:"duration"`
36+
Seed uint64 `yaml:"seed"`
37+
FundingAmount string `yaml:"funding_amount"`
38+
Transactions []loadTestTransactionDef `yaml:"transactions"`
39+
}
40+
41+
type loadTestTransactionDef struct {
42+
Type string `yaml:"type"`
43+
Weight uint64 `yaml:"weight"`
44+
}
45+
46+
type loadTestPayloadWorker struct {
47+
log log.Logger
48+
prefundSK string
49+
loadTestBin string
50+
elRPCURL string
51+
gasLimit uint64
52+
params LoadTestPayloadDefinition
53+
mempool *mempool.StaticWorkloadMempool
54+
proxyServer *proxy.ProxyServer
55+
configFilePath string
56+
}
57+
58+
// NewLoadTestPayloadWorker creates a worker that runs the base-load-test binary
59+
// as an external transaction generator, capturing transactions via a proxy server.
60+
func NewLoadTestPayloadWorker(
61+
log log.Logger,
62+
elRPCURL string,
63+
params types.RunParams,
64+
prefundedPrivateKey ecdsa.PrivateKey,
65+
prefundAmount *big.Int,
66+
loadTestBin string,
67+
chainID *big.Int,
68+
definition LoadTestPayloadDefinition,
69+
) (worker.Worker, error) {
70+
mp := mempool.NewStaticWorkloadMempool(log, chainID)
71+
ps := proxy.NewProxyServer(elRPCURL, log, proxyPort, mp)
72+
73+
w := &loadTestPayloadWorker{
74+
log: log,
75+
prefundSK: hex.EncodeToString(prefundedPrivateKey.D.Bytes()),
76+
loadTestBin: loadTestBin,
77+
elRPCURL: elRPCURL,
78+
gasLimit: params.GasLimit,
79+
params: definition,
80+
mempool: mp,
81+
proxyServer: ps,
82+
}
83+
84+
return w, nil
85+
}
86+
87+
func (w *loadTestPayloadWorker) Mempool() mempool.FakeMempool {
88+
return w.mempool
89+
}
90+
91+
func (w *loadTestPayloadWorker) Setup(ctx context.Context) error {
92+
if err := w.proxyServer.Run(ctx); err != nil {
93+
return errors.Wrap(err, "failed to run proxy server")
94+
}
95+
96+
configPath, err := w.writeConfig()
97+
if err != nil {
98+
return errors.Wrap(err, "failed to write load-test config")
99+
}
100+
w.configFilePath = configPath
101+
102+
w.log.Info("Starting load test", "binary", w.loadTestBin, "config", configPath)
103+
104+
cmd := exec.CommandContext(ctx, w.loadTestBin, configPath)
105+
cmd.Stdout = os.Stdout
106+
cmd.Stderr = os.Stdout
107+
cmd.Env = append(os.Environ(), fmt.Sprintf("FUNDER_KEY=%s", w.prefundSK))
108+
109+
if err := cmd.Start(); err != nil {
110+
return errors.Wrap(err, "failed to start load test binary")
111+
}
112+
113+
return nil
114+
}
115+
116+
func (w *loadTestPayloadWorker) Stop(ctx context.Context) error {
117+
w.proxyServer.Stop()
118+
119+
if w.configFilePath != "" {
120+
os.Remove(w.configFilePath)
121+
}
122+
123+
return nil
124+
}
125+
126+
func (w *loadTestPayloadWorker) SendTxs(ctx context.Context) error {
127+
w.log.Info("Collecting txs from load test")
128+
pendingTxs := w.proxyServer.PendingTxs()
129+
w.proxyServer.ClearPendingTxs()
130+
131+
w.mempool.AddTransactions(pendingTxs)
132+
return nil
133+
}
134+
135+
// writeConfig generates a temporary YAML config file for the load-test binary
136+
// with the RPC URL pointing to the proxy server.
137+
func (w *loadTestPayloadWorker) writeConfig() (string, error) {
138+
senderCount := w.params.SenderCount
139+
if senderCount == 0 {
140+
senderCount = 10
141+
}
142+
143+
targetGPS := w.params.TargetGPS
144+
if targetGPS == 0 {
145+
targetGPS = w.gasLimit / 2
146+
}
147+
148+
duration := w.params.Duration
149+
if duration == "" {
150+
duration = "600s"
151+
}
152+
153+
config := loadTestConfig{
154+
RPC: fmt.Sprintf("http://localhost:%d", proxyPort),
155+
SenderCount: senderCount,
156+
TargetGPS: targetGPS,
157+
Duration: duration,
158+
Seed: 12345,
159+
FundingAmount: "10000000000000000000",
160+
Transactions: []loadTestTransactionDef{
161+
{Type: "transfer", Weight: 70},
162+
{Type: "calldata", Weight: 20},
163+
{Type: "precompile", Weight: 10},
164+
},
165+
}
166+
167+
data, err := yaml.Marshal(&config)
168+
if err != nil {
169+
return "", errors.Wrap(err, "failed to marshal load-test config")
170+
}
171+
172+
tmpFile, err := os.CreateTemp("", "load-test-config-*.yaml")
173+
if err != nil {
174+
return "", errors.Wrap(err, "failed to create temp config file")
175+
}
176+
defer tmpFile.Close()
177+
178+
if _, err := tmpFile.Write(data); err != nil {
179+
return "", errors.Wrap(err, "failed to write temp config file")
180+
}
181+
182+
return tmpFile.Name(), nil
183+
}

0 commit comments

Comments
 (0)