Skip to content

Commit

Permalink
feat: implement core functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
0x416e746f6e committed Mar 14, 2024
1 parent 08dcbd5 commit 06e96ec
Show file tree
Hide file tree
Showing 28 changed files with 1,565 additions and 0 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: release

on:
workflow_dispatch:
push:
tags:
- "v*"

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4

- name: setup go dependencies
uses: actions/setup-go@v5
with:
go-version: "1.22"

- name: setup quemu
uses: docker/setup-qemu-action@v3

- name: setup docker buildx
uses: docker/setup-buildx-action@v3

- name: login to docker hub
uses: docker/login-action@v2
with:
username: ${{ secrets.FLASHBOTS_DOCKERHUB_USERNAME }}
password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }}

- name: login to ghcr
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: build and publish backend release
uses: goreleaser/goreleaser-action@v5
with:
args: release --clean
distribution: goreleaser
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56 changes: 56 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
env:
- CGO_ENABLED=0

builds:
- main: ./cmd
ldflags:
- -s
- -w
- -X main.version={{ .Version }}
targets:
- linux_amd64
- linux_arm64

archives:
- id: zip
format: zip
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
files:
- none*

checksum:
name_template: 'checksums.txt'

release:
prerelease: auto

dockers:
- dockerfile: Dockerfile.goreleaser
goarch: amd64
goos: linux
use: buildx
build_flag_templates:
- --platform=linux/amd64
image_templates:
- "flashbots/node-monitor:{{ .Tag }}-amd64"
- "ghcr.io/flashbots/node-monitor:{{ .Tag }}-amd64"

- dockerfile: Dockerfile.goreleaser
goarch: arm64
goos: linux
use: buildx
build_flag_templates:
- --platform=linux/arm64
image_templates:
- "flashbots/node-monitor:{{ .Tag }}-arm64"
- "ghcr.io/flashbots/node-monitor:{{ .Tag }}-arm64"

docker_manifests:
- name_template: "flashbots/node-monitor:{{ .Tag }}"
image_templates:
- "flashbots/node-monitor:{{ .Tag }}-amd64"
- "flashbots/node-monitor:{{ .Tag }}-arm64"
- name_template: "ghcr.io/flashbots/node-monitor:{{ .Tag }}"
image_templates:
- "ghcr.io/flashbots/node-monitor:{{ .Tag }}-amd64"
- "ghcr.io/flashbots/node-monitor:{{ .Tag }}-arm64"
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# stage: build ---------------------------------------------------------

FROM golang:1.22-alpine as build

RUN apk add --no-cache gcc musl-dev linux-headers

WORKDIR /go/src/github.com/flashbots/node-monitor

COPY go.* ./
RUN go mod download

COPY . .

RUN go build -o bin/node-monitor -ldflags "-s -w" github.com/flashbots/node-monitor/cmd

# stage: run -----------------------------------------------------------

FROM alpine

RUN apk add --no-cache ca-certificates

WORKDIR /app

COPY --from=build /go/src/github.com/flashbots/node-monitor/bin/node-monitor ./node-monitor

ENTRYPOINT ["/app/node-monitor"]
9 changes: 9 additions & 0 deletions Dockerfile.goreleaser
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# stage: run

FROM gcr.io/distroless/static-debian12 as runner

WORKDIR /app

COPY node-monitor ./

ENTRYPOINT [ "./node-monitor" ]
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
VERSION := $(shell git describe --tags --always --dirty="-dev" --match "v*.*.*" || echo "development" )
VERSION := $(VERSION:v%=%)

.PHONY: build
build:
@CGO_ENABLED=0 go build \
-ldflags "-X main.version=${VERSION}" \
-o ./bin/node-monitor \
github.com/flashbots/node-monitor/cmd

.PHONY: snapshot
snapshot:
@goreleaser release --snapshot --clean
Empty file added bin/.gitkeep
Empty file.
74 changes: 74 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"fmt"
"os"

"github.com/flashbots/node-monitor/config"
"github.com/flashbots/node-monitor/logutils"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
)

var (
version = "development"
)

func main() {
cfg := &config.Config{}

flags := []cli.Flag{
&cli.StringFlag{
Destination: &cfg.Log.Level,
EnvVars: []string{"NODE_MONITOR_LOG_LEVEL"},
Name: "log-level",
Usage: "logging level",
Value: "info",
},

&cli.StringFlag{
Destination: &cfg.Log.Mode,
EnvVars: []string{"NODE_MONITOR_LOG_MODE"},
Name: "log-mode",
Usage: "logging mode",
Value: "prod",
},
}

commands := []*cli.Command{
CommandServe(cfg),
}

app := &cli.App{
Name: "node-monitor",
Usage: "Monitor blockchain nodes",
Version: version,

Flags: flags,
Commands: commands,
DefaultCommand: commands[0].Name,

Before: func(_ *cli.Context) error {
// setup logger
l, err := logutils.NewLogger(&cfg.Log)
if err != nil {
return err
}
zap.ReplaceGlobals(l)

return nil
},

Action: func(clictx *cli.Context) error {
return cli.ShowAppHelp(clictx)
},
}

defer func() {
zap.L().Sync() //nolint:errcheck
}()
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "\nFailed with error:\n\n%s\n\n", err.Error())
os.Exit(1)
}
}
123 changes: 123 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package main

import (
"errors"
"fmt"
"slices"
"strings"
"time"

"github.com/flashbots/node-monitor/config"
"github.com/flashbots/node-monitor/server"
"github.com/flashbots/node-monitor/utils"
"github.com/urfave/cli/v2"
)

const (
categoryEth = "ETHEREUM:"
categoryServer = "SERVER:"
)

var (
ErrUnexpectedExecutionEndpoint = errors.New("unexpected execution endpoint rpc (must look like `id=127.0.0.1:8546`)")
)

func CommandServe(cfg *config.Config) *cli.Command {
executionEndpoints := &cli.StringSlice{}
externalExecutionEndpoints := &cli.StringSlice{}

ethFlags := []cli.Flag{
&cli.StringSliceFlag{
Category: categoryEth,
Destination: executionEndpoints,
EnvVars: []string{"NODE_MONITOR_ETH_EL_ENDPOINTS"},
Name: "eth-el-endpoints",
Usage: "eth execution endpoints (websocket) in the format of `id=hostname:port`",
},

&cli.StringSliceFlag{
Category: categoryEth,
Destination: externalExecutionEndpoints,
EnvVars: []string{"NODE_MONITOR_ETH_EXT_EL_ENDPOINTS"},
Name: "eth-ext-el-endpoints",
Usage: "external eth execution endpoints (websocket) in the format of `id=hostname:port`",
},

&cli.DurationFlag{
Category: categoryEth,
Destination: &cfg.Eth.ResubscribeInterval,
EnvVars: []string{"NODE_MONITOR_RESUBSCRIBE_INTERVAL"},
Name: "resubscribe-interval",
Usage: "an `interval` at which the monitor will try to (re-)subscribe to node events",
Value: 15 * time.Second,
},
}

serverFlags := []cli.Flag{
&cli.StringFlag{
Category: categoryServer,
Destination: &cfg.Server.ListenAddress,
EnvVars: []string{"NODE_MONITOR_LISTEN_ADDRESS"},
Name: "listen-address",
Usage: "`host:port` for the server to listen on",
Value: "0.0.0.0:8080",
},

&cli.StringFlag{
Category: categoryServer,
Destination: &cfg.Server.Name,
EnvVars: []string{"NODE_MONITOR_SERVER_NAME"},
Name: "server-name",
Usage: "service `name` to report in prometheus metrics",
Value: "node-monitor",
},
}

flags := slices.Concat(
ethFlags,
serverFlags,
)

return &cli.Command{
Name: "serve",
Usage: "run the monitor server",
Flags: flags,

Before: func(ctx *cli.Context) error {
executionEndpoints := slices.Concat(
executionEndpoints.Value(),
externalExecutionEndpoints.Value(),
)
for idx, ee := range executionEndpoints {
ee = strings.TrimSpace(ee)
parts := strings.Split(ee, "=")
if len(parts) != 2 {
return fmt.Errorf("%w: %s", ErrUnexpectedExecutionEndpoint, ee)
}
for idx, part := range parts {
parts[idx] = strings.TrimSpace(part)
}
id := parts[0]
uri := parts[1]
parsed, err := utils.ParseRawURI(uri)
if err != nil {
return err
}
if parsed.Scheme == "" {
parsed.Scheme = "ws"
}
executionEndpoints[idx] = fmt.Sprintf("%s=%s", id, parsed.String())
}
cfg.Eth.ExecutionEndpoints = executionEndpoints
return nil
},

Action: func(_ *cli.Context) error {
s, err := server.New(cfg)
if err != nil {
return err
}
return s.Run()
},
}
}
7 changes: 7 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package config

type Config struct {
Eth Eth `yaml:"eth"`
Log Log `yaml:"log"`
Server Server `yaml:"server"`
}
9 changes: 9 additions & 0 deletions config/eth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package config

import "time"

type Eth struct {
ExecutionEndpoints []string `yaml:"execution_endpoints"`
ExternalExecutionEndpoints []string `yaml:"external_execution_endpoints"`
ResubscribeInterval time.Duration `yaml:"resubscribe_interval"`
}
6 changes: 6 additions & 0 deletions config/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package config

type Log struct {
Level string `yaml:"level"`
Mode string `yaml:"mode"`
}
6 changes: 6 additions & 0 deletions config/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package config

type Server struct {
ListenAddress string `yaml:"listen_address"`
Name string `yaml:"name"`
}
Loading

0 comments on commit 06e96ec

Please sign in to comment.