@@ -101,8 +101,8 @@ func (o *OpRbuilder) Name() string {
101101
102102type 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
108108func (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
161161type 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
815815type Contender struct {
816+ ExtraArgs []string
816817}
817818
818819func (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+
822851func (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 ... ).
0 commit comments