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

fix: only take non-mempool tx to calculate bundle price #42

Merged
merged 4 commits into from
Jul 23, 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
21 changes: 21 additions & 0 deletions core/types/bundle_gasless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package types

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)

type SimulateGaslessBundleArgs struct {
Txs []hexutil.Bytes `json:"txs"`
}

type GaslessTxSimResult struct {
Hash common.Hash
GasUsed uint64
Valid bool
}

type SimulateGaslessBundleResp struct {
Results []GaslessTxSimResult
BasedBlockNumber int64
}
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ func (b *EthAPIBackend) SendBundle(ctx context.Context, bundle *types.Bundle) er
return b.eth.txPool.AddBundle(bundle)
}

func (b *EthAPIBackend) SimulateGaslessBundle(bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error) {
return b.Miner().SimulateGaslessBundle(bundle)
}

func (b *EthAPIBackend) BundlePrice() *big.Int {
bundles := b.eth.txPool.AllBundles()
gasFloor := big.NewInt(b.eth.config.Miner.MevGasPriceFloor)
Expand Down
10 changes: 10 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,16 @@ func (ec *Client) BestBidGasFee(ctx context.Context, parentHash common.Hash) (*b
return fee, nil
}

// SimulateGaslessBundle simulates a gasless bundle
func (ec *Client) SimulateGaslessBundle(ctx context.Context, args types.SimulateGaslessBundleArgs) (*types.SimulateGaslessBundleResp, error) {
var bundle types.SimulateGaslessBundleResp
err := ec.c.CallContext(ctx, &bundle, "eth_simulateGaslessBundle", args)
if err != nil {
return nil, err
}
return &bundle, nil
}

// SendBundle sends a bundle
func (ec *Client) SendBundle(ctx context.Context, args types.SendBundleArgs) (common.Hash, error) {
var hash common.Hash
Expand Down
23 changes: 23 additions & 0 deletions internal/ethapi/api_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ func (s *PrivateTxBundleAPI) BundlePrice(ctx context.Context) *big.Int {
return s.b.BundlePrice()
}

// SimulateGaslessBundle simulates the execution of a list of transactions with order
func (s *PrivateTxBundleAPI) SimulateGaslessBundle(_ context.Context, args types.SimulateGaslessBundleArgs) (*types.SimulateGaslessBundleResp, error) {
if len(args.Txs) == 0 {
return nil, newBundleError(errors.New("bundle missing txs"))
}

var txs types.Transactions

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

bundle := &types.Bundle{
Txs: txs,
}

return s.b.SimulateGaslessBundle(bundle)
}

// 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, args types.SendBundleArgs) (common.Hash, error) {
Expand Down
4 changes: 4 additions & 0 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) er
func (b testBackend) SendBundle(ctx context.Context, bundle *types.Bundle) error {
panic("implement me")
}
func (b *testBackend) SimulateGaslessBundle(bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error) {
//TODO implement me
panic("implement me")
}
func (b testBackend) BundlePrice() *big.Int {
panic("implement me")
}
Expand Down
1 change: 1 addition & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type Backend interface {
// Transaction pool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
SendBundle(ctx context.Context, bundle *types.Bundle) error
SimulateGaslessBundle(bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error)
BundlePrice() *big.Int
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
GetPoolTransactions() (types.Transactions, error)
Expand Down
6 changes: 5 additions & 1 deletion internal/ethapi/transaction_args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,11 @@ func (b *backendMock) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.S
}
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
func (b *backendMock) SendBundle(ctx context.Context, bundle *types.Bundle) error { return nil }
func (b *backendMock) BundlePrice() *big.Int { return nil }
func (b *backendMock) SimulateGaslessBundle(bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error) {
//TODO implement me
panic("implement me")
}
func (b *backendMock) BundlePrice() *big.Int { return nil }
func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
return false, nil, [32]byte{}, 0, 0, nil
}
Expand Down
65 changes: 46 additions & 19 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package miner

import (
"errors"
"fmt"
"math/big"
"sync"
Expand Down Expand Up @@ -310,6 +309,47 @@ func (miner *Miner) GasCeil() uint64 {

func (miner *Miner) SimulateBundle(bundle *types.Bundle) (*big.Int, error) {
parent := miner.eth.BlockChain().CurrentBlock()

parentState, err := miner.eth.BlockChain().StateAt(parent.Root)
if err != nil {
return nil, err
}

env, err := miner.prepareSimulationEnv(parent, parentState)
if err != nil {
return nil, err
}

s, err := miner.worker.simulateBundle(env, bundle, parentState, env.gasPool, 0, true, true)
if err != nil {
return nil, err
}

return s.BundleGasPrice, nil
}

func (miner *Miner) SimulateGaslessBundle(bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error) {
parent := miner.eth.BlockChain().CurrentBlock()

parentState, err := miner.eth.BlockChain().StateAt(parent.Root)
if err != nil {
return nil, err
}

env, err := miner.prepareSimulationEnv(parent, parentState)
if err != nil {
return nil, err
}

resp, err := miner.worker.simulateGaslessBundle(env, bundle)
if err != nil {
return nil, err
}

return resp, nil
}

func (miner *Miner) prepareSimulationEnv(parent *types.Header, state *state.StateDB) (*environment, error) {
timestamp := time.Now().Unix()
if parent.Time >= uint64(timestamp) {
timestamp = int64(parent.Time + 1)
Expand Down Expand Up @@ -352,30 +392,17 @@ func (miner *Miner) SimulateBundle(bundle *types.Bundle) (*big.Int, error) {
// }
}

state, err := miner.eth.BlockChain().StateAt(parent.Root)
if err != nil {
return nil, err
}

env := &environment{
header: header,
state: state.Copy(),
signer: types.MakeSigner(miner.worker.chainConfig, header.Number, header.Time),
header: header,
state: state.Copy(),
signer: types.MakeSigner(miner.worker.chainConfig, header.Number, header.Time),
gasPool: prepareGasPool(header.GasLimit),
}

if !miner.worker.chainConfig.IsFeynman(header.Number, header.Time) {
// Handle upgrade build-in system contract code
systemcontracts.UpgradeBuildInSystemContract(miner.worker.chainConfig, header.Number, parent.Time, header.Time, env.state)
}

s, err := miner.worker.simulateBundles(env, []*types.Bundle{bundle})
if err != nil {
return nil, err
}

if len(s) == 0 {
return nil, errors.New("no valid sim result")
}

return s[0].BundleGasPrice, nil
return env, nil
}
100 changes: 75 additions & 25 deletions miner/worker_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,39 +438,52 @@ func (w *worker) simulateBundle(
return nil, err
}

bundleGasUsed += receipt.GasUsed
if !w.eth.TxPool().Has(tx.Hash()) {
bundleGasUsed += receipt.GasUsed

txGasUsed := new(big.Int).SetUint64(receipt.GasUsed)
effectiveTip, err := tx.EffectiveGasTip(env.header.BaseFee)
if err != nil {
return nil, err
}
txGasFees := new(big.Int).Mul(txGasUsed, effectiveTip)
txGasUsed := new(big.Int).SetUint64(receipt.GasUsed)
effectiveTip, er := tx.EffectiveGasTip(env.header.BaseFee)
if er != nil {
return nil, er
}

if env.header.BaseFee != nil {
log.Info("simulate bundle: header base fee", "value", env.header.BaseFee.String())
effectiveTip.Add(effectiveTip, env.header.BaseFee)
}

txGasFees := new(big.Int).Mul(txGasUsed, effectiveTip)

if tx.Type() == types.BlobTxType {
blobFee := new(big.Int).SetUint64(receipt.BlobGasUsed)
blobFee.Mul(blobFee, receipt.BlobGasPrice)
txGasFees.Add(txGasFees, blobFee)
if tx.Type() == types.BlobTxType {
blobFee := new(big.Int).SetUint64(receipt.BlobGasUsed)
blobFee.Mul(blobFee, receipt.BlobGasPrice)
txGasFees.Add(txGasFees, blobFee)
}
bundleGasFees.Add(bundleGasFees, txGasFees)
sysBalanceAfter := state.GetBalance(consensus.SystemAddress)
sysDelta := new(uint256.Int).Sub(sysBalanceAfter, sysBalanceBefore)
sysDelta.Sub(sysDelta, uint256.MustFromBig(txGasFees))
ethSentToSystem.Add(ethSentToSystem, sysDelta.ToBig())
}
bundleGasFees.Add(bundleGasFees, txGasFees)
sysBalanceAfter := state.GetBalance(consensus.SystemAddress)
sysDelta := new(uint256.Int).Sub(sysBalanceAfter, sysBalanceBefore)
sysDelta.Sub(sysDelta, uint256.MustFromBig(txGasFees))
ethSentToSystem.Add(ethSentToSystem, sysDelta.ToBig())
}

bundleGasPrice := new(big.Int).Div(bundleGasFees, new(big.Int).SetUint64(bundleGasUsed))
// if all txs in the bundle are from mempool, we accept the bundle without checking gas price
bundleGasPrice := big.NewInt(0)

if bundleGasPrice.Cmp(big.NewInt(w.config.MevGasPriceFloor)) < 0 {
err := errBundlePriceTooLow
log.Warn("fail to simulate bundle", "hash", bundle.Hash().String(), "err", err)
if bundleGasUsed != 0 {
bundleGasPrice = new(big.Int).Div(bundleGasFees, new(big.Int).SetUint64(bundleGasUsed))

if prune {
log.Warn("prune bundle", "hash", bundle.Hash().String())
w.eth.TxPool().PruneBundle(bundle.Hash())
}
if bundleGasPrice.Cmp(big.NewInt(w.config.MevGasPriceFloor)) < 0 {
err := errBundlePriceTooLow
log.Warn("fail to simulate bundle", "hash", bundle.Hash().String(), "err", err)

if prune {
log.Warn("prune bundle", "hash", bundle.Hash().String())
w.eth.TxPool().PruneBundle(bundle.Hash())
}

return nil, err
return nil, err
}
}

return &types.SimulatedBundle{
Expand All @@ -482,6 +495,43 @@ func (w *worker) simulateBundle(
}, nil
}

func (w *worker) simulateGaslessBundle(env *environment, bundle *types.Bundle) (*types.SimulateGaslessBundleResp, error) {
result := make([]types.GaslessTxSimResult, 0)

txIdx := 0
for _, tx := range bundle.Txs {
env.state.SetTxContext(tx.Hash(), txIdx)

var (
snap = env.state.Snapshot()
gp = env.gasPool.Gas()
valid = true
)

receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &w.coinbase, env.gasPool, env.state, env.header, tx,
&env.header.GasUsed, *w.chain.GetVMConfig())
if err != nil {
env.state.RevertToSnapshot(snap)
env.gasPool.SetGas(gp)
valid = false
log.Warn("fail to simulate gasless bundle, skipped", "txHash", tx.Hash(), "err", err)
} else {
txIdx++
}

result = append(result, types.GaslessTxSimResult{
Hash: tx.Hash(),
GasUsed: receipt.GasUsed,
Valid: valid,
})
}

return &types.SimulateGaslessBundleResp{
Results: result,
BasedBlockNumber: env.header.Number.Int64(),
}, nil
}

func containsHash(arr []common.Hash, match common.Hash) bool {
for _, elem := range arr {
if elem == match {
Expand Down
Loading