From 13e52b897b5f7b8ba882b113f67c2b802bde1f75 Mon Sep 17 00:00:00 2001 From: prathamesh0 Date: Wed, 14 Sep 2022 15:49:48 +0530 Subject: [PATCH] Use API implementation from geth with custom backend --- pkg/debug/api.go | 194 ------------------------- pkg/debug/backend.go | 54 +++++++ pkg/eth/backend.go | 9 +- pkg/graphql/graphql.go | 2 +- pkg/rpc/ipc.go | 1 + pkg/serve/service.go | 25 ++-- test/watch_address_integration_test.go | 1 + 7 files changed, 78 insertions(+), 208 deletions(-) delete mode 100644 pkg/debug/api.go create mode 100644 pkg/debug/backend.go diff --git a/pkg/debug/api.go b/pkg/debug/api.go deleted file mode 100644 index 69ae807d..00000000 --- a/pkg/debug/api.go +++ /dev/null @@ -1,194 +0,0 @@ -// VulcanizeDB -// Copyright © 2022 Vulcanize - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package debug - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "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/eth/tracers/logger" - "github.com/ethereum/go-ethereum/rpc" - - "github.com/cerc-io/ipld-eth-server/v4/pkg/eth" -) - -const ( - // defaultTraceTimeout is the amount of time a single transaction can execute - // by default before being forcefully aborted. - defaultTraceTimeout = 30 * time.Second -) - -// TraceCallConfig is the config for traceCall API. It holds one more -// field to override the state for tracing. -type TraceCallConfig struct { - tracers.TraceConfig - StateOverrides *eth.StateOverride -} - -// APIName is the namespace for the watcher's debug api -const APIName = "debug" - -// APIVersion is the version of the watcher's debug api -const APIVersion = "0.0.1" - -type DebugAPI struct { - // Local db backend - B *eth.Backend -} - -// NewAPI creates a new API definition for the tracing methods of the Ethereum service. -func NewDebugAPI(backend *eth.Backend) *DebugAPI { - return &DebugAPI{B: backend} -} - -// blockByNumber is the wrapper of the chain access function offered by the backend. -// It will return an error if the block is not found. -func (api *DebugAPI) blockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { - block, err := api.B.BlockByNumber(ctx, number) - if err != nil { - return nil, err - } - if block == nil { - return nil, fmt.Errorf("block #%d not found", number) - } - return block, nil -} - -// blockByHash is the wrapper of the chain access function offered by the backend. -// It will return an error if the block is not found. -func (api *DebugAPI) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - block, err := api.B.BlockByHash(ctx, hash) - if err != nil { - return nil, err - } - if block == nil { - return nil, fmt.Errorf("block %s not found", hash.Hex()) - } - return block, nil -} - -// TODO: use API implementation from geth directly -// TraceCall lets you trace a given eth_call. It collects the structured logs -// created during the execution of EVM if the given transaction was added on -// top of the provided block and returns them as a JSON object. -func (api *DebugAPI) TraceCall(ctx context.Context, args eth.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { - // Try to retrieve the specified block - var ( - err error - block *types.Block - ) - if hash, ok := blockNrOrHash.Hash(); ok { - block, err = api.blockByHash(ctx, hash) - } else if number, ok := blockNrOrHash.Number(); ok { - if number == rpc.PendingBlockNumber { - // We don't have access to the miner here. For tracing 'future' transactions, - // it can be done with block- and state-overrides instead, which offers - // more flexibility and stability than trying to trace on 'pending', since - // the contents of 'pending' is unstable and probably not a true representation - // of what the next actual block is likely to contain. - return nil, errors.New("tracing on top of pending is not supported") - } - block, err = api.blockByNumber(ctx, number) - } else { - return nil, errors.New("invalid arguments; neither block nor hash specified") - } - if err != nil { - return nil, err - } - - statedb, _, err := api.B.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if err != nil { - return nil, err - } - vmctx := core.NewEVMBlockContext(block.Header(), api.B, nil) - // Apply the customization rules if required. - if config != nil { - if err := config.StateOverrides.Apply(statedb); err != nil { - return nil, err - } - } - // Execute the trace - msg, err := args.ToMessage(api.B.Config.RPCGasCap.Uint64(), block.BaseFee()) - if err != nil { - return nil, err - } - - var traceConfig *tracers.TraceConfig - if config != nil { - traceConfig = &tracers.TraceConfig{ - Config: config.Config, - Tracer: config.Tracer, - Timeout: config.Timeout, - Reexec: config.Reexec, - } - } - return api.traceTx(ctx, msg, new(tracers.Context), vmctx, statedb, traceConfig) -} - -// traceTx configures a new tracer according to the provided configuration, and -// executes the given message in the provided environment. The return value will -// be tracer dependent. -func (api *DebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *tracers.TraceConfig) (interface{}, error) { - var ( - tracer tracers.Tracer - err error - timeout = defaultTraceTimeout - txContext = core.NewEVMTxContext(message) - ) - if config == nil { - config = &tracers.TraceConfig{} - } - // Default tracer is the struct logger - tracer = logger.NewStructLogger(config.Config) - if config.Tracer != nil { - tracer, err = tracers.New(*config.Tracer, txctx, config.TracerConfig) - if err != nil { - return nil, err - } - } - // Define a meaningful timeout of a single transaction trace - if config.Timeout != nil { - if timeout, err = time.ParseDuration(*config.Timeout); err != nil { - return nil, err - } - } - deadlineCtx, cancel := context.WithTimeout(ctx, timeout) - go func() { - <-deadlineCtx.Done() - if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { - tracer.Stop(errors.New("execution timeout")) - } - }() - defer cancel() - - // Run the transaction with tracing enabled. - vmenv := vm.NewEVM(vmctx, txContext, statedb, api.B.Config.ChainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) - // Call Prepare to clear out the statedb access list - statedb.Prepare(txctx.TxHash, txctx.TxIndex) - if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())); err != nil { - return nil, fmt.Errorf("tracing failed: %w", err) - } - return tracer.GetResult() -} diff --git a/pkg/debug/backend.go b/pkg/debug/backend.go new file mode 100644 index 00000000..bf4a1b40 --- /dev/null +++ b/pkg/debug/backend.go @@ -0,0 +1,54 @@ +// VulcanizeDB +// Copyright © 2022 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package debug + +import ( + "context" + "errors" + + "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/rpc" + + "github.com/cerc-io/ipld-eth-server/v4/pkg/eth" +) + +var _ tracers.Backend = &Backend{} + +var ( + errMethodNotSupported = errors.New("backend method not supported") +) + +// Backend implements tracers.Backend interface +type Backend struct { + eth.Backend +} + +// StateAtBlock retrieves the state database associated with a certain block +func (b *Backend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error) { + rpcBlockNumber := rpc.BlockNumber(block.NumberU64()) + statedb, _, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpcBlockNumber)) + return statedb, err +} + +// StateAtTransaction returns the execution environment of a certain transaction +func (b *Backend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { + return nil, vm.BlockContext{}, nil, errMethodNotSupported +} diff --git a/pkg/eth/backend.go b/pkg/eth/backend.go index 0a1d9517..7e55877c 100644 --- a/pkg/eth/backend.go +++ b/pkg/eth/backend.go @@ -258,6 +258,11 @@ func (b *Backend) GetTd(blockHash common.Hash) (*big.Int, error) { return td, nil } +// ChainConfig returns the active chain configuration. +func (b *Backend) ChainConfig() *params.ChainConfig { + return b.Config.ChainConfig +} + // CurrentBlock returns the current block func (b *Backend) CurrentBlock() (*types.Block, error) { block, err := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber) @@ -849,8 +854,8 @@ func (b *Backend) ValidateTrie(stateRoot common.Hash) error { } // RPCGasCap returns the configured gas cap for the rpc server -func (b *Backend) RPCGasCap() *big.Int { - return b.Config.RPCGasCap +func (b *Backend) RPCGasCap() uint64 { + return b.Config.RPCGasCap.Uint64() } func (b *Backend) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { diff --git a/pkg/graphql/graphql.go b/pkg/graphql/graphql.go index 9047839d..b4dc9ffe 100644 --- a/pkg/graphql/graphql.go +++ b/pkg/graphql/graphql.go @@ -838,7 +838,7 @@ func (b *Block) Call(ctx context.Context, args struct { return nil, err } } - result, err := eth.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, 5*time.Second, b.backend.RPCGasCap().Uint64()) + result, err := eth.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, 5*time.Second, b.backend.RPCGasCap()) if err != nil { return nil, err } diff --git a/pkg/rpc/ipc.go b/pkg/rpc/ipc.go index e18fe4af..5a09f5af 100644 --- a/pkg/rpc/ipc.go +++ b/pkg/rpc/ipc.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/rpc" log "github.com/sirupsen/logrus" + "github.com/cerc-io/ipld-eth-server/v4/pkg/prom" ) diff --git a/pkg/serve/service.go b/pkg/serve/service.go index ab56bc36..297b7183 100644 --- a/pkg/serve/service.go +++ b/pkg/serve/service.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers" ethnode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" @@ -139,22 +140,24 @@ func (sap *Service) APIs() []rpc.API { Service: net.NewPublicNetAPI(networkID, sap.client), Public: true, }, - { - Namespace: debug.APIName, - Version: debug.APIVersion, - Service: debug.NewDebugAPI(sap.backend), - }, } + ethAPI, err := eth.NewPublicEthAPI(sap.backend, sap.client, sap.supportsStateDiffing, sap.forwardEthCalls, sap.proxyOnError) if err != nil { log.Fatalf("unable to create public eth api: %v", err) } - return append(apis, rpc.API{ - Namespace: eth.APIName, - Version: eth.APIVersion, - Service: ethAPI, - Public: true, - }) + + debugTracerAPI := tracers.APIs(&debug.Backend{Backend: *sap.backend})[0] + + return append(apis, + rpc.API{ + Namespace: eth.APIName, + Version: eth.APIVersion, + Service: ethAPI, + Public: true, + }, + debugTracerAPI, + ) } // Serve listens for incoming converter data off the screenAndServePayload from the Sync process diff --git a/test/watch_address_integration_test.go b/test/watch_address_integration_test.go index ffa00aee..0ee334b7 100644 --- a/test/watch_address_integration_test.go +++ b/test/watch_address_integration_test.go @@ -13,6 +13,7 @@ import ( sdtypes "github.com/ethereum/go-ethereum/statediff/types" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + integration "github.com/cerc-io/ipld-eth-server/v4/test" )