Skip to content
Draft
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
5,012 changes: 5,012 additions & 0 deletions core/internal/features/svr/dual_aggregator.go

Large diffs are not rendered by default.

192 changes: 192 additions & 0 deletions core/internal/features/svr/flashbots_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package svr

import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"

gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient/simulated"
)

// FlashbotsMockServer is a mock HTTP server that receives secondary transactions
// (Flashbots endpoint) and submits them to the chain.
type FlashbotsMockServer struct {
server *httptest.Server
t *testing.T
backend *simulated.Backend
receivedTxs sync.Map // map[string]bool - track received transactions by hash
submittedTxs sync.Map // map[string]bool - track transaction hashes submitted to chain
txCount int64
txCountMu sync.Mutex
}

// setupFlashbotsMock creates and starts a new FlashbotsMockServer.
func setupFlashbotsMock(t *testing.T, backend *simulated.Backend) *FlashbotsMockServer {
mock := &FlashbotsMockServer{
t: t,
backend: backend,
}
mock.server = httptest.NewServer(http.HandlerFunc(mock.handleRequest))
return mock
}

// URL returns the URL of the mock server.
func (m *FlashbotsMockServer) URL() string {
return m.server.URL
}

// Close shuts down the mock server.
func (m *FlashbotsMockServer) Close() {
m.server.Close()
}

// TransactionCount returns the number of transactions received by the mock server.
func (m *FlashbotsMockServer) TransactionCount() int64 {
m.txCountMu.Lock()
defer m.txCountMu.Unlock()
return m.txCount
}

// WasSubmitted checks if a transaction with the given hash was submitted to the chain.
func (m *FlashbotsMockServer) WasSubmitted(txHash string) bool {
_, ok := m.submittedTxs.Load(txHash)
return ok
}

// handleRequest handles incoming HTTP requests to the mock Flashbots server.
func (m *FlashbotsMockServer) handleRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
m.t.Logf("Mock Flashbots server: failed to read request body: %v", err)
w.WriteHeader(http.StatusBadRequest)
return
}
defer r.Body.Close()

m.t.Logf("Mock Flashbots server: received request: %s", string(body))

// Try to parse as JSON-RPC format
var jsonRPCReq struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID interface{} `json:"id"`
}

// Try to parse as plain JSON with transaction data
var txData struct {
Tx string `json:"tx"` // hex encoded transaction
TxHash string `json:"txHash"` // transaction hash
RawTx string `json:"rawTx"` // raw transaction bytes
SignedTx string `json:"signedTx"` // signed transaction
}

var txBytes []byte
var txHash string

// First try JSON-RPC format
if err := json.Unmarshal(body, &jsonRPCReq); err == nil && jsonRPCReq.Method != "" {
// Parse params - could be array or object
var params []interface{}
if err := json.Unmarshal(jsonRPCReq.Params, &params); err == nil && len(params) > 0 {
// Params might be an array with transaction data
if txStr, ok := params[0].(string); ok {
txBytes, _ = hex.DecodeString(strings.TrimPrefix(txStr, "0x"))
}
} else {
// Try as object
var paramsObj map[string]interface{}
if err := json.Unmarshal(jsonRPCReq.Params, &paramsObj); err == nil {
if tx, ok := paramsObj["tx"].(string); ok {
txBytes, _ = hex.DecodeString(strings.TrimPrefix(tx, "0x"))
}
}
}
} else if err := json.Unmarshal(body, &txData); err == nil {
// Try plain JSON format
if txData.RawTx != "" {
txBytes, _ = hex.DecodeString(strings.TrimPrefix(txData.RawTx, "0x"))
txHash = txData.TxHash
} else if txData.Tx != "" {
txBytes, _ = hex.DecodeString(strings.TrimPrefix(txData.Tx, "0x"))
} else if txData.SignedTx != "" {
txBytes, _ = hex.DecodeString(strings.TrimPrefix(txData.SignedTx, "0x"))
}
} else {
// Try to decode as raw hex
bodyStr := strings.TrimSpace(string(body))
if strings.HasPrefix(bodyStr, "0x") || strings.HasPrefix(bodyStr, "\"0x") {
bodyStr = strings.Trim(bodyStr, "\"")
txBytes, _ = hex.DecodeString(strings.TrimPrefix(bodyStr, "0x"))
}
}

if len(txBytes) == 0 {
m.t.Logf("Mock Flashbots server: could not extract transaction from request body: %s", string(body))
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error": "could not parse transaction"}`))
return
}

// Decode the transaction
var tx gethtypes.Transaction
if err := tx.UnmarshalBinary(txBytes); err != nil {
m.t.Logf("Mock Flashbots server: failed to unmarshal transaction: %v, body: %s", err, string(body))
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error": "invalid transaction format"}`))
return
}

// Calculate hash if not provided
if txHash == "" {
txHash = tx.Hash().Hex()
}

// Check if we've already received this transaction
if _, exists := m.receivedTxs.LoadOrStore(txHash, true); exists {
m.t.Logf("Mock Flashbots server: duplicate transaction %s", txHash)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "duplicate"}`))
return
}

// Submit transaction to chain
m.t.Logf("Mock Flashbots server: submitting secondary transaction %s to chain", txHash)
err = m.backend.Client().SendTransaction(context.Background(), &tx)
if err != nil {
m.t.Logf("Mock Flashbots server: failed to submit transaction to chain: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf(`{"error": "failed to submit: %v"}`, err)))
return
}

// Commit the block to include the transaction
m.backend.Commit()

m.txCountMu.Lock()
m.txCount++
count := m.txCount
m.txCountMu.Unlock()

// Track that this transaction hash was submitted to chain by the mock server
m.submittedTxs.Store(txHash, true)

m.t.Logf("Mock Flashbots server: successfully submitted secondary transaction %s to chain (count: %d)", txHash, count)

// Return success response
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"status": "success", "txHash": "%s"}`, txHash)))

Check warning

Code scanning / CodeQL

Reflected cross-site scripting Medium

Cross-site scripting vulnerability due to
user-provided value
.

Copilot Autofix

AI about 10 hours ago

The correct fix is to ensure that all user-supplied data interpolated into responses is sanitized/escaped, particularly for contexts like HTML or JavaScript but also for JSON if not using safe methods. In Go, the safest way to construct a JSON response is to use json.Marshal to encode a struct/map, which takes care of necessary escaping automatically. For this file, replace the manual fmt.Sprintf with a standard struct containing status and txHash, and marshal it using json.Marshal. On error, fall back to a safe error response. No changes are needed outside this file. You only need to import the well-known encoding/json package (already imported).

Suggested changeset 1
core/internal/features/svr/flashbots_mock.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/core/internal/features/svr/flashbots_mock.go b/core/internal/features/svr/flashbots_mock.go
--- a/core/internal/features/svr/flashbots_mock.go
+++ b/core/internal/features/svr/flashbots_mock.go
@@ -188,5 +188,14 @@
 
 	// Return success response
 	w.WriteHeader(http.StatusOK)
-	w.Write([]byte(fmt.Sprintf(`{"status": "success", "txHash": "%s"}`, txHash)))
+	resp := map[string]string{
+		"status": "success",
+		"txHash": txHash,
+	}
+	jsonBytes, err := json.Marshal(resp)
+	if err != nil {
+		w.Write([]byte(`{"status": "success", "txHash": ""}`))
+		return
+	}
+	w.Write(jsonBytes)
 }
EOF
@@ -188,5 +188,14 @@

// Return success response
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"status": "success", "txHash": "%s"}`, txHash)))
resp := map[string]string{
"status": "success",
"txHash": txHash,
}
jsonBytes, err := json.Marshal(resp)
if err != nil {
w.Write([]byte(`{"status": "success", "txHash": ""}`))
return
}
w.Write(jsonBytes)
}
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
}
Loading
Loading