From aabc57b1107d5cc0de0a042247f426e7b885e551 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Wed, 2 Mar 2022 18:53:03 -0500 Subject: [PATCH 01/20] first attempt at cherry picking eth_callBundle --- core/state_processor.go | 58 ++++- eth/backend.go | 2 +- internal/ethapi/api.go | 435 +++++++++++++++++++++++++++++++++++++ internal/ethapi/backend.go | 7 +- les/client.go | 2 +- 5 files changed, 500 insertions(+), 4 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index b6025e4b94b6..0ad8a241f63e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -28,8 +28,8 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/custom" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" ) // StateProcessor is a basic Processor, which takes care of transitioning @@ -153,6 +153,51 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } +func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, 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.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, @@ -167,3 +212,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } + +func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { + msg, err := tx.AsMessage(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 a53982166d78..3609e3dda5a4 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -292,7 +292,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/internal/ethapi/api.go b/internal/ethapi/api.go index 65e34752bf41..b9cd6b8ed0d0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -18,6 +18,8 @@ package ethapi import ( "context" + "crypto/rand" + "encoding/hex" "errors" "fmt" "math/big" @@ -47,6 +49,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" ) // PublicEthereumAPI provides an API to access Ethereum related information. @@ -2083,3 +2086,435 @@ 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} +} + +// SendBundleArgs represents the arguments for a SendBundle 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"` +} + +// SendMegabundleArgs represents the arguments for a SendMegabundle call. +type SendMegabundleArgs struct { + Txs []hexutil.Bytes `json:"txs"` + BlockNumber uint64 `json:"blockNumber"` + MinTimestamp *uint64 `json:"minTimestamp"` + MaxTimestamp *uint64 `json:"maxTimestamp"` + RevertingTxHashes []common.Hash `json:"revertingTxHashes"` + RelaySignature hexutil.Bytes `json:"relaySignature"` +} + +// UnsignedMegabundle is used for serialization and subsequent digital signing. +type UnsignedMegabundle struct { + Txs []hexutil.Bytes + BlockNumber uint64 + MinTimestamp uint64 + MaxTimestamp uint64 + RevertingTxHashes []common.Hash +} + +// // 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 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 args.Txs { +// tx := new(types.Transaction) +// if err := tx.UnmarshalBinary(encodedTx); err != nil { +// return err +// } +// txs = append(txs, tx) +// } + +// var minTimestamp, maxTimestamp uint64 +// if args.MinTimestamp != nil { +// minTimestamp = *args.MinTimestamp +// } +// if args.MaxTimestamp != nil { +// maxTimestamp = *args.MaxTimestamp +// } + +// return s.b.SendBundle(ctx, txs, args.BlockNumber, minTimestamp, maxTimestamp, args.RevertingTxHashes) +// } + +// // Recovers the Ethereum address of the trusted relay that signed the megabundle. +// func RecoverRelayAddress(args SendMegabundleArgs) (common.Address, error) { +// megabundle := UnsignedMegabundle{Txs: args.Txs, BlockNumber: args.BlockNumber, RevertingTxHashes: args.RevertingTxHashes} +// if args.MinTimestamp != nil { +// megabundle.MinTimestamp = *args.MinTimestamp +// } else { +// megabundle.MinTimestamp = 0 +// } +// if args.MaxTimestamp != nil { +// megabundle.MaxTimestamp = *args.MaxTimestamp +// } else { +// megabundle.MaxTimestamp = 0 +// } +// rlpEncoding, _ := rlp.EncodeToBytes(megabundle) +// signature := args.RelaySignature +// signature[64] -= 27 // account for Ethereum V +// recoveredPubkey, err := crypto.SigToPub(accounts.TextHash(rlpEncoding), args.RelaySignature) +// if err != nil { +// return common.Address{}, err +// } +// return crypto.PubkeyToAddress(*recoveredPubkey), nil +// } + +// // SendMegabundle will add the signed megabundle to one of the workers for evaluation. +// func (s *PrivateTxBundleAPI) SendMegabundle(ctx context.Context, args SendMegabundleArgs) error { +// log.Info("Received a Megabundle request", "signature", args.RelaySignature) +// var txs types.Transactions +// if len(args.Txs) == 0 { +// return errors.New("megabundle missing txs") +// } +// if args.BlockNumber == 0 { +// return errors.New("megabundle missing blockNumber") +// } +// for _, encodedTx := range args.Txs { +// tx := new(types.Transaction) +// if err := tx.UnmarshalBinary(encodedTx); err != nil { +// return err +// } +// txs = append(txs, tx) +// } +// var minTimestamp, maxTimestamp uint64 +// if args.MinTimestamp != nil { +// minTimestamp = *args.MinTimestamp +// } +// if args.MaxTimestamp != nil { +// maxTimestamp = *args.MaxTimestamp +// } +// relayAddr, err := RecoverRelayAddress(args) +// log.Info("Megabundle", "relayAddr", relayAddr, "err", err) +// if err != nil { +// return err +// } +// return s.b.SendMegabundle(ctx, txs, rpc.BlockNumber(args.BlockNumber), minTimestamp, maxTimestamp, args.RevertingTxHashes, relayAddr) +// } + +// 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/internal/ethapi/backend.go b/internal/ethapi/backend.go index 362b1bf02d35..93aebf337625 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -97,7 +97,7 @@ type Backend interface { Engine() consensus.Engine } -func GetAPIs(apiBackend Backend) []rpc.API { +func GetAPIs(apiBackend Backend, chain *core.BlockChain) []rpc.API { nonceLock := new(AddrLocker) return []rpc.API{ { @@ -139,6 +139,11 @@ func GetAPIs(apiBackend Backend) []rpc.API { Version: "1.0", Service: NewPrivateAccountAPI(apiBackend, nonceLock), Public: false, + }, { + Namespace: "eth", + Version: "1.0", + Service: NewBundleAPI(apiBackend, chain), + Public: true, }, } } diff --git a/les/client.go b/les/client.go index 43207f3443ec..922c51627824 100644 --- a/les/client.go +++ b/les/client.go @@ -282,7 +282,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{ { From 3ad8176d2f5f126dd133c568728f8ba4924bba13 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Wed, 2 Mar 2022 20:02:47 -0800 Subject: [PATCH 02/20] intermediate comments --- core/state_processor.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/state_processor.go b/core/state_processor.go index 0ad8a241f63e..85eddd12e4ee 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -213,7 +213,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } +// ApplyTransactionWithResult attempts to apply a transaction to the given state database +// and returns an intermediate state root to be used in conjunction with other transactions func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { + // can we accept the other message type here? msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) if err != nil { return nil, nil, err From 8d11d6ef89ac199f8601d17fe058af90ba9418ae Mon Sep 17 00:00:00 2001 From: Dmarz Date: Thu, 3 Mar 2022 16:21:16 -0700 Subject: [PATCH 03/20] intermediate notes, not working --- core/state_processor.go | 15 ++++++++++++++- core/tx_pool.go | 36 ++++++++++++++++++------------------ core/types/transaction.go | 1 + internal/ethapi/api.go | 13 +++++++++++++ 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 85eddd12e4ee..3fd0878faeba 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -164,6 +164,13 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b return nil, nil, err } + traceResult := result.Return() + returnVal := fmt.Sprintf("%x", traceResult) + fmt.Println(returnVal) + + if err != nil { + return nil, nil, err + } // Update the state with pending changes. var root []byte if config.IsByzantium(header.Number) { @@ -221,8 +228,14 @@ func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, aut if err != nil { return nil, nil, err } + var ( + tracer vm.EVMLogger + ) + // Add tracer native go tracer, code from eth/tracers/api.go + tracer = custom.NewCallTracer(statedb) + // 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) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) } diff --git a/core/tx_pool.go b/core/tx_pool.go index 141410602ea5..c108355f204e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -395,7 +395,7 @@ func (pool *TxPool) loop() { pool.removeTx(tx.Hash(), true) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: list, + Txs: list, Reason: dropOld, }) queuedEvictionMeter.Mark(int64(len(list))) @@ -474,7 +474,7 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { } pool.priced.Removed(len(drop)) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: drop, + Txs: drop, Reason: dropGasPriceUpdated, }) } @@ -728,7 +728,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.removeTx(tx.Hash(), false) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: drop, + Txs: drop, Reason: dropUnderpriced, }) } @@ -747,8 +747,8 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.priced.Removed(1) pendingReplaceMeter.Mark(1) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: []*types.Transaction{old}, - Reason: dropReplaced, + Txs: []*types.Transaction{old}, + Reason: dropReplaced, Replacement: tx, }) } @@ -803,7 +803,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo pool.priced.Removed(1) queuedReplaceMeter.Mark(1) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: []*types.Transaction{old}, + Txs: []*types.Transaction{old}, Reason: dropReplaced, }) } else { @@ -863,7 +863,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T pool.priced.Removed(1) pendingReplaceMeter.Mark(1) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: []*types.Transaction{old}, + Txs: []*types.Transaction{old}, Reason: dropReplaced, }) } else { @@ -965,7 +965,7 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error { } if err != nil { pool.rejectTxFeed.Send(RejectedTxEvent{ - Tx: txs[nilSlot], + Tx: txs[nilSlot], Reason: err, }) } @@ -1065,7 +1065,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { // Reduce the pending counter pendingGauge.Dec(int64(1 + len(invalids))) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: invalids, + Txs: invalids, Reason: dropUnexecutable, }) return @@ -1375,7 +1375,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans } log.Trace("Removed old queued transactions", "count", len(forwards)) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: forwards, + Txs: forwards, Reason: dropLowNonce, }) // Drop all transactions that are too costly (low balance or out of gas) @@ -1387,7 +1387,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans log.Trace("Removed unpayable queued transactions", "count", len(drops)) queuedNofundsMeter.Mark(int64(len(drops))) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: drops, + Txs: drops, Reason: dropUnpayable, }) @@ -1413,7 +1413,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans } queuedRateLimitMeter.Mark(int64(len(caps))) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: caps, + Txs: caps, Reason: dropAccountCap, }) } @@ -1481,7 +1481,7 @@ func (pool *TxPool) truncatePending() { log.Trace("Removed fairness-exceeding pending transaction", "hash", hash) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: caps, + Txs: caps, Reason: dropAccountCap, }) pool.priced.Removed(len(caps)) @@ -1512,7 +1512,7 @@ func (pool *TxPool) truncatePending() { log.Trace("Removed fairness-exceeding pending transaction", "hash", hash) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: caps, + Txs: caps, Reason: dropAccountCap, }) pool.priced.Removed(len(caps)) @@ -1560,7 +1560,7 @@ func (pool *TxPool) truncateQueue() { pool.removeTx(tx.Hash(), true) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: txs, + Txs: txs, Reason: dropTruncating, }) drop -= size @@ -1574,7 +1574,7 @@ func (pool *TxPool) truncateQueue() { drop-- queuedRateLimitMeter.Mark(1) pool.dropTxFeed.Send(DropTxsEvent{ - Txs: []*types.Transaction{txs[i]}, + Txs: []*types.Transaction{txs[i]}, Reason: dropTruncating, }) } @@ -1601,7 +1601,7 @@ func (pool *TxPool) demoteUnexecutables() { log.Trace("Removed old pending transaction", "hash", hash) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: olds, + Txs: olds, Reason: dropLowNonce, }) // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later @@ -1612,7 +1612,7 @@ func (pool *TxPool) demoteUnexecutables() { pool.all.Remove(hash) } pool.dropTxFeed.Send(DropTxsEvent{ - Txs: drops, + Txs: drops, Reason: dropUnpayable, }) pool.priced.Removed(len(olds) + len(drops)) diff --git a/core/types/transaction.go b/core/types/transaction.go index 83f1766e67e2..2b80c68e5d7f 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -611,6 +611,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) { msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap) } var err error + // recover sender address msg.from, err = Sender(s, tx) return msg, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b9cd6b8ed0d0..a9594f7fbf4b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2250,6 +2250,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st return nil, errors.New("bundle missing blockNumber") } + // ******** old tx stuff ******** var txs types.Transactions for _, encodedTx := range args.Txs { @@ -2259,13 +2260,16 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st } txs = append(txs, tx) } + // ******** old tx stuff ******** defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) + // default timeout timeoutMilliSeconds := int64(5000) if args.Timeout != nil { timeoutMilliSeconds = *args.Timeout } timeout := time.Millisecond * time.Duration(timeoutMilliSeconds) + // grab given header, add default? state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash) if state == nil || err != nil { return nil, err @@ -2280,6 +2284,8 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st if args.Coinbase != nil { coinbase = common.HexToAddress(*args.Coinbase) } + // fields not used in EstimateGasBundle + // in EstimateGasBundle, these fields are pulled in from `parent` difficulty := parent.Difficulty if args.Difficulty != nil { difficulty = args.Difficulty @@ -2294,6 +2300,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st } else if s.b.ChainConfig().IsLondon(big.NewInt(args.BlockNumber.Int64())) { baseFee = misc.CalcBaseFee(s.b.ChainConfig(), parent) } + // fields not used in EstimateGasBundle header := &types.Header{ ParentHash: parent.Hash(), Number: blockNumber, @@ -2326,13 +2333,19 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st coinbaseBalanceBefore := state.GetBalance(coinbase) bundleHash := sha3.NewLegacyKeccak256() + // want to get rid of this signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) var totalGasUsed uint64 gasFees := new(big.Int) + + // loop over transactions and pass them into evm + // return the intermediate state for i, tx := range txs { + // we may not need this? coinbaseBalanceBeforeTx := state.GetBalance(coinbase) state.Prepare(tx.Hash(), i) + // we need ApplyTransactionWithResult to return tracing results 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()) From 2936819d4f3ed652d9dbd5997ddb805544de301f Mon Sep 17 00:00:00 2001 From: Dmarz Date: Thu, 10 Mar 2022 16:35:23 -0700 Subject: [PATCH 04/20] commenting out code to test core functionality --- core/state_processor.go | 42 ++++++++++------ internal/ethapi/api.go | 108 ++++++++++++++++++++-------------------- 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 3fd0878faeba..caf9477c801b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -153,7 +153,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } -func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, *ExecutionResult, error) { +func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, 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) @@ -182,22 +182,22 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b // 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} + receipt := &types.Receipt{Type: 0, PostState: root, CumulativeGasUsed: *usedGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { receipt.Status = types.ReceiptStatusSuccessful } - receipt.TxHash = tx.Hash() + // 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()) - } + // 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.Hash()) + // receipt.Logs = statedb.GetLogs(tx.Hash(), header.Hash()) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.BlockHash = header.Hash() receipt.BlockNumber = header.Number @@ -222,12 +222,26 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // ApplyTransactionWithResult attempts to apply a transaction to the given state database // and returns an intermediate state root to be used in conjunction with other transactions -func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { - // can we accept the other message type here? - msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) - if err != nil { - return nil, nil, err - } +// func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { +// // can we accept the other message type here? +// msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) +// if err != nil { +// return nil, nil, err +// } +// var ( +// tracer vm.EVMLogger +// ) +// // Add tracer native go tracer, code from eth/tracers/api.go +// tracer = custom.NewCallTracer(statedb) + +// // Create a new context to be used in the EVM environment +// blockContext := NewEVMBlockContext(header, bc, author) +// vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) +// return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +// } + +func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { + // Main functionality needed now is passing in a tracer and making sure applyTransactionWithResult works var ( tracer vm.EVMLogger ) @@ -237,5 +251,5 @@ 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, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) + return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, msg, usedGas, vmenv) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a9594f7fbf4b..00579fd585df 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2225,7 +2225,8 @@ func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI { // CallBundleArgs represents the arguments for a call. type CallBundleArgs struct { - Txs []hexutil.Bytes `json:"txs"` + //Txs []hexutil.Bytes `json:"txs"` + UnsignedTxs []TransactionArgs `json:"txs"` BlockNumber rpc.BlockNumber `json:"blockNumber"` StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"` Coinbase *string `json:"coinbase"` @@ -2243,33 +2244,35 @@ type CallBundleArgs struct { // 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") + // ******** Unsigned Txn Attempt + if len(args.UnsignedTxs) == 0 { + return nil, errors.New("bundle missing unsigned txs") } + + // ******** Unsigned Txn Attempt + // if len(args.Txs) == 0 { + // return nil, errors.New("bundle missing txs") + // } if args.BlockNumber == 0 { return nil, errors.New("bundle missing blockNumber") } - // ******** old tx stuff ******** - var txs types.Transactions + // 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) - } - // ******** old tx stuff ******** + // 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()) - // default timeout timeoutMilliSeconds := int64(5000) if args.Timeout != nil { timeoutMilliSeconds = *args.Timeout } timeout := time.Millisecond * time.Duration(timeoutMilliSeconds) - // grab given header, add default? state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash) if state == nil || err != nil { return nil, err @@ -2284,8 +2287,6 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st if args.Coinbase != nil { coinbase = common.HexToAddress(*args.Coinbase) } - // fields not used in EstimateGasBundle - // in EstimateGasBundle, these fields are pulled in from `parent` difficulty := parent.Difficulty if args.Difficulty != nil { difficulty = args.Difficulty @@ -2300,7 +2301,6 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st } else if s.b.ChainConfig().IsLondon(big.NewInt(args.BlockNumber.Int64())) { baseFee = misc.CalcBaseFee(s.b.ChainConfig(), parent) } - // fields not used in EstimateGasBundle header := &types.Header{ ParentHash: parent.Hash(), Number: blockNumber, @@ -2333,47 +2333,50 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st coinbaseBalanceBefore := state.GetBalance(coinbase) bundleHash := sha3.NewLegacyKeccak256() - // want to get rid of this - signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) + // signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) var totalGasUsed uint64 gasFees := new(big.Int) - // loop over transactions and pass them into evm - // return the intermediate state - for i, tx := range txs { - // we may not need this? - coinbaseBalanceBeforeTx := state.GetBalance(coinbase) - state.Prepare(tx.Hash(), i) + // RPC Call gas cap + globalGasCap := s.b.RPCGasCap() + + for i, tx := range args.UnsignedTxs { + // fb: dont need coinbase code, but then I realized, fb searchers could use us + // coinbaseBalanceBeforeTx := state.GetBalance(coinbase) + // fb: we need to use a random hash + // state.Prepare(tx.Hash(), i) - // we need ApplyTransactionWithResult to return tracing results - receipt, result, err := core.ApplyTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, tx, &header.GasUsed, vmconfig) + // Since its a txCall we'll just prepare the + // state with a random hash + var randomHash common.Hash + // TODO : not sure what rand.Read does + rand.Read(randomHash[:]) + // New random hash since its a call + // TODO : estimateGas uses a copy of the stateDb... may be hard to test + state.Prepare(randomHash, i) + + // bn : prepare msg out here + // TODO : figure out more of the difference between ToMessage and AsMessage + // it honestly might mainly just be gas + + msg, err := tx.ToMessage(globalGasCap, header.BaseFee) if err != nil { - return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) + return nil, err } - txHash := tx.Hash().String() - from, err := types.Sender(signer, tx) + // bn : using our new unsigned func + receipt, result, err := core.ApplyUnsignedTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, msg, &header.GasUsed, vmconfig) if err != nil { - return nil, fmt.Errorf("err: %w; txhash %s", err, tx.Hash()) - } - to := "0x" - if tx.To() != nil { - to = tx.To().String() + // TODO : create better log here + return nil, fmt.Errorf("err: %w; txhash %s", err, tx.From) } + // TODO : Add gas features back jsonResult := map[string]interface{}{ - "txHash": txHash, "gasUsed": receipt.GasUsed, - "fromAddress": from.String(), - "toAddress": to, + "fromAddress": tx.from, + "toAddress": tx.To, + // Add trace results here } - 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() @@ -2385,21 +2388,16 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st 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["coinbaseDiff"] = coinbaseDiff.String() ret["gasFees"] = gasFees.String() - ret["ethSentToCoinbase"] = new(big.Int).Sub(coinbaseDiff, 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() From 86b46752b555531b77a444d71cfbfbe8ffcb4d94 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Thu, 10 Mar 2022 16:59:20 -0700 Subject: [PATCH 05/20] add logs --- internal/ethapi/api.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 00579fd585df..13f5095f8b6d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2245,10 +2245,11 @@ type CallBundleArgs struct { // nonce and ensuring validity func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[string]interface{}, error) { // ******** Unsigned Txn Attempt + log.Error("here1") if len(args.UnsignedTxs) == 0 { return nil, errors.New("bundle missing unsigned txs") } - + log.Error("here2") // ******** Unsigned Txn Attempt // if len(args.Txs) == 0 { // return nil, errors.New("bundle missing txs") @@ -2256,7 +2257,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st if args.BlockNumber == 0 { return nil, errors.New("bundle missing blockNumber") } - + log.Error("here1") // var txs types.Transactions // for _, encodedTx := range args.Txs { @@ -2267,7 +2268,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st // txs = append(txs, tx) // } defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) - + log.Error("here1") timeoutMilliSeconds := int64(5000) if args.Timeout != nil { timeoutMilliSeconds = *args.Timeout From f7030f4d6d8ef64e6a00b8c56efaae9aa8ac3d84 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Thu, 17 Mar 2022 21:28:34 -0400 Subject: [PATCH 06/20] redo param parse --- internal/ethapi/api.go | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 13f5095f8b6d..80ade4eba294 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -49,7 +49,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/tyler-smith/go-bip39" - "golang.org/x/crypto/sha3" ) // PublicEthereumAPI provides an API to access Ethereum related information. @@ -2225,8 +2224,7 @@ func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI { // CallBundleArgs represents the arguments for a call. type CallBundleArgs struct { - //Txs []hexutil.Bytes `json:"txs"` - UnsignedTxs []TransactionArgs `json:"txs"` + Txs []TransactionArgs `json:"txs"` BlockNumber rpc.BlockNumber `json:"blockNumber"` StateBlockNumberOrHash rpc.BlockNumberOrHash `json:"stateBlockNumber"` Coinbase *string `json:"coinbase"` @@ -2245,30 +2243,18 @@ type CallBundleArgs struct { // nonce and ensuring validity func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[string]interface{}, error) { // ******** Unsigned Txn Attempt - log.Error("here1") - if len(args.UnsignedTxs) == 0 { + log.Info("here1") + if len(args.Txs) == 0 { return nil, errors.New("bundle missing unsigned txs") } log.Error("here2") - // ******** Unsigned Txn Attempt - // if len(args.Txs) == 0 { - // return nil, errors.New("bundle missing txs") - // } if args.BlockNumber == 0 { return nil, errors.New("bundle missing blockNumber") } - log.Error("here1") // 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()) - log.Error("here1") + log.Error("here3") timeoutMilliSeconds := int64(5000) if args.Timeout != nil { timeoutMilliSeconds = *args.Timeout @@ -2311,6 +2297,9 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st Coinbase: coinbase, BaseFee: baseFee, } + if header != nil { + log.Error("here4") + } // Setup context so it may be cancelled the call has completed // or, in case of unmetered gas, setup a context with a timeout. @@ -2333,7 +2322,8 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st results := []map[string]interface{}{} coinbaseBalanceBefore := state.GetBalance(coinbase) - bundleHash := sha3.NewLegacyKeccak256() + // is there utility to having a bundle hash? + // bundleHash := sha3.NewLegacyKeccak256() // signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) var totalGasUsed uint64 gasFees := new(big.Int) @@ -2341,7 +2331,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st // RPC Call gas cap globalGasCap := s.b.RPCGasCap() - for i, tx := range args.UnsignedTxs { + for i, tx := range args.Txs { // fb: dont need coinbase code, but then I realized, fb searchers could use us // coinbaseBalanceBeforeTx := state.GetBalance(coinbase) // fb: we need to use a random hash @@ -2396,14 +2386,16 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st ret := map[string]interface{}{} ret["results"] = results coinbaseDiff := new(big.Int).Sub(state.GetBalance(coinbase), coinbaseBalanceBefore) - // ret["coinbaseDiff"] = coinbaseDiff.String() + ret["coinbaseDiff"] = coinbaseDiff.String() ret["gasFees"] = gasFees.String() - // ret["ethSentToCoinbase"] = new(big.Int).Sub(coinbaseDiff, 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)) + // ret["bundleHash"] = "0x" + common.Bytes2Hex(bundleHash.Sum(nil)) + + ret["args"] = header return ret, nil } From cc8eecf1b47422e565fae5147849dd6c8bc72706 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Thu, 17 Mar 2022 21:49:24 -0400 Subject: [PATCH 07/20] comment out bundleGasPrice bc div by 0 --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 80ade4eba294..e3b6a5512caf 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2389,7 +2389,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st 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["bundleGasPrice"] = new(big.Int).Div(coinbaseDiff, big.NewInt(int64(totalGasUsed))).String() ret["totalGasUsed"] = totalGasUsed ret["stateBlockNumber"] = parent.Number.Int64() From 191219479e31815c8904d06cd8b271eecf99ae83 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Fri, 18 Mar 2022 00:02:35 -0400 Subject: [PATCH 08/20] call tx.from() --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e3b6a5512caf..588c3136d3a8 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2364,7 +2364,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st // TODO : Add gas features back jsonResult := map[string]interface{}{ "gasUsed": receipt.GasUsed, - "fromAddress": tx.from, + "fromAddress": tx.from(), "toAddress": tx.To, // Add trace results here } From 1720ba8533699d6e848b9a00fd99d30e6a1a8d5d Mon Sep 17 00:00:00 2001 From: Dmarz Date: Tue, 22 Mar 2022 15:16:30 -0400 Subject: [PATCH 09/20] try new struct logger tracert --- core/state_processor.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index caf9477c801b..e0f84ac2f0ae 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/custom" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -245,8 +246,11 @@ func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainCont var ( tracer vm.EVMLogger ) + // Add tracer native go tracer, code from eth/tracers/api.go - tracer = custom.NewCallTracer(statedb) + //tracer = custom.NewCallTracer(statedb) + + tracer = logger.NewStructLogger(nil) // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) From 9e7f56c55cce39d6cbaa02838d1d2cccdc2a2b78 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Tue, 22 Mar 2022 21:23:15 -0400 Subject: [PATCH 10/20] implied type on logger --- core/state_processor.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index e0f84ac2f0ae..2c5fb026df1f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -154,7 +154,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } -func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM) (*types.Receipt, *ExecutionResult, error) { +func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM, tracer *logger.StructLogger) (*types.Receipt, *ExecutionResult, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -165,7 +165,7 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b return nil, nil, err } - traceResult := result.Return() + traceResult := tracer.StructLogs() returnVal := fmt.Sprintf("%x", traceResult) fmt.Println(returnVal) @@ -243,17 +243,17 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { // Main functionality needed now is passing in a tracer and making sure applyTransactionWithResult works - var ( - tracer vm.EVMLogger - ) + // var ( + // tracer logger.NewStructLogger + // ) // Add tracer native go tracer, code from eth/tracers/api.go //tracer = custom.NewCallTracer(statedb) - tracer = logger.NewStructLogger(nil) + tracer := logger.NewStructLogger(nil) // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, msg, usedGas, vmenv) + return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, msg, usedGas, vmenv, tracer) } From 6f14602579b79044e809d763a2793157b5122412 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Tue, 22 Mar 2022 22:46:24 -0400 Subject: [PATCH 11/20] add trace results to call --- core/state_processor.go | 63 +++++++++++++++++++++++++++++++++++++---- internal/ethapi/api.go | 3 +- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 2c5fb026df1f..bc32296176f2 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -154,7 +154,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } -func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM, tracer *logger.StructLogger) (*types.Receipt, *ExecutionResult, error) { +func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM, tracer *logger.StructLogger) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -162,15 +162,15 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b // Apply the transaction to the current state (included in the env). result, err := ApplyMessage(evm, msg, gp) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - traceResult := tracer.StructLogs() + traceResult := FormatLogs(tracer.StructLogs()) returnVal := fmt.Sprintf("%x", traceResult) fmt.Println(returnVal) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Update the state with pending changes. var root []byte @@ -203,7 +203,7 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b receipt.BlockHash = header.Hash() receipt.BlockNumber = header.Number receipt.TransactionIndex = uint(statedb.TxIndex()) - return receipt, result, err + return receipt, result, traceResult, err } // ApplyTransaction attempts to apply a transaction to the given state database @@ -241,7 +241,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) // } -func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { +func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { // Main functionality needed now is passing in a tracer and making sure applyTransactionWithResult works // var ( // tracer logger.NewStructLogger @@ -257,3 +257,54 @@ func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainCont vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, msg, usedGas, vmenv, tracer) } + +// StructLogRes stores a structured log emitted by the EVM while replaying a +// transaction in debug mode +type StructLogRes struct { + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error string `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` +} + +// FormatLogs formats EVM returned structured logs for json output +func FormatLogs(logs []logger.StructLog) []StructLogRes { + formatted := make([]StructLogRes, len(logs)) + for index, trace := range logs { + formatted[index] = StructLogRes{ + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.ErrorString(), + } + if trace.Stack != nil { + stack := make([]string, len(trace.Stack)) + for i, stackValue := range trace.Stack { + stack[i] = stackValue.Hex() + } + formatted[index].Stack = &stack + } + if trace.Memory != nil { + memory := make([]string, 0, (len(trace.Memory)+31)/32) + for i := 0; i+32 <= len(trace.Memory); i += 32 { + memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + } + formatted[index].Memory = &memory + } + if trace.Storage != nil { + storage := make(map[string]string) + for i, storageValue := range trace.Storage { + storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + formatted[index].Storage = &storage + } + } + return formatted +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 588c3136d3a8..c7aa792f00b0 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2356,7 +2356,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st } // bn : using our new unsigned func - receipt, result, err := core.ApplyUnsignedTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, msg, &header.GasUsed, vmconfig) + receipt, result, traceResult, err := core.ApplyUnsignedTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, msg, &header.GasUsed, vmconfig) if err != nil { // TODO : create better log here return nil, fmt.Errorf("err: %w; txhash %s", err, tx.From) @@ -2366,6 +2366,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st "gasUsed": receipt.GasUsed, "fromAddress": tx.from(), "toAddress": tx.To, + "traceResult": traceResult, // Add trace results here } if result.Err != nil { From aa16fddbd51d4fd020e438cb743f3b537c80604a Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 28 Mar 2022 13:22:01 -0400 Subject: [PATCH 12/20] payload changes --- internal/ethapi/api.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c7aa792f00b0..7f8304413a85 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2369,6 +2369,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st "traceResult": traceResult, // Add trace results here } + totalGasUsed += receipt.GasUsed if result.Err != nil { jsonResult["error"] = result.Err.Error() revert := result.Revert() @@ -2386,15 +2387,9 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st 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)) + ret["gasUsed"] = totalGasUsed + ret["blockNumber"] = parent.Number.Int64() ret["args"] = header return ret, nil From 31a03c2d2f314c63d0894b744a00982a2554361e Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 28 Mar 2022 13:30:41 -0400 Subject: [PATCH 13/20] clean up --- core/state_processor.go | 31 +---- internal/ethapi/api.go | 263 +--------------------------------------- 2 files changed, 3 insertions(+), 291 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 05f53c3ccd11..93121e44ef01 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -27,9 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/custom" "github.com/ethereum/go-ethereum/eth/tracers/logger" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -211,35 +209,8 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } -// ApplyTransactionWithResult attempts to apply a transaction to the given state database -// and returns an intermediate state root to be used in conjunction with other transactions -// func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) { -// // can we accept the other message type here? -// msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) -// if err != nil { -// return nil, nil, err -// } -// var ( -// tracer vm.EVMLogger -// ) -// // Add tracer native go tracer, code from eth/tracers/api.go -// tracer = custom.NewCallTracer(statedb) - -// // Create a new context to be used in the EVM environment -// blockContext := NewEVMBlockContext(header, bc, author) -// vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) -// return applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) -// } - func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { - // Main functionality needed now is passing in a tracer and making sure applyTransactionWithResult works - // var ( - // tracer logger.NewStructLogger - // ) - - // Add tracer native go tracer, code from eth/tracers/api.go - //tracer = custom.NewCallTracer(statedb) - + // Create struct logger to get JSON stack traces tracer := logger.NewStructLogger(nil) // Create a new context to be used in the EVM environment diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7f8304413a85..64b805285031 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2098,119 +2098,6 @@ func NewPrivateTxBundleAPI(b Backend) *PrivateTxBundleAPI { return &PrivateTxBundleAPI{b} } -// SendBundleArgs represents the arguments for a SendBundle 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"` -} - -// SendMegabundleArgs represents the arguments for a SendMegabundle call. -type SendMegabundleArgs struct { - Txs []hexutil.Bytes `json:"txs"` - BlockNumber uint64 `json:"blockNumber"` - MinTimestamp *uint64 `json:"minTimestamp"` - MaxTimestamp *uint64 `json:"maxTimestamp"` - RevertingTxHashes []common.Hash `json:"revertingTxHashes"` - RelaySignature hexutil.Bytes `json:"relaySignature"` -} - -// UnsignedMegabundle is used for serialization and subsequent digital signing. -type UnsignedMegabundle struct { - Txs []hexutil.Bytes - BlockNumber uint64 - MinTimestamp uint64 - MaxTimestamp uint64 - RevertingTxHashes []common.Hash -} - -// // 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 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 args.Txs { -// tx := new(types.Transaction) -// if err := tx.UnmarshalBinary(encodedTx); err != nil { -// return err -// } -// txs = append(txs, tx) -// } - -// var minTimestamp, maxTimestamp uint64 -// if args.MinTimestamp != nil { -// minTimestamp = *args.MinTimestamp -// } -// if args.MaxTimestamp != nil { -// maxTimestamp = *args.MaxTimestamp -// } - -// return s.b.SendBundle(ctx, txs, args.BlockNumber, minTimestamp, maxTimestamp, args.RevertingTxHashes) -// } - -// // Recovers the Ethereum address of the trusted relay that signed the megabundle. -// func RecoverRelayAddress(args SendMegabundleArgs) (common.Address, error) { -// megabundle := UnsignedMegabundle{Txs: args.Txs, BlockNumber: args.BlockNumber, RevertingTxHashes: args.RevertingTxHashes} -// if args.MinTimestamp != nil { -// megabundle.MinTimestamp = *args.MinTimestamp -// } else { -// megabundle.MinTimestamp = 0 -// } -// if args.MaxTimestamp != nil { -// megabundle.MaxTimestamp = *args.MaxTimestamp -// } else { -// megabundle.MaxTimestamp = 0 -// } -// rlpEncoding, _ := rlp.EncodeToBytes(megabundle) -// signature := args.RelaySignature -// signature[64] -= 27 // account for Ethereum V -// recoveredPubkey, err := crypto.SigToPub(accounts.TextHash(rlpEncoding), args.RelaySignature) -// if err != nil { -// return common.Address{}, err -// } -// return crypto.PubkeyToAddress(*recoveredPubkey), nil -// } - -// // SendMegabundle will add the signed megabundle to one of the workers for evaluation. -// func (s *PrivateTxBundleAPI) SendMegabundle(ctx context.Context, args SendMegabundleArgs) error { -// log.Info("Received a Megabundle request", "signature", args.RelaySignature) -// var txs types.Transactions -// if len(args.Txs) == 0 { -// return errors.New("megabundle missing txs") -// } -// if args.BlockNumber == 0 { -// return errors.New("megabundle missing blockNumber") -// } -// for _, encodedTx := range args.Txs { -// tx := new(types.Transaction) -// if err := tx.UnmarshalBinary(encodedTx); err != nil { -// return err -// } -// txs = append(txs, tx) -// } -// var minTimestamp, maxTimestamp uint64 -// if args.MinTimestamp != nil { -// minTimestamp = *args.MinTimestamp -// } -// if args.MaxTimestamp != nil { -// maxTimestamp = *args.MaxTimestamp -// } -// relayAddr, err := RecoverRelayAddress(args) -// log.Info("Megabundle", "relayAddr", relayAddr, "err", err) -// if err != nil { -// return err -// } -// return s.b.SendMegabundle(ctx, txs, rpc.BlockNumber(args.BlockNumber), minTimestamp, maxTimestamp, args.RevertingTxHashes, relayAddr) -// } - // BundleAPI offers an API for accepting bundled transactions type BundleAPI struct { b Backend @@ -2242,19 +2129,15 @@ type CallBundleArgs struct { // 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) { - // ******** Unsigned Txn Attempt - log.Info("here1") if len(args.Txs) == 0 { return nil, errors.New("bundle missing unsigned txs") } - log.Error("here2") if args.BlockNumber == 0 { return nil, errors.New("bundle missing blockNumber") } - // var txs types.Transactions defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) - log.Error("here3") + timeoutMilliSeconds := int64(5000) if args.Timeout != nil { timeoutMilliSeconds = *args.Timeout @@ -2320,11 +2203,7 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st gp := new(core.GasPool).AddGas(math.MaxUint64) results := []map[string]interface{}{} - coinbaseBalanceBefore := state.GetBalance(coinbase) - // is there utility to having a bundle hash? - // bundleHash := sha3.NewLegacyKeccak256() - // signer := types.MakeSigner(s.b.ChainConfig(), blockNumber) var totalGasUsed uint64 gasFees := new(big.Int) @@ -2332,42 +2211,28 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st globalGasCap := s.b.RPCGasCap() for i, tx := range args.Txs { - // fb: dont need coinbase code, but then I realized, fb searchers could use us - // coinbaseBalanceBeforeTx := state.GetBalance(coinbase) - // fb: we need to use a random hash - // state.Prepare(tx.Hash(), i) - // Since its a txCall we'll just prepare the // state with a random hash var randomHash common.Hash - // TODO : not sure what rand.Read does rand.Read(randomHash[:]) // New random hash since its a call - // TODO : estimateGas uses a copy of the stateDb... may be hard to test state.Prepare(randomHash, i) - // bn : prepare msg out here - // TODO : figure out more of the difference between ToMessage and AsMessage - // it honestly might mainly just be gas - msg, err := tx.ToMessage(globalGasCap, header.BaseFee) if err != nil { return nil, err } - // bn : using our new unsigned func receipt, result, traceResult, err := core.ApplyUnsignedTransactionWithResult(s.b.ChainConfig(), s.chain, &coinbase, gp, state, header, msg, &header.GasUsed, vmconfig) if err != nil { - // TODO : create better log here return nil, fmt.Errorf("err: %w; txhash %s", err, tx.From) } - // TODO : Add gas features back + jsonResult := map[string]interface{}{ "gasUsed": receipt.GasUsed, "fromAddress": tx.from(), "toAddress": tx.To, "traceResult": traceResult, - // Add trace results here } totalGasUsed += receipt.GasUsed if result.Err != nil { @@ -2394,127 +2259,3 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st ret["args"] = header 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 -} From e5c9cce70219e74cc892b1e8af87ba3e5db1f1ab Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 28 Mar 2022 13:32:06 -0400 Subject: [PATCH 14/20] more clean up --- internal/ethapi/api.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 64b805285031..f7f3bdf5f277 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2086,7 +2086,7 @@ func toHexSlice(b [][]byte) []string { return r } -// ---------------------------------------------------------------- FlashBots ---------------------------------------------------------------- +// ---------------------------------------------------------------- FlashBots inspired code ---------------------------------------------------------------- // PrivateTxBundleAPI offers an API for accepting bundled transactions type PrivateTxBundleAPI struct { @@ -2180,10 +2180,6 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st Coinbase: coinbase, BaseFee: baseFee, } - if header != nil { - log.Error("here4") - } - // 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 From 17debf7392d918c529c775e443c39f5c2d43351a Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 28 Mar 2022 13:51:16 -0400 Subject: [PATCH 15/20] remove log --- core/state_processor.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 93121e44ef01..a24478c2d180 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -154,8 +154,6 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b } traceResult := FormatLogs(tracer.StructLogs()) - returnVal := fmt.Sprintf("%x", traceResult) - fmt.Println(returnVal) if err != nil { return nil, nil, nil, err From bbac394897e4a2e934aef2a9be688217a1f789c9 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 28 Mar 2022 13:52:47 -0400 Subject: [PATCH 16/20] a lil more clean up --- core/state_processor.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index a24478c2d180..c0f4338da5df 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -184,8 +184,6 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b // } // Set the receipt logs and create the bloom filter. - // receipt.Logs = statedb.GetLogs(tx.Hash(), header.Hash()) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.BlockHash = header.Hash() receipt.BlockNumber = header.Number receipt.TransactionIndex = uint(statedb.TxIndex()) From 28d9c3d12ab177eea460d6df911729c6f311c197 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Tue, 5 Apr 2022 21:27:33 -0400 Subject: [PATCH 17/20] switch tracer --- core/state_processor.go | 193 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 191 insertions(+), 2 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index c0f4338da5df..1b51188f01dc 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -19,8 +19,10 @@ package core import ( "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" @@ -28,7 +30,9 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" ) // StateProcessor is a basic Processor, which takes care of transitioning @@ -207,8 +211,8 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { // Create struct logger to get JSON stack traces - tracer := logger.NewStructLogger(nil) - + // tracer := logger.NewStructLogger(nil) + tracer := NewCallTracer(statedb) // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) @@ -265,3 +269,188 @@ func FormatLogs(logs []logger.StructLog) []StructLogRes { } return formatted } + +type call struct { + Type string `json:"type"` + From common.Address `json:"from"` + To common.Address `json:"to"` + Value *hexutil.Big `json:"value,omitempty"` + Gas hexutil.Uint64 `json:"gas"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Input hexutil.Bytes `json:"input"` + Output hexutil.Bytes `json:"output"` + Time string `json:"time,omitempty"` + Calls []*call `json:"calls,omitempty"` + Error string `json:"error,omitempty"` + startTime time.Time + outOff uint64 + outLen uint64 + gasIn uint64 + gasCost uint64 +} + +type TracerResult interface { + vm.EVMLogger + GetResult() (interface{}, error) +} + +type CallTracer struct { + callStack []*call + descended bool + statedb *state.StateDB +} + +func NewCallTracer(statedb *state.StateDB) TracerResult { + return &CallTracer{ + callStack: []*call{}, + descended: false, + statedb: statedb, + } +} + +func (tracer *CallTracer) i() int { + return len(tracer.callStack) - 1 +} + +func (tracer *CallTracer) GetResult() (interface{}, error) { + return tracer.callStack[0], nil +} + +func (tracer *CallTracer) CaptureStart(evm *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + hvalue := hexutil.Big(*value) + tracer.callStack = []*call{&call{ + From: from, + To: to, + Value: &hvalue, + Gas: hexutil.Uint64(gas), + Input: hexutil.Bytes(input), + Calls: []*call{}, + }} +} +func (tracer *CallTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { + tracer.callStack[tracer.i()].GasUsed = hexutil.Uint64(gasUsed) + tracer.callStack[tracer.i()].Time = fmt.Sprintf("%v", t) + tracer.callStack[tracer.i()].Output = hexutil.Bytes(output) +} + +func (tracer *CallTracer) descend(newCall *call) { + tracer.callStack[tracer.i()].Calls = append(tracer.callStack[tracer.i()].Calls, newCall) + tracer.callStack = append(tracer.callStack, newCall) + tracer.descended = true +} + +func toAddress(value *uint256.Int) common.Address { + return common.BytesToAddress(value.Bytes()) +} + +func (tracer *CallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + // for depth < len(tracer.callStack) { + // c := tracer.callStack[tracer.i()] + // c.GasUsed = c.Gas - gas + // tracer.callStack = tracer.callStack[:tracer.i()] + // } + defer func() { + if r := recover(); r != nil { + tracer.callStack[tracer.i()].Error = "internal failure" + log.Warn("Panic during trace. Recovered.", "err", r) + } + }() + if op == vm.CREATE || op == vm.CREATE2 { + inOff := scope.Stack.Back(1).Uint64() + inLen := scope.Stack.Back(2).Uint64() + hvalue := hexutil.Big(*scope.Contract.Value()) + tracer.descend(&call{ + Type: op.String(), + From: scope.Contract.Caller(), + Input: scope.Memory.GetCopy(int64(inOff), int64(inLen)), + gasIn: gas, + gasCost: cost, + Value: &hvalue, + startTime: time.Now(), + }) + return + } + if op == vm.SELFDESTRUCT { + hvalue := hexutil.Big(*tracer.statedb.GetBalance(scope.Contract.Caller())) + tracer.descend(&call{ + Type: op.String(), + From: scope.Contract.Caller(), + To: toAddress(scope.Stack.Back(0)), + // TODO: Is this input correct? + Input: scope.Contract.Input, + Value: &hvalue, + gasIn: gas, + gasCost: cost, + startTime: time.Now(), + }) + return + } + if op == vm.CALL || op == vm.CALLCODE || op == vm.DELEGATECALL || op == vm.STATICCALL { + toAddress := toAddress(scope.Stack.Back(1)) + if _, isPrecompile := vm.PrecompiledContractsIstanbul[toAddress]; isPrecompile { + return + } + off := 1 + if op == vm.DELEGATECALL || op == vm.STATICCALL { + off = 0 + } + inOff := scope.Stack.Back(2 + off).Uint64() + inLength := scope.Stack.Back(3 + off).Uint64() + newCall := &call{ + Type: op.String(), + From: scope.Contract.Address(), + To: toAddress, + Input: scope.Memory.GetCopy(int64(inOff), int64(inLength)), + gasIn: gas, + gasCost: cost, + outOff: scope.Stack.Back(4 + off).Uint64(), + outLen: scope.Stack.Back(5 + off).Uint64(), + startTime: time.Now(), + } + if off == 1 { + value := hexutil.Big(*new(big.Int).SetBytes(scope.Stack.Back(2).Bytes())) + newCall.Value = &value + } + tracer.descend(newCall) + return + } + if tracer.descended { + if depth >= len(tracer.callStack) { + tracer.callStack[tracer.i()].Gas = hexutil.Uint64(gas) + } + tracer.descended = false + } + if op == vm.REVERT { + tracer.callStack[tracer.i()].Error = "execution reverted" + return + } + if depth == len(tracer.callStack)-1 { + c := tracer.callStack[tracer.i()] + // c.Time = fmt.Sprintf("%v", time.Since(c.startTime)) + tracer.callStack = tracer.callStack[:len(tracer.callStack)-1] + if vm.StringToOp(c.Type) == vm.CREATE || vm.StringToOp(c.Type) == vm.CREATE2 { + c.GasUsed = hexutil.Uint64(c.gasIn - c.gasCost - gas) + ret := scope.Stack.Back(0) + if ret.Uint64() != 0 { + c.To = common.BytesToAddress(ret.Bytes()) + c.Output = tracer.statedb.GetCode(c.To) + } else if c.Error == "" { + c.Error = "internal failure" + } + } else { + c.GasUsed = hexutil.Uint64(c.gasIn - c.gasCost + uint64(c.Gas) - gas) + ret := scope.Stack.Back(0) + if ret.Uint64() != 0 { + c.Output = hexutil.Bytes(scope.Memory.GetCopy(int64(c.outOff), int64(c.outLen))) + } else if c.Error == "" { + c.Error = "internal failure" + } + } + } + return +} +func (tracer *CallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.ScopeContext, depth int, err error) { +} +func (tracer *CallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +} +func (tracer *CallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} From ff8c8e55a99ab9535113c6408a0c9cec933d8b08 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Tue, 5 Apr 2022 22:00:22 -0400 Subject: [PATCH 18/20] more tracer changes --- core/state_processor.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 1b51188f01dc..4e97af291943 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -146,7 +146,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon return receipt, err } -func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM, tracer *logger.StructLogger) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { +func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx types.Message, usedGas *uint64, evm *vm.EVM, tracer TracerResult) (*types.Receipt, *ExecutionResult, interface{}, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -157,11 +157,11 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b return nil, nil, nil, err } - traceResult := FormatLogs(tracer.StructLogs()) + traceResult, err := tracer.GetResult() - if err != nil { - return nil, nil, nil, err - } + // if err != nil { + // return nil, nil, nil, err + // } // Update the state with pending changes. var root []byte if config.IsByzantium(header.Number) { @@ -209,7 +209,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } -func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, []StructLogRes, error) { +func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, interface{}, error) { // Create struct logger to get JSON stack traces // tracer := logger.NewStructLogger(nil) tracer := NewCallTracer(statedb) From 0825eb2bb4e274ce1a47f6f9f593408f75213c85 Mon Sep 17 00:00:00 2001 From: Dmarz Date: Sun, 10 Apr 2022 19:10:58 -0400 Subject: [PATCH 19/20] use pending block for call bundles --- core/state_processor.go | 1 + internal/ethapi/api.go | 49 +++++++++++++++++------------------------ 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 4e97af291943..48cd090ec5ff 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -165,6 +165,7 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b // Update the state with pending changes. var root []byte if config.IsByzantium(header.Number) { + // statedb.GetRefund() statedb.Finalise(true) } else { root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f7f3bdf5f277..8559cd7b2751 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2109,32 +2109,24 @@ func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI { return &BundleAPI{b, chain} } -// CallBundleArgs represents the arguments for a call. +// CallBundleArgs represents the arguments for a bundle of calls. type CallBundleArgs 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"` - 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 + Txs []TransactionArgs `json:"txs"` + 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 on top of +// the most recent block. Partially follows flashbots spec v0.5. func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[string]interface{}, error) { if len(args.Txs) == 0 { return nil, errors.New("bundle missing unsigned txs") } - if args.BlockNumber == 0 { - return nil, errors.New("bundle missing blockNumber") - } defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) @@ -2143,11 +2135,12 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st timeoutMilliSeconds = *args.Timeout } timeout := time.Millisecond * time.Duration(timeoutMilliSeconds) - state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash) + blockNumberRPC := rpc.BlockNumber(-1) + state, parent, err := s.b.StateAndHeaderByNumber(ctx, blockNumberRPC) + if state == nil || err != nil { return nil, err } - blockNumber := big.NewInt(int64(args.BlockNumber)) timestamp := parent.Time + 1 if args.Timestamp != nil { @@ -2166,14 +2159,12 @@ func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[st 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) - } + // Assume bn simulaton occur after london hardfork + + baseFee = misc.CalcBaseFee(s.b.ChainConfig(), parent) header := &types.Header{ ParentHash: parent.Hash(), - Number: blockNumber, + Number: big.NewInt(parent.Number.Int64()), GasLimit: gasLimit, Time: timestamp, Difficulty: difficulty, From b6567d49026c508a70f52ed22e54b435641cee2f Mon Sep 17 00:00:00 2001 From: Dmarz Date: Mon, 11 Apr 2022 14:15:20 -0400 Subject: [PATCH 20/20] small clean up --- core/state_processor.go | 12 +----------- internal/ethapi/api.go | 2 -- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 48cd090ec5ff..c9b6beaa3af9 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -158,10 +158,6 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b } traceResult, err := tracer.GetResult() - - // if err != nil { - // return nil, nil, nil, err - // } // Update the state with pending changes. var root []byte if config.IsByzantium(header.Number) { @@ -183,11 +179,6 @@ func applyTransactionWithResult(msg types.Message, config *params.ChainConfig, b // 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.BlockHash = header.Hash() receipt.BlockNumber = header.Number @@ -211,8 +202,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo } func ApplyUnsignedTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, interface{}, error) { - // Create struct logger to get JSON stack traces - // tracer := logger.NewStructLogger(nil) + // Create call tracer to get JSON stack traces tracer := NewCallTracer(statedb) // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 8559cd7b2751..53c256ff3b58 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2086,8 +2086,6 @@ func toHexSlice(b [][]byte) []string { return r } -// ---------------------------------------------------------------- FlashBots inspired code ---------------------------------------------------------------- - // PrivateTxBundleAPI offers an API for accepting bundled transactions type PrivateTxBundleAPI struct { b Backend