Skip to content

Commit

Permalink
feat: add api to simulate gasless bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
irrun committed Jul 17, 2024
1 parent 3a67ebf commit ef70abf
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 16 deletions.
23 changes: 23 additions & 0 deletions core/types/bundle_gasless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package types

import (
"math/big"

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

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

type GaslessTx struct {
Hash common.Hash
GasUsed uint64
GasFee *big.Int
}

type SimulateGaslessBundleResp struct {
ValidTxs []GaslessTx
BasedBlockNumber uint64
}
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
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
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
62 changes: 46 additions & 16 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,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 @@ -351,28 +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)
}

gasPool := prepareGasPool(env.header.GasLimit)
s, err := miner.worker.simulateBundle(env, bundle, state, gasPool, 0, true, true)

if err != nil {
return nil, err
}

return s.BundleGasPrice, nil
return env, nil
}
52 changes: 52 additions & 0 deletions miner/worker_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,58 @@ func (w *worker) simulateBundle(
}, nil
}

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

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

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

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)
log.Warn("fail to simulate gasless bundle, skipped", "txHash", tx.Hash(), "err", err)
continue
}

effectiveTip, er := tx.EffectiveGasTip(env.header.BaseFee)
if er != nil {
return nil, er
}

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

gasFee := new(big.Int).Mul(new(big.Int).SetUint64(receipt.GasUsed), gasPrice)

if tx.Type() == types.BlobTxType {
blobFee := new(big.Int).Mul(new(big.Int).SetUint64(receipt.BlobGasUsed), receipt.BlobGasPrice)
gasFee.Add(gasFee, blobFee)
}

validTxs = append(validTxs, types.GaslessTx{
Hash: tx.Hash(),
GasUsed: receipt.GasUsed,
GasFee: gasFee,
})
}

return &types.SimulateGaslessBundleResp{
ValidTxs: validTxs,
BasedBlockNumber: env.header.Number.Uint64(),
}, nil

}

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

0 comments on commit ef70abf

Please sign in to comment.