diff --git a/core/state_processor.go b/core/state_processor.go index 40a953f0d4..f757e0db06 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -18,7 +18,6 @@ package core import ( "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" @@ -148,3 +147,9 @@ 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, tx, usedGas, vmenv) } + +func ApplyTransactionForPreExec(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, msg types.Message, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { + blockContext := NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) + return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +} \ No newline at end of file diff --git a/core/tx_pool.go b/core/tx_pool.go index 5db1d3df32..adc631c959 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -482,6 +482,25 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common return pending, queued } +// ContentByAccount retrieves the data content of the transaction pool with a specific account, +// returning the pending as well as queued transaction. +func (pool *TxPool) ContentByAccount(addr common.Address) (pending, queued types.Transactions) { + pool.mu.Lock() + defer pool.mu.Unlock() + + list, ok := pool.pending[addr] + if ok { + pending = list.Flatten() + } + + list, ok = pool.queue[addr] + if ok { + queued = list.Flatten() + } + + return +} + // Pending retrieves all currently processable transactions, grouped by origin // account and sorted by nonce. The returned transaction set is a copy and can be // freely modified by calling code. diff --git a/eth/api_backend.go b/eth/api_backend.go index 7ac1f82a86..028feb6c30 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -263,6 +263,10 @@ func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions, return b.eth.TxPool().Content() } +func (b *EthAPIBackend) TxPoolContentByAccount(addr common.Address) (pending, queued types.Transactions) { + return b.eth.TxPool().ContentByAccount(addr) +} + func (b *EthAPIBackend) TxPool() *core.TxPool { return b.eth.TxPool() } diff --git a/eth/api_pre_exec.go b/eth/api_pre_exec.go new file mode 100644 index 0000000000..64d7d44231 --- /dev/null +++ b/eth/api_pre_exec.go @@ -0,0 +1,230 @@ +package eth + +import ( + "context" + "errors" + "fmt" + "hash" + "math/big" + "time" + + "github.com/DeBankDeFi/eth/txtrace" + "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/eth/tracers" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/rpc" + "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), + hexutil.MustDecode(origin.Data), + nil, false, + ) + + 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.Prepare(d.tx.Hash(), d.block.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, nil +} + +// TraceTransaction tracing pre-exec transaction object. +func (api *PreExecAPI) TraceTransaction(ctx context.Context, origin *PreExecTx, config *tracers.TraceConfig) (interface{}, error) { + var ( + bc = api.e.blockchain + tracer vm.Tracer + err error + ) + d, err := api.prepareData(ctx, origin) + if err != nil { + return nil, err + } + txContext := core.NewEVMTxContext(d.msg) + + switch { + case config != nil && config.Tracer != nil: + // Define a meaningful timeout of a single transaction trace + timeout := 5 * time.Second + if config.Timeout != nil { + if timeout, err = time.ParseDuration(*config.Timeout); err != nil { + return nil, err + } + } + // Constuct the JavaScript tracer to execute with + if tracer, err = tracers.New(*config.Tracer, txContext); err != nil { + return nil, err + } + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + go func() { + <-deadlineCtx.Done() + if deadlineCtx.Err() == context.DeadlineExceeded { + tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) + } + }() + case config == nil: + fallthrough + default: + // Constuct the txtrace.StructLogger tracer to execute with + tracer = txtrace.NewTraceStructLogger(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}) + + txIndex := 0 + // Call Prepare to clear out the statedb access list + d.stateDb.Prepare(d.tx.Hash(), d.block.Hash(), txIndex) + + // check if type of tracer is txtrace.StructLogger, in that case, fill info. + var traceLogger *txtrace.StructLogger + switch tracer.(type) { + case *txtrace.StructLogger: + traceLogger = tracer.(*txtrace.StructLogger) + traceLogger.SetFrom(d.msg.From()) + traceLogger.SetTo(d.msg.To()) + traceLogger.SetValue(*d.msg.Value()) + traceLogger.SetGasUsed(d.tx.Gas()) + traceLogger.SetBlockHash(d.block.Hash()) + traceLogger.SetBlockNumber(d.block.Number()) + traceLogger.SetTx(d.tx.Hash()) + traceLogger.SetTxIndex(uint(txIndex)) + } + + result, err := core.ApplyMessage(vmenv, d.msg, new(core.GasPool).AddGas(d.msg.Gas())) + if err != nil { + return nil, fmt.Errorf("tracing failed: %v", err) + } + // Depending on the tracer type, format and return the output. + switch tracer := tracer.(type) { + case *vm.StructLogger: + // If the result contains a revert reason, return it. + returnVal := fmt.Sprintf("%x", result.Return()) + if len(result.Revert()) > 0 { + returnVal = fmt.Sprintf("%x", result.Revert()) + } + return ðapi.ExecutionResult{ + Gas: result.UsedGas, + Failed: result.Failed(), + ReturnValue: returnVal, + StructLogs: ethapi.FormatLogs(tracer.StructLogs()), + }, nil + case *tracers.Tracer: + return tracer.GetResult() + case *txtrace.StructLogger: + tracer.Finalize() + return tracer.GetResult(), nil + default: + panic(fmt.Sprintf("bad tracer type %T", tracer)) + } +} diff --git a/eth/backend.go b/eth/backend.go index 7d8b0c52c6..9171c6ea30 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -345,6 +345,12 @@ func (s *Ethereum) APIs() []rpc.API { Service: s.netRPCService, Public: true, }, + { + Namespace: "pre", + Version: "1.0", + Service: NewPreExecAPI(s), + Public: true, + }, }...) } diff --git a/go.mod b/go.mod index 512d541f41..34f3ed30cc 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,8 @@ module github.com/ethereum/go-ethereum go 1.15 require ( - github.com/Azure/azure-pipeline-go v0.2.2 // indirect github.com/Azure/azure-storage-blob-go v0.7.0 - github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/DeBankDeFi/eth v1.10.3 github.com/VictoriaMetrics/fastcache v1.5.7 github.com/aws/aws-sdk-go-v2 v1.2.0 github.com/aws/aws-sdk-go-v2/config v1.1.1 @@ -18,15 +16,12 @@ require ( github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea - github.com/dlclark/regexp2 v1.2.0 // indirect github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 github.com/edsrzf/mmap-go v1.0.0 github.com/fatih/color v1.7.0 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff - github.com/go-ole/go-ole v1.2.1 // indirect - github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect github.com/go-stack/stack v1.8.0 github.com/golang/protobuf v1.4.3 github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 @@ -43,10 +38,8 @@ require ( github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e github.com/julienschmidt/httprouter v1.2.0 github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 - github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.0 github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 - github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 @@ -57,7 +50,6 @@ require ( github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 - github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c @@ -67,5 +59,4 @@ require ( gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 - gotest.tools v2.2.0+incompatible // indirect ) diff --git a/go.sum b/go.sum index b6a27a2cf1..6f84281a6e 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,10 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DeBankDeFi/eth v1.10.3-rc1 h1:c/JTocFaCThfMdq/R3dCENWBYj0VQ8YhpCAqEcLKsyA= +github.com/DeBankDeFi/eth v1.10.3-rc1/go.mod h1:UAiugF3perbugrqRhIGYBzh4Rl930XLXkmHlqh0YcQs= +github.com/DeBankDeFi/eth v1.10.3 h1:Niun1jAilCbYaYNOvbiMg3iyochzyFK8zt1VCsH27kk= +github.com/DeBankDeFi/eth v1.10.3/go.mod h1:UAiugF3perbugrqRhIGYBzh4Rl930XLXkmHlqh0YcQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -126,6 +130,7 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.10.3/go.mod h1:99onQmSd1GRGOziyGldI41YQb7EESX3Q4H41IfJgIQQ= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fe3f80c038..e852fce4a5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -99,6 +99,31 @@ func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { return &PublicTxPoolAPI{b} } +// ContentByAccount returns the transactions contained within the tx pool by given account address. +func (s *PublicTxPoolAPI) ContentByAccount(addr common.Address) map[string]map[string]*RPCTransaction { + // map[(queued|pending)]map[tx.Nonce()]tx + content := map[string]map[string]*RPCTransaction{ + "pending": make(map[string]*RPCTransaction), + "queued": make(map[string]*RPCTransaction), + } + + pending, queued := s.b.TxPoolContentByAccount(addr) + + dump := make(map[string]*RPCTransaction) + for _, tx := range pending { + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx) + } + content["pending"] = dump + + dump = make(map[string]*RPCTransaction) + for _, tx := range queued { + dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx) + } + content["queued"] = dump + + return content +} + // Content returns the transactions contained within the transaction pool. func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { content := map[string]map[string]map[string]*RPCTransaction{ diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 07e76583f3..67b6e04e20 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -76,6 +76,7 @@ type Backend interface { GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + TxPoolContentByAccount(address common.Address) (pending, queued types.Transactions) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription // Filter API diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 1934412c90..562568c2b8 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -773,7 +773,15 @@ web3._extend({ const TxpoolJs = ` web3._extend({ property: 'txpool', - methods: [], + methods: + [ + new web3._extend.Method({ + name: 'contentByAccount', + call: 'txpool_contentByAccount', + params: 1, + inputFormatter: [null] + }), + ], properties: [ new web3._extend.Property({ diff --git a/les/api_backend.go b/les/api_backend.go index 60c64a8bdf..5779af52f1 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -212,6 +212,11 @@ func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, return b.eth.txPool.Content() } +func (b *LesApiBackend) TxPoolContentByAccount(addr common.Address) (pending, queued types.Transactions) { + // TODO(barryz) not implement for light node. + return nil, nil +} + func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { return b.eth.txPool.SubscribeNewTxsEvent(ch) } diff --git a/params/version.go b/params/version.go index 1c7bf8d88e..b1bdd1e857 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 10 // Minor version component of the current release - VersionPatch = 3 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 10 // Minor version component of the current release + VersionPatch = 3 // Patch version component of the current release + VersionMeta = "unstable-pre-exec" // Version metadata to append to the version string ) // Version holds the textual version string.