diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index cca9a115af..02b4c50625 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -578,6 +578,12 @@ var ( Value: "0x870e2734DdBe2Fba9864f33f3420d59Bc641f2be", Category: flags.MinerCategory, } + MinerBlocklistFileFlag = &cli.StringFlag{ + Name: "miner.blocklist", + Usage: "flashbots - Path to JSON file with list of blocked addresses. Miner will ignore txs that touch mentioned addresses.", + Value: "", + Category: flags.MinerCategory, + } // Account settings UnlockedAccountFlag = &cli.StringFlag{ @@ -1759,6 +1765,17 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) { } } log.Info("Trusted relays set as", "addresses", cfg.TrustedRelays) + + if ctx.IsSet(MinerBlocklistFileFlag.Name) { + bytes, err := os.ReadFile(ctx.String(MinerBlocklistFileFlag.Name)) + if err != nil { + Fatalf("Failed to read blocklist file: %s", err) + } + + if err := json.Unmarshal(bytes, &cfg.Blocklist); err != nil { + Fatalf("Failed to parse blocklist: %s", err) + } + } } func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { diff --git a/eth/tracers/logger/account_touch_tracer.go b/eth/tracers/logger/account_touch_tracer.go new file mode 100644 index 0000000000..120f449bb0 --- /dev/null +++ b/eth/tracers/logger/account_touch_tracer.go @@ -0,0 +1,69 @@ +// Copyright 2022 flashbots +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package logger + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "math/big" + "time" +) + +type AccountTouchTracer struct { + touched map[common.Address]struct{} +} + +// NewAccountTouchTracer creates new AccountTouchTracer +// that collect all addresses touched in the given tx +// including tx sender and tx.to from the top level call +func NewAccountTouchTracer() *AccountTouchTracer { + return &AccountTouchTracer{ + touched: map[common.Address]struct{}{}, + } +} + +func (t *AccountTouchTracer) TouchedAddresses() []common.Address { + result := make([]common.Address, 0, len(t.touched)) + + for address := range t.touched { + result = append(result, address) + } + return result +} + +func (t *AccountTouchTracer) CaptureTxStart(uint64) {} + +func (t *AccountTouchTracer) CaptureTxEnd(uint64) {} + +func (t *AccountTouchTracer) CaptureStart(_ *vm.EVM, from common.Address, to common.Address, _ bool, _ []byte, _ uint64, _ *big.Int) { + t.touched[from] = struct{}{} + t.touched[to] = struct{}{} +} + +func (t *AccountTouchTracer) CaptureEnd([]byte, uint64, time.Duration, error) {} + +func (t *AccountTouchTracer) CaptureEnter(_ vm.OpCode, _ common.Address, to common.Address, _ []byte, _ uint64, _ *big.Int) { + t.touched[to] = struct{}{} +} + +func (t *AccountTouchTracer) CaptureExit([]byte, uint64, error) {} + +func (t *AccountTouchTracer) CaptureState(uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, []byte, int, error) { +} + +func (t *AccountTouchTracer) CaptureFault(uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, int, error) { +} diff --git a/miner/worker.go b/miner/worker.go index e6bf12ec3a..02e0f9cd86 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -210,6 +211,7 @@ type worker struct { engine consensus.Engine eth Backend chain *core.BlockChain + blockList map[common.Address]struct{} // Feeds pendingLogsFeed event.Feed @@ -330,6 +332,11 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus }() } + blockList := make(map[common.Address]struct{}) + for _, address := range config.Blocklist { + blockList[address] = struct{}{} + } + worker := &worker{ config: config, chainConfig: chainConfig, @@ -957,22 +964,47 @@ func (w *worker) updateSnapshot(env *environment) { } func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { - var ( - snap = env.state.Snapshot() - gp = env.gasPool.Gas() - ) + gasPool := *env.gasPool + envGasUsed := env.header.GasUsed + var stateDB *state.StateDB + if len(w.blockList) != 0 { + stateDB = env.state.Copy() + } else { + stateDB = env.state + } + + snapshot := stateDB.Snapshot() gasPrice, err := tx.EffectiveGasTip(env.header.BaseFee) if err != nil { return nil, err } - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) + var tracer *logger.AccountTouchTracer + config := *w.chain.GetVMConfig() + if len(w.blockList) != 0 { + tracer = logger.NewAccountTouchTracer() + config.Tracer = tracer + config.Debug = true + } + + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, &gasPool, stateDB, env.header, tx, &envGasUsed, config) if err != nil { - env.state.RevertToSnapshot(snap) - env.gasPool.SetGas(gp) + stateDB.RevertToSnapshot(snapshot) return nil, err } + if len(w.blockList) != 0 { + for _, address := range tracer.TouchedAddresses() { + if _, in := w.blockList[address]; in { + return nil, errBlocklistViolation + } + } + } + + *env.gasPool = gasPool + env.header.GasUsed = envGasUsed + env.state = stateDB + env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) @@ -1395,6 +1427,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout)) } blockBundles = mergedBundles + log.Info("Filled block with transactions", "time", time.Since(start), "gas used", work.header.GasUsed) } block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals) if err != nil { @@ -1674,13 +1707,27 @@ func (w *worker) computeBundleGas(env *environment, bundle types.MevBundle, stat state.Prepare(tx.Hash(), i+currentTxCount) coinbaseBalanceBefore := state.GetBalance(env.coinbase) - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, gasPool, state, env.header, tx, &tempGasUsed, *w.chain.GetVMConfig()) + config := *w.chain.GetVMConfig() + var tracer *logger.AccountTouchTracer + if len(w.blockList) != 0 { + tracer = logger.NewAccountTouchTracer() + config.Tracer = tracer + config.Debug = true + } + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, gasPool, state, env.header, tx, &tempGasUsed, config) if err != nil { return simulatedBundle{}, err } if receipt.Status == types.ReceiptStatusFailed && !containsHash(bundle.RevertingTxHashes, receipt.TxHash) { return simulatedBundle{}, errors.New("failed tx") } + if len(w.blockList) != 0 { + for _, address := range tracer.TouchedAddresses() { + if _, in := w.blockList[address]; in { + return simulatedBundle{}, errBlocklistViolation + } + } + } totalGasUsed += receipt.GasUsed