Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions configs/examples/load-test-simulator.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Simulator load-test throughput test
description: Test builder throughput using base-load-tester with the simulator payload. The Simulator contract is deployed automatically via CREATE before the test starts if not already present.
payloads:
- name: Load Test Simulator
type: load-test
id: load-test-simulator
sender_count: 5
transactions:
- weight: 100
type: simulator
create_storage: 10
create_accounts: 5

benchmarks:
- variables:
- type: payload
value: load-test-simulator
- type: node_type
value: builder
- type: validator_node_type
value: base-reth-node
- type: num_blocks
value: 20
- type: gas_limit
value: 1000000000
4 changes: 4 additions & 0 deletions runner/clients/baserethnode/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,7 @@ func (r *BaseRethNodeClient) FlashblocksClient() types.FlashblocksClient {
func (r *BaseRethNodeClient) SupportsFlashblocks() bool {
return true
}

func (r *BaseRethNodeClient) FlashblocksWsURL() string {
return ""
}
8 changes: 8 additions & 0 deletions runner/clients/builder/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,11 @@ func (r *BuilderClient) FlashblocksClient() types.FlashblocksClient {
func (r *BuilderClient) SupportsFlashblocks() bool {
return false
}

// FlashblocksWsURL returns the local WebSocket URL of the flashblocks server hosted by the builder.
func (r *BuilderClient) FlashblocksWsURL() string {
if r.websocketPort == 0 {
return ""
}
return fmt.Sprintf("ws://localhost:%d", r.websocketPort)
}
52 changes: 51 additions & 1 deletion runner/clients/common/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,21 @@ func (p *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
return
}

var request struct {
// Detect JSON batch requests (arrays of RPC calls).
if len(body) > 0 && body[0] == '[' {
p.handleBatchRequest(w, body)
return
}

type rpcRequest struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID interface{} `json:"id"`
JSONRPC string `json:"jsonrpc"`
}

var request rpcRequest

if err := json.Unmarshal(body, &request); err != nil {
http.Error(w, "Error parsing request", http.StatusBadRequest)
return
Expand Down Expand Up @@ -164,6 +172,48 @@ func (p *ProxyServer) handleRequest(w http.ResponseWriter, r *http.Request) {
p.DebugResponse(request.Method, request.Params, respBody)
}

func (p *ProxyServer) handleBatchRequest(w http.ResponseWriter, body []byte) {
type rpcRequest struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID interface{} `json:"id"`
JSONRPC string `json:"jsonrpc"`
}
var requests []rpcRequest
if err := json.Unmarshal(body, &requests); err != nil {
http.Error(w, "Error parsing batch request", http.StatusBadRequest)
return
}

type rpcResponse struct {
JSONRPC string `json:"jsonrpc"`
ID interface{} `json:"id"`
Result json.RawMessage `json:"result,omitempty"`
Error interface{} `json:"error,omitempty"`
}

responses := make([]rpcResponse, 0, len(requests))
for _, req := range requests {
handled, result, err := p.OverrideRequest(req.Method, req.Params)
var resp rpcResponse
resp.JSONRPC = "2.0"
resp.ID = req.ID
if err != nil {
resp.Error = map[string]interface{}{"code": -32000, "message": err.Error()}
} else if handled {
resp.Result = result
} else {
resp.Error = map[string]interface{}{"code": -32601, "message": "method not supported in proxy batch mode"}
}
responses = append(responses, resp)
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(responses); err != nil {
p.log.Error("Error encoding batch response", "err", err)
}
}

func (p *ProxyServer) OverrideRequest(method string, rawParams json.RawMessage) (bool, json.RawMessage, error) {
switch method {
case "eth_getTransactionCount":
Expand Down
4 changes: 4 additions & 0 deletions runner/clients/geth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,7 @@ func (g *GethClient) FlashblocksClient() types.FlashblocksClient {
func (g *GethClient) SupportsFlashblocks() bool {
return false
}

func (g *GethClient) FlashblocksWsURL() string {
return ""
}
4 changes: 4 additions & 0 deletions runner/clients/reth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,7 @@ func (r *RethClient) FlashblocksClient() types.FlashblocksClient {
func (r *RethClient) SupportsFlashblocks() bool {
return true
}

func (r *RethClient) FlashblocksWsURL() string {
return ""
}
4 changes: 4 additions & 0 deletions runner/clients/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ type ExecutionClient interface {
SetHead(ctx context.Context, blockNumber uint64) error
FlashblocksClient() FlashblocksClient // returns nil for clients that don't support flashblocks
SupportsFlashblocks() bool // returns true if the client supports receiving flashblock payloads
// FlashblocksWsURL returns the local WebSocket URL of the flashblocks server hosted by this
// client, or an empty string if the client does not host one. Used by the load-test worker to
// configure flashblocks_ws in the Rust load-tester binary.
FlashblocksWsURL() string
}
2 changes: 1 addition & 1 deletion runner/payload/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewPayloadWorker(ctx context.Context, log log.Logger, testConfig *benchtype
def = &loadtest.LoadTestPayloadDefinition{}
}
worker, err = loadtest.NewLoadTestPayloadWorker(
log, sequencerClient.ClientURL(), params, privateKey, amount, config, genesis.Config.ChainID, *def)
log, sequencerClient.ClientURL(), sequencerClient.FlashblocksWsURL(), params, privateKey, amount, config, genesis.Config.ChainID, *def)
case "transfer-only":
worker, err = transferonly.NewTransferPayloadWorker(
ctx, log, sequencerClient.ClientURL(), params, privateKey, amount, &genesis, definition.Params)
Expand Down
68 changes: 39 additions & 29 deletions runner/payload/loadtest/load_test_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,36 @@ type LoadTestPayloadDefinition struct {

// loadTestConfig is the YAML config written to a temp file for the load-test binary.
type loadTestConfig struct {
RPC string `yaml:"rpc"`
SenderCount uint64 `yaml:"sender_count"`
TargetGPS uint64 `yaml:"target_gps"`
Duration string `yaml:"duration"`
Seed uint64 `yaml:"seed"`
FundingAmount string `yaml:"funding_amount"`
Transactions yaml.Node `yaml:"transactions"`
RPC string `yaml:"transaction_submission_rpcs"`
SetupRPC string `yaml:"setup_rpc,omitempty"`
FlashblocksWs string `yaml:"flashblocks_ws"`
SenderCount uint64 `yaml:"sender_count"`
TargetGPS uint64 `yaml:"target_gps"`
Duration string `yaml:"duration"`
Seed uint64 `yaml:"seed"`
FundingAmount string `yaml:"funding_amount"`
Transactions yaml.Node `yaml:"transactions"`
}

type loadTestPayloadWorker struct {
log log.Logger
prefundSK string
loadTestBin string
elRPCURL string
gasLimit uint64
blockTimeSec uint64
params LoadTestPayloadDefinition
mempool *mempool.StaticWorkloadMempool
proxyServer *proxy.ProxyServer
cmd *exec.Cmd
configFilePath string
log log.Logger
prefundSK string
loadTestBin string
elRPCURL string
flashblocksWsURL string
gasLimit uint64
blockTimeSec uint64
params LoadTestPayloadDefinition
mempool *mempool.StaticWorkloadMempool
proxyServer *proxy.ProxyServer
cmd *exec.Cmd
configFilePath string
}

// NewLoadTestPayloadWorker creates a worker that runs the base-load-test binary
// as an external transaction generator, capturing transactions via a proxy server.
func NewLoadTestPayloadWorker(
log log.Logger,
elRPCURL string,
flashblocksWsURL string,
params types.RunParams,
prefundedPrivateKey ecdsa.PrivateKey,
prefundAmount *big.Int,
Expand All @@ -76,15 +78,16 @@ func NewLoadTestPayloadWorker(
}

w := &loadTestPayloadWorker{
log: log,
prefundSK: hex.EncodeToString(prefundedPrivateKey.D.Bytes()),
loadTestBin: cfg.LoadTestBinary(),
elRPCURL: elRPCURL,
gasLimit: params.GasLimit,
blockTimeSec: blockTimeSec,
params: definition,
mempool: mp,
proxyServer: ps,
log: log,
prefundSK: hex.EncodeToString(prefundedPrivateKey.D.Bytes()),
loadTestBin: cfg.LoadTestBinary(),
elRPCURL: elRPCURL,
flashblocksWsURL: flashblocksWsURL,
gasLimit: params.GasLimit,
blockTimeSec: blockTimeSec,
params: definition,
mempool: mp,
proxyServer: ps,
}

return w, nil
Expand Down Expand Up @@ -206,8 +209,15 @@ func (w *loadTestPayloadWorker) writeConfig() (string, error) {
transactions = defaultTransactions()
}

flashblocksWs := w.flashblocksWsURL
if flashblocksWs == "" {
flashblocksWs = "ws://localhost:7111"
}

config := loadTestConfig{
RPC: w.proxyServer.ClientURL(),
SetupRPC: w.elRPCURL,
FlashblocksWs: flashblocksWs,
SenderCount: senderCount,
TargetGPS: targetGPS,
Duration: "99999s",
Expand Down
Loading