This commit is contained in:
vahoo5 2023-05-22 01:09:25 +02:00
parent 944e1a0f90
commit 2c89a7f4f0
7 changed files with 437 additions and 3 deletions

View file

@ -17,6 +17,7 @@
package core
import (
"encoding/json"
"fmt"
"math/big"
@ -159,3 +160,55 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
func applyTransactionWithResult(msg *Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msgTx *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)
// Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp)
if err != nil {
return nil, nil, nil, err
}
traceResult, err := tracer.GetResult()
// Update the state with pending changes.
var root []byte
if config.IsByzantium(header.Number) {
// statedb.GetRefund()
} 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: 0, PostState: root, CumulativeGasUsed: *usedGas}
if result.Failed() {
receipt.Status = types.ReceiptStatusFailed
} else {
receipt.Status = types.ReceiptStatusSuccessful
}
// receipt.TxHash = tx.Hash()
receipt.GasUsed = result.UsedGas
// Set the receipt logs and create the bloom filter.
receipt.BlockHash = header.Hash()
receipt.BlockNumber = header.Number
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, result, traceResult, err
}
func ApplyTransactionWithResult(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, msg *Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, *ExecutionResult, error) {
// 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)
receipt, result, _, err := applyTransactionWithResult(msg, config, bc, author, gp, statedb, header, msg, usedGas, vmenv, nil)
return receipt, result, err
}
type TracerResult interface {
GetResult() (json.RawMessage, error)
}

View file

@ -594,3 +594,20 @@ func deriveChainId(v *big.Int) *big.Int {
v = new(big.Int).Sub(v, big.NewInt(35))
return v.Div(v, big.NewInt(2))
}
func MakeSigner2(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer
switch {
case config.IsLondon(blockNumber):
signer = NewLondonSigner(config.ChainID)
case config.IsBerlin(blockNumber):
signer = NewEIP2930Signer(config.ChainID)
case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainID)
case config.IsHomestead(blockNumber):
signer = HomesteadSigner{}
default:
signer = FrontierSigner{}
}
return signer
}

View file

@ -282,7 +282,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())...)

View file

@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"time"
@ -54,6 +55,15 @@ type EthereumAPI struct {
b Backend
}
type BundleAPI struct {
b Backend
chain *core.BlockChain
}
func NewBundleAPI(b Backend, chain *core.BlockChain) *BundleAPI {
return &BundleAPI{b, chain}
}
// NewEthereumAPI creates a new Ethereum protocol API.
func NewEthereumAPI(b Backend) *EthereumAPI {
return &EthereumAPI{b}
@ -1045,6 +1055,158 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
return result, nil
}
// func DoCallBundle(ctx context.Context, b Backend, args TransactionArgsBundle, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) ([]map[string]interface{}, error) {
// defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
// state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
// if state == nil || err != nil {
// return nil, err
// }
// if err := overrides.Apply(state); err != nil {
// return nil, err
// }
// // 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()
// // Get a new instance of the EVM.
// results := []map[string]interface{}{}
// // tx will be the first of the bundle
// blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
// if blockOverrides != nil {
// blockOverrides.Apply(&blockCtx)
// }
// gp := new(core.GasPool).AddGas(math.MaxUint64)
// for i, tx := range args.Transactions {
// msg, err := tx.ToMessage(globalGasCap, header.BaseFee)
// if err != nil {
// return nil, err
// }
// result, err := core.ApplyTransaction(s.b.ChainConfig(), )
// // print result
// fmt.Println(result)
// jsonResult := map[string]interface{}{
// "gasUsed": result.UsedGas,
// }
// fmt.Println(i)
// if result.Err != nil {
// fmt.Println("error 1")
// jsonResult["error"] = result.Err.Error()
// revert := result.Revert()
// if len(revert) > 0 {
// jsonResult["revert"] = string(revert)
// }
// } else {
// fmt.Println("error 2")
// dst := make([]byte, hex.EncodedLen(len(result.Return())))
// hex.Encode(dst, result.Return())
// jsonResult["value"] = "0x" + string(dst)
// }
// results = append(results, jsonResult)
// }
// return results, nil
// }
func DoCall2(ctx context.Context, b Backend, args TransactionArgs2, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
}
if err := overrides.Apply(state); err != nil {
return nil, err
}
// 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()
args1 := TransactionArgs{
From: args.From,
To: args.To,
Gas: args.Gas,
GasPrice: args.GasPrice,
MaxFeePerGas: args.MaxFeePerGas,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
Value: args.Value,
Nonce: args.Nonce,
Data: args.Data,
Input: args.Input,
AccessList: args.AccessList,
ChainID: args.ChainID,
}
args2 := TransactionArgs{
From: args.From1,
To: args.To1,
Gas: args.Gas1,
GasPrice: args.GasPrice1,
MaxFeePerGas: args.MaxFeePerGas1,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas1,
Value: args.Value1,
Nonce: args.Nonce1,
Data: args.Data1,
Input: args.Input1,
AccessList: args.AccessList1,
ChainID: args.ChainID1,
}
// Get a new instance of the EVM.
msg1, err := args1.ToMessage(globalGasCap, header.BaseFee)
if err != nil {
return nil, err
}
msg2, err := args2.ToMessage(globalGasCap, header.BaseFee)
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
if blockOverrides != nil {
blockOverrides.Apply(&blockCtx)
}
evm, vmError := b.GetEVM(ctx, msg1, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
go func() {
<-ctx.Done()
evm.Cancel()
}()
// Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
core.ApplyMessage(evm, msg1, gp)
result, err := core.ApplyMessage(evm, msg2, gp)
if err := vmError(); err != nil {
return nil, err
}
// If the timer caused an abort, return an appropriate error message
if evm.Cancelled() {
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout)
}
if err != nil {
return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg1.GasLimit)
}
return result, nil
}
func newRevertError(result *core.ExecutionResult) *revertError {
reason, errUnpack := abi.UnpackRevert(result.Revert())
err := errors.New("execution reverted")
@ -1093,6 +1255,160 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
return result.Return(), result.Err
}
func (s *BlockChainAPI) BatchCall(ctx context.Context, args TransactionArgs2, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) {
result, err := DoCall2(ctx, s.b, args, blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil {
return nil, err
}
// If the result contains a revert reason, try to unpack and return it.
if len(result.Revert()) > 0 {
return nil, newRevertError(result)
}
return result.Return(), result.Err
}
// func (s *BundleAPI) BundleCall(ctx context.Context, args TransactionArgsBundle, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) ([]map[string]interface{}, error) {
// result, _ := DoCallBundle(ctx, s.b, args, blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
// return result, nil
// }
func (s *BundleAPI) CallBundle(ctx context.Context, args CallBundleArgs) (map[string]interface{}, error) {
if len(args.Transactions) == 0 {
return nil, errors.New("bundle missing 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())
timeoutMilliSeconds := int64(5000)
if args.Timeout != nil {
timeoutMilliSeconds = *args.Timeout
}
timeout := time.Millisecond * time.Duration(timeoutMilliSeconds)
fmt.Println("state", args.StateBlockNumberOrHash)
state, parent, err := s.b.StateAndHeaderByNumberOrHash(ctx, args.StateBlockNumberOrHash)
if state == nil || err != nil {
return nil, err
}
blockNumber := big.NewInt(int64(args.BlockNumber))
fmt.Println("blockNumber", 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)
var totalGasUsed uint64
gasFees := new(big.Int)
uint64MaxValue := uint64(math.MaxUint64)
for i, tx := range args.Transactions {
fmt.Println("tx", tx)
msg, err := tx.ToMessage(uint64MaxValue, header.BaseFee)
if err != nil {
return nil, err
}
coinbaseBalanceBeforeTx := state.GetBalance(coinbase)
randomHash := common.HexToHash("0x" + strconv.Itoa(i))
state.SetTxContext(randomHash, i)
receipt, result, err := core.ApplyTransactionWithResult(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, randomHash)
}
txHash := randomHash.String()
if err != nil {
return nil, fmt.Errorf("err: %w; txhash %s", err, randomHash)
}
to := "0x"
jsonResult := map[string]interface{}{
"txHash": txHash,
"gasUsed": receipt.GasUsed,
"toAddress": to,
}
totalGasUsed += receipt.GasUsed
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["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()
return ret, nil
}
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap uint64) (hexutil.Uint64, error) {
// Binary search the gas requirement, as it may be higher than the amount used
var (

View file

@ -99,7 +99,7 @@ type Backend interface {
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
}
func GetAPIs(apiBackend Backend) []rpc.API {
func GetAPIs(apiBackend Backend, chain *core.BlockChain) []rpc.API {
nonceLock := new(AddrLocker)
return []rpc.API{
{
@ -123,6 +123,11 @@ func GetAPIs(apiBackend Backend) []rpc.API {
}, {
Namespace: "personal",
Service: NewPersonalAccountAPI(apiBackend, nonceLock),
}, {
Namespace: "eth",
Version: "1.0",
Service: NewBundleAPI(apiBackend, chain),
Public: true,
},
}
}

View file

@ -55,6 +55,49 @@ type TransactionArgs struct {
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
type TransactionArgsBundle struct {
Transactions []TransactionArgs `json:"transactions"`
}
type CallBundleArgs struct {
Transactions []TransactionArgs `json:"transactions"`
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"`
}
type TransactionArgs2 struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Gas *hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
Value *hexutil.Big `json:"value"`
Nonce *hexutil.Uint64 `json:"nonce"`
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
From1 *common.Address `json:"from1"`
To1 *common.Address `json:"to1"`
Gas1 *hexutil.Uint64 `json:"gas1"`
GasPrice1 *hexutil.Big `json:"gasPrice1"`
MaxFeePerGas1 *hexutil.Big `json:"maxFeePerGas1"`
MaxPriorityFeePerGas1 *hexutil.Big `json:"maxPriorityFeePerGas1"`
Value1 *hexutil.Big `json:"value1"`
Nonce1 *hexutil.Uint64 `json:"nonce1"`
Data1 *hexutil.Bytes `json:"data1"`
Input1 *hexutil.Bytes `json:"input1"`
AccessList1 *types.AccessList `json:"accessList1,omitempty"`
ChainID1 *hexutil.Big `json:"chainId1,omitempty"`
}
// from retrieves the transaction sender address.
func (args *TransactionArgs) from() common.Address {
if args.From == nil {

View file

@ -289,7 +289,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{
{