-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Copy WIP from #16708 #20380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
cll-gg
wants to merge
16
commits into
develop
Choose a base branch
from
OEV-699-secondary-transmission-integration-test
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+6,724
−0
Draft
Copy WIP from #16708 #20380
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
80d5c95
Copy WIP from https://github.com/smartcontractkit/chainlink/pull/16708
cll-gg b84757f
Update imports
cll-gg a8b7622
Couple more compilation fixes
cll-gg d61960f
Add Cursor plan
cll-gg e99f4c2
Add imports for log verification in SVR test
cll-gg b07984d
Fix compilation: remove unused rpc import
cll-gg 99bfe31
Update log search patterns to match actual TXM log messages
cll-gg 8e5c225
Fix log verification: persist found state across iterations
cll-gg 144e57a
Refine log search patterns to match exact TXM log messages
cll-gg 04feb36
More log writing helpers
cll-gg d933f19
Run mock Flashbots to write to chain
cll-gg 15b3394
Update test
cll-gg c39ea1e
Add check to install protoc script (seems to fail)
cll-gg 78f1561
Revert "Add check to install protoc script (seems to fail)"
cll-gg ac65c06
Extract flashbots mock from test
cll-gg ca89e94
Refactor flashbots mock into struct
cll-gg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, ¶ms); 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, ¶msObj); 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))) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / CodeQL
Reflected cross-site scripting Medium
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.Marshalto encode a struct/map, which takes care of necessary escaping automatically. For this file, replace the manualfmt.Sprintfwith a standard struct containingstatusandtxHash, and marshal it usingjson.Marshal. On error, fall back to a safe error response. No changes are needed outside this file. You only need to import the well-knownencoding/jsonpackage (already imported).