Skip to content
Merged
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
11 changes: 10 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var labels playground.MapStringFlag
var disableLogs bool
var platform string
var contenderEnabled bool
var contenderArgs []string

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

cookCmd.AddCommand(recipeCmd)
}
Expand Down Expand Up @@ -226,7 +228,14 @@ func runIt(recipe playground.Recipe) error {
return err
}

svcManager := recipe.Apply(&playground.ExContext{LogLevel: logLevel, ContenderEnabled: contenderEnabled}, artifacts)
// if contender.tps is set, assume contender is enabled
svcManager := recipe.Apply(&playground.ExContext{
LogLevel: logLevel,
Contender: &playground.ContenderContext{
Enabled: contenderEnabled,
ExtraArgs: contenderArgs,
},
}, artifacts)
if err := svcManager.Validate(); err != nil {
return fmt.Errorf("failed to validate manifest: %w", err)
}
Expand Down
140 changes: 115 additions & 25 deletions playground/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ func (o *OpRbuilder) Name() string {

type FlashblocksRPC struct {
FlashblocksWSService string
BaseOverlay bool
UseWebsocketProxy bool // Whether to add /ws path for websocket proxy
BaseOverlay bool
UseWebsocketProxy bool // Whether to add /ws path for websocket proxy
}

func (f *FlashblocksRPC) Run(service *Service, ctx *ExContext) {
Expand Down Expand Up @@ -130,19 +130,19 @@ func (f *FlashblocksRPC) Run(service *Service, ctx *ExContext) {
)
}
service.WithArgs(
"--authrpc.port", `{{Port "authrpc" 8551}}`,
"--authrpc.addr", "0.0.0.0",
"--authrpc.jwtsecret", "/data/jwtsecret",
"--http",
"--http.addr", "0.0.0.0",
"--http.port", `{{Port "http" 8545}}`,
"--chain", "/data/l2-genesis.json",
"--datadir", "/data_op_reth",
"--disable-discovery",
"--color", "never",
"--metrics", `0.0.0.0:{{Port "metrics" 9090}}`,
"--port", `{{Port "rpc" 30303}}`,
).
"--authrpc.port", `{{Port "authrpc" 8551}}`,
"--authrpc.addr", "0.0.0.0",
"--authrpc.jwtsecret", "/data/jwtsecret",
"--http",
"--http.addr", "0.0.0.0",
"--http.port", `{{Port "http" 8545}}`,
"--chain", "/data/l2-genesis.json",
"--datadir", "/data_op_reth",
"--disable-discovery",
"--color", "never",
"--metrics", `0.0.0.0:{{Port "metrics" 9090}}`,
"--port", `{{Port "rpc" 30303}}`,
).
WithArtifact("/data/jwtsecret", "jwtsecret").
WithArtifact("/data/l2-genesis.json", "l2-genesis.json").
WithVolume("data", "/data_flashblocks_rpc")
Expand All @@ -159,13 +159,13 @@ func (f *FlashblocksRPC) Name() string {
}

type BProxy struct {
TargetAuthrpc string
Peers []string
Flashblocks bool
TargetAuthrpc string
Peers []string
Flashblocks bool
FlashblocksBuilderURL string
}

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

type Contender struct {
ExtraArgs []string
}

func (c *Contender) Name() string {
return "contender"
}

// parse "key=value" OR "key value"; remainder after first space is the value (may contain spaces)
func parseKV(s string) (name, val string, hasVal, usedEq bool) {
s = strings.TrimSpace(s)
if s == "" {
return "", "", false, false
}
eq := strings.IndexByte(s, '=')
ws := indexWS(s)

// prefer '=' if it appears before any whitespace
if eq > 0 && (ws == -1 || eq < ws) {
return strings.TrimSpace(s[:eq]), strings.TrimSpace(s[eq+1:]), true, true
}
if ws == -1 {
return s, "", false, false
}
return strings.TrimSpace(s[:ws]), strings.TrimSpace(s[ws+1:]), true, false
}

func indexWS(s string) int {
for i, r := range s {
if r == ' ' || r == '\t' {
return i
}
}
return -1
}

func (c *Contender) Run(service *Service, ctx *ExContext) {
args := []string{
"spam",
"-l", // loop indefinitely
"--min-balance", "10 ether", // give each spammer 10 ether (sender must have 100 ether because default number of spammers is 10)
"-r", Connect("el", "http"), // connect to whatever EL node is available
"--tps", "20", // send 20 txs per second
type opt struct {
name string
val string
hasVal bool
}
defaults := []opt{
{name: "-l"},
{name: "--min-balance", val: "10 ether", hasVal: true},
{name: "-r", val: Connect("el", "http"), hasVal: true},
{name: "--tps", val: "20", hasVal: true},
}

// Parse extras and track seen flags
type extra struct {
name string
val string
hasVal bool
usedEq bool
}
var extras []extra
seen := map[string]bool{}

for _, s := range c.ExtraArgs {
name, val, hasVal, usedEq := parseKV(s)
if name == "" {
continue
}
extras = append(extras, extra{name, val, hasVal, usedEq})
seen[name] = true
}

// Minimal conflict example: --loops overrides default "-l"
conflict := func(flag string) bool {
if seen[flag] {
return true
}
if flag == "-l" && seen["--loops"] {
return true
}
return false
}

args := []string{"spam"}

// Add defaults unless overridden
for _, d := range defaults {
if conflict(d.name) {
continue
}
args = append(args, d.name)
if d.hasVal {
args = append(args, d.val)
}
}

// Append extras verbatim, preserving "=" vs space
for _, e := range extras {
if !e.hasVal {
args = append(args, e.name)
continue
}
if e.usedEq {
args = append(args, e.name+"="+e.val)
} else {
args = append(args, e.name, e.val)
}
}

service.WithImage("flashbots/contender").
WithTag("latest").
WithArgs(args...).
Expand Down
10 changes: 9 additions & 1 deletion playground/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ func (l *LogLevel) Unmarshal(s string) error {
return nil
}

type ContenderContext struct {
// Run `contender spam` automatically once all playground services are running.
Enabled bool

// Provide additional args to contender's CLI
ExtraArgs []string
}

// Execution context
type ExContext struct {
LogLevel LogLevel
Expand All @@ -83,7 +91,7 @@ type ExContext struct {
// TODO: Extend for CL nodes too
Bootnode *BootnodeRef

ContenderEnabled bool
Contender *ContenderContext
}

type BootnodeRef struct {
Expand Down
6 changes: 4 additions & 2 deletions playground/recipe_buildernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ func (b *BuilderNetRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest
})
}

if ctx.ContenderEnabled {
svcManager.AddService("contender", &Contender{})
if ctx.Contender.Enabled {
svcManager.AddService("contender", &Contender{
ExtraArgs: ctx.Contender.ExtraArgs,
})
}

return svcManager
Expand Down
6 changes: 4 additions & 2 deletions playground/recipe_l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
})
}

if ctx.ContenderEnabled {
svcManager.AddService("contender", &Contender{})
if ctx.Contender.Enabled {
svcManager.AddService("contender", &Contender{
ExtraArgs: ctx.Contender.ExtraArgs,
})
}

return svcManager
Expand Down
21 changes: 11 additions & 10 deletions playground/recipe_opstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type OpRecipe struct {
flashblocksBuilderURL string

// Indicates that flashblocks-rpc should use base image
baseOverlay bool
baseOverlay bool

// whether to enable websocket proxy
enableWebsocketProxy bool
Expand Down Expand Up @@ -108,13 +108,13 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
if o.flashblocks {
peers = append(peers, "flashblocks-rpc")
}

// Only enable bproxy if flashblocks is enabled (since flashblocks-rpc is the only service that needs it)
if o.flashblocks {
svcManager.AddService("bproxy", &BProxy{
TargetAuthrpc: externalBuilderRef,
Peers: peers,
Flashblocks: o.flashblocks,
TargetAuthrpc: externalBuilderRef,
Peers: peers,
Flashblocks: o.flashblocks,
FlashblocksBuilderURL: flashblocksBuilderURLRef,
})
}
Expand Down Expand Up @@ -146,7 +146,6 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
})
}


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

svcManager.AddService("flashblocks-rpc", &FlashblocksRPC{
FlashblocksWSService: flashblocksWSService,
BaseOverlay: o.baseOverlay,
UseWebsocketProxy: useWebsocketProxy,
BaseOverlay: o.baseOverlay,
UseWebsocketProxy: useWebsocketProxy,
})
}

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

if ctx.ContenderEnabled {
svcManager.AddService("contender", &Contender{})
if ctx.Contender.Enabled {
svcManager.AddService("contender", &Contender{
ExtraArgs: ctx.Contender.ExtraArgs,
})
}

return svcManager
Expand Down