Skip to content

Commit b2cf72f

Browse files
authored
Merge pull request #195 from flashbots/feat/contender-tps-flag
add --contender.arg flag
1 parent bfcb2b0 commit b2cf72f

File tree

6 files changed

+153
-41
lines changed

6 files changed

+153
-41
lines changed

main.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var labels playground.MapStringFlag
3131
var disableLogs bool
3232
var platform string
3333
var contenderEnabled bool
34+
var contenderArgs []string
3435

3536
var rootCmd = &cobra.Command{
3637
Use: "playground",
@@ -181,6 +182,7 @@ func main() {
181182
recipeCmd.Flags().BoolVar(&disableLogs, "disable-logs", false, "disable logs")
182183
recipeCmd.Flags().StringVar(&platform, "platform", "", "docker platform to use")
183184
recipeCmd.Flags().BoolVar(&contenderEnabled, "contender", false, "spam nodes with contender")
185+
recipeCmd.Flags().StringArrayVar(&contenderArgs, "contender.arg", []string{}, "add/override contender CLI flags")
184186

185187
cookCmd.AddCommand(recipeCmd)
186188
}
@@ -226,7 +228,14 @@ func runIt(recipe playground.Recipe) error {
226228
return err
227229
}
228230

229-
svcManager := recipe.Apply(&playground.ExContext{LogLevel: logLevel, ContenderEnabled: contenderEnabled}, artifacts)
231+
// if contender.tps is set, assume contender is enabled
232+
svcManager := recipe.Apply(&playground.ExContext{
233+
LogLevel: logLevel,
234+
Contender: &playground.ContenderContext{
235+
Enabled: contenderEnabled,
236+
ExtraArgs: contenderArgs,
237+
},
238+
}, artifacts)
230239
if err := svcManager.Validate(); err != nil {
231240
return fmt.Errorf("failed to validate manifest: %w", err)
232241
}

playground/components.go

Lines changed: 115 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ func (o *OpRbuilder) Name() string {
101101

102102
type FlashblocksRPC struct {
103103
FlashblocksWSService string
104-
BaseOverlay bool
105-
UseWebsocketProxy bool // Whether to add /ws path for websocket proxy
104+
BaseOverlay bool
105+
UseWebsocketProxy bool // Whether to add /ws path for websocket proxy
106106
}
107107

108108
func (f *FlashblocksRPC) Run(service *Service, ctx *ExContext) {
@@ -130,19 +130,19 @@ func (f *FlashblocksRPC) Run(service *Service, ctx *ExContext) {
130130
)
131131
}
132132
service.WithArgs(
133-
"--authrpc.port", `{{Port "authrpc" 8551}}`,
134-
"--authrpc.addr", "0.0.0.0",
135-
"--authrpc.jwtsecret", "/data/jwtsecret",
136-
"--http",
137-
"--http.addr", "0.0.0.0",
138-
"--http.port", `{{Port "http" 8545}}`,
139-
"--chain", "/data/l2-genesis.json",
140-
"--datadir", "/data_op_reth",
141-
"--disable-discovery",
142-
"--color", "never",
143-
"--metrics", `0.0.0.0:{{Port "metrics" 9090}}`,
144-
"--port", `{{Port "rpc" 30303}}`,
145-
).
133+
"--authrpc.port", `{{Port "authrpc" 8551}}`,
134+
"--authrpc.addr", "0.0.0.0",
135+
"--authrpc.jwtsecret", "/data/jwtsecret",
136+
"--http",
137+
"--http.addr", "0.0.0.0",
138+
"--http.port", `{{Port "http" 8545}}`,
139+
"--chain", "/data/l2-genesis.json",
140+
"--datadir", "/data_op_reth",
141+
"--disable-discovery",
142+
"--color", "never",
143+
"--metrics", `0.0.0.0:{{Port "metrics" 9090}}`,
144+
"--port", `{{Port "rpc" 30303}}`,
145+
).
146146
WithArtifact("/data/jwtsecret", "jwtsecret").
147147
WithArtifact("/data/l2-genesis.json", "l2-genesis.json").
148148
WithVolume("data", "/data_flashblocks_rpc")
@@ -159,13 +159,13 @@ func (f *FlashblocksRPC) Name() string {
159159
}
160160

161161
type BProxy struct {
162-
TargetAuthrpc string
163-
Peers []string
164-
Flashblocks bool
162+
TargetAuthrpc string
163+
Peers []string
164+
Flashblocks bool
165165
FlashblocksBuilderURL string
166166
}
167167

168-
func (f* BProxy) Run(service *Service, ctx *ExContext) {
168+
func (f *BProxy) Run(service *Service, ctx *ExContext) {
169169
peers := []string{}
170170
for _, peer := range f.Peers {
171171
peers = append(peers, Connect(peer, "authrpc"))
@@ -813,20 +813,110 @@ func (n *nullService) Name() string {
813813
}
814814

815815
type Contender struct {
816+
ExtraArgs []string
816817
}
817818

818819
func (c *Contender) Name() string {
819820
return "contender"
820821
}
821822

823+
// parse "key=value" OR "key value"; remainder after first space is the value (may contain spaces)
824+
func parseKV(s string) (name, val string, hasVal, usedEq bool) {
825+
s = strings.TrimSpace(s)
826+
if s == "" {
827+
return "", "", false, false
828+
}
829+
eq := strings.IndexByte(s, '=')
830+
ws := indexWS(s)
831+
832+
// prefer '=' if it appears before any whitespace
833+
if eq > 0 && (ws == -1 || eq < ws) {
834+
return strings.TrimSpace(s[:eq]), strings.TrimSpace(s[eq+1:]), true, true
835+
}
836+
if ws == -1 {
837+
return s, "", false, false
838+
}
839+
return strings.TrimSpace(s[:ws]), strings.TrimSpace(s[ws+1:]), true, false
840+
}
841+
842+
func indexWS(s string) int {
843+
for i, r := range s {
844+
if r == ' ' || r == '\t' {
845+
return i
846+
}
847+
}
848+
return -1
849+
}
850+
822851
func (c *Contender) Run(service *Service, ctx *ExContext) {
823-
args := []string{
824-
"spam",
825-
"-l", // loop indefinitely
826-
"--min-balance", "10 ether", // give each spammer 10 ether (sender must have 100 ether because default number of spammers is 10)
827-
"-r", Connect("el", "http"), // connect to whatever EL node is available
828-
"--tps", "20", // send 20 txs per second
852+
type opt struct {
853+
name string
854+
val string
855+
hasVal bool
856+
}
857+
defaults := []opt{
858+
{name: "-l"},
859+
{name: "--min-balance", val: "10 ether", hasVal: true},
860+
{name: "-r", val: Connect("el", "http"), hasVal: true},
861+
{name: "--tps", val: "20", hasVal: true},
862+
}
863+
864+
// Parse extras and track seen flags
865+
type extra struct {
866+
name string
867+
val string
868+
hasVal bool
869+
usedEq bool
870+
}
871+
var extras []extra
872+
seen := map[string]bool{}
873+
874+
for _, s := range c.ExtraArgs {
875+
name, val, hasVal, usedEq := parseKV(s)
876+
if name == "" {
877+
continue
878+
}
879+
extras = append(extras, extra{name, val, hasVal, usedEq})
880+
seen[name] = true
881+
}
882+
883+
// Minimal conflict example: --loops overrides default "-l"
884+
conflict := func(flag string) bool {
885+
if seen[flag] {
886+
return true
887+
}
888+
if flag == "-l" && seen["--loops"] {
889+
return true
890+
}
891+
return false
829892
}
893+
894+
args := []string{"spam"}
895+
896+
// Add defaults unless overridden
897+
for _, d := range defaults {
898+
if conflict(d.name) {
899+
continue
900+
}
901+
args = append(args, d.name)
902+
if d.hasVal {
903+
args = append(args, d.val)
904+
}
905+
}
906+
907+
// Append extras verbatim, preserving "=" vs space
908+
for _, e := range extras {
909+
if !e.hasVal {
910+
args = append(args, e.name)
911+
continue
912+
}
913+
if e.usedEq {
914+
args = append(args, e.name+"="+e.val)
915+
} else {
916+
args = append(args, e.name, e.val)
917+
}
918+
}
919+
830920
service.WithImage("flashbots/contender").
831921
WithTag("latest").
832922
WithArgs(args...).

playground/manifest.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ func (l *LogLevel) Unmarshal(s string) error {
7070
return nil
7171
}
7272

73+
type ContenderContext struct {
74+
// Run `contender spam` automatically once all playground services are running.
75+
Enabled bool
76+
77+
// Provide additional args to contender's CLI
78+
ExtraArgs []string
79+
}
80+
7381
// Execution context
7482
type ExContext struct {
7583
LogLevel LogLevel
@@ -83,7 +91,7 @@ type ExContext struct {
8391
// TODO: Extend for CL nodes too
8492
Bootnode *BootnodeRef
8593

86-
ContenderEnabled bool
94+
Contender *ContenderContext
8795
}
8896

8997
type BootnodeRef struct {

playground/recipe_buildernet.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ func (b *BuilderNetRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest
5959
})
6060
}
6161

62-
if ctx.ContenderEnabled {
63-
svcManager.AddService("contender", &Contender{})
62+
if ctx.Contender.Enabled {
63+
svcManager.AddService("contender", &Contender{
64+
ExtraArgs: ctx.Contender.ExtraArgs,
65+
})
6466
}
6567

6668
return svcManager

playground/recipe_l1.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
107107
})
108108
}
109109

110-
if ctx.ContenderEnabled {
111-
svcManager.AddService("contender", &Contender{})
110+
if ctx.Contender.Enabled {
111+
svcManager.AddService("contender", &Contender{
112+
ExtraArgs: ctx.Contender.ExtraArgs,
113+
})
112114
}
113115

114116
return svcManager

playground/recipe_opstack.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type OpRecipe struct {
3232
flashblocksBuilderURL string
3333

3434
// Indicates that flashblocks-rpc should use base image
35-
baseOverlay bool
35+
baseOverlay bool
3636

3737
// whether to enable websocket proxy
3838
enableWebsocketProxy bool
@@ -108,13 +108,13 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
108108
if o.flashblocks {
109109
peers = append(peers, "flashblocks-rpc")
110110
}
111-
111+
112112
// Only enable bproxy if flashblocks is enabled (since flashblocks-rpc is the only service that needs it)
113113
if o.flashblocks {
114114
svcManager.AddService("bproxy", &BProxy{
115-
TargetAuthrpc: externalBuilderRef,
116-
Peers: peers,
117-
Flashblocks: o.flashblocks,
115+
TargetAuthrpc: externalBuilderRef,
116+
Peers: peers,
117+
Flashblocks: o.flashblocks,
118118
FlashblocksBuilderURL: flashblocksBuilderURLRef,
119119
})
120120
}
@@ -146,7 +146,6 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
146146
})
147147
}
148148

149-
150149
if o.flashblocks {
151150
// Determine which service to use for flashblocks websocket connection
152151
flashblocksWSService := "rollup-boost"
@@ -158,8 +157,8 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
158157

159158
svcManager.AddService("flashblocks-rpc", &FlashblocksRPC{
160159
FlashblocksWSService: flashblocksWSService,
161-
BaseOverlay: o.baseOverlay,
162-
UseWebsocketProxy: useWebsocketProxy,
160+
BaseOverlay: o.baseOverlay,
161+
UseWebsocketProxy: useWebsocketProxy,
163162
})
164163
}
165164

@@ -176,8 +175,10 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
176175
MaxChannelDuration: o.batcherMaxChannelDuration,
177176
})
178177

179-
if ctx.ContenderEnabled {
180-
svcManager.AddService("contender", &Contender{})
178+
if ctx.Contender.Enabled {
179+
svcManager.AddService("contender", &Contender{
180+
ExtraArgs: ctx.Contender.ExtraArgs,
181+
})
181182
}
182183

183184
return svcManager

0 commit comments

Comments
 (0)