Skip to content
Merged
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
48 changes: 40 additions & 8 deletions rpcserver/jsonrpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"io"
"log/slog"
"net/http"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -40,13 +41,19 @@ var (
const (
maxOriginIDLength = 255
requestSizeThreshold = 50_000

highPriorityHeader = "high_prio"
builderNetSentAtHeader = "X-BuilderNet-SentAtUs"
flashbotsSignatureHeader = "X-Flashbots-Signature"
flashbotsOriginHeader = "X-Flashbots-Origin"
)

type (
highPriorityKey struct{}
signerKey struct{}
originKey struct{}
sizeKey struct{}
highPriorityKey struct{}
builderNetSentAtKey struct{}
signerKey struct{}
originKey struct{}
sizeKey struct{}
)

type jsonRPCRequest struct {
Expand Down Expand Up @@ -96,6 +103,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
Expand Down Expand Up @@ -240,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)
Expand All @@ -249,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())
Expand Down Expand Up @@ -289,15 +298,29 @@ 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)
}
}

if h.ExtractBuilderNetSentAtFromHeader {
builderNetSentAt := r.Header.Get(builderNetSentAtHeader)
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)
}
}
Comment on lines +319 to +320
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header parsing silently ignores errors when the timestamp is invalid or missing. Consider logging invalid header values for debugging purposes, as this could help identify issues during benchmarking.

Suggested change
}
}
} else {
slog.Warn("Invalid or missing builderNetSentAtHeader value", "header", builderNetSentAtHeader, "value", builderNetSentAt, "error", err)
}

Copilot uses AI. Check for mistakes.

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")
Expand Down Expand Up @@ -374,6 +397,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 {
Expand Down