From b81c35c7468edfcf843045560f64b23a82d3c08c Mon Sep 17 00:00:00 2001
From: Chris Hager
Date: Thu, 24 Apr 2025 13:35:31 +0200
Subject: [PATCH 1/4] Persistent TLS certificates
---
cmd/receiver-proxy/main.go | 27 +++++++++++++++++++++++++--
go.mod | 2 +-
go.sum | 4 ++--
proxy/receiver_proxy.go | 5 ++++-
proxy/receiver_proxy_test.go | 17 ++++++++++-------
5 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/cmd/receiver-proxy/main.go b/cmd/receiver-proxy/main.go
index 1acff0a..c81fd2d 100644
--- a/cmd/receiver-proxy/main.go
+++ b/cmd/receiver-proxy/main.go
@@ -23,6 +23,9 @@ const (
flagSystemListenAddr = "system-listen-addr"
flagCertListenAddr = "cert-listen-addr"
flagMaxUserRPS = "max-user-requests-per-second"
+
+ flagCertPath = "cert-path"
+ flagCertKeyPath = "cert-key-path"
)
var flags = []cli.Flag{
@@ -111,6 +114,16 @@ var flags = []cli.Flag{
Usage: "generated certificate hosts",
EnvVars: []string{"CERT_HOSTS"},
},
+ &cli.DurationFlag{
+ Name: flagCertPath,
+ Usage: "path where to store the generated certificate",
+ EnvVars: []string{"CERT_PATH"},
+ },
+ &cli.StringFlag{
+ Name: flagCertKeyPath,
+ Usage: "path where to store the generated certificate key",
+ EnvVars: []string{"CERT_KEY_PATH"},
+ },
// Logging, metrics and debug
&cli.StringFlag{
@@ -216,8 +229,7 @@ func runMain(cCtx *cli.Context) error {
builderEndpoint := cCtx.String("builder-endpoint")
rpcEndpoint := cCtx.String("rpc-endpoint")
- certDuration := cCtx.Duration("cert-duration")
- certHosts := cCtx.StringSlice("cert-hosts")
+
builderConfigHubEndpoint := cCtx.String("builder-confighub-endpoint")
archiveEndpoint := cCtx.String("orderflow-archive-endpoint")
flashbotsSignerStr := cCtx.String("flashbots-orderflow-signer-address")
@@ -227,10 +239,21 @@ func runMain(cCtx *cli.Context) error {
maxUserRPS := cCtx.Int(flagMaxUserRPS)
+ certDuration := cCtx.Duration("cert-duration")
+ certHosts := cCtx.StringSlice("cert-hosts")
+ certPath := cCtx.String(flagCertPath)
+ certKeyPath := cCtx.String(flagCertKeyPath)
+ if certPath == "" || certKeyPath == "" {
+ log.Error("cert-path and cert-key-path must be set")
+ return nil
+ }
+
proxyConfig := &proxy.ReceiverProxyConfig{
ReceiverProxyConstantConfig: proxy.ReceiverProxyConstantConfig{Log: log, FlashbotsSignerAddress: flashbotsSignerAddress},
CertValidDuration: certDuration,
CertHosts: certHosts,
+ CertPath: certPath,
+ CertKeyPath: certKeyPath,
BuilderConfigHubEndpoint: builderConfigHubEndpoint,
ArchiveEndpoint: archiveEndpoint,
ArchiveConnections: connectionsPerPeer,
diff --git a/go.mod b/go.mod
index a40fa87..5298cc6 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
github.com/VictoriaMetrics/metrics v1.35.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/ethereum/go-ethereum v1.15.5
- github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5
+ github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5
github.com/google/uuid v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/stretchr/testify v1.10.0
diff --git a/go.sum b/go.sum
index f4b8622..c4a8ee3 100644
--- a/go.sum
+++ b/go.sum
@@ -32,10 +32,10 @@ github.com/ethereum/go-ethereum v1.15.5 h1:Fo2TbBWC61lWVkFw9tsMoHCNX1ndpuaQBRJ8H
github.com/ethereum/go-ethereum v1.15.5/go.mod h1:1LG2LnMOx2yPRHR/S+xuipXH29vPr6BIH6GElD8N/fo=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
-github.com/flashbots/go-utils v0.10.0 h1:75XWewRO5GIhdLn8+vqdzzuoqJh+j8wN54A++Id7W0Y=
-github.com/flashbots/go-utils v0.10.0/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5 h1:oEfjmk2NQ/FtX+1h1Rx4PMpJtbNAa6T/l6otbUmzZp8=
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
+github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5 h1:F6pO8NC+JSG8jSFzTOAjP4TaR+CwZKygQ4vSLT+XPPw=
+github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
diff --git a/proxy/receiver_proxy.go b/proxy/receiver_proxy.go
index 91c1bed..b15150a 100644
--- a/proxy/receiver_proxy.go
+++ b/proxy/receiver_proxy.go
@@ -76,8 +76,11 @@ type ReceiverProxyConstantConfig struct {
type ReceiverProxyConfig struct {
ReceiverProxyConstantConfig
+
CertValidDuration time.Duration
CertHosts []string
+ CertPath string
+ CertKeyPath string
BuilderConfigHubEndpoint string
ArchiveEndpoint string
@@ -99,7 +102,7 @@ func NewReceiverProxy(config ReceiverProxyConfig) (*ReceiverProxy, error) {
return nil, err
}
- cert, key, err := utils_tls.GenerateTLS(config.CertValidDuration, config.CertHosts)
+ cert, key, err := utils_tls.GetOrGenerateTLS(config.CertPath, config.CertKeyPath, config.CertValidDuration, config.CertHosts)
if err != nil {
return nil, err
}
diff --git a/proxy/receiver_proxy_test.go b/proxy/receiver_proxy_test.go
index 1f40571..8ff248e 100644
--- a/proxy/receiver_proxy_test.go
+++ b/proxy/receiver_proxy_test.go
@@ -74,11 +74,11 @@ func (setup *OrderflowProxyTestSetup) Close() {
setup.proxy.Stop()
}
-func StartTestOrderflowProxy(name string) (*OrderflowProxyTestSetup, error) {
+func StartTestOrderflowProxy(name, certPath, certKeyPath string) (*OrderflowProxyTestSetup, error) {
localBuilderRequests := make(chan *RequestData, 1)
localBuilderServer := ServeHTTPRequestToChan(localBuilderRequests)
- proxy := createProxy(localBuilderServer.URL, name)
+ proxy := createProxy(localBuilderServer.URL, name, certPath, certKeyPath)
publicProxyServer := &http.Server{ //nolint:gosec
Handler: proxy.SystemHandler,
TLSConfig: proxy.TLSConfig(),
@@ -169,7 +169,7 @@ func TestMain(m *testing.M) {
defer archiveServer.Close()
for i := range 3 {
- proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i))
+ proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i), "/tmp/orderflow-proxy-test.cert", "/tmp/orderflow-proxy-test.key")
proxies = append(proxies, proxy)
if err != nil {
panic(err)
@@ -184,7 +184,7 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
-func createProxy(localBuilder, name string) *ReceiverProxy {
+func createProxy(localBuilder, name, certPath, certKeyPath string) *ReceiverProxy {
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
proxy, err := NewReceiverProxy(ReceiverProxyConfig{
ReceiverProxyConstantConfig: ReceiverProxyConstantConfig{
@@ -192,8 +192,11 @@ func createProxy(localBuilder, name string) *ReceiverProxy {
Name: name,
FlashbotsSignerAddress: flashbotsSigner.Address(),
},
- CertValidDuration: time.Hour * 24,
- CertHosts: []string{"localhost", "127.0.0.1"},
+ CertValidDuration: time.Hour * 24,
+ CertHosts: []string{"localhost", "127.0.0.1"},
+ CertPath: certPath,
+ CertKeyPath: certKeyPath,
+
BuilderConfigHubEndpoint: builderHub.URL,
ArchiveEndpoint: archiveServer.URL,
LocalBuilderEndpoint: localBuilder,
@@ -551,7 +554,7 @@ func TestProxyBidSubsidiseBlockCall(t *testing.T) {
}
func TestBuilderNetRootCall(t *testing.T) {
- proxy, err := StartTestOrderflowProxy("1")
+ proxy, err := StartTestOrderflowProxy("1", "/tmp/orderflow-proxy-test.cert", "/tmp/orderflow-proxy-test.key")
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/", nil)
From 3afa50de4b3730867fe58bff6d3d4600c14b9773 Mon Sep 17 00:00:00 2001
From: Chris Hager
Date: Thu, 24 Apr 2025 13:41:31 +0200
Subject: [PATCH 2/4] docs in html
---
cmd/receiver-proxy/main.go | 4 ++--
proxy/html/index.html | 39 ++++++++++++++++++++++----------------
2 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/cmd/receiver-proxy/main.go b/cmd/receiver-proxy/main.go
index c81fd2d..f417348 100644
--- a/cmd/receiver-proxy/main.go
+++ b/cmd/receiver-proxy/main.go
@@ -2,6 +2,7 @@ package main
import (
"context"
+ "errors"
"log"
"net/http"
"net/http/pprof"
@@ -244,8 +245,7 @@ func runMain(cCtx *cli.Context) error {
certPath := cCtx.String(flagCertPath)
certKeyPath := cCtx.String(flagCertKeyPath)
if certPath == "" || certKeyPath == "" {
- log.Error("cert-path and cert-key-path must be set")
- return nil
+ return errors.New("cert-path and cert-key-path must be set")
}
proxyConfig := &proxy.ReceiverProxyConfig{
diff --git a/proxy/html/index.html b/proxy/html/index.html
index 7f18cd6..7f00424 100644
--- a/proxy/html/index.html
+++ b/proxy/html/index.html
@@ -120,24 +120,36 @@ Sending Orderflow to BuilderNet
Example curl Request
curl https://_BUILDERNET_INSTANCE_ \
--cacert builder-cert.pem \ # or using --insecure
+ --header 'Content-Type: application/json' \
--header 'X-Flashbots-Signature: _public_key_address_:_signature_' \
--data '{
- "jsonrpc":"2.0",
- "method":"eth_sendRawTransaction",
- "params":["0x000000..."],
- "id":1
- }'
- See also the full documentation at buildernet.org/docs/send-orderflow
-
-
- Note: Currently, requests are rate-limited to 3 requests / IP / second. This is expected to be raised soon.
-
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "eth_sendBundle",
+ "params": [{
+ "txs": [
+ "0x100000...", "0x200000..."
+ ],
+ "blockNumber": "0x1361bd3"
+ }]
+ }'
+
+ See also the full documentation at
+
+
Downloading the TLS certificate
You can get the TLS certificate through a TEE-attested channel (see "TEE Proof Validation" below), or download it directly with curl:
curl -w %{certs} -k https://_BUILDERNET_INSTANCE_
+
+ Note: Currently, requests are rate-limited to 3 requests / IP / second. This is expected to be raised soon.
+
+
@@ -145,15 +157,10 @@ Downloading the TLS certificate
Instance TLS Certificate
- BuilderNet instances create (and rotate) their own unique TLS certificate, and use it to prove their identity and to encrypt incoming traffic.
+ BuilderNet instances create their own unique TLS certificate, and use it to prove their identity and to encrypt incoming traffic.
This is the TLS certificate of this specific instance:
{{ .Cert }}
-
-
- Note: The certificate changes on every restart of the server. This is a feature, not a bug, as the certificate
- represents the unique identity of this server, and the private key never leaves the in-process memory.
-
From 1c54971beca63ddab0cdb4c3d5756a77f33683ab Mon Sep 17 00:00:00 2001
From: Chris Hager
Date: Fri, 25 Apr 2025 10:25:09 +0200
Subject: [PATCH 3/4] tests: use temp dirs
---
proxy/receiver_proxy_test.go | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/proxy/receiver_proxy_test.go b/proxy/receiver_proxy_test.go
index 8ff248e..b827350 100644
--- a/proxy/receiver_proxy_test.go
+++ b/proxy/receiver_proxy_test.go
@@ -11,6 +11,7 @@ import (
"net/http"
"net/http/httptest"
"os"
+ "path"
"testing"
"time"
@@ -117,6 +118,12 @@ func StartTestOrderflowProxy(name, certPath, certKeyPath string) (*OrderflowProx
}, nil
}
+func check(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
func TestMain(m *testing.M) {
signer, err := signature.NewRandomSigner()
if err != nil {
@@ -168,17 +175,27 @@ func TestMain(m *testing.M) {
archiveServer = ServeHTTPRequestToChan(archiveServerRequests)
defer archiveServer.Close()
+ tempDirs := make([]string, 0)
for i := range 3 {
- proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i), "/tmp/orderflow-proxy-test.cert", "/tmp/orderflow-proxy-test.key")
+ tempDir, err := os.MkdirTemp("", "orderflow-proxy-test")
+ check(err)
+ tempDirs = append(tempDirs, tempDir)
+ certPath := path.Join(tempDir, "cert")
+ keyPath := path.Join(tempDir, "key")
+
+ proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i), certPath, keyPath)
proxies = append(proxies, proxy)
- if err != nil {
- panic(err)
- }
+ check(err)
}
+
defer func() {
for _, prx := range proxies {
prx.Close()
}
+
+ for _, dir := range tempDirs {
+ _ = os.RemoveAll(dir)
+ }
}()
os.Exit(m.Run())
@@ -554,7 +571,10 @@ func TestProxyBidSubsidiseBlockCall(t *testing.T) {
}
func TestBuilderNetRootCall(t *testing.T) {
- proxy, err := StartTestOrderflowProxy("1", "/tmp/orderflow-proxy-test.cert", "/tmp/orderflow-proxy-test.key")
+ tempDir := t.TempDir()
+ certPath := path.Join(tempDir, "cert")
+ keyPath := path.Join(tempDir, "key")
+ proxy, err := StartTestOrderflowProxy("1", certPath, keyPath)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "/", nil)
From 927d7a09646328e1a47de1dbc2aa53a3004df95e Mon Sep 17 00:00:00 2001
From: Chris Hager
Date: Fri, 25 Apr 2025 10:28:44 +0200
Subject: [PATCH 4/4] update go-utils to v0.11.0
---
go.mod | 2 +-
go.sum | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/go.mod b/go.mod
index 5298cc6..f7845da 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
github.com/VictoriaMetrics/metrics v1.35.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/ethereum/go-ethereum v1.15.5
- github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5
+ github.com/flashbots/go-utils v0.11.0
github.com/google/uuid v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/stretchr/testify v1.10.0
diff --git a/go.sum b/go.sum
index c4a8ee3..f8c4725 100644
--- a/go.sum
+++ b/go.sum
@@ -36,6 +36,8 @@ github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5 h1:oEfjmk2NQ
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5 h1:F6pO8NC+JSG8jSFzTOAjP4TaR+CwZKygQ4vSLT+XPPw=
github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
+github.com/flashbots/go-utils v0.11.0 h1:MuI9OOl40MukSL2ucKKQG1sxxl5Cqjla41TRubGNu0w=
+github.com/flashbots/go-utils v0.11.0/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=