From 6f2d95b201b59b2fafe2ea25cab0107dfaf8454f Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 7 Oct 2025 11:01:06 +0200 Subject: [PATCH 1/2] feat: add X-BuilderNet-SentAtUs header handling --- rpcserver/jsonrpc_server.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/rpcserver/jsonrpc_server.go b/rpcserver/jsonrpc_server.go index 21d597a..965de84 100644 --- a/rpcserver/jsonrpc_server.go +++ b/rpcserver/jsonrpc_server.go @@ -12,6 +12,7 @@ import ( "io" "log/slog" "net/http" + "strconv" "strings" "time" @@ -43,10 +44,11 @@ const ( ) type ( - highPriorityKey struct{} - signerKey struct{} - originKey struct{} - sizeKey struct{} + highPriorityKey struct{} + builderNetSentAtKey struct{} + signerKey struct{} + originKey struct{} + sizeKey struct{} ) type jsonRPCRequest struct { @@ -96,6 +98,8 @@ type JSONRPCHandlerOpts struct { // If true high_prio header value will be extracted (true or false) // Result can be extracted from the context using GetHighPriority ExtractPriorityFromHeader bool + // If true, extracts the `X-BuilderNet-SentAtUs` header value and sets it in the context. + ExtractBuilderNetSentAtFromHeader bool // If true extract value from x-flashbots-origin header // Result can be extracted from the context using GetOrigin ExtractOriginFromHeader bool @@ -296,6 +300,20 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } + if h.ExtractBuilderNetSentAtFromHeader { + builderNetSentAt := r.Header.Get("X-BuilderNet-SentAtUs") + ts, err := strconv.ParseInt(builderNetSentAt, 10, 64) + if err == nil { + // Convert microseconds to seconds and nanoseconds + seconds := ts / 1e6 + nanoseconds := (ts % 1e6) * 1e3 // Convert remaining microseconds to nanoseconds + + // Create time.Time object + t := time.Unix(seconds, nanoseconds) + ctx = context.WithValue(ctx, builderNetSentAtKey{}, t) + } + } + if h.ExtractOriginFromHeader { origin := r.Header.Get("x-flashbots-origin") if origin != "" { @@ -374,6 +392,15 @@ func GetSigner(ctx context.Context) common.Address { return value } +func GetBuilderNetSentAt(ctx context.Context) time.Time { + value, ok := ctx.Value(builderNetSentAtKey{}).(time.Time) + if !ok { + return time.Time{} + } + + return value +} + func GetOrigin(ctx context.Context) string { value, ok := ctx.Value(originKey{}).(string) if !ok { From 3e39ca2f64e820c0978081e9ca01cd14201fd648 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 7 Oct 2025 11:12:55 +0200 Subject: [PATCH 2/2] refactor: use consts for header keys --- rpcserver/jsonrpc_server.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/rpcserver/jsonrpc_server.go b/rpcserver/jsonrpc_server.go index 965de84..d8799d5 100644 --- a/rpcserver/jsonrpc_server.go +++ b/rpcserver/jsonrpc_server.go @@ -41,6 +41,11 @@ var ( const ( maxOriginIDLength = 255 requestSizeThreshold = 50_000 + + highPriorityHeader = "high_prio" + builderNetSentAtHeader = "X-BuilderNet-SentAtUs" + flashbotsSignatureHeader = "X-Flashbots-Signature" + flashbotsOriginHeader = "X-Flashbots-Origin" ) type ( @@ -244,7 +249,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { stepStartAt = time.Now() if h.ForbidEmptySigner { - signatureHeader := r.Header.Get("x-flashbots-signature") + signatureHeader := r.Header.Get(flashbotsSignatureHeader) if signatureHeader == "" { h.writeJSONRPCError(w, nil, CodeInvalidRequest, "signature is required") incIncorrectRequest(h.ServerName) @@ -253,7 +258,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.VerifyRequestSignatureFromHeader { - signatureHeader := r.Header.Get("x-flashbots-signature") + signatureHeader := r.Header.Get(flashbotsSignatureHeader) signer, err := signature.Verify(signatureHeader, body) if err != nil { h.writeJSONRPCError(w, nil, CodeInvalidRequest, err.Error()) @@ -293,7 +298,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.ExtractUnverifiedRequestSignatureFromHeader { - signature := r.Header.Get("x-flashbots-signature") + signature := r.Header.Get(flashbotsSignatureHeader) if split := strings.Split(signature, ":"); len(split) > 0 { signer := common.HexToAddress(split[0]) ctx = context.WithValue(ctx, signerKey{}, signer) @@ -301,7 +306,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.ExtractBuilderNetSentAtFromHeader { - builderNetSentAt := r.Header.Get("X-BuilderNet-SentAtUs") + builderNetSentAt := r.Header.Get(builderNetSentAtHeader) ts, err := strconv.ParseInt(builderNetSentAt, 10, 64) if err == nil { // Convert microseconds to seconds and nanoseconds @@ -315,7 +320,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.ExtractOriginFromHeader { - origin := r.Header.Get("x-flashbots-origin") + origin := r.Header.Get(flashbotsOriginHeader) if origin != "" { if len(origin) > maxOriginIDLength { h.writeJSONRPCError(w, req.ID, CodeInvalidRequest, "x-flashbots-origin header is too long")