Skip to content

Commit

Permalink
Add eth_sendBundle RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
jparyani committed Aug 24, 2021
1 parent 2667545 commit f78b35f
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 5 deletions.
30 changes: 30 additions & 0 deletions core/mev_bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 <http://www.gnu.org/licenses/>.

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
}
63 changes: 58 additions & 5 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,12 @@ type TxPool struct {
locals *accountSet // Set of local transaction to exempt from eviction rules
journal *txJournal // Journal of local transaction to back up to disk

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
all *txLookup // All transactions to allow lookups
priced *txPricedList // All transactions sorted by price
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
all *txLookup // All transactions to allow lookups
priced *txPricedList // All transactions sorted by price

chainHeadCh chan ChainHeadEvent
chainHeadSub event.Subscription
Expand Down Expand Up @@ -542,6 +543,58 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact
return pending, nil
}

/// AllMevBundles returns all the MEV Bundles currently in the pool
func (pool *TxPool) AllMevBundles() []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) {
pool.mu.Lock()
defer pool.mu.Unlock()

// returned values
var txBundles []types.Transactions
// rolled over values
var bundles []mevBundle

for _, bundle := range pool.mevBundles {
// Prune outdated bundles
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 {
bundles = append(bundles, bundle)
continue
}

// return the ones which are in time
txBundles = append(txBundles, bundle.txs)
// keep the bundles around internally until they need to be pruned
bundles = append(bundles, bundle)
}

pool.mevBundles = bundles
return txBundles, nil
}

// AddMevBundle adds a mev bundle to the pool
func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp, maxTimestamp uint64) error {
pool.mu.Lock()
defer pool.mu.Unlock()

pool.mevBundles = append(pool.mevBundles, mevBundle{
txs: txs,
blockNumber: blockNumber,
minTimestamp: minTimestamp,
maxTimestamp: maxTimestamp,
})
return nil
}

// Locals retrieves the accounts currently considered local by the pool.
func (pool *TxPool) Locals() []common.Address {
pool.mu.Lock()
Expand Down
10 changes: 10 additions & 0 deletions core/tx_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2541,3 +2541,13 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) {
pool.Stop()
}
}

func checkBundles(t *testing.T, pool *TxPool, block int64, timestamp uint64, expectedRes int, expectedRemaining int) {
res, _ := pool.MevBundles(big.NewInt(block), timestamp)
if len(res) != expectedRes {
t.Fatalf("expected returned bundles did not match got %d, expected %d", len(res), expectedRes)
}
if len(pool.mevBundles) != expectedRemaining {
t.Fatalf("expected remaining bundles did not match got %d, expected %d", len(pool.mevBundles), expectedRemaining)
}
}
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ 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) GetPoolTransactions() (types.Transactions, error) {
pending, err := b.eth.txPool.Pending(false)
if err != nil {
Expand Down
36 changes: 36 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2076,3 +2076,39 @@ func toHexSlice(b [][]byte) []string {
}
return r
}

// ---------------------------------------------------------------- FlashBots ----------------------------------------------------------------

// PrivateTxBundleAPI offers an API for accepting bundled transactions
type PrivateTxBundleAPI struct {
b Backend
}

// NewPrivateTxBundleAPI creates a new Tx Bundle API instance.
func NewPrivateTxBundleAPI(b Backend) *PrivateTxBundleAPI {
return &PrivateTxBundleAPI{b}
}

// 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 {
var txs types.Transactions

for _, encodedTx := range encodedTxs {
tx := new(types.Transaction)
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
return err
}
txs = append(txs, tx)
}

var minTimestamp, maxTimestamp uint64
if minTimestampPtr != nil {
minTimestamp = *minTimestampPtr
}
if maxTimestampPtr != nil {
maxTimestamp = *maxTimestampPtr
}

return s.b.SendBundle(ctx, txs, blockNumber, minTimestamp, maxTimestamp)
}
6 changes: 6 additions & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +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
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
GetPoolTransactions() (types.Transactions, error)
GetPoolTransaction(txHash common.Hash) *types.Transaction
Expand Down Expand Up @@ -134,6 +135,11 @@ func GetAPIs(apiBackend Backend) []rpc.API {
Version: "1.0",
Service: NewPrivateAccountAPI(apiBackend, nonceLock),
Public: false,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPrivateTxBundleAPI(apiBackend),
Public: true,
},
}
}
5 changes: 5 additions & 0 deletions internal/web3ext/web3ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,11 @@ web3._extend({
params: 3,
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null]
}),
new web3._extend.Method({
name: 'sendBundle',
call: 'eth_sendBundle',
params: 4
}),
],
properties: [
new web3._extend.Property({
Expand Down
3 changes: 3 additions & 0 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ 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) GetPoolTransactions() (types.Transactions, error) {
return b.eth.txPool.GetTransactions()
Expand Down
11 changes: 11 additions & 0 deletions light/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,14 @@ func (pool *TxPool) RemoveTx(hash common.Hash) {
pool.chainDb.Delete(hash[:])
pool.relay.Discard([]common.Hash{hash})
}

// 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) {
return nil, nil
}

// AddMevBundle adds a mev bundle to the pool
func (pool *TxPool) AddMevBundle(txs types.Transactions, blockNumber *big.Int, minTimestamp uint64, maxTimestamp uint64) error {
return nil
}

0 comments on commit f78b35f

Please sign in to comment.