Skip to content

Add Flashblocks rpc #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 3, 2025
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
15 changes: 14 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var withPrometheus bool
var networkName string
var labels playground.MapStringFlag
var disableLogs bool
var platform string

var rootCmd = &cobra.Command{
Use: "playground",
Expand Down Expand Up @@ -177,6 +178,7 @@ func main() {
recipeCmd.Flags().StringVar(&networkName, "network", "", "network name")
recipeCmd.Flags().Var(&labels, "labels", "list of labels to apply to the resources")
recipeCmd.Flags().BoolVar(&disableLogs, "disable-logs", false, "disable logs")
recipeCmd.Flags().StringVar(&platform, "platform", "", "docker platform to use")

cookCmd.AddCommand(recipeCmd)
}
Expand Down Expand Up @@ -255,7 +257,18 @@ func runIt(recipe playground.Recipe) error {
}
}

dockerRunner, err := playground.NewLocalRunner(artifacts.Out, svcManager, overrides, interactive, !bindExternal, networkName, labels, !disableLogs)
cfg := &playground.RunnerConfig{
Out: artifacts.Out,
Manifest: svcManager,
Overrides: overrides,
Interactive: interactive,
BindHostPortsLocally: !bindExternal,
NetworkName: networkName,
Labels: labels,
LogInternally: !disableLogs,
Platform: platform,
}
dockerRunner, err := playground.NewLocalRunner(cfg)
if err != nil {
return fmt.Errorf("failed to create docker runner: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions playground/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func init() {
register(&BuilderHubMockProxy{})
register(&nullService{})
register(&OpRbuilder{})
register(&FlashblocksRPC{})
}

func FindComponent(name string) ServiceGen {
Expand Down
39 changes: 39 additions & 0 deletions playground/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,45 @@ func (o *OpRbuilder) Name() string {
return "op-rbuilder"
}

type FlashblocksRPC struct {
FlashblocksWSService string
}

func (f *FlashblocksRPC) Run(service *Service, ctx *ExContext) {
service.WithImage("flashbots/flashblocks-rpc").
WithTag("sha-7caffb9").
WithArgs(
"node",
"--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}}`,
"--flashblocks.enabled",
"--flashblocks.websocket-url", ConnectWs(f.FlashblocksWSService, "flashblocks"),
).
WithArtifact("/data/jwtsecret", "jwtsecret").
WithArtifact("/data/l2-genesis.json", "l2-genesis.json").
WithVolume("data", "/data_flashblocks_rpc")

if ctx.Bootnode != nil {
service.WithArgs(
"--trusted-peers", ctx.Bootnode.Connect(),
)
}
}

func (f *FlashblocksRPC) Name() string {
return "flashblocks-rpc"
}

type OpBatcher struct {
L1Node string
L2Node string
Expand Down
69 changes: 44 additions & 25 deletions playground/local_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ type LocalRunner struct {

// logInternally outputs the logs of the service to the artifacts folder
logInternally bool

// platform is the docker platform to use for the services
platform string
}

type task struct {
Expand Down Expand Up @@ -109,24 +112,35 @@ func newDockerClient() (*client.Client, error) {
return client, nil
}

// TODO: add a runner config struct
func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string, interactive bool, bindHostPortsLocally bool, networkName string, labels map[string]string, logInternally bool) (*LocalRunner, error) {
type RunnerConfig struct {
Out *output
Manifest *Manifest
Overrides map[string]string
Interactive bool
BindHostPortsLocally bool
NetworkName string
Labels map[string]string
LogInternally bool
Platform string
}

func NewLocalRunner(cfg *RunnerConfig) (*LocalRunner, error) {
client, err := newDockerClient()
if err != nil {
return nil, fmt.Errorf("failed to create docker client: %w", err)
}

// merge the overrides with the manifest overrides
if overrides == nil {
overrides = make(map[string]string)
if cfg.Overrides == nil {
cfg.Overrides = make(map[string]string)
}
for k, v := range manifest.overrides {
overrides[k] = v
for k, v := range cfg.Manifest.overrides {
cfg.Overrides[k] = v
}

// Create the concrete instances to run
instances := []*instance{}
for _, service := range manifest.Services {
for _, service := range cfg.Manifest.Services {
component := FindComponent(service.ComponentName)
if component == nil {
return nil, fmt.Errorf("component not found '%s'", service.ComponentName)
Expand All @@ -135,8 +149,8 @@ func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string
service: service,
component: component,
}
if logInternally {
log_output, err := out.LogOutput(service.Name)
if cfg.LogInternally {
log_output, err := cfg.Out.LogOutput(service.Name)
if err != nil {
return nil, fmt.Errorf("error getting log output: %w", err)
}
Expand All @@ -160,11 +174,11 @@ func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string
return nil, fmt.Errorf("service '%s' must implement the ReleaseService interface", ss.Name)
}
releaseArtifact := releaseService.ReleaseArtifact()
bin, err := DownloadRelease(out.homeDir, releaseArtifact)
bin, err := DownloadRelease(cfg.Out.homeDir, releaseArtifact)
if err != nil {
return nil, fmt.Errorf("failed to download release artifact for service '%s': %w", ss.Name, err)
}
overrides[ss.Name] = bin
cfg.Overrides[ss.Name] = bin
}
}

Expand All @@ -173,7 +187,7 @@ func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string
// - a path to an executable: In that case, we need to run it on the host machine
// and use the override map <- We only check this case, and if it is not a path, we assume
// it is a docker image. If it is not a docker image either, the error will be catched during the execution
for k, v := range overrides {
for k, v := range cfg.Overrides {
if _, err := os.Stat(v); err != nil {
// this is a path to an executable, remove it from the overrides since we
// assume it s a docker image and add it to manifest
Expand All @@ -182,45 +196,46 @@ func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string
return nil, fmt.Errorf("invalid override docker image %s, expected image:tag", v)
}

srv := manifest.MustGetService(k)
srv := cfg.Manifest.MustGetService(k)
srv.Image = parts[0]
srv.Tag = parts[1]

delete(overrides, k)
delete(cfg.Overrides, k)
continue
}
}

tasks := map[string]*task{}
for _, svc := range manifest.Services {
for _, svc := range cfg.Manifest.Services {
tasks[svc.Name] = &task{
status: taskStatusPending,
logs: nil,
}
}

if networkName == "" {
networkName = defaultNetworkName
if cfg.NetworkName == "" {
cfg.NetworkName = defaultNetworkName
}
d := &LocalRunner{
out: out,
manifest: manifest,
out: cfg.Out,
manifest: cfg.Manifest,
client: client,
reservedPorts: map[int]bool{},
overrides: overrides,
overrides: cfg.Overrides,
handles: []*exec.Cmd{},
tasks: tasks,
taskUpdateCh: make(chan struct{}),
exitErr: make(chan error, 2),
bindHostPortsLocally: bindHostPortsLocally,
bindHostPortsLocally: cfg.BindHostPortsLocally,
sessionID: uuid.New().String(),
networkName: networkName,
networkName: cfg.NetworkName,
instances: instances,
labels: labels,
logInternally: logInternally,
labels: cfg.Labels,
logInternally: cfg.LogInternally,
platform: cfg.Platform,
}

if interactive {
if cfg.Interactive {
go d.printStatus()

select {
Expand Down Expand Up @@ -656,6 +671,10 @@ func (d *LocalRunner) toDockerComposeService(s *Service) (map[string]interface{}
"labels": labels,
}

if d.platform != "" {
service["platform"] = d.platform
}

if len(envs) > 0 {
service["environment"] = envs
}
Expand Down
7 changes: 7 additions & 0 deletions playground/recipe_opstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
FlashblocksBuilderURL: flashblocksBuilderURLRef,
})
}

if o.flashblocks {
svcManager.AddService("flashblocks-rpc", &FlashblocksRPC{
FlashblocksWSService: "rollup-boost", // rollup-boost provides the websocket stream
})
}

svcManager.AddService("op-node", &OpNode{
L1Node: "el",
L1Beacon: "beacon",
Expand Down
Loading