From c0fb93c687f54758e1aacbb7d0d7180dce419b53 Mon Sep 17 00:00:00 2001 From: Jason Paryani Date: Thu, 8 Apr 2021 20:35:50 -0700 Subject: [PATCH] Use object in eth_sendBundle params and add revertingTxHashes param Fixes #40 Fixes #30 --- core/mev_bundle.go | 30 ------------------------- core/tx_pool.go | 31 +++++++++++++------------- core/types/transaction.go | 8 +++++++ eth/api_backend.go | 4 ++-- internal/ethapi/api.go | 29 ++++++++++++++++++------ internal/ethapi/backend.go | 2 +- internal/web3ext/web3ext.go | 2 +- les/api_backend.go | 4 ++-- light/txpool.go | 2 +- miner/worker.go | 44 ++++++++++++++++++++++--------------- 10 files changed, 79 insertions(+), 77 deletions(-) delete mode 100644 core/mev_bundle.go diff --git a/core/mev_bundle.go b/core/mev_bundle.go deleted file mode 100644 index 257411708e74..000000000000 --- a/core/mev_bundle.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// 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 core - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/core/types" -) - -type mevBundle struct { - txs types.Transactions - blockNumber *big.Int - minTimestamp uint64 - maxTimestamp uint64 -} diff --git a/core/tx_pool.go b/core/tx_pool.go index 5749b93ec387..acbe4cd5e9e8 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -245,7 +245,7 @@ type TxPool struct { pending map[common.Address]*txList // All currently processable transactions queue map[common.Address]*txList // Queued but non-processable transactions beats map[common.Address]time.Time // Last heartbeat from each known account - mevBundles []mevBundle + mevBundles []types.MevBundle all *txLookup // All transactions to allow lookups priced *txPricedList // All transactions sorted by price @@ -544,53 +544,54 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact } /// AllMevBundles returns all the MEV Bundles currently in the pool -func (pool *TxPool) AllMevBundles() []mevBundle { +func (pool *TxPool) AllMevBundles() []types.MevBundle { return pool.mevBundles } // MevBundles returns a list of bundles valid for the given blockNumber/blockTimestamp // also prunes bundles that are outdated -func (pool *TxPool) MevBundles(blockNumber *big.Int, blockTimestamp uint64) ([]types.Transactions, error) { +func (pool *TxPool) MevBundles(blockNumber *big.Int, blockTimestamp uint64) ([]types.MevBundle, error) { pool.mu.Lock() defer pool.mu.Unlock() // returned values - var txBundles []types.Transactions + var ret []types.MevBundle // rolled over values - var bundles []mevBundle + var bundles []types.MevBundle for _, bundle := range pool.mevBundles { // Prune outdated bundles - if (bundle.maxTimestamp != 0 && blockTimestamp > bundle.maxTimestamp) || blockNumber.Cmp(bundle.blockNumber) > 0 { + if (bundle.MaxTimestamp != 0 && blockTimestamp > bundle.MaxTimestamp) || blockNumber.Cmp(bundle.BlockNumber) > 0 { continue } // Roll over future bundles - if (bundle.minTimestamp != 0 && blockTimestamp < bundle.minTimestamp) || blockNumber.Cmp(bundle.blockNumber) < 0 { + if (bundle.MinTimestamp != 0 && blockTimestamp < bundle.MinTimestamp) || blockNumber.Cmp(bundle.BlockNumber) < 0 { bundles = append(bundles, bundle) continue } // return the ones which are in time - txBundles = append(txBundles, bundle.txs) + ret = append(ret, bundle) // keep the bundles around internally until they need to be pruned bundles = append(bundles, bundle) } pool.mevBundles = bundles - return txBundles, nil + return ret, nil } // AddMevBundle adds a mev bundle to the pool -func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp, maxTimestamp uint64) error { +func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp, maxTimestamp uint64, revertingTxHashes []common.Hash) error { pool.mu.Lock() defer pool.mu.Unlock() - pool.mevBundles = append(pool.mevBundles, mevBundle{ - txs: txs, - blockNumber: blockNumber, - minTimestamp: minTimestamp, - maxTimestamp: maxTimestamp, + pool.mevBundles = append(pool.mevBundles, types.MevBundle{ + Txs: txs, + BlockNumber: blockNumber, + MinTimestamp: minTimestamp, + MaxTimestamp: maxTimestamp, + RevertingTxHashes: revertingTxHashes, }) return nil } diff --git a/core/types/transaction.go b/core/types/transaction.go index a556f4b57fba..c68b03b3ee13 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -632,3 +632,11 @@ func (m Message) Nonce() uint64 { return m.nonce } func (m Message) Data() []byte { return m.data } func (m Message) AccessList() AccessList { return m.accessList } func (m Message) CheckNonce() bool { return m.checkNonce } + +type MevBundle struct { + Txs Transactions + BlockNumber *big.Int + MinTimestamp uint64 + MaxTimestamp uint64 + RevertingTxHashes []common.Hash +} diff --git a/eth/api_backend.go b/eth/api_backend.go index 6d571e84c2ae..b1a107fede1a 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -234,8 +234,8 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) return b.eth.txPool.AddLocal(signedTx) } -func (b *EthAPIBackend) SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64) error { - return b.eth.txPool.AddMevBundle(txs, big.NewInt(blockNumber.Int64()), minTimestamp, maxTimestamp) +func (b *EthAPIBackend) SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64, revertingTxHashes []common.Hash) error { + return b.eth.txPool.AddMevBundle(txs, big.NewInt(blockNumber.Int64()), minTimestamp, maxTimestamp, revertingTxHashes) } func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9c89f855713a..5a72a4b7060b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2087,12 +2087,27 @@ func NewPrivateTxBundleAPI(b Backend) *PrivateTxBundleAPI { return &PrivateTxBundleAPI{b} } +// SendBundleArgs represents the arguments for a call. +type SendBundleArgs struct { + Txs []hexutil.Bytes `json:"txs"` + BlockNumber rpc.BlockNumber `json:"blockNumber"` + MinTimestamp *uint64 `json:"minTimestamp"` + MaxTimestamp *uint64 `json:"maxTimestamp"` + RevertingTxHashes []common.Hash `json:"revertingTxHashes"` +} + // SendBundle will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce and ensuring validity -func (s *PrivateTxBundleAPI) SendBundle(ctx context.Context, encodedTxs []hexutil.Bytes, blockNumber rpc.BlockNumber, minTimestampPtr, maxTimestampPtr *uint64) error { +func (s *PrivateTxBundleAPI) SendBundle(ctx context.Context, args SendBundleArgs) error { var txs types.Transactions + if len(args.Txs) == 0 { + return errors.New("bundle missing txs") + } + if args.BlockNumber == 0 { + return errors.New("bundle missing blockNumber") + } - for _, encodedTx := range encodedTxs { + for _, encodedTx := range args.Txs { tx := new(types.Transaction) if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return err @@ -2101,12 +2116,12 @@ func (s *PrivateTxBundleAPI) SendBundle(ctx context.Context, encodedTxs []hexuti } var minTimestamp, maxTimestamp uint64 - if minTimestampPtr != nil { - minTimestamp = *minTimestampPtr + if args.MinTimestamp != nil { + minTimestamp = *args.MinTimestamp } - if maxTimestampPtr != nil { - maxTimestamp = *maxTimestampPtr + if args.MaxTimestamp != nil { + maxTimestamp = *args.MaxTimestamp } - return s.b.SendBundle(ctx, txs, blockNumber, minTimestamp, maxTimestamp) + return s.b.SendBundle(ctx, txs, args.BlockNumber, minTimestamp, maxTimestamp, args.RevertingTxHashes) } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 7b5f3b0ff19f..f49ca12b105b 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -71,7 +71,7 @@ type Backend interface { // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error - SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64) error + SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64, revertingTxHashes []common.Hash) error GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) GetPoolTransactions() (types.Transactions, error) GetPoolTransaction(txHash common.Hash) *types.Transaction diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 4ef71b67e822..e4fef6a7d5aa 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -561,7 +561,7 @@ web3._extend({ new web3._extend.Method({ name: 'sendBundle', call: 'eth_sendBundle', - params: 4 + params: 1 }), ], properties: [ diff --git a/les/api_backend.go b/les/api_backend.go index 128347e455a7..c9a8832e19a8 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -197,8 +197,8 @@ func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) func (b *LesApiBackend) RemoveTx(txHash common.Hash) { b.eth.txPool.RemoveTx(txHash) } -func (b *LesApiBackend) SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64) error { - return b.eth.txPool.AddMevBundle(txs, big.NewInt(blockNumber.Int64()), minTimestamp, maxTimestamp) +func (b *LesApiBackend) SendBundle(ctx context.Context, txs types.Transactions, blockNumber rpc.BlockNumber, minTimestamp uint64, maxTimestamp uint64, revertingTxHashes []common.Hash) error { + return b.eth.txPool.AddMevBundle(txs, big.NewInt(blockNumber.Int64()), minTimestamp, maxTimestamp, revertingTxHashes) } func (b *LesApiBackend) GetPoolTransactions() (types.Transactions, error) { diff --git a/light/txpool.go b/light/txpool.go index c16be9b91660..f8563f91d3d6 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -558,6 +558,6 @@ func (pool *TxPool) MevBundles(blockNumber *big.Int, blockTimestamp uint64) ([]t } // AddMevBundle adds a mev bundle to the pool -func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp uint64, maxTimestamp uint64) error { +func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp uint64, maxTimestamp uint64, revertingTxHashes []common.Hash) error { return nil } diff --git a/miner/worker.go b/miner/worker.go index 27599afdaca8..16ff280dbe04 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1188,11 +1188,11 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) log.Error("Failed to generate flashbots bundle", "err", err) return } - log.Info("Flashbots bundle", "ethToCoinbase", ethIntToFloat(bundle.totalEth), "gasUsed", bundle.totalGasUsed, "bundleScore", bundle.mevGasPrice, "bundleLength", len(bundle.txs)) - if len(bundle.txs) == 0 { + log.Info("Flashbots bundle", "ethToCoinbase", ethIntToFloat(bundle.totalEth), "gasUsed", bundle.totalGasUsed, "bundleScore", bundle.mevGasPrice, "bundleLength", len(bundle.originalBundle.Txs)) + if len(bundle.originalBundle.Txs) == 0 { return } - if w.commitBundle(bundle.txs, w.coinbase, interrupt) { + if w.commitBundle(bundle.originalBundle.Txs, w.coinbase, interrupt) { return } } @@ -1245,13 +1245,13 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st } type simulatedBundle struct { - txs types.Transactions - mevGasPrice *big.Int - totalEth *big.Int - totalGasUsed uint64 + mevGasPrice *big.Int + totalEth *big.Int + totalGasUsed uint64 + originalBundle types.MevBundle } -func (w *worker) findMostProfitableBundle(bundles []types.Transactions, coinbase common.Address, parent *types.Block, header *types.Header, pendingTxs map[common.Address]types.Transactions) (simulatedBundle, error) { +func (w *worker) findMostProfitableBundle(bundles []types.MevBundle, coinbase common.Address, parent *types.Block, header *types.Header, pendingTxs map[common.Address]types.Transactions) (simulatedBundle, error) { maxBundle := simulatedBundle{mevGasPrice: new(big.Int)} for _, bundle := range bundles { @@ -1260,7 +1260,7 @@ func (w *worker) findMostProfitableBundle(bundles []types.Transactions, coinbase return simulatedBundle{}, err } gasPool := new(core.GasPool).AddGas(header.GasLimit) - if len(bundle) == 0 { + if len(bundle.Txs) == 0 { continue } simmed, err := w.computeBundleGas(bundle, parent, header, state, gasPool, pendingTxs) @@ -1278,22 +1278,30 @@ func (w *worker) findMostProfitableBundle(bundles []types.Transactions, coinbase return maxBundle, nil } +func containsHash(arr []common.Hash, match common.Hash) bool { + for _, elem := range arr { + if elem == match { + return true + } + } + return false +} + // Compute the adjusted gas price for a whole bundle // Done by calculating all gas spent, adding transfers to the coinbase, and then dividing by gas used -func (w *worker) computeBundleGas(bundle types.Transactions, parent *types.Block, header *types.Header, state *state.StateDB, gasPool *core.GasPool, pendingTxs map[common.Address]types.Transactions) (simulatedBundle, error) { +func (w *worker) computeBundleGas(bundle types.MevBundle, parent *types.Block, header *types.Header, state *state.StateDB, gasPool *core.GasPool, pendingTxs map[common.Address]types.Transactions) (simulatedBundle, error) { var totalGasUsed uint64 = 0 var tempGasUsed uint64 totalEth := new(big.Int) - for _, tx := range bundle { + for _, tx := range bundle.Txs { coinbaseBalanceBefore := state.GetBalance(w.coinbase) - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &w.coinbase, gasPool, state, header, tx, &tempGasUsed, *w.chain.GetVMConfig()) if err != nil { return simulatedBundle{}, err } - if receipt.Status == types.ReceiptStatusFailed { - return simulatedBundle{}, errors.New("revert") + if receipt.Status == types.ReceiptStatusFailed && !containsHash(bundle.RevertingTxHashes, receipt.TxHash) { + return simulatedBundle{}, errors.New("failed tx") } totalGasUsed += receipt.GasUsed @@ -1327,10 +1335,10 @@ func (w *worker) computeBundleGas(bundle types.Transactions, parent *types.Block } return simulatedBundle{ - txs: bundle, - mevGasPrice: new(big.Int).Div(totalEth, new(big.Int).SetUint64(totalGasUsed)), - totalEth: totalEth, - totalGasUsed: totalGasUsed, + mevGasPrice: new(big.Int).Div(totalEth, new(big.Int).SetUint64(totalGasUsed)), + totalEth: totalEth, + totalGasUsed: totalGasUsed, + originalBundle: bundle, }, nil }