From c02f39778f48656ebb0d2d60b720ecb7c717d270 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Tue, 16 May 2023 11:58:52 -0700 Subject: [PATCH 1/8] Prepare for tx --- core/state/interface.go | 2 +- core/state/statedb.go | 4 ++-- core/state_transition.go | 2 +- core/vm/runtime/runtime.go | 6 +++--- tests/state_test.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/state/interface.go b/core/state/interface.go index 0f8b797344d2..5569c323eb52 100644 --- a/core/state/interface.go +++ b/core/state/interface.go @@ -71,7 +71,7 @@ type StateDBI interface { // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) - Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + PrepareForTx(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) RevertToSnapshot(int) Snapshot() int diff --git a/core/state/statedb.go b/core/state/statedb.go index b92ad1035f02..cf77da4382f3 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1080,7 +1080,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { return root, nil } -// Prepare handles the preparatory steps for executing a state transition with. +// PrepareForTx handles the preparatory steps for executing a state transition with. // This method must be invoked before state transition. // // Berlin fork: @@ -1093,7 +1093,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { // - Reset access list (Berlin) // - Add coinbase to access list (EIP-3651) // - Reset transient storage (EIP-1153) -func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { +func (s *StateDB) PrepareForTx(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { if rules.IsBerlin { // Clear out any leftover from previous executions al := NewAccessList() diff --git a/core/state_transition.go b/core/state_transition.go index 394264bf840d..eb6d56f3c09d 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -361,7 +361,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.PrecompileManager.GetActive(&rules), msg.AccessList) + st.state.PrepareForTx(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.PrecompileManager.GetActive(&rules), msg.AccessList) var ( ret []byte diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 8927d3b7bca7..1b7029bca1be 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -118,7 +118,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, state.StateDBI, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) + cfg.State.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) cfg.State.CreateAccount(address) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) @@ -151,7 +151,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vmenv.PrecompileManager.GetActive(&rules), nil) + cfg.State.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, nil, vmenv.PrecompileManager.GetActive(&rules), nil) // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( sender, @@ -179,7 +179,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) + statedb.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( diff --git a/tests/state_test.go b/tests/state_test.go index 4ad177d07759..4845cac2c20d 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -239,7 +239,7 @@ func runBenchmark(b *testing.B, t *StateTest) { b.ResetTimer() for n := 0; n < b.N; n++ { snapshot := statedb.Snapshot() - statedb.Prepare(rules, msg.From, context.Coinbase, msg.To, evm.PrecompileManager.GetActive(&rules), msg.AccessList) + statedb.PrepareForTx(rules, msg.From, context.Coinbase, msg.To, evm.PrecompileManager.GetActive(&rules), msg.AccessList) b.StartTimer() start := time.Now() From 80edb155bfb0dfe2acad67749547b697cd66a45a Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Wed, 17 May 2023 14:57:29 -0400 Subject: [PATCH 2/8] added support for bundles --- cmd/evm/internal/t8ntool/execution.go | 2 +- core/chain_makers.go | 2 +- core/state/interface.go | 3 +- core/state/statedb.go | 4 +- core/state_prefetcher.go | 2 +- core/state_processor.go | 58 ++++- eth/backend.go | 3 +- eth/state_accessor.go | 2 +- eth/tracers/api.go | 8 +- ethapi/api.go | 311 ++++++++++++++++++++++++++ ethapi/backend.go | 5 +- go.sum | 3 - internal/web3ext/web3ext.go | 10 + les/client.go | 2 +- les/state_accessor.go | 2 +- miner/worker.go | 2 +- 16 files changed, 398 insertions(+), 21 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index aa4528fea5c6..f5c062109a4a 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -175,7 +175,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } vmConfig.Tracer = tracer vmConfig.Debug = (tracer != nil) - statedb.SetTxContext(tx.Hash(), txIndex) + statedb.Prepare(tx.Hash(), txIndex) var ( txContext = core.NewEVMTxContext(msg) diff --git a/core/chain_makers.go b/core/chain_makers.go index 4e2b2f59d306..9182659c9324 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -97,7 +97,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti if b.gasPool == nil { b.SetCoinbase(common.Address{}) } - b.statedb.SetTxContext(tx.Hash(), len(b.txs)) + b.statedb.Prepare(tx.Hash(), len(b.txs)) receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig) if err != nil { panic(err) diff --git a/core/state/interface.go b/core/state/interface.go index 5569c323eb52..cc260064c25d 100644 --- a/core/state/interface.go +++ b/core/state/interface.go @@ -73,6 +73,8 @@ type StateDBI interface { AddSlotToAccessList(addr common.Address, slot common.Hash) PrepareForTx(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + Prepare(hash common.Hash, number int) + RevertToSnapshot(int) Snapshot() int @@ -101,7 +103,6 @@ type StateDBI interface { Finalise(deleteEmptyObjects bool) Commit(deleteEmptyObjects bool) (common.Hash, error) Copy() StateDBI - SetTxContext(thash common.Hash, ti int) StopPrefetcher() StartPrefetcher(namespace string) IntermediateRoot(deleteEmptyObjects bool) common.Hash diff --git a/core/state/statedb.go b/core/state/statedb.go index cf77da4382f3..cf7c57f46f47 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -939,10 +939,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.trie.Hash() } -// SetTxContext sets the current transaction hash and index which are +// Prepare sets the current transaction hash and index which are // used when the EVM emits new state logs. It should be invoked before // transaction execution. -func (s *StateDB) SetTxContext(thash common.Hash, ti int) { +func (s *StateDB) Prepare(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 59de9d8d24bd..8171b01666cf 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -67,7 +67,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb state.StateDBI, c if err != nil { return // Also invalid block, bail out } - statedb.SetTxContext(tx.Hash(), i) + statedb.Prepare(tx.Hash(), i) if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { return // Ugh, something went horribly wrong, bail out } diff --git a/core/state_processor.go b/core/state_processor.go index 5703761fe42c..2c075ccada03 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -78,7 +78,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.StateDBI, cfg if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } - statedb.SetTxContext(tx.Hash(), i) + statedb.Prepare(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) @@ -142,6 +142,51 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta return receipt, err } +func applyTransactionWithResult(msg *Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, *ExecutionResult, error) { + // Create a new context to be used in the EVM environment. + txContext := NewEVMTxContext(msg) + evm.Reset(txContext, statedb) + + // Apply the transaction to the current state (included in the env). + result, err := ApplyMessage(evm, msg, gp) + if err != nil { + return nil, nil, err + } + + // Update the state with pending changes. + var root []byte + if config.IsByzantium(header.Number) { + statedb.Finalise(true) + } else { + root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + } + *usedGas += result.UsedGas + + // Create a new receipt for the transaction, storing the intermediate root and gas used + // by the tx. + receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} + if result.Failed() { + receipt.Status = types.ReceiptStatusFailed + } else { + receipt.Status = types.ReceiptStatusSuccessful + } + receipt.TxHash = tx.Hash() + receipt.GasUsed = result.UsedGas + + // If the transaction created a contract, store the creation address in the receipt. + if msg.To == nil { + receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce()) + } + + // Set the receipt logs and create the bloom filter. + receipt.Logs = statedb.GetLogs(tx.Hash(), header.Number.Uint64(), header.Hash()) + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.BlockHash = header.Hash() + receipt.BlockNumber = header.Number + receipt.TransactionIndex = uint(statedb.TxIndex()) + return receipt, result, err +} + // ApplyTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, @@ -156,3 +201,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } + +func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { + msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee) + if err != nil { + return nil, nil, err + } + // Create a new context to be used in the EVM environment + blockContext := NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) + return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +} diff --git a/eth/backend.go b/eth/backend.go index 9bde9cd34bd1..9ab37c790b7f 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -288,8 +288,7 @@ func makeExtraData(extra []byte) []byte { // APIs return the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *Ethereum) APIs() []rpc.API { - apis := ethapi.GetAPIs(s.APIBackend) - + apis := ethapi.GetAPIs(s.APIBackend, s.BlockChain()) // Append any APIs exposed explicitly by the consensus engine apis = append(apis, s.engine.APIs(s.BlockChain())...) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 3259777d4628..d455aa97fbcd 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -222,7 +222,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, if err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("failed to get evm: %v", err) } - statedb.SetTxContext(tx.Hash(), idx) + statedb.Prepare(tx.Hash(), idx) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index db82fdb79f57..4eb240a88790 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -558,7 +558,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config txContext = core.NewEVMTxContext(msg) vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) - statedb.SetTxContext(tx.Hash(), i) + statedb.Prepare(tx.Hash(), i) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not @@ -703,7 +703,7 @@ txloop: // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - statedb.SetTxContext(tx.Hash(), i) + statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { failed = err @@ -812,7 +812,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) - statedb.SetTxContext(tx.Hash(), i) + statedb.Prepare(tx.Hash(), i) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) if writer != nil { writer.Flush() @@ -985,7 +985,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte defer cancel() // Call Prepare to clear out the statedb access list - statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) + statedb.Prepare(txctx.TxHash, txctx.TxIndex) if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/ethapi/api.go b/ethapi/api.go index a875d96f96cb..fad2aafba0e5 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -18,6 +18,7 @@ package ethapi import ( "context" + "crypto/rand" "encoding/hex" "errors" "fmt" @@ -47,6 +48,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/tyler-smith/go-bip39" + "golang.org/x/crypto/sha3" ) // EthereumAPI provides an API to access Ethereum related information. @@ -2104,3 +2106,312 @@ func toHexSlice(b [][]byte) []string { } return r } + +// ---------------------------------------------------------------- FlashBots ---------------------------------------------------------------- + +// BundleAPI offers an API for accepting bundled transactions +type BundleAPI struct { + b Backend + chain *core.BlockChain +} + +// NewBundleAPI creates a new Tx Bundle API instance. +func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI { + return &BundleAPI{b, chain} +} + +// CallBundleArgs represents the arguments for a call. +type CallBundleArgs struct { + Txs []hexutil.Bytes `json:"txs"` + BlockNumber rpc.BlockNumber `json:"blockNumber"` + StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"` + Coinbase *string `json:"coinbase"` + Timestamp *uint64 `json:"timestamp"` + Timeout *int64 `json:"timeout"` + GasLimit *uint64 `json:"gasLimit"` + Difficulty *big.Int `json:"difficulty"` + BaseFee *big.Int `json:"baseFee"` +} + +// CallBundle will simulate a bundle of transactions at the top of a given block +// number with the state of another (or the same) block. This can be used to +// simulate future blocks with the current state, or it can be used to simulate +// a past block. +// The sender is responsible for signing the transactions and using the correct +// nonce and ensuring validity +func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[string]interface{}, error) { + if len(args.Txs) == 0 { + return nil, errors.New("bundle missing txs") + } + if args.BlockNumber == 0 { + return nil, errors.New("bundle missing blockNumber") + } + + 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) + } + defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) + + timeoutMilliSeconds := int64(5000) + if args.Timeout != nil { + timeoutMilliSeconds = *args.Timeout + } + timeout := time.Millisecond * time.Duration(timeoutMilliSeconds) + state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash) + if state == nil || err != nil { + return nil, err + } + blockNumber := big.NewInt(int64(args.BlockNumber)) + + timestamp := parent.Time + 1 + if args.Timestamp != nil { + timestamp = *args.Timestamp + } + coinbase := parent.Coinbase + if args.Coinbase != nil { + coinbase = common.HexToAddress(*args.Coinbase) + } + difficulty := parent.Difficulty + if args.Difficulty != nil { + difficulty = args.Difficulty + } + gasLimit := parent.GasLimit + if args.GasLimit != nil { + gasLimit = *args.GasLimit + } + var baseFee *big.Int + if args.BaseFee != nil { + baseFee = args.BaseFee + } else if s.b.ChainConfig().IsLondon(big.NewInt(args.BlockNumber.Int64())) { + baseFee = misc.CalcBaseFee(s.b.ChainConfig(), parent) + } + header := &types.Header{ + ParentHash: parent.Hash(), + Number: blockNumber, + GasLimit: gasLimit, + Time: timestamp, + Difficulty: difficulty, + Coinbase: coinbase, + BaseFee: baseFee, + } + + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer cancel() + + vmconfig := vm.Config{} + + // Setup the gas pool (also for unmetered requests) + // and apply the message. + gp := new(core.GasPool).AddGas(math.MaxUint64) + + results := []map[string]interface{}{} + coinbaseBalanceBefore := state.GetBalance(coinbase) + + bundleHash := sha3.NewLegacyKeccak256() + signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) + var totalGasUsed uint64 + gasFees := new(big.Int) + for i, tx := range txs { + coinbaseBalanceBeforeTx := state.GetBalance(coinbase) + state.Prepare(tx.Hash(), i) + + receipt, result, err := core.ApplyTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed, vmconfig) + if err != nil { + return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) + } + + txHash := tx.Hash().String() + from, err := types.Sender(signer, tx) + if err != nil { + return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) + } + to := "0x" + if tx.To() != nil { + to = tx.To().String() + } + jsonResult := map[string]interface{}{ + "txHash": txHash, + "gasUsed": receipt.GasUsed, + "fromAddress": from.String(), + "toAddress": to, + } + totalGasUsed += receipt.GasUsed + gasPrice, err := tx.EffectiveGasTip(header.BaseFee) + if err != nil { + return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) + } + gasFeesTx := new(big.Int).Mul(big.NewInt(int64(receipt.GasUsed)), gasPrice) + gasFees.Add(gasFees, gasFeesTx) + bundleHash.Write(tx.Hash().Bytes()) + if result.Err != nil { + jsonResult["error"] = result.Err.Error() + revert := result.Revert() + if len(revert) > 0 { + jsonResult["revert"] = string(revert) + } + } else { + dst := make([]byte, hex.EncodedLen(len(result.Return()))) + hex.Encode(dst, result.Return()) + jsonResult["value"] = "0x" + string(dst) + } + coinbaseDiffTx := new(big.Int).Sub(state.GetBalance(coinbase), coinbaseBalanceBeforeTx) + jsonResult["coinbaseDiff"] = coinbaseDiffTx.String() + jsonResult["gasFees"] = gasFeesTx.String() + jsonResult["ethSentToCoinbase"] = new(big.Int).Sub(coinbaseDiffTx, gasFeesTx).String() + jsonResult["gasPrice"] = new(big.Int).Div(coinbaseDiffTx, big.NewInt(int64(receipt.GasUsed))).String() + jsonResult["gasUsed"] = receipt.GasUsed + results = append(results, jsonResult) + } + + ret := map[string]interface{}{} + ret["results"] = results + coinbaseDiff := new(big.Int).Sub(state.GetBalance(coinbase), coinbaseBalanceBefore) + ret["coinbaseDiff"] = coinbaseDiff.String() + ret["gasFees"] = gasFees.String() + ret["ethSentToCoinbase"] = new(big.Int).Sub(coinbaseDiff, gasFees).String() + ret["bundleGasPrice"] = new(big.Int).Div(coinbaseDiff, big.NewInt(int64(totalGasUsed))).String() + ret["totalGasUsed"] = totalGasUsed + ret["stateBlockNumber"] = parent.Number.Int64() + + ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil)) + return ret, nil +} + +// EstimateGasBundleArgs represents the arguments for a call +type EstimateGasBundleArgs struct { + Txs []TransactionArgs `json:"txs"` + BlockNumber rpc.BlockNumber `json:"blockNumber"` + StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"` + Coinbase *string `json:"coinbase"` + Timestamp *uint64 `json:"timestamp"` + Timeout *int64 `json:"timeout"` +} + +func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundleArgs) (map[string]interface{}, error) { + if len(args.Txs) == 0 { + return nil, errors.New("bundle missing txs") + } + if args.BlockNumber == 0 { + return nil, errors.New("bundle missing blockNumber") + } + + timeoutMS := int64(5000) + if args.Timeout != nil { + timeoutMS = *args.Timeout + } + timeout := time.Millisecond * time.Duration(timeoutMS) + + state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash) + if state == nil || err != nil { + return nil, err + } + blockNumber := big.NewInt(int64(args.BlockNumber)) + timestamp := parent.Time + 1 + if args.Timestamp != nil { + timestamp = *args.Timestamp + } + coinbase := parent.Coinbase + if args.Coinbase != nil { + coinbase = common.HexToAddress(*args.Coinbase) + } + + header := &types.Header{ + ParentHash: parent.Hash(), + Number: blockNumber, + GasLimit: parent.GasLimit, + Time: timestamp, + Difficulty: parent.Difficulty, + Coinbase: coinbase, + BaseFee: parent.BaseFee, + } + + // Setup context so it may be cancelled when the call + // has completed or, in case of unmetered gas, setup + // a context with a timeout + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + + // Make sure the context is cancelled when the call has completed + // This makes sure resources are cleaned up + defer cancel() + + // RPC Call gas cap + globalGasCap := s.b.RPCGasCap() + + // Results + results := []map[string]interface{}{} + + // Copy the original db so we don't modify it + statedb := state.Copy() + + // Gas pool + gp := new(core.GasPool).AddGas(math.MaxUint64) + + // Block context + blockContext := core.NewEVMBlockContext(header, s.chain, &coinbase) + + // Feed each of the transactions into the VM ctx + // And try and estimate the gas used + for i, txArgs := range args.Txs { + // Since its a txCall we'll just prepare the + // state with a random hash + var randomHash common.Hash + rand.Read(randomHash[:]) + + // New random hash since its a call + statedb.Prepare(randomHash, i) + + // Convert tx args to msg to apply state transition + msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee) + if err != nil { + return nil, err + } + + // Prepare the hashes + txContext := core.NewEVMTxContext(msg) + + // Get EVM Environment + vmenv := vm.NewEVM(blockContext, txContext, statedb, s.b.ChainConfig(), vm.Config{NoBaseFee: true}) + + // Apply state transition + result, err := core.ApplyMessage(vmenv, msg, gp) + if err != nil { + return nil, err + } + + // Modifications are committed to the state + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + statedb.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber)) + + // Append result + jsonResult := map[string]interface{}{ + "gasUsed": result.UsedGas, + } + results = append(results, jsonResult) + } + + // Return results + ret := map[string]interface{}{} + ret["results"] = results + + return ret, nil +} diff --git a/ethapi/backend.go b/ethapi/backend.go index 472e67bd5c4f..c56724d28456 100644 --- a/ethapi/backend.go +++ b/ethapi/backend.go @@ -99,7 +99,7 @@ type Backend interface { ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) } -func GetAPIs(apiBackend Backend) []rpc.API { +func GetAPIs(apiBackend Backend, chain *core.BlockChain) []rpc.API { nonceLock := new(AddrLocker) return []rpc.API{ { @@ -123,6 +123,9 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "personal", Service: NewPersonalAccountAPI(apiBackend, nonceLock), + }, { + Namespace: "eth", + Service: NewBundleAPI(apiBackend, chain), }, } } diff --git a/go.sum b/go.sum index acd799c15814..51beb15cd24a 100644 --- a/go.sum +++ b/go.sum @@ -773,9 +773,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 388eed8a2fa6..974d404fca70 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -611,6 +611,16 @@ web3._extend({ params: 3, inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null], }), + new web3._extend.Method({ + name: 'callBundle', + call: 'eth_callBundle', + params: 1, + }), + new web3._extend.Method({ + name: 'estimateGasBundle', + call: 'eth_estimateGasBundle', + params: 1, + }), ], properties: [ new web3._extend.Property({ diff --git a/les/client.go b/les/client.go index 60a2a3d2856b..ed1d4590ddc7 100644 --- a/les/client.go +++ b/les/client.go @@ -297,7 +297,7 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { - apis := ethapi.GetAPIs(s.ApiBackend) + apis := ethapi.GetAPIs(s.ApiBackend, nil) apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...) return append(apis, []rpc.API{ { diff --git a/les/state_accessor.go b/les/state_accessor.go index a3ca551d887f..7a92d39b70e4 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -63,7 +63,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) - statedb.SetTxContext(tx.Hash(), idx) + statedb.Prepare(tx.Hash(), idx) if idx == txIndex { return msg, context, statedb, release, nil } diff --git a/miner/worker.go b/miner/worker.go index 47d22fa062bd..d0d8e202c64e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -914,7 +914,7 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP continue } // Start executing the transaction - env.state.SetTxContext(tx.Hash(), env.tcount) + env.state.Prepare(tx.Hash(), env.tcount) logs, err := w.commitTransaction(env, tx) switch { From 40635f7835337e4a3ce98c5bd0caf857b915af94 Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Wed, 17 May 2023 15:13:57 -0400 Subject: [PATCH 3/8] remove useless Prepare function --- cmd/evm/internal/t8ntool/execution.go | 2 +- core/chain_makers.go | 2 +- core/state/interface.go | 3 +-- core/state/statedb.go | 2 +- core/state_prefetcher.go | 2 +- core/state_processor.go | 2 +- core/vm/runtime/runtime.go | 2 +- eth/state_accessor.go | 2 +- eth/tracers/api.go | 8 ++++---- ethapi/api.go | 4 ++-- les/state_accessor.go | 2 +- miner/worker.go | 2 +- 12 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index f5c062109a4a..aa4528fea5c6 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -175,7 +175,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } vmConfig.Tracer = tracer vmConfig.Debug = (tracer != nil) - statedb.Prepare(tx.Hash(), txIndex) + statedb.SetTxContext(tx.Hash(), txIndex) var ( txContext = core.NewEVMTxContext(msg) diff --git a/core/chain_makers.go b/core/chain_makers.go index 9182659c9324..4e2b2f59d306 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -97,7 +97,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti if b.gasPool == nil { b.SetCoinbase(common.Address{}) } - b.statedb.Prepare(tx.Hash(), len(b.txs)) + b.statedb.SetTxContext(tx.Hash(), len(b.txs)) receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig) if err != nil { panic(err) diff --git a/core/state/interface.go b/core/state/interface.go index cc260064c25d..5569c323eb52 100644 --- a/core/state/interface.go +++ b/core/state/interface.go @@ -73,8 +73,6 @@ type StateDBI interface { AddSlotToAccessList(addr common.Address, slot common.Hash) PrepareForTx(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) - Prepare(hash common.Hash, number int) - RevertToSnapshot(int) Snapshot() int @@ -103,6 +101,7 @@ type StateDBI interface { Finalise(deleteEmptyObjects bool) Commit(deleteEmptyObjects bool) (common.Hash, error) Copy() StateDBI + SetTxContext(thash common.Hash, ti int) StopPrefetcher() StartPrefetcher(namespace string) IntermediateRoot(deleteEmptyObjects bool) common.Hash diff --git a/core/state/statedb.go b/core/state/statedb.go index cf7c57f46f47..3b5625889a59 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -942,7 +942,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Prepare sets the current transaction hash and index which are // used when the EVM emits new state logs. It should be invoked before // transaction execution. -func (s *StateDB) Prepare(thash common.Hash, ti int) { +func (s *StateDB) SetTxContext(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 8171b01666cf..59de9d8d24bd 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -67,7 +67,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb state.StateDBI, c if err != nil { return // Also invalid block, bail out } - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { return // Ugh, something went horribly wrong, bail out } diff --git a/core/state_processor.go b/core/state_processor.go index 2c075ccada03..77dfe56c87e4 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -78,7 +78,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.StateDBI, cfg if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 1b7029bca1be..cfa413242da8 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -179,7 +179,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - statedb.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) + statedb.SetTxContextForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( diff --git a/eth/state_accessor.go b/eth/state_accessor.go index d455aa97fbcd..3259777d4628 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -222,7 +222,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, if err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("failed to get evm: %v", err) } - statedb.Prepare(tx.Hash(), idx) + statedb.SetTxContext(tx.Hash(), idx) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 4eb240a88790..db82fdb79f57 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -558,7 +558,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config txContext = core.NewEVMTxContext(msg) vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not @@ -703,7 +703,7 @@ txloop: // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { failed = err @@ -812,7 +812,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) - statedb.Prepare(tx.Hash(), i) + statedb.SetTxContext(tx.Hash(), i) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) if writer != nil { writer.Flush() @@ -985,7 +985,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte defer cancel() // Call Prepare to clear out the statedb access list - statedb.Prepare(txctx.TxHash, txctx.TxIndex) + statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/ethapi/api.go b/ethapi/api.go index fad2aafba0e5..ca142f4c3b2b 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -2228,7 +2228,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st gasFees := new(big.Int) for i, tx := range txs { coinbaseBalanceBeforeTx := state.GetBalance(coinbase) - state.Prepare(tx.Hash(), i) + state.SetTxContext(tx.Hash(), i) receipt, result, err := core.ApplyTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed, vmconfig) if err != nil { @@ -2378,7 +2378,7 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl rand.Read(randomHash[:]) // New random hash since its a call - statedb.Prepare(randomHash, i) + statedb.SetTxContext(randomHash, i) // Convert tx args to msg to apply state transition msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee) diff --git a/les/state_accessor.go b/les/state_accessor.go index 7a92d39b70e4..a3ca551d887f 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -63,7 +63,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) - statedb.Prepare(tx.Hash(), idx) + statedb.SetTxContext(tx.Hash(), idx) if idx == txIndex { return msg, context, statedb, release, nil } diff --git a/miner/worker.go b/miner/worker.go index d0d8e202c64e..47d22fa062bd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -914,7 +914,7 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP continue } // Start executing the transaction - env.state.Prepare(tx.Hash(), env.tcount) + env.state.SetTxContext(tx.Hash(), env.tcount) logs, err := w.commitTransaction(env, tx) switch { From fd5d521bc68df09e741b2642d46a97e442eb32c2 Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Wed, 17 May 2023 16:09:37 -0400 Subject: [PATCH 4/8] core.ChainConfig instead of *core.BlockChain for BundleAPI --- ethapi/api.go | 6 +++--- ethapi/backend.go | 2 +- les/client.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethapi/api.go b/ethapi/api.go index ca142f4c3b2b..339e90079a5d 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -2112,15 +2112,15 @@ func toHexSlice(b [][]byte) []string { // BundleAPI offers an API for accepting bundled transactions type BundleAPI struct { b Backend - chain *core.BlockChain + chain core.ChainContext } // NewBundleAPI creates a new Tx Bundle API instance. -func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI { +func NewBundleAPI(b Backend, chain core.ChainContext) *BundleAPI { return &BundleAPI{b, chain} } -// CallBundleArgs represents the arguments for a call. +// CallBundleArgs represents the arguinterface ments for a call. type CallBundleArgs struct { Txs []hexutil.Bytes `json:"txs"` BlockNumber rpc.BlockNumber `json:"blockNumber"` diff --git a/ethapi/backend.go b/ethapi/backend.go index c56724d28456..c74ef9dadc09 100644 --- a/ethapi/backend.go +++ b/ethapi/backend.go @@ -99,7 +99,7 @@ type Backend interface { ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) } -func GetAPIs(apiBackend Backend, chain *core.BlockChain) []rpc.API { +func GetAPIs(apiBackend Backend, chain core.ChainContext) []rpc.API { nonceLock := new(AddrLocker) return []rpc.API{ { diff --git a/les/client.go b/les/client.go index ed1d4590ddc7..bfbcbf65809c 100644 --- a/les/client.go +++ b/les/client.go @@ -297,7 +297,7 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { - apis := ethapi.GetAPIs(s.ApiBackend, nil) + apis := ethapi.GetAPIs(s.ApiBackend, s.BlockChain()) apis = append(apis, s.engine.APIs(s.BlockChain().HeaderChain())...) return append(apis, []rpc.API{ { From 2ea16c0bb996bc878f2b0ff0812dc92d7156ac30 Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Wed, 17 May 2023 16:55:39 -0400 Subject: [PATCH 5/8] remove PrepareForTx --- core/state/interface.go | 2 +- core/state/statedb.go | 2 +- core/state_transition.go | 2 +- core/vm/runtime/runtime.go | 6 +++--- tests/state_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/state/interface.go b/core/state/interface.go index 5569c323eb52..0f8b797344d2 100644 --- a/core/state/interface.go +++ b/core/state/interface.go @@ -71,7 +71,7 @@ type StateDBI interface { // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) - PrepareForTx(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) + Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) RevertToSnapshot(int) Snapshot() int diff --git a/core/state/statedb.go b/core/state/statedb.go index 3b5625889a59..b25a0def06dd 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1093,7 +1093,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { // - Reset access list (Berlin) // - Add coinbase to access list (EIP-3651) // - Reset transient storage (EIP-1153) -func (s *StateDB) PrepareForTx(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { +func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { if rules.IsBerlin { // Clear out any leftover from previous executions al := NewAccessList() diff --git a/core/state_transition.go b/core/state_transition.go index eb6d56f3c09d..394264bf840d 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -361,7 +361,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - st.state.PrepareForTx(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.PrecompileManager.GetActive(&rules), msg.AccessList) + st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.PrecompileManager.GetActive(&rules), msg.AccessList) var ( ret []byte diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index cfa413242da8..8927d3b7bca7 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -118,7 +118,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, state.StateDBI, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) + cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) cfg.State.CreateAccount(address) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) @@ -151,7 +151,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - cfg.State.PrepareForTx(rules, cfg.Origin, cfg.Coinbase, nil, vmenv.PrecompileManager.GetActive(&rules), nil) + cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vmenv.PrecompileManager.GetActive(&rules), nil) // Call the code with the given configuration. code, address, leftOverGas, err := vmenv.Create( sender, @@ -179,7 +179,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) - statedb.SetTxContextForTx(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) + statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vmenv.PrecompileManager.GetActive(&rules), nil) // Call the code with the given configuration. ret, leftOverGas, err := vmenv.Call( diff --git a/tests/state_test.go b/tests/state_test.go index 4845cac2c20d..4ad177d07759 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -239,7 +239,7 @@ func runBenchmark(b *testing.B, t *StateTest) { b.ResetTimer() for n := 0; n < b.N; n++ { snapshot := statedb.Snapshot() - statedb.PrepareForTx(rules, msg.From, context.Coinbase, msg.To, evm.PrecompileManager.GetActive(&rules), msg.AccessList) + statedb.Prepare(rules, msg.From, context.Coinbase, msg.To, evm.PrecompileManager.GetActive(&rules), msg.AccessList) b.StartTimer() start := time.Now() From f92c97260fe726bc9083e65c753b0fe962592c92 Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Thu, 18 May 2023 11:16:34 -0400 Subject: [PATCH 6/8] ApplyTransactionWithEVMWithResult(...) in CallBundle(...) --- core/state/statedb.go | 4 ++-- core/state_processor.go | 8 ++++++++ ethapi/api.go | 19 +++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index b25a0def06dd..b92ad1035f02 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -939,7 +939,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.trie.Hash() } -// Prepare sets the current transaction hash and index which are +// SetTxContext sets the current transaction hash and index which are // used when the EVM emits new state logs. It should be invoked before // transaction execution. func (s *StateDB) SetTxContext(thash common.Hash, ti int) { @@ -1080,7 +1080,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { return root, nil } -// PrepareForTx handles the preparatory steps for executing a state transition with. +// Prepare handles the preparatory steps for executing a state transition with. // This method must be invoked before state transition. // // Berlin fork: diff --git a/core/state_processor.go b/core/state_processor.go index f3ddbe71b846..5db85392b16c 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -220,3 +220,11 @@ func ApplyTransactionWithEVM(vmenv *vm.EVM, config *params.ChainConfig, gp *GasP } return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } + +func ApplyTransactionWithEVMWithResult(vmenv *vm.EVM, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, *ExecutionResult, error) { + msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee) + if err != nil { + return nil, nil, err + } + return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +} diff --git a/ethapi/api.go b/ethapi/api.go index 339e90079a5d..0dd144eb513f 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -2213,7 +2213,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st // this makes sure resources are cleaned up. defer cancel() - vmconfig := vm.Config{} + vmConfig := vm.Config{} // Setup the gas pool (also for unmetered requests) // and apply the message. @@ -2226,11 +2226,26 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) var totalGasUsed uint64 gasFees := new(big.Int) + + msg, err := core.TransactionToMessage(txs[0], signer, header.BaseFee) + + if err != nil { + return nil, err + } + + blockCtx := core.NewEVMBlockContext(header, s.chain, nil) + txCtx := core.NewEVMTxContext(msg) + chainConfig := s.b.ChainConfig() + rules := chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) + chainRules := vm.NewPrecompileManager(&rules) + + evm := vm.NewEVMWithPrecompiles(blockCtx, txCtx, state, chainConfig, vmConfig, chainRules) + for i, tx := range txs { coinbaseBalanceBeforeTx := state.GetBalance(coinbase) state.SetTxContext(tx.Hash(), i) - receipt, result, err := core.ApplyTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed, vmconfig) + receipt, result, err := core.ApplyTransactionWithEVMWithResult(evm, s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed) if err != nil { return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) } From a2edb526f040bcd9b88f5ae15795290acbbdbf51 Mon Sep 17 00:00:00 2001 From: shampoo bera Date: Thu, 18 May 2023 12:45:20 -0400 Subject: [PATCH 7/8] removed useless param --- core/state_processor.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 5db85392b16c..cb3c54e09583 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -142,7 +142,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta return receipt, err } -func applyTransactionWithResult(msg *Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, *ExecutionResult, error) { +func applyTransactionWithResult(msg *Message, config *params.ChainConfig, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, *ExecutionResult, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -210,7 +210,7 @@ func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, aut // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) + return applyTransactionWithResult(msg, config, author, gp, statedb, header, tx, usedGas, vmenv) } func ApplyTransactionWithEVM(vmenv *vm.EVM, config *params.ChainConfig, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { @@ -226,5 +226,5 @@ func ApplyTransactionWithEVMWithResult(vmenv *vm.EVM, config *params.ChainConfig if err != nil { return nil, nil, err } - return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) + return applyTransactionWithResult(msg, config, author, gp, statedb, header, tx, usedGas, vmenv) } From 22f1c9979b9cfe2e6e313cb428e1cdcab935d70c Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Thu, 18 May 2023 12:38:07 -0700 Subject: [PATCH 8/8] cleanup --- core/state_processor.go | 2 +- ethapi/api.go | 49 +++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index cb3c54e09583..2ff45e4dd877 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -221,7 +221,7 @@ func ApplyTransactionWithEVM(vmenv *vm.EVM, config *params.ChainConfig, gp *GasP return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } -func ApplyTransactionWithEVMWithResult(vmenv *vm.EVM, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, *ExecutionResult, error) { +func ApplyTransactionWithEVMWithResult(vmenv *vm.EVM, config *params.ChainConfig, author *common.Address, gp *GasPool, statedb state.StateDBI, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, *ExecutionResult, error) { msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee) if err != nil { return nil, nil, err diff --git a/ethapi/api.go b/ethapi/api.go index 0dd144eb513f..edc159823e1d 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -2107,9 +2107,11 @@ func toHexSlice(b [][]byte) []string { return r } -// ---------------------------------------------------------------- FlashBots ---------------------------------------------------------------- +// --------------------------------------- FlashBots ------------------------------------------- // -// BundleAPI offers an API for accepting bundled transactions +// BundleAPI offers an API for accepting bundled transactions. The BundleAPI has been heavily +// inspired by the original mev-geth implementation. +// (https://github.com/flashbots/mev-geth/blob/master/internal/ethapi/api.go#L2038) type BundleAPI struct { b Backend chain core.ChainContext @@ -2148,7 +2150,6 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st } var txs types.Transactions - for _, encodedTx := range args.Txs { tx := new(types.Transaction) if err := tx.UnmarshalBinary(encodedTx); err != nil { @@ -2213,8 +2214,6 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st // this makes sure resources are cleaned up. defer cancel() - vmConfig := vm.Config{} - // Setup the gas pool (also for unmetered requests) // and apply the message. gp := new(core.GasPool).AddGas(math.MaxUint64) @@ -2228,27 +2227,26 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st gasFees := new(big.Int) msg, err := core.TransactionToMessage(txs[0], signer, header.BaseFee) - if err != nil { return nil, err } - blockCtx := core.NewEVMBlockContext(header, s.chain, nil) - txCtx := core.NewEVMTxContext(msg) - chainConfig := s.b.ChainConfig() - rules := chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) - chainRules := vm.NewPrecompileManager(&rules) - - evm := vm.NewEVMWithPrecompiles(blockCtx, txCtx, state, chainConfig, vmConfig, chainRules) + vmenv, vmError, err := s.b.GetEVM(ctx, msg, state, header, &vm.Config{}) + if err != nil { + return nil, err + } for i, tx := range txs { coinbaseBalanceBeforeTx := state.GetBalance(coinbase) state.SetTxContext(tx.Hash(), i) - receipt, result, err := core.ApplyTransactionWithEVMWithResult(evm, s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed) + receipt, result, err := core.ApplyTransactionWithEVMWithResult(vmenv, s.b.ChainConfig(), &coinbase, gp, state, header, tx, &header.GasUsed) if err != nil { return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) } + if err := vmError(); err != nil { + return nil, fmt.Errorf("execution error: %v", err) + } txHash := tx.Hash().String() from, err := types.Sender(signer, tx) @@ -2375,15 +2373,9 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl // Results results := []map[string]interface{}{} - // Copy the original db so we don't modify it - statedb := state.Copy() - // Gas pool gp := new(core.GasPool).AddGas(math.MaxUint64) - // Block context - blockContext := core.NewEVMBlockContext(header, s.chain, &coinbase) - // Feed each of the transactions into the VM ctx // And try and estimate the gas used for i, txArgs := range args.Txs { @@ -2393,7 +2385,7 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl rand.Read(randomHash[:]) // New random hash since its a call - statedb.SetTxContext(randomHash, i) + state.SetTxContext(randomHash, i) // Convert tx args to msg to apply state transition msg, err := txArgs.ToMessage(globalGasCap, header.BaseFee) @@ -2401,11 +2393,11 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl return nil, err } - // Prepare the hashes - txContext := core.NewEVMTxContext(msg) - // Get EVM Environment - vmenv := vm.NewEVM(blockContext, txContext, statedb, s.b.ChainConfig(), vm.Config{NoBaseFee: true}) + vmenv, vmError, err := s.b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}) + if err != nil { + return nil, err + } // Apply state transition result, err := core.ApplyMessage(vmenv, msg, gp) @@ -2413,9 +2405,14 @@ func (s *BundleAPI) EstimateGasBundle(ctx context.Context, args EstimateGasBundl return nil, err } + // Check for the vm error + if err := vmError(); err != nil { + return nil, fmt.Errorf("execution error: %v", err) + } + // Modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber)) + state.Finalise(vmenv.ChainConfig().IsEIP158(blockNumber)) // Append result jsonResult := map[string]interface{}{