Skip to content
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

stop sending txs to mempool #139

Merged
merged 2 commits into from
May 8, 2024
Merged
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
71 changes: 6 additions & 65 deletions server/request_sendrawtx.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package server

import (
"encoding/hex"
"fmt"
"strings"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/flashbots/rpc-endpoint/types"

ethtypes "github.com/ethereum/go-ethereum/core/types"
)

const (
Expand Down Expand Up @@ -107,8 +104,7 @@ func (r *RpcRequest) handle_sendRawTransaction() {
}

// Check if transaction needs protection
needsProtection := r.doesTxNeedFrontrunningProtection(r.tx)
r.ethSendRawTxEntry.NeedsFrontRunningProtection = needsProtection
r.ethSendRawTxEntry.NeedsFrontRunningProtection = true
// If users specify a bundle ID, cache this transaction
if r.isWhitehatBundleCollection {
r.logger.Info("[WhitehatBundleCollection] Adding tx to bundle", "whiteHatBundleId", r.whitehatBundleId, "tx", r.rawTxHex)
Expand All @@ -126,68 +122,13 @@ func (r *RpcRequest) handle_sendRawTransaction() {
if r.tx.To() != nil && len(r.tx.Data()) <= 2 && txFromLower == strings.ToLower(r.tx.To().Hex()) {
r.ethSendRawTxEntry.IsCancelTx = true
requestDone := r.handleCancelTx() // returns true if tx was cancelled at the relay and response has been sent to the user
if requestDone { // a cancel-tx to fast endpoint is also sent to mempool
return
if !requestDone {
r.ethSendRawTxEntry.IsCancelTx = false
r.logger.Warn("[cancel-tx] This is not a cancellation tx, since we don't have original one. So we process it as usual tx", "txFromLower", txFromLower, "txNonce", r.tx.Nonce())
r.sendTxToRelay()
}

// It's a cancel-tx for the mempool
needsProtection = false
r.logger.Info("[cancel-tx] Sending to mempool", "txFromLower", txFromLower, "txNonce", r.tx.Nonce())
}

if needsProtection {
r.sendTxToRelay()
return
}

if DebugDontSendTx {
r.logger.Info("[sendRawTransaction] Faked sending tx to mempool, did nothing")
r.writeRpcResult(r.tx.Hash().Hex())
return
}

// Proxy to public node now
readJsonRpcSuccess := r.proxyRequestRead()
r.ethSendRawTxEntry.WasSentToMempool = true
// Log after proxying
if !readJsonRpcSuccess {
r.logger.Error("[sendRawTransaction] Proxy to mempool failed")
r.writeRpcError("internal server error", types.JsonRpcInternalError)
return
}

// at the end, save the nonce for further spam protection checks
go RState.SetSenderMaxNonce(txFromLower, r.tx.Nonce())

if r.jsonRes.Error != nil {
r.logger.Info("[sendRawTransaction] Proxied eth_sendRawTransaction to mempool", "jsonRpcError", r.jsonRes.Error.Message)
r.ethSendRawTxEntry.Error = r.jsonRes.Error.Message
r.ethSendRawTxEntry.ErrorCode = r.jsonRes.Error.Code
if r.jsonRes.Error.Message == "nonce too low" {
RState.SetBlockedTxHash(txHashLower, "nonce too low")
}
} else {
r.logger.Info("[sendRawTransaction] Proxied eth_sendRawTransaction to mempool")
}
}

// Check if a request needs frontrunning protection. There are many transactions that don't need frontrunning protection,
// for example simple ERC20 transfers.
func (r *RpcRequest) doesTxNeedFrontrunningProtection(tx *ethtypes.Transaction) bool {
gas := tx.Gas()
r.logger.Info("[protect-check]", "gas", gas)

data := hex.EncodeToString(tx.Data())
r.logger.Info("[protect-check] ", "tx-data", data)

if len(data) < 8 {
return false
}

if isOnFunctionWhitelist(data[0:8]) {
return false // function being called is on our whitelist and no protection needed
} else {
r.logger.Info("[protect-check] Tx needs protection - function", "tx-data", data[0:8])
return true // needs protection if not on whitelist
}
r.sendTxToRelay()
}
17 changes: 0 additions & 17 deletions server/whitelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,6 @@ package server

import "strings"

// Functions that never need protection
var allowedFunctions = map[string]bool{
"a9059cbb": true, // transfer
"23b872dd": true, // transferFrom
"095ea7b3": true, // approve
"2e1a7d4d": true, // weth withdraw
"d0e30db0": true, // weth deposit
"f242432a": true, // safe transfer NFT
}

func isOnFunctionWhitelist(data string) bool {
if len(data) < 8 {
return false
}
return allowedFunctions[data[0:8]]
}

var allowedLargeTxTargets = map[string]bool{
"0xff1f2b4adb9df6fc8eafecdcbf96a2b351680455": true, // Aztec rollup contract
"0x47312450B3Ac8b5b8e247a6bB6d523e7605bDb60": true, // StarkWare SHARP Verifier (Mainnet)
Expand Down
23 changes: 0 additions & 23 deletions server/whitelist_test.go

This file was deleted.

11 changes: 5 additions & 6 deletions tests/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,11 @@ func TestMetamaskFix(t *testing.T) {
req_getTransactionCount := types.NewJsonRpcRequest(1, "eth_getTransactionCount", []interface{}{testutils.TestTx_MM2_From, "latest"})
txCountBefore := testutils.SendRpcAndParseResponseOrFailNowString(t, req_getTransactionCount)

// first sendRawTransaction call: rawTx that triggers the error (creates MM cache entry)
//first sendRawTransaction call: rawTx that triggers the error (creates MM cache entry)
req_sendRawTransaction := types.NewJsonRpcRequest(1, "eth_sendRawTransaction", []interface{}{testutils.TestTx_MM2_RawTx})
r1 := testutils.SendRpcAndParseResponseOrFailNowAllowRpcError(t, req_sendRawTransaction)
require.Nil(t, r1.Error, r1.Error)
fmt.Printf("\n\n\n\n\n")
require.NotNil(t, r1.Error, r1.Error) // tx will actually fail due to incorrect nonce but it's fine for this test, since we mark it as sent to relay
//fmt.Printf("\n\n\n\n\n")

// call getTxReceipt to trigger query to Tx API
req_getTransactionReceipt := types.NewJsonRpcRequest(1, "eth_getTransactionReceipt", []interface{}{testutils.TestTx_MM2_Hash})
Expand Down Expand Up @@ -382,9 +382,8 @@ func TestRelayCancelTxWithoutInitialTx(t *testing.T) {
req_cancelTx := types.NewJsonRpcRequest(1, "eth_sendRawTransaction", []interface{}{testutils.TestTx_CancelAtRelay_Cancel_RawTx})
cancelResp := testutils.SendRpcAndParseResponseOrFailNow(t, req_cancelTx)

// Ensure that request called eth_sendRawTransaction on the mempool node, instead of eth_sendPrivateTransaction on the Relay
// (since no valid initial tx was found)
require.Equal(t, "eth_sendRawTransaction", testutils.MockBackendLastJsonRpcRequest.Method)
// Ensure that request called eth_sendRawTransaction on the relay, cause we don't send txs to mempool now
require.Equal(t, "eth_sendPrivateTransaction", testutils.MockBackendLastJsonRpcRequest.Method)
var res string
json.Unmarshal(cancelResp.Result, &res)

Expand Down
Loading