mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
354 lines
9.9 KiB
Go
354 lines
9.9 KiB
Go
package eth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"hash"
|
|
"math"
|
|
"math/big"
|
|
"math/rand"
|
|
"strings"
|
|
|
|
txtrace "github.com/DeBankDeFi/etherlib/pkg/txtracev1"
|
|
txtrace2 "github.com/DeBankDeFi/etherlib/pkg/txtracev2"
|
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core/state"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
|
|
// "github.com/DeBankDeFi/eth/txtrace"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
type helpHash struct {
|
|
hashed hash.Hash
|
|
}
|
|
|
|
func newHash() *helpHash {
|
|
|
|
return &helpHash{hashed: sha3.NewLegacyKeccak256()}
|
|
}
|
|
|
|
func (h *helpHash) Reset() {
|
|
h.hashed.Reset()
|
|
}
|
|
|
|
func (h *helpHash) Update(key, val []byte) {
|
|
h.hashed.Write(key)
|
|
h.hashed.Write(val)
|
|
}
|
|
|
|
func (h *helpHash) Hash() common.Hash {
|
|
return common.BytesToHash(h.hashed.Sum(nil))
|
|
}
|
|
|
|
type PreExecTx struct {
|
|
ChainId *big.Int
|
|
From, To, Data, Value, Gas, GasPrice, Nonce string
|
|
}
|
|
|
|
type preData struct {
|
|
block *types.Block
|
|
tx *types.Transaction
|
|
msg types.Message
|
|
stateDb *state.StateDB
|
|
header *types.Header
|
|
}
|
|
|
|
// PreExecAPI provides pre exec info for rpc
|
|
type PreExecAPI struct {
|
|
e *Ethereum
|
|
}
|
|
|
|
func NewPreExecAPI(e *Ethereum) *PreExecAPI {
|
|
return &PreExecAPI{e: e}
|
|
}
|
|
|
|
func (api *PreExecAPI) getBlockAndMsg(origin *PreExecTx, number *big.Int) (*types.Block, types.Message) {
|
|
fromAddr := common.HexToAddress(origin.From)
|
|
toAddr := common.HexToAddress(origin.To)
|
|
|
|
tx := types.NewTx(&types.LegacyTx{
|
|
Nonce: hexutil.MustDecodeUint64(origin.Nonce),
|
|
To: &toAddr,
|
|
Value: hexutil.MustDecodeBig(origin.Value),
|
|
Gas: hexutil.MustDecodeUint64(origin.Gas),
|
|
GasPrice: hexutil.MustDecodeBig(origin.GasPrice),
|
|
Data: hexutil.MustDecode(origin.Data),
|
|
})
|
|
|
|
number.Add(number, big.NewInt(1))
|
|
block := types.NewBlock(
|
|
&types.Header{Number: number},
|
|
[]*types.Transaction{tx}, nil, nil, newHash())
|
|
|
|
msg := types.NewMessage(
|
|
fromAddr,
|
|
&toAddr,
|
|
hexutil.MustDecodeUint64(origin.Nonce),
|
|
hexutil.MustDecodeBig(origin.Value),
|
|
hexutil.MustDecodeUint64(origin.Gas),
|
|
hexutil.MustDecodeBig(origin.GasPrice),
|
|
tx.GasFeeCap(),
|
|
tx.GasTipCap(),
|
|
hexutil.MustDecode(origin.Data),
|
|
nil, false, true,
|
|
)
|
|
return block, msg
|
|
}
|
|
|
|
func (api *PreExecAPI) prepareData(ctx context.Context, origin *PreExecTx) (*preData, error) {
|
|
var (
|
|
d preData
|
|
err error
|
|
)
|
|
bc := api.e.blockchain
|
|
d.header, err = api.e.APIBackend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
latestNumber := d.header.Number
|
|
parent := api.e.blockchain.GetBlockByNumber(latestNumber.Uint64())
|
|
d.stateDb, err = state.New(parent.Header().Root, bc.StateCache(), bc.Snapshots())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d.block, d.msg = api.getBlockAndMsg(origin, latestNumber)
|
|
d.tx = d.block.Transactions()[0]
|
|
return &d, nil
|
|
}
|
|
|
|
func (api *PreExecAPI) GetLogs(ctx context.Context, origin *PreExecTx) (*types.Receipt, error) {
|
|
var (
|
|
bc = api.e.blockchain
|
|
)
|
|
d, err := api.prepareData(ctx, origin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
gas := d.tx.Gas()
|
|
gp := new(core.GasPool).AddGas(gas)
|
|
|
|
d.stateDb.SetTxHashAndIndex(d.tx.Hash(), 0)
|
|
receipt, err := core.ApplyTransactionForPreExec(
|
|
bc.Config(), bc, nil, gp, d.stateDb, d.header, d.tx, d.msg, &gas, *bc.GetVMConfig())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return receipt, receipt.Err
|
|
}
|
|
|
|
// TraceTransaction tracing pre-exec transaction object.
|
|
func (api *PreExecAPI) TraceTransaction(ctx context.Context, origin *PreExecTx) (interface{}, error) {
|
|
var (
|
|
bc = api.e.blockchain
|
|
tracer *txtrace.OeTracer
|
|
err error
|
|
)
|
|
d, err := api.prepareData(ctx, origin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
txContext := core.NewEVMTxContext(d.msg)
|
|
txIndex := 0
|
|
|
|
tracer = txtrace.NewOeTracer(nil)
|
|
// Run the transaction with tracing enabled.
|
|
vmenv := vm.NewEVM(core.NewEVMBlockContext(d.header, bc, nil), txContext, d.stateDb, bc.Config(), vm.Config{Debug: true, Tracer: tracer, PreExec: true})
|
|
vmenv.Context.BaseFee = big.NewInt(0)
|
|
vmenv.Context.BlockNumber.Add(vmenv.Context.BlockNumber, big.NewInt(rand.Int63n(6)+6))
|
|
vmenv.Context.Time += uint64(rand.Int63n(60) + 30)
|
|
|
|
// Call Prepare to clear out the statedb access list
|
|
d.stateDb.SetTxHashAndIndex(d.tx.Hash(), 0)
|
|
|
|
tracer.SetMessage(d.block.Number(), d.block.Hash(), d.tx.Hash(), uint(txIndex), d.msg.From(), d.msg.To(), *d.msg.Value())
|
|
|
|
_, err = core.ApplyMessage(vmenv, d.msg, new(core.GasPool).AddGas(d.msg.Gas()))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
|
}
|
|
|
|
tracer.Finalize()
|
|
return tracer.GetResult(), nil
|
|
}
|
|
|
|
const (
|
|
UnKnown = 1000
|
|
InsufficientBalane = 1001
|
|
Reverted = 1002
|
|
)
|
|
|
|
type PreArgs struct {
|
|
ChainId *big.Int `json:"chainId,omitempty"`
|
|
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"`
|
|
}
|
|
|
|
type PreError struct {
|
|
Code int `json:"code"`
|
|
Msg string `json:"msg"`
|
|
}
|
|
|
|
func toPreError(err error, result *core.ExecutionResult) PreError {
|
|
preErr := PreError{
|
|
Code: UnKnown,
|
|
}
|
|
if err != nil {
|
|
preErr.Msg = err.Error()
|
|
}
|
|
if result != nil && result.Err != nil {
|
|
preErr.Msg = result.Err.Error()
|
|
}
|
|
if strings.HasPrefix(preErr.Msg, "execution reverted") {
|
|
preErr.Code = Reverted
|
|
if result != nil {
|
|
preErr.Msg, _ = abi.UnpackRevert(result.Revert())
|
|
}
|
|
}
|
|
if strings.HasPrefix(preErr.Msg, "out of gas") {
|
|
preErr.Code = Reverted
|
|
}
|
|
if strings.HasPrefix(preErr.Msg, "insufficient funds for transfer") {
|
|
preErr.Code = InsufficientBalane
|
|
}
|
|
if strings.HasPrefix(preErr.Msg, "insufficient balance for transfer") {
|
|
preErr.Code = InsufficientBalane
|
|
}
|
|
if strings.HasPrefix(preErr.Msg, "insufficient funds for gas * price") {
|
|
preErr.Code = InsufficientBalane
|
|
}
|
|
return preErr
|
|
}
|
|
|
|
type PreResult struct {
|
|
Trace txtrace2.ActionTraceList `json:"trace"`
|
|
Logs []*types.Log `json:"logs"`
|
|
StateDiff txtrace2.StateDiff `json:"stateDiff"`
|
|
Error PreError `json:"error,omitempty"`
|
|
GasUsed uint64 `json:"gasUsed"`
|
|
}
|
|
|
|
func (api *PreExecAPI) TraceMany(ctx context.Context, origins []PreArgs) ([]PreResult, error) {
|
|
preResList := make([]PreResult, 0)
|
|
state, header, err := api.e.APIBackend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber))
|
|
if state == nil || err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < len(origins); i++ {
|
|
origin := origins[i]
|
|
if origin.Nonce == nil {
|
|
preResList = append(preResList, PreResult{
|
|
Error: PreError{
|
|
Code: UnKnown,
|
|
Msg: "nonce is nil",
|
|
},
|
|
})
|
|
continue
|
|
}
|
|
if i > 0 && (uint64)(*origin.Nonce) <= (uint64)(*origins[i-1].Nonce) {
|
|
preResList = append(preResList, PreResult{
|
|
Error: PreError{
|
|
Code: UnKnown,
|
|
Msg: fmt.Sprintf("nonce decreases, tx index %d has nonce %d, tx index %d has nonce %d", i-1, (uint64)(*origins[i-1].Nonce), i, (uint64)(*origin.Nonce)),
|
|
},
|
|
})
|
|
continue
|
|
}
|
|
txArgs := ethapi.TransactionArgs{
|
|
ChainID: (*hexutil.Big)(big.NewInt(1)),
|
|
From: origin.From,
|
|
To: origin.To,
|
|
Gas: origin.Gas,
|
|
GasPrice: origin.GasPrice,
|
|
MaxFeePerGas: origin.MaxFeePerGas,
|
|
MaxPriorityFeePerGas: origin.MaxPriorityFeePerGas,
|
|
Value: origin.Value,
|
|
Data: origin.Data,
|
|
Input: origin.Input,
|
|
}
|
|
// Get a new instance of the EVM.
|
|
msg, err := txArgs.ToMessage(api.e.APIBackend.RPCGasCap(), header.BaseFee)
|
|
if err != nil {
|
|
preResList = append(preResList, PreResult{
|
|
Error: PreError{
|
|
Code: UnKnown,
|
|
Msg: err.Error(),
|
|
},
|
|
})
|
|
continue
|
|
}
|
|
txHash := common.BigToHash(big.NewInt(int64(i)))
|
|
tracer := txtrace2.NewOeTracer(nil, header.Hash(), header.Number, txHash, uint64(i))
|
|
evm, vmError, err := api.e.APIBackend.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true, Debug: true, Tracer: tracer, PreExec: true})
|
|
evm.Context.BaseFee = big.NewInt(0)
|
|
evm.Context.BlockNumber.Add(evm.Context.BlockNumber, big.NewInt(rand.Int63n(6)+6))
|
|
evm.Context.Time += uint64(rand.Int63n(60) + 30)
|
|
if err != nil {
|
|
preResList = append(preResList, PreResult{
|
|
Error: PreError{
|
|
Code: UnKnown,
|
|
Msg: err.Error(),
|
|
},
|
|
})
|
|
continue
|
|
}
|
|
// Execute the message.
|
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
|
state.SetTxHashAndIndex(txHash, i)
|
|
result, err := core.ApplyMessage(evm, msg, gp)
|
|
if err := vmError(); err != nil {
|
|
preRes := PreResult{
|
|
Error: toPreError(err, result),
|
|
}
|
|
if result != nil {
|
|
preRes.GasUsed = result.UsedGas
|
|
}
|
|
preResList = append(preResList, preRes)
|
|
continue
|
|
}
|
|
if err != nil {
|
|
preRes := PreResult{
|
|
Error: toPreError(err, result),
|
|
}
|
|
if result != nil {
|
|
preRes.GasUsed = result.UsedGas
|
|
}
|
|
preResList = append(preResList, preRes)
|
|
continue
|
|
}
|
|
preRes := PreResult{
|
|
Trace: tracer.GetTraces(),
|
|
Logs: state.GetLogs(txHash, header.Number.Uint64(), header.Hash()),
|
|
StateDiff: tracer.GetStateDiff(),
|
|
}
|
|
if result != nil {
|
|
preRes.GasUsed = result.UsedGas
|
|
if result.Failed() {
|
|
preRes.Error = toPreError(err, result)
|
|
}
|
|
}
|
|
|
|
if preRes.Error.Msg == "" && len(preRes.Trace) > 0 && (preRes.Trace)[0].Error != "" {
|
|
preRes.Error = PreError{
|
|
Code: Reverted,
|
|
Msg: (preRes.Trace)[0].Error,
|
|
}
|
|
}
|
|
preResList = append(preResList, preRes)
|
|
}
|
|
return preResList, nil
|
|
}
|