Rpcdaemon as lib (#940)

* share config object

* create default config and logger

* move db connection to common func

* move server start to cli package

* clear

* clear

* rename cli to rpc

* use unified SetupLogger func

* make all root flag persistent

* use common flags in different packages

* use common flags in different packages

* move TraceTx method to eth package

* use native slice flags

* create package "turbo"

* disable geth api

* disable geth api

* move more data types to turbo/adapter package

* add support for customApiList

* run more

* run more

* run more

* dog-food

* move DoCall

* move DoCall

* fix tests

* fix test
This commit is contained in:
Alex Sharov 2020-08-19 18:46:20 +07:00 committed by GitHub
parent eb46dd68df
commit 1dcc2b141a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 975 additions and 1056 deletions

View File

@ -32,8 +32,7 @@ import (
"github.com/ledgerwatch/turbo-geth/crypto"
"github.com/ledgerwatch/turbo-geth/internal/flags"
"github.com/ledgerwatch/turbo-geth/log"
cli "github.com/urfave/cli"
"github.com/urfave/cli"
)
var (

View File

@ -142,10 +142,12 @@ func makeFullNode(ctx *cli.Context) (*node.Node, *eth.Ethereum) {
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
utils.RegisterGraphQLService(stack, backend.APIBackend, cfg.Node)
}
// TurboGeth - moved all API's to RPCDaemon service
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, backend.APIBackend, cfg.Ethstats.URL)
}
//if cfg.Ethstats.URL != "" {
// utils.RegisterEthStatsService(stack, backend.APIBackend, cfg.Ethstats.URL)
//}
return stack, backend
}

View File

@ -31,7 +31,7 @@ import (
)
const (
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
)

View File

@ -241,7 +241,7 @@ func init() {
// See config.go
dumpConfigCommand,
// See retesteth.go
retestethCommand,
//retestethCommand,
// See cmd/utils/flags_legacy.go
utils.ShowDeprecated,
}

View File

@ -16,11 +16,11 @@
package main
/* Retesteth tool is deprecated
import (
"bytes"
"context"
"fmt"
"io"
"math/big"
"os"
"os/signal"
@ -29,8 +29,6 @@ import (
"time"
"github.com/holiman/uint256"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
@ -383,7 +381,7 @@ func (api *RetestethAPI) SetChainParams(_ context.Context, chainParams ChainPara
ParentHash: chainParams.Genesis.ParentHash,
Alloc: accounts,
}
chainConfig, genesisHash, _, err := core.SetupGenesisBlock(ethDb, genesis, false /* history */, false /* overwrite */)
chainConfig, genesisHash, _, err := core.SetupGenesisBlock(ethDb, genesis, false, false)
if err != nil {
return false, err
}
@ -834,20 +832,7 @@ func splitAndTrim(input string) []string {
}
func retesteth(ctx *cli.Context) error {
var (
ostream log.Handler
glogger *log.GlogHandler
)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.LvlInfo)
log.SetupDefaultTerminalLogger(log.LvlInfo, "", "")
log.Info("Welcome to retesteth!")
// register signer API with server
@ -923,3 +908,4 @@ func retesteth(ctx *cli.Context) error {
log.Info("Exiting...", "signal", sig)
return nil
}
*/

View File

@ -16,6 +16,7 @@
package main
/*
import (
"math/big"
@ -146,3 +147,4 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter
return fields, nil
}
*/

View File

@ -7,7 +7,6 @@ import (
"encoding/binary"
"flag"
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
@ -42,8 +41,6 @@ import (
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rlp"
"github.com/ledgerwatch/turbo-geth/trie"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/wcharczuk/go-chart"
"github.com/wcharczuk/go-chart/util"
)
@ -1685,21 +1682,7 @@ func supply(chaindata string) error {
func main() {
flag.Parse()
var (
ostream log.Handler
glogger *log.GlogHandler
)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.Lvl(*verbosity))
log.SetupDefaultTerminalLogger(log.Lvl(*verbosity), "", "")
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/ethdb"
@ -32,7 +33,7 @@ var cmdCompareBucket = &cobra.Command{
Use: "compare_bucket",
Short: "compare bucket to the same bucket in '--reference_chaindata'",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if referenceChaindata == "" {
referenceChaindata = chaindata + "-copy"
}
@ -49,7 +50,7 @@ var cmdCompareStates = &cobra.Command{
Use: "compare_states",
Short: "compare state buckets to buckets in '--reference_chaindata'",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if referenceChaindata == "" {
referenceChaindata = chaindata + "-copy"
}

View File

@ -3,6 +3,7 @@ package commands
import (
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"os"
"path"
"sync"
@ -24,7 +25,7 @@ var cmdResetState = &cobra.Command{
Use: "reset_state",
Short: "Reset StateStages (5,6,7,8,9,10) and buckets",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
err := resetState(ctx)
if err != nil {
log.Error(err.Error())
@ -44,7 +45,7 @@ var cmdClearUnwindStack = &cobra.Command{
Use: "clear_unwind_stack",
Short: "Clear unwind stack",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
err := clearUnwindStack(ctx)
if err != nil {
log.Error(err.Error())

View File

@ -1,16 +1,9 @@
package commands
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/debug"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/migrations"
"github.com/spf13/cobra"
)
@ -19,10 +12,6 @@ var rootCmd = &cobra.Command{
Use: "integration",
Short: "long and heavy integration tests for turbo-geth",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if err := debug.SetupCobra(cmd); err != nil {
panic(err)
}
if len(chaindata) > 0 {
db := ethdb.MustOpen(chaindata)
defer db.Close()
@ -31,36 +20,9 @@ var rootCmd = &cobra.Command{
}
}
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
debug.Exit()
},
}
func init() {
utils.CobraFlags(rootCmd, append(debug.Flags, utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, utils.MetricsHTTPFlag, utils.MetricsPortFlag))
}
func rootContext() context.Context {
ctx, cancel := context.WithCancel(context.Background())
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(ch)
select {
case <-ch:
log.Info("Got interrupt, shutting down...")
case <-ctx.Done():
}
cancel()
}()
return ctx
}
func Execute() {
if err := rootCmd.ExecuteContext(rootContext()); err != nil {
fmt.Println(err)
os.Exit(1)
}
func RootCommand() *cobra.Command {
utils.CobraFlags(rootCmd, append(debug.Flags, utils.MetricFlags...))
return rootCmd
}

View File

@ -2,6 +2,7 @@ package commands
import (
"context"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"runtime"
"time"
@ -20,7 +21,7 @@ var cmdStageSenders = &cobra.Command{
Use: "stage_senders",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageSenders(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -33,7 +34,7 @@ var cmdStageExec = &cobra.Command{
Use: "stage_exec",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageExec(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -46,7 +47,7 @@ var cmdStageIHash = &cobra.Command{
Use: "stage_ih",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageIHash(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -59,7 +60,7 @@ var cmdStageHashState = &cobra.Command{
Use: "stage_hash_state",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageHashState(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -72,7 +73,7 @@ var cmdStageHistory = &cobra.Command{
Use: "stage_history",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageHistory(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -85,7 +86,7 @@ var cmdStageTxLookup = &cobra.Command{
Use: "stage_tx_lookup",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := stageTxLookup(ctx); err != nil {
log.Error("Error", "err", err)
return err
@ -97,7 +98,7 @@ var cmdPrintStages = &cobra.Command{
Use: "print_stages",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := printAllStages(ctx); err != nil {
log.Error("Error", "err", err)
return err

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/changeset"
@ -26,7 +27,7 @@ var stateStags = &cobra.Command{
`,
Example: "go run ./cmd/integration state_stages --chaindata=... --verbosity=3 --unwind=100 --unwind_every=100000 --block=2000000",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := rootContext()
ctx := utils.RootContext()
if err := syncBySmallSteps(ctx, chaindata); err != nil {
log.Error("Error", "err", err)
return err

View File

@ -1,8 +1,21 @@
package main
import "github.com/ledgerwatch/turbo-geth/cmd/integration/commands"
import (
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/integration/commands"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"os"
)
func main() {
commands.Execute()
}
rootCmd := commands.RootCommand()
if err := utils.SetupCobra(rootCmd); err != nil {
panic(err)
}
defer utils.StopDebug()
if err := rootCmd.ExecuteContext(utils.RootContext()); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -1,14 +1,7 @@
package commands
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ledgerwatch/turbo-geth/cmd/restapi/rest"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/spf13/cobra"
)
@ -32,27 +25,6 @@ var rootCmd = &cobra.Command{
},
}
func Execute() {
if err := rootCmd.ExecuteContext(rootContext()); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func rootContext() context.Context {
ctx, cancel := context.WithCancel(context.Background())
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(ch)
select {
case <-ch:
log.Info("Got interrupt, shutting down...")
case <-ctx.Done():
}
cancel()
}()
return ctx
func RootCommand() *cobra.Command {
return rootCmd
}

View File

@ -1,30 +1,19 @@
package main
import (
"io"
"fmt"
"os"
"github.com/ledgerwatch/turbo-geth/cmd/restapi/commands"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
func main() {
var (
ostream log.Handler
glogger *log.GlogHandler
)
log.SetupDefaultTerminalLogger(log.LvlInfo, "", "")
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
if err := commands.RootCommand().ExecuteContext(utils.RootContext()); err != nil {
fmt.Println(err)
os.Exit(1)
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.LvlInfo)
commands.Execute()
}

View File

@ -0,0 +1,97 @@
package cli
import (
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/debug"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/node"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/spf13/cobra"
)
type Flags struct {
PrivateApiAddr string
Chaindata string
HttpListenAddress string
HttpPort int
HttpCORSDomain []string
HttpVirtualHost []string
API []string
Gascap uint64
}
var rootCmd = &cobra.Command{
Use: "rpcdaemon",
Short: "rpcdaemon is JSON RPC server that connects to turbo-geth node for remote DB access",
}
func RootCommand() (*cobra.Command, *Flags) {
utils.CobraFlags(rootCmd, append(debug.Flags, utils.MetricFlags...))
cfg := &Flags{}
rootCmd.PersistentFlags().StringVar(&cfg.PrivateApiAddr, "private.api.addr", "127.0.0.1:9090", "private api network address, for example: 127.0.0.1:9090, empty string means not to start the listener. do not expose to public network. serves remote database interface")
rootCmd.PersistentFlags().StringVar(&cfg.Chaindata, "chaindata", "", "path to the database")
rootCmd.PersistentFlags().StringVar(&cfg.HttpListenAddress, "http.addr", node.DefaultHTTPHost, "HTTP-RPC server listening interface")
rootCmd.PersistentFlags().IntVar(&cfg.HttpPort, "http.port", node.DefaultHTTPPort, "HTTP-RPC server listening port")
rootCmd.PersistentFlags().StringSliceVar(&cfg.HttpCORSDomain, "http.corsdomain", []string{}, "Comma separated list of domains from which to accept cross origin requests (browser enforced)")
rootCmd.PersistentFlags().StringSliceVar(&cfg.HttpVirtualHost, "http.vhosts", node.DefaultConfig.HTTPVirtualHosts, "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.")
rootCmd.PersistentFlags().StringSliceVar(&cfg.API, "http.api", []string{"eth"}, "API's offered over the HTTP-RPC interface")
rootCmd.PersistentFlags().Uint64Var(&cfg.Gascap, "rpc.gascap", 0, "Sets a cap on gas that can be used in eth_call/estimateGas")
return rootCmd, cfg
}
func OpenDB(cfg Flags) (ethdb.KV, ethdb.Backend, error) {
var db ethdb.KV
var txPool ethdb.Backend
var err error
if cfg.PrivateApiAddr != "" {
db, txPool, err = ethdb.NewRemote().Path(cfg.PrivateApiAddr).Open()
if err != nil {
return nil, nil, fmt.Errorf("could not connect to remoteDb: %w", err)
}
} else if cfg.Chaindata != "" {
if database, errOpen := ethdb.Open(cfg.Chaindata); errOpen == nil {
db = database.KV()
} else {
err = errOpen
}
} else {
return nil, nil, fmt.Errorf("either remote db or bolt db must be specified")
}
if err != nil {
return nil, nil, fmt.Errorf("could not connect to remoteDb: %w", err)
}
return db, txPool, err
}
func StartRpcServer(ctx context.Context, cfg Flags, rpcAPI []rpc.API) {
// register apis and create handler stack
httpEndpoint := fmt.Sprintf("%s:%d", cfg.HttpListenAddress, cfg.HttpPort)
srv := rpc.NewServer()
if err := node.RegisterApisFromWhitelist(rpcAPI, cfg.API, srv, false); err != nil {
log.Error("Could not start register RPC apis", "error", err)
return
}
handler := node.NewHTTPHandlerStack(srv, cfg.HttpCORSDomain, cfg.HttpVirtualHost)
listener, _, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
if err != nil {
log.Error("Could not start RPC api", "error", err)
return
}
extapiURL := fmt.Sprintf("http://%s", httpEndpoint)
log.Info("HTTP endpoint opened", "url", extapiURL)
defer func() {
listener.Close()
log.Info("HTTP endpoint closed", "url", httpEndpoint)
}()
sig := <-ctx.Done()
log.Info("Exiting...", "signal", sig)
}

View File

@ -4,28 +4,23 @@ import (
"context"
"errors"
"fmt"
"math/big"
"time"
"github.com/holiman/uint256"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/rpchelper"
"github.com/ledgerwatch/turbo-geth/turbo/transactions"
"math/big"
)
const callTimeout = 5 * time.Second
func (api *APIImpl) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account) (hexutil.Bytes, error) {
result, err := api.doCall(ctx, args, blockNrOrHash, overrides)
result, err := transactions.DoCall(ctx, args, api.db, api.dbReader, blockNrOrHash, overrides, api.GasCap)
if err != nil {
return nil, err
}
@ -38,136 +33,13 @@ func (api *APIImpl) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHas
return result.Return(), result.Err
}
func (api *APIImpl) doCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account) (*core.ExecutionResult, error) {
// todo: Pending state is only known by the miner
/*
if blockNrOrHash.BlockNumber != nil && *blockNrOrHash.BlockNumber == rpc.PendingBlockNumber {
block, state, _ := b.eth.miner.Pending()
return state, block.Header(), nil
}
*/
blockNumber, hash, err := GetBlockNumber(blockNrOrHash, api.dbReader)
if err != nil {
return nil, err
}
ds := state.NewPlainDBState(api.db, blockNumber)
state := state.New(ds)
if state == nil {
return nil, fmt.Errorf("can't get the state for %d", blockNumber)
}
header := rawdb.ReadHeader(api.dbReader, hash, blockNumber)
if header == nil {
return nil, fmt.Errorf("block %d(%x) not found", blockNumber, hash)
}
// Override the fields of specified contracts before execution.
if overrides != nil {
for addr, account := range *overrides {
// Override account nonce.
if account.Nonce != nil {
state.SetNonce(addr, uint64(*account.Nonce))
}
// Override account(contract) code.
if account.Code != nil {
state.SetCode(addr, *account.Code)
}
// Override account balance.
if account.Balance != nil {
balance, _ := uint256.FromBig((*big.Int)(*account.Balance))
state.SetBalance(addr, balance)
}
if account.State != nil && account.StateDiff != nil {
return nil, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
}
// Replace entire state if caller requires.
if account.State != nil {
state.SetStorage(addr, *account.State)
}
// Apply state diff into specified accounts.
if account.StateDiff != nil {
for key, value := range *account.StateDiff {
key := key
state.SetState(addr, &key, value)
}
}
}
}
// 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 callTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, callTimeout)
} 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.
msg := args.ToMessage(cfg.gascap)
evmCtx := GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, api.dbReader)
evm := vm.NewEVM(evmCtx, state, params.MainnetChainConfig, vm.Config{})
// 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()
}()
gp := new(core.GasPool).AddGas(msg.Gas())
result, err := core.ApplyMessage(evm, msg, gp)
if 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)", callTimeout)
}
return result, nil
}
func GetEvmContext(msg core.Message, header *types.Header, requireCanonical bool, dbReader rawdb.DatabaseReader) vm.Context {
return vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHashGetter(requireCanonical, dbReader),
Origin: msg.From(),
Coinbase: header.Coinbase,
BlockNumber: new(big.Int).Set(header.Number),
Time: new(big.Int).SetUint64(header.Time),
Difficulty: new(big.Int).Set(header.Difficulty),
GasLimit: header.GasLimit,
GasPrice: msg.GasPrice().ToBig(),
}
}
func getHashGetter(requireCanonical bool, dbReader rawdb.DatabaseReader) func(uint64) common.Hash {
return func(n uint64) common.Hash {
hash, err := GetHashByNumber(n, requireCanonical, dbReader)
if err != nil {
log.Debug("can't get block hash by number", "number", n, "only-canonical", requireCanonical)
}
return hash
}
}
// EstimateGas returns an estimate of the amount of gas needed to execute the
// given transaction against the current pending block.
func (api *APIImpl) EstimateGas(ctx context.Context, args ethapi.CallArgs) (hexutil.Uint64, error) {
//fixme: blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
hash := rawdb.ReadHeadBlockHash(api.dbReader)
return api.DoEstimateGas(ctx, args, rpc.BlockNumberOrHash{BlockHash: &hash}, big.NewInt(0).SetUint64(cfg.gascap))
return api.DoEstimateGas(ctx, args, rpc.BlockNumberOrHash{BlockHash: &hash}, big.NewInt(0).SetUint64(api.GasCap))
}
func (api *APIImpl) DoEstimateGas(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) {
@ -182,7 +54,7 @@ func (api *APIImpl) DoEstimateGas(ctx context.Context, args ethapi.CallArgs, blo
args.From = new(common.Address)
}
blockNumber, hash, err := GetBlockNumber(blockNrOrHash, api.dbReader)
blockNumber, hash, err := rpchelper.GetBlockNumber(blockNrOrHash, api.dbReader)
if err != nil {
return 0, err
}
@ -233,7 +105,7 @@ func (api *APIImpl) DoEstimateGas(ctx context.Context, args ethapi.CallArgs, blo
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)
result, err := api.doCall(ctx, args, blockNrOrHash, nil)
result, err := transactions.DoCall(ctx, args, api.db, api.dbReader, blockNrOrHash, nil, api.GasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
// Special case, raise gas limit

View File

@ -3,100 +3,14 @@ package commands
import (
"context"
"fmt"
"math/big"
"strings"
"github.com/spf13/cobra"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/consensus"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/cli"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/node"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/adapter/ethapi"
)
// splitAndTrim splits input separated by a comma
// and trims excessive white space from the substrings.
func splitAndTrim(input string) []string {
result := strings.Split(input, ",")
for i, r := range result {
result[i] = strings.TrimSpace(r)
}
return result
}
type chainContext struct {
db rawdb.DatabaseReader
}
func NewChainContext(db rawdb.DatabaseReader) *chainContext {
return &chainContext{
db: db,
}
}
type powEngine struct {
}
func (c *powEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
panic("must not be called")
}
func (c *powEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (func(), <-chan error) {
panic("must not be called")
}
func (c *powEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
panic("must not be called")
}
func (c *powEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Finalize(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction, uncles []*types.Header) {
panic("must not be called")
}
func (c *powEngine) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
panic("must not be called")
}
func (c *powEngine) Seal(_ consensus.Cancel, chain consensus.ChainHeaderReader, block *types.Block, results chan<- consensus.ResultWithContext, stop <-chan struct{}) error {
panic("must not be called")
}
func (c *powEngine) SealHash(header *types.Header) common.Hash {
panic("must not be called")
}
func (c *powEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
panic("must not be called")
}
func (c *powEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
panic("must not be called")
}
func (c *powEngine) Close() error {
panic("must not be called")
}
func (c *powEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
func (c *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
return rawdb.ReadHeader(c.db, hash, number)
}
func (c *chainContext) Engine() consensus.Engine {
return &powEngine{}
}
// GetBlockByNumber see https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber
// see internal/ethapi.PublicBlockChainAPI.GetBlockByNumber
func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
@ -108,7 +22,7 @@ func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber
}
additionalFields["totalDifficulty"] = rawdb.ReadTd(api.dbReader, block.Hash(), uint64(number.Int64()))
response, err := api.rpcMarshalBlock(block, true, fullTx, additionalFields)
response, err := ethapi.RPCMarshalBlock(block, true, fullTx, additionalFields)
if err == nil && number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
@ -119,30 +33,15 @@ func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber
return response, err
}
// rpcMarshalBlock reimplementation of ethapi.rpcMarshalBlock
func (api *APIImpl) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool, additional map[string]interface{}) (map[string]interface{}, error) {
fields, err := ethapi.RPCMarshalBlock(b, inclTx, fullTx)
if err != nil {
return nil, err
}
for k, v := range additional {
fields[k] = v
}
return fields, err
}
func GetAPI(db ethdb.KV, eth ethdb.Backend, enabledApis []string) []rpc.API {
func APIList(db ethdb.KV, eth ethdb.Backend, cfg cli.Flags, customApiList []rpc.API) []rpc.API {
var rpcAPI []rpc.API
dbReader := ethdb.NewObjectDatabase(db)
chainContext := NewChainContext(dbReader)
apiImpl := NewAPI(db, dbReader, chainContext, eth)
apiImpl := NewAPI(db, dbReader, eth, cfg.Gascap)
netImpl := NewNetAPIImpl(eth)
dbgAPIImpl := NewPrivateDebugAPI(db, dbReader, chainContext)
dbgAPIImpl := NewPrivateDebugAPI(db, dbReader)
for _, enabledAPI := range enabledApis {
for _, enabledAPI := range cfg.API {
switch enabledAPI {
case "eth":
rpcAPI = append(rpcAPI, rpc.API{
@ -167,67 +66,10 @@ func GetAPI(db ethdb.KV, eth ethdb.Backend, enabledApis []string) []rpc.API {
})
default:
log.Error("Unrecognised", "api", enabledAPI)
// TODO: enable validation after checking customApiList
//log.Error("Unrecognised", "api", enabledAPI)
}
}
return rpcAPI
}
func daemon(cmd *cobra.Command, cfg Config) {
vhosts := splitAndTrim(cfg.httpVirtualHost)
cors := splitAndTrim(cfg.httpCORSDomain)
enabledApis := splitAndTrim(cfg.API)
var db ethdb.KV
var txPool ethdb.Backend
var err error
if cfg.privateApiAddr != "" {
db, txPool, err = ethdb.NewRemote().Path(cfg.privateApiAddr).Open()
if err != nil {
log.Error("Could not connect to remoteDb", "error", err)
return
}
} else if cfg.chaindata != "" {
if database, errOpen := ethdb.Open(cfg.chaindata); errOpen == nil {
db = database.KV()
} else {
err = errOpen
}
} else {
err = fmt.Errorf("either remote db or bolt db must be specified")
}
if err != nil {
log.Error("Could not connect to remoteDb", "error", err)
return
}
var rpcAPI = GetAPI(db, txPool, enabledApis)
httpEndpoint := fmt.Sprintf("%s:%d", cfg.httpListenAddress, cfg.httpPort)
// register apis and create handler stack
srv := rpc.NewServer()
err = node.RegisterApisFromWhitelist(rpcAPI, enabledApis, srv, false)
if err != nil {
log.Error("Could not start register RPC apis", "error", err)
return
}
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
listener, _, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
if err != nil {
log.Error("Could not start RPC api", "error", err)
return
}
extapiURL := fmt.Sprintf("http://%s", httpEndpoint)
log.Info("HTTP endpoint opened", "url", extapiURL)
defer func() {
listener.Close()
log.Info("HTTP endpoint closed", "url", httpEndpoint)
}()
sig := <-cmd.Context().Done()
log.Info("Exiting...", "signal", sig)
return append(rpcAPI, customApiList...)
}

View File

@ -14,6 +14,8 @@ import (
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
"github.com/ledgerwatch/turbo-geth/turbo/transactions"
)
// PrivateDebugAPI
@ -33,17 +35,18 @@ type PrivateDebugAPIImpl struct {
}
// NewPrivateDebugAPI returns PrivateDebugAPIImpl instance
func NewPrivateDebugAPI(db ethdb.KV, dbReader ethdb.Getter, chainContext core.ChainContext) *PrivateDebugAPIImpl {
func NewPrivateDebugAPI(db ethdb.KV, dbReader ethdb.Getter) *PrivateDebugAPIImpl {
return &PrivateDebugAPIImpl{
db: db,
dbReader: dbReader,
chainContext: chainContext,
db: db,
dbReader: dbReader,
}
}
// StorageRangeAt re-implementation of eth/api.go:StorageRangeAt
func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
_, _, _, stateReader, err := ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.db, blockHash, txIndex)
bc := adapter.NewBlockGetter(api.dbReader)
cc := adapter.NewChainContext(api.dbReader)
_, _, _, stateReader, err := transactions.ComputeTxEnv(ctx, bc, params.MainnetChainConfig, cc, api.db, blockHash, txIndex)
if err != nil {
return StorageRangeResult{}, err
}

View File

@ -2,11 +2,9 @@ package commands
import (
"context"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
"github.com/ledgerwatch/turbo-geth/ethdb"
@ -33,15 +31,16 @@ type APIImpl struct {
ethBackend ethdb.Backend
dbReader ethdb.Getter
chainContext core.ChainContext
GasCap uint64
}
// NewAPI returns APIImpl instance
func NewAPI(db ethdb.KV, dbReader ethdb.Getter, chainContext core.ChainContext, eth ethdb.Backend) *APIImpl {
func NewAPI(db ethdb.KV, dbReader ethdb.Getter, eth ethdb.Backend, gascap uint64) *APIImpl {
return &APIImpl{
db: db,
dbReader: dbReader,
chainContext: chainContext,
ethBackend: eth,
db: db,
dbReader: dbReader,
ethBackend: eth,
GasCap: gascap,
}
}
@ -52,16 +51,3 @@ func (api *APIImpl) BlockNumber(ctx context.Context) (hexutil.Uint64, error) {
}
return hexutil.Uint64(execution), nil
}
type blockGetter struct {
dbReader rawdb.DatabaseReader
}
func (g *blockGetter) GetBlockByHash(hash common.Hash) *types.Block {
return rawdb.ReadBlockByHash(g.dbReader, hash)
}
func (g *blockGetter) GetBlock(hash common.Hash, number uint64) *types.Block {
return rawdb.ReadBlock(g.dbReader, hash, number)
}

View File

@ -3,67 +3,23 @@ package commands
import (
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/turbo/rpchelper"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/rpc"
)
func (api *APIImpl) GetBalance(_ context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
blockNumber, _, err := GetBlockNumber(blockNrOrHash, api.dbReader)
blockNumber, _, err := rpchelper.GetBlockNumber(blockNrOrHash, api.dbReader)
if err != nil {
return nil, err
}
acc, err := GetAccount(api.db, blockNumber, address)
acc, err := rpchelper.GetAccount(api.db, blockNumber, address)
if err != nil {
return nil, fmt.Errorf("cant get a balance for account %q for block %v", address.String(), blockNumber)
}
return (*hexutil.Big)(acc.Balance.ToBig()), nil
}
func GetBlockNumber(blockNrOrHash rpc.BlockNumberOrHash, dbReader rawdb.DatabaseReader) (uint64, common.Hash, error) {
var blockNumber uint64
var err error
hash, ok := blockNrOrHash.Hash()
if !ok {
blockNumber = uint64(blockNrOrHash.BlockNumber.Int64())
hash, err = GetHashByNumber(blockNumber, blockNrOrHash.RequireCanonical, dbReader)
if err != nil {
return 0, common.Hash{}, err
}
} else {
block := rawdb.ReadBlockByHash(dbReader, hash)
if block == nil {
return 0, common.Hash{}, fmt.Errorf("block %x not found", hash)
}
blockNumber = block.NumberU64()
if blockNrOrHash.RequireCanonical && rawdb.ReadCanonicalHash(dbReader, blockNumber) != hash {
return 0, common.Hash{}, fmt.Errorf("hash %q is not currently canonical", hash.String())
}
}
return blockNumber, hash, nil
}
func GetAccount(chainKV ethdb.KV, blockNumber uint64, address common.Address) (*accounts.Account, error) {
reader := NewStateReader(chainKV, blockNumber)
return reader.ReadAccountData(address)
}
func GetHashByNumber(blockNumber uint64, requireCanonical bool, dbReader rawdb.DatabaseReader) (common.Hash, error) {
if requireCanonical {
return rawdb.ReadCanonicalHash(dbReader, blockNumber), nil
}
block := rawdb.ReadBlockByNumber(dbReader, blockNumber)
if block == nil {
return common.Hash{}, fmt.Errorf("block %d not found", blockNumber)
}
return block.Hash(), nil
}

View File

@ -3,6 +3,8 @@ package commands
import (
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
"github.com/ledgerwatch/turbo-geth/turbo/transactions"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
@ -25,14 +27,14 @@ func GetReceipts(ctx context.Context, db rawdb.DatabaseReader, cfg *params.Chain
return cached, nil
}
bc := &blockGetter{db}
_, _, ibs, dbstate, err := ComputeTxEnv(ctx, bc, params.MainnetChainConfig, &chainContext{db: db}, db.(ethdb.HasKV).KV(), hash, 0)
cc := adapter.NewChainContext(db)
bc := adapter.NewBlockGetter(db)
_, _, ibs, dbstate, err := transactions.ComputeTxEnv(ctx, bc, params.MainnetChainConfig, cc, db.(ethdb.HasKV).KV(), hash, 0)
if err != nil {
return nil, err
}
var receipts types.Receipts
cc := &chainContext{db}
gp := new(core.GasPool).AddGas(block.GasLimit())
var usedGas = new(uint64)
for i, tx := range block.Transactions() {

View File

@ -1,130 +0,0 @@
package commands
import (
"context"
"fmt"
"io"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"strings"
"syscall"
"github.com/spf13/cobra"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/node"
)
type Config struct {
privateApiAddr string
chaindata string
httpListenAddress string
httpPort int
httpCORSDomain string
httpVirtualHost string
API string
gascap uint64
}
var (
cpuprofile string
cpuProfileFile io.WriteCloser
memprofile string
cfg Config
)
func init() {
rootCmd.PersistentFlags().StringVar(&cpuprofile, "pprof.cpuprofile", "", "write cpu profile `file`")
rootCmd.PersistentFlags().StringVar(&memprofile, "memprofile", "", "write memory profile `file`")
rootCmd.Flags().StringVar(&cfg.privateApiAddr, "private.api.addr", "", "private api network address, for example: 127.0.0.1:9090, empty string means not to start the listener. do not expose to public network. serves remote database interface")
rootCmd.Flags().StringVar(&cfg.chaindata, "chaindata", "", "path to the database")
rootCmd.Flags().StringVar(&cfg.httpListenAddress, "http.addr", node.DefaultHTTPHost, "HTTP-RPC server listening interface")
rootCmd.Flags().IntVar(&cfg.httpPort, "http.port", node.DefaultHTTPPort, "HTTP-RPC server listening port")
rootCmd.Flags().StringVar(&cfg.httpCORSDomain, "http.corsdomain", "", "Comma separated list of domains from which to accept cross origin requests (browser enforced)")
rootCmd.Flags().StringVar(&cfg.httpVirtualHost, "http.vhosts", strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.")
rootCmd.Flags().StringVar(&cfg.API, "http.api", "", "API's offered over the HTTP-RPC interface")
rootCmd.Flags().Uint64Var(&cfg.gascap, "rpc.gascap", 0, "Sets a cap on gas that can be used in eth_call/estimateGas")
}
var rootCmd = &cobra.Command{
Use: "rpcdaemon",
Short: "rpcdaemon is JSON RPC server that connects to turbo-geth node for remote DB access",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
startProfilingIfNeeded()
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
stopProfilingIfNeeded()
},
RunE: func(cmd *cobra.Command, args []string) error {
daemon(cmd, cfg)
return nil
},
}
func Execute() {
if err := rootCmd.ExecuteContext(rootContext()); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func rootContext() context.Context {
ctx, cancel := context.WithCancel(context.Background())
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(ch)
select {
case <-ch:
log.Info("Got interrupt, shutting down...")
case <-ctx.Done():
}
cancel()
}()
return ctx
}
func startProfilingIfNeeded() {
if cpuprofile != "" {
fmt.Println("starting CPU profiling")
cpuProfileFile, err := os.Create(cpuprofile)
if err != nil {
log.Error("could not create CPU profile", "error", err)
return
}
if err := pprof.StartCPUProfile(cpuProfileFile); err != nil {
log.Error("could not start CPU profile", "error", err)
return
}
}
}
func stopProfilingIfNeeded() {
if cpuprofile != "" {
fmt.Println("stopping CPU profiling")
pprof.StopCPUProfile()
}
if cpuProfileFile != nil {
cpuProfileFile.Close()
}
if memprofile != "" {
f, err := os.Create(memprofile)
if err != nil {
log.Error("could not create mem profile", "error", err)
return
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Error("could not write memory profile", "error", err)
return
}
}
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"github.com/holiman/uint256"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
)
// StorageRangeResult is the result of a debug_storageRangeAt API call.
@ -21,7 +21,7 @@ type StorageEntry struct {
Value common.Hash `json:"value"`
}
func StorageRangeAt(stateReader *StateReader, contractAddress common.Address, start []byte, maxResult int) (StorageRangeResult, error) {
func StorageRangeAt(stateReader *adapter.StateReader, contractAddress common.Address, start []byte, maxResult int) (StorageRangeResult, error) {
//account, err := stateReader.ReadAccountData(contractAddress)
//if err != nil {
// return StorageRangeResult{}, fmt.Errorf("error reading account %x: %v", contractAddress, err)

View File

@ -2,27 +2,13 @@ package commands
import (
"context"
"errors"
"fmt"
"time"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/eth"
"github.com/ledgerwatch/turbo-geth/eth/tracers"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/params"
)
const (
// defaultTraceTimeout is the amount of time a single transaction can execute
// by default before being forcefully aborted.
defaultTraceTimeout = 5 * time.Second
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
"github.com/ledgerwatch/turbo-geth/turbo/transactions"
)
// TraceTransaction returns the structured logs created during the execution of EVM
@ -33,136 +19,12 @@ func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash commo
if tx == nil {
return nil, fmt.Errorf("transaction %#x not found", hash)
}
msg, vmctx, ibs, _, err := ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.db, blockHash, txIndex)
getter := adapter.NewBlockGetter(api.dbReader)
chainContext := adapter.NewChainContext(api.dbReader)
msg, vmctx, ibs, _, err := transactions.ComputeTxEnv(ctx, getter, params.MainnetChainConfig, chainContext, api.db, blockHash, txIndex)
if err != nil {
return nil, err
}
// Trace the transaction and return
return api.traceTx(ctx, msg, vmctx, ibs, config)
}
// 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 *PrivateDebugAPIImpl) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, ibs vm.IntraBlockState,
config *eth.TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.Tracer
err error
)
switch {
case config != nil && config.Tracer != nil:
// Define a meaningful timeout of a single transaction trace
timeout := defaultTraceTimeout
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); err != nil {
return nil, err
}
// Handle timeouts and RPC cancellations
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
go func() {
<-deadlineCtx.Done()
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
}()
defer cancel()
case config == nil:
tracer = vm.NewStructLogger(nil)
default:
tracer = vm.NewStructLogger(config.LogConfig)
}
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, ibs, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.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:
return &ethapi.ExecutionResult{
Gas: result.UsedGas,
Failed: result.Failed(),
ReturnValue: fmt.Sprintf("%x", result.Return()),
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
case *tracers.Tracer:
return tracer.GetResult()
default:
panic(fmt.Sprintf("bad tracer type %T", tracer))
}
}
// computeTxEnv returns the execution environment of a certain transaction.
func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainKV ethdb.KV, blockHash common.Hash, txIndex uint64) (core.Message, vm.Context, *state.IntraBlockState, *StateReader, error) {
// Create the parent state database
block := blockGetter.GetBlockByHash(blockHash)
if block == nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("block %x not found", blockHash)
}
parent := blockGetter.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("parent %x not found", block.ParentHash())
}
statedb, reader := ComputeIntraBlockState(chainKV, parent)
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.Context{}, statedb, reader, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(cfg, block.Number())
for idx, tx := range block.Transactions() {
select {
default:
case <-ctx.Done():
return nil, vm.Context{}, nil, nil, ctx.Err()
}
statedb.Prepare(tx.Hash(), blockHash, idx)
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer)
EVMcontext := core.NewEVMContext(msg, block.Header(), chain, nil)
if idx == int(txIndex) {
return msg, EVMcontext, statedb, reader, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(EVMcontext, statedb, cfg, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction %x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
_ = statedb.FinalizeTx(vmenv.ChainConfig().WithEIPsFlags(context.Background(), block.Number()), reader)
}
return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %x", txIndex, blockHash)
}
// computeIntraBlockState retrieves the state database associated with a certain block.
// If no state is locally available for the given block, a number of blocks are
// attempted to be reexecuted to generate the desired state.
func ComputeIntraBlockState(chainKV ethdb.KV, block *types.Block) (*state.IntraBlockState, *StateReader) {
// If we have the state fully available, use that
reader := NewStateReader(chainKV, block.NumberU64())
statedb := state.New(reader)
return statedb, reader
}
type BlockGetter interface {
// GetBlockByHash retrieves a block from the database by hash, caching it if found.
GetBlockByHash(hash common.Hash) *types.Block
// GetBlock retrieves a block from the database by hash and number,
// caching it if found.
GetBlock(hash common.Hash, number uint64) *types.Block
return transactions.TraceTx(ctx, msg, vmctx, ibs, config)
}

View File

@ -1,30 +1,36 @@
package main
import (
"io"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"os"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/cli"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/commands"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
)
func main() {
var (
ostream log.Handler
glogger *log.GlogHandler
)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
cmd, cfg := cli.RootCommand()
if err := utils.SetupCobra(cmd); err != nil {
panic(err)
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.LvlInfo)
defer utils.StopDebug()
commands.Execute()
cmd.RunE = func(cmd *cobra.Command, args []string) error {
db, txPool, err := cli.OpenDB(*cfg)
if err != nil {
log.Error("Could not connect to remoteDb", "error", err)
return nil
}
var rpcAPI = commands.APIList(db, txPool, *cfg, nil)
cli.StartRpcServer(cmd.Context(), *cfg, rpcAPI)
return nil
}
if err := cmd.ExecuteContext(utils.RootContext()); err != nil {
log.Error(err.Error())
os.Exit(1)
}
}

View File

@ -1,6 +1,7 @@
package service
import (
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/cli"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/commands"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/ethdb"
@ -8,7 +9,7 @@ import (
)
func New(db ethdb.HasKV, ethereum core.Backend, stack *node.Node) {
apis := commands.GetAPI(db.KV(), core.NewEthBackend(ethereum), []string{"eth", "debug"})
apis := commands.APIList(db.KV(), core.NewEthBackend(ethereum), cli.Flags{API: []string{"eth", "debug"}}, nil)
stack.RegisterAPIs(apis)
}

View File

@ -8,6 +8,22 @@ Content-Type: application/json
###
# curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber", "params": ["0x1b4", true], "id":1}' localhost:8545
POST localhost:8545
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": [
"0x1b4",
true
],
"id": 1
}
###
POST localhost:8545
Content-Type: application/json

View File

@ -5,30 +5,14 @@ import (
"fmt"
"github.com/ledgerwatch/turbo-geth/cmd/rpctest/rpctest"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
"io"
"os"
"os/signal"
"syscall"
)
func main() {
var (
ostream log.Handler
glogger *log.GlogHandler
)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.LvlInfo)
log.SetupDefaultTerminalLogger(log.Lvl(3), "", "")
var (
needCompare bool

View File

@ -5,7 +5,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
@ -22,8 +21,6 @@ import (
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/p2p"
"github.com/ledgerwatch/turbo-geth/p2p/enode"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli"
)
@ -55,7 +52,7 @@ func init() {
app.Flags = append(app.Flags, flags...)
app.Before = func(ctx *cli.Context) error {
setupLogger(ctx)
log.SetupDefaultTerminalLogger(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)), "", "")
runtime.GOMAXPROCS(runtime.NumCPU())
if err := debug.Setup(ctx); err != nil {
return err
@ -104,23 +101,6 @@ func rootContext() context.Context {
return ctx
}
func setupLogger(cliCtx *cli.Context) {
var (
ostream log.Handler
glogger *log.GlogHandler
)
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
log.Root().SetHandler(glogger)
glogger.Verbosity(log.Lvl(cliCtx.GlobalInt(VerbosityFlag.Name)))
}
func tester(cliCtx *cli.Context) error {
ctx := rootContext()
nodeToConnect, err := getTargetAddr(cliCtx)

View File

@ -21,6 +21,8 @@ import (
"compress/gzip"
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/urfave/cli"
"io"
"os"
"os/signal"
@ -313,3 +315,35 @@ func ExportPreimages(db ethdb.Database, fn string) error {
log.Info("Exported preimages", "file", fn)
return nil
}
func SetupCobra(cmd *cobra.Command) error {
return debug.SetupCobra(cmd)
}
func StopDebug() {
debug.Exit()
}
func SetupUrfave(ctx *cli.Context) error {
return debug.Setup(ctx)
}
func RootContext() context.Context {
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer cancel()
ch := make(chan os.Signal, 1)
defer close(ch)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(ch)
select {
case <-ch:
log.Info("Got interrupt, shutting down...")
case <-ctx.Done():
}
}()
return ctx
}

View File

@ -50,7 +50,6 @@ import (
"github.com/ledgerwatch/turbo-geth/eth/downloader"
"github.com/ledgerwatch/turbo-geth/eth/gasprice"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/ethstats"
"github.com/ledgerwatch/turbo-geth/graphql"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/internal/flags"
@ -763,6 +762,8 @@ var (
}
)
var MetricFlags = []cli.Flag{MetricsEnabledFlag, MetricsEnabledExpensiveFlag, MetricsHTTPFlag, MetricsPortFlag}
// MakeDataDir retrieves the currently requested data directory, terminating
// if none (or the empty string) is specified. If the node is starting a testnet,
// then a subdirectory of the specified datadir will be used.
@ -1744,11 +1745,11 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) *eth.Ethereum {
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
// the given node.
func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
Fatalf("Failed to register the Ethereum Stats service: %v", err)
}
}
//func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
// if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
// Fatalf("Failed to register the Ethereum Stats service: %v", err)
// }
//}
// RegisterGraphQLService is a utility function to construct a new service and register it against a node.
func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, cfg node.Config) {

View File

@ -178,9 +178,9 @@ func TestWelcome(t *testing.T) {
if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) {
t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want)
}
if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) {
t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
}
//if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) {
// t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
//}
if want := "at block: 0"; !strings.Contains(output, want) {
t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want)
}

View File

@ -17,31 +17,34 @@
package eth
import (
"compress/gzip"
"context"
"errors"
"fmt"
"io"
"math/big"
"os"
"runtime"
"strings"
"time"
"github.com/holiman/uint256"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/rlp"
"github.com/ledgerwatch/turbo-geth/rpc"
)
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
type BadBlockArgs struct {
Hash common.Hash `json:"hash"`
Block map[string]interface{} `json:"block"`
RLP string `json:"rlp"`
}
// AccountRangeMaxResults is the maximum number of results to be returned per call
const AccountRangeMaxResults = 256
/*
// StorageRangeResult is the result of a debug_storageRangeAt API call.
type StorageRangeResult struct {
Storage StorageMap `json:"storage"`
NextKey *common.Hash `json:"nextKey"` // nil if Storage includes the last key in the trie.
}
type StorageMap map[common.Hash]StorageEntry
type StorageEntry struct {
Key *common.Hash `json:"key"`
Value common.Hash `json:"value"`
}
// PublicEthereumAPI provides an API to access Ethereum full node-related
// information.
type PublicEthereumAPI struct {
@ -165,6 +168,7 @@ type PrivateAdminAPI struct {
eth *Ethereum
}
// NewPrivateAdminAPI creates a new API definition for the full node private
// admin methods of the Ethereum service.
func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI {
@ -321,13 +325,6 @@ func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hex
return nil, errors.New("unknown preimage")
}
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
type BadBlockArgs struct {
Hash common.Hash `json:"hash"`
Block map[string]interface{} `json:"block"`
RLP string `json:"rlp"`
}
// GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
// and returns them as a JSON list of block-hashes
func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
@ -351,9 +348,6 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
return results, nil
}
// AccountRangeMaxResults is the maximum number of results to be returned per call
const AccountRangeMaxResults = 256
// AccountRange enumerates all accounts in the given block and start point in paging request
func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
var blockNumber uint64
@ -382,19 +376,6 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta
return dumper.IteratorDump(nocode, nostorage, incompletes, start, maxResults)
}
// StorageRangeResult is the result of a debug_storageRangeAt API call.
type StorageRangeResult struct {
Storage StorageMap `json:"storage"`
NextKey *common.Hash `json:"nextKey"` // nil if Storage includes the last key in the trie.
}
type StorageMap map[common.Hash]StorageEntry
type StorageEntry struct {
Key *common.Hash `json:"key"`
Value common.Hash `json:"value"`
}
// StorageRangeAt returns the storage at the given block height and transaction index.
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
_, _, _, dbstate, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, txIndex)
@ -496,3 +477,4 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
dirty, err := ethdb.GetModifiedAccounts(api.eth.blockchain.ChainDb(), startNum, endNum)
return dirty, err
}
*/

View File

@ -14,12 +14,16 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
package eth_test
/*
TODO: revive this tests for RPCDaemon - https://github.com/ledgerwatch/turbo-geth/issues/939
import (
"bytes"
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
"reflect"
"sort"
"strconv"
@ -27,11 +31,12 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/holiman/uint256"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/commands"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/u256"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/crypto"
"github.com/ledgerwatch/turbo-geth/eth"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/trie"
)
@ -70,7 +75,7 @@ func TestAccountRange(t *testing.T) {
var (
tds = state.NewTrieDbState(common.Hash{}, db, 0)
sdb = state.New(tds)
addrs = [AccountRangeMaxResults * 2]common.Address{}
addrs = [eth.AccountRangeMaxResults * 2]common.Address{}
m = map[common.Address]bool{}
)
@ -97,10 +102,10 @@ func TestAccountRange(t *testing.T) {
trie := tds.Trie()
accountRangeTest(t, trie, db.KV(), 0, sdb, []byte{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2)
accountRangeTest(t, trie, db.KV(), 0, sdb, []byte{}, eth.AccountRangeMaxResults/2, eth.AccountRangeMaxResults/2)
// test pagination
firstResult := accountRangeTest(t, trie, db.KV(), 0, sdb, []byte{}, AccountRangeMaxResults, AccountRangeMaxResults)
secondResult := accountRangeTest(t, trie, db.KV(), 0, sdb, firstResult.Next, AccountRangeMaxResults, AccountRangeMaxResults)
firstResult := accountRangeTest(t, trie, db.KV(), 0, sdb, []byte{}, eth.AccountRangeMaxResults, eth.AccountRangeMaxResults)
secondResult := accountRangeTest(t, trie, db.KV(), 0, sdb, firstResult.Next, eth.AccountRangeMaxResults, eth.AccountRangeMaxResults)
hList := make(resultHash, 0)
for addr1 := range firstResult.Accounts {
@ -117,8 +122,8 @@ func TestAccountRange(t *testing.T) {
// Test to see if it's possible to recover from the middle of the previous
// set and get an even split between the first and second sets.
sort.Sort(hList)
middleH := hList[AccountRangeMaxResults/2]
middleResult := accountRangeTest(t, trie, db.KV(), 0, sdb, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
middleH := hList[eth.AccountRangeMaxResults/2]
middleResult := accountRangeTest(t, trie, db.KV(), 0, sdb, middleH, eth.AccountRangeMaxResults, eth.AccountRangeMaxResults)
missing, infirst, insecond := 0, 0, 0
for h := range middleResult.Accounts {
if _, ok := firstResult.Accounts[h]; ok {
@ -132,11 +137,11 @@ func TestAccountRange(t *testing.T) {
if missing != 0 {
t.Fatalf("%d hashes in the 'middle' set were neither in the first not the second set", missing)
}
if infirst != AccountRangeMaxResults/2 {
t.Fatalf("Imbalance in the number of first-test results: %d != %d", infirst, AccountRangeMaxResults/2)
if infirst != eth.AccountRangeMaxResults/2 {
t.Fatalf("Imbalance in the number of first-test results: %d != %d", infirst, eth.AccountRangeMaxResults/2)
}
if insecond != AccountRangeMaxResults/2 {
t.Fatalf("Imbalance in the number of second-test results: %d != %d", insecond, AccountRangeMaxResults/2)
if insecond != eth.AccountRangeMaxResults/2 {
t.Fatalf("Imbalance in the number of second-test results: %d != %d", insecond, eth.AccountRangeMaxResults/2)
}
}
@ -151,7 +156,7 @@ func TestEmptyAccountRange(t *testing.T) {
if err != nil {
t.Error(err)
}
results, err1 := state.NewDumper(db.KV(), 0).IteratorDump(true, true, true, (common.Hash{}).Bytes(), AccountRangeMaxResults)
results, err1 := state.NewDumper(db.KV(), 0).IteratorDump(true, true, true, (common.Hash{}).Bytes(), eth.AccountRangeMaxResults)
if err1 != nil {
t.Fatal(err1)
}
@ -177,7 +182,7 @@ func TestStorageRangeAt(t *testing.T) {
common.HexToHash("48078cfed56339ea54962e72c37c7f588fc4f8e5bc173827ba75cb10a63a96a5"),
common.HexToHash("5723d2c3a83af9b735e3b7f21531e5623d183a9095a56604ead41f3582fdfb75"),
}
storage = StorageMap{
storage = commands.StorageMap{
keys[0]: {Key: &common.Hash{0x02}, Value: common.Hash{0x01}},
keys[1]: {Key: &common.Hash{0x04}, Value: common.Hash{0x02}},
keys[2]: {Key: &common.Hash{0x01}, Value: common.Hash{0x03}},
@ -214,34 +219,35 @@ func TestStorageRangeAt(t *testing.T) {
tests := []struct {
start []byte
limit int
want StorageRangeResult
want commands.StorageRangeResult
}{
{
start: []byte{}, limit: 0,
want: StorageRangeResult{StorageMap{}, &keys[0]},
want: commands.StorageRangeResult{commands.StorageMap{}, &keys[0]},
},
{
start: []byte{}, limit: 100,
want: StorageRangeResult{storage, nil},
want: commands.StorageRangeResult{storage, nil},
},
{
start: []byte{}, limit: 2,
want: StorageRangeResult{StorageMap{keys[0]: storage[keys[0]], keys[1]: storage[keys[1]]}, &keys[2]},
want: commands.StorageRangeResult{commands.StorageMap{keys[0]: storage[keys[0]], keys[1]: storage[keys[1]]}, &keys[2]},
},
{
start: []byte{0x00}, limit: 4,
want: StorageRangeResult{storage, nil},
want: commands.StorageRangeResult{storage, nil},
},
{
start: []byte{0x40}, limit: 2,
want: StorageRangeResult{StorageMap{keys[1]: storage[keys[1]], keys[2]: storage[keys[2]]}, &keys[3]},
want: commands.StorageRangeResult{commands.StorageMap{keys[1]: storage[keys[1]], keys[2]: storage[keys[2]]}, &keys[3]},
},
}
dbs := state.NewDbState(db.KV(), 1)
dbs := adapter.NewStateReader(db.KV(), 1)
for i, test := range tests {
test := test
t.Run("test_"+strconv.Itoa(i), func(t *testing.T) {
result, err := StorageRangeAt(dbs, addr, test.start, test.limit)
result, err := commands.StorageRangeAt(dbs, addr, test.start, test.limit)
if err != nil {
t.Error(err)
}
@ -252,3 +258,4 @@ func TestStorageRangeAt(t *testing.T) {
})
}
}
*/

View File

@ -17,43 +17,11 @@
package eth
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"math/big"
"os"
"runtime"
"sync"
"time"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/eth/tracers"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rlp"
"github.com/ledgerwatch/turbo-geth/rpc"
)
const (
// defaultTraceTimeout is the amount of time a single transaction can execute
// by default before being forcefully aborted.
defaultTraceTimeout = 5 * time.Second
// defaultTraceReexec is the number of blocks the tracer is willing to go back
// and reexecute to produce missing historical state necessary to run a specific
// trace.
defaultTraceReexec = uint64(128)
)
// TraceConfig holds extra parameters to trace functions.
@ -101,6 +69,20 @@ type txTraceTask struct {
index int // Transaction offset in the block
}
/*
const (
// defaultTraceTimeout is the amount of time a single transaction can execute
// by default before being forcefully aborted.
defaultTraceTimeout = 5 * time.Second
// defaultTraceReexec is the number of blocks the tracer is willing to go back
// and reexecute to produce missing historical state necessary to run a specific
// trace.
defaultTraceReexec = uint64(128)
)
// TraceChain returns the structured logs created during the execution of EVM
// between two blocks (excluding start) and returns them as a JSON object.
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
@ -518,12 +500,6 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
if parent == nil {
return nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
/*
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
*/
statedb, dbstate := ComputeIntraBlockState(api.eth.ChainKV(), parent)
// Retrieve the tracing configurations, or use default values
var (
@ -702,6 +678,8 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
}
}
type BlockGetter interface {
// GetBlockByHash retrieves a block from the database by hash, caching it if found.
GetBlockByHash(hash common.Hash) *types.Block
@ -736,6 +714,7 @@ func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.Chai
case <-ctx.Done():
return nil, vm.Context{}, nil, nil, ctx.Err()
}
statedb.Prepare(tx.Hash(), blockHash, idx)
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer)
@ -754,3 +733,4 @@ func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.Chai
}
return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %x", txIndex, blockHash)
}
*/

View File

@ -371,45 +371,52 @@ func (s *Ethereum) APIs() []rpc.API {
// Append all the local APIs and return
return append(apis, []rpc.API{
//{
// Namespace: "eth",
// Version: "1.0",
// Service: NewPublicEthereumAPI(s),
// Public: true,
//},
//{
// Namespace: "eth",
// Version: "1.0",
// Service: NewPublicMinerAPI(s),
// Public: true,
//},
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicMinerAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
Public: true,
}, {
Namespace: "miner",
Version: "1.0",
Service: NewPrivateMinerAPI(s),
Public: false,
}, {
},
//{
// Namespace: "miner",
// Version: "1.0",
// Service: NewPrivateMinerAPI(s),
// Public: false,
//},
{
Namespace: "eth",
Version: "1.0",
Service: filters.NewPublicFilterAPI(s.APIBackend, false),
Public: true,
}, {
Namespace: "admin",
Version: "1.0",
Service: NewPrivateAdminAPI(s),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPublicDebugAPI(s),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(s),
}, {
},
//{
// Namespace: "admin",
// Version: "1.0",
// Service: NewPrivateAdminAPI(s),
//},
//{
// Namespace: "debug",
// Version: "1.0",
// Service: NewPublicDebugAPI(s),
// Public: true,
//}, {
// Namespace: "debug",
// Version: "1.0",
// Service: NewPrivateDebugAPI(s),
//},
{
Namespace: "net",
Version: "1.0",
Service: s.netRPCService,

View File

@ -1,11 +1,11 @@
package stagedsync
import (
"errors"
"testing"
"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

View File

@ -17,6 +17,7 @@
// Package ethstats implements the network stats reporting service.
package ethstats
/*
import (
"context"
"encoding/json"
@ -767,3 +768,4 @@ func (s *Service) reportStats(conn *connWrapper) error {
}
return conn.WriteJSON(report)
}
*/

1
go.mod
View File

@ -55,7 +55,6 @@ require (
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222
github.com/petar/GoLLRB v0.0.0-20190514000832-33fb24c13b99
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/tsdb v0.10.0
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00

View File

@ -18,7 +18,6 @@ package debug
import (
"fmt"
"io"
"net/http"
_ "net/http/pprof"
"os"
@ -31,8 +30,6 @@ import (
"github.com/ledgerwatch/turbo-geth/metrics"
"github.com/ledgerwatch/turbo-geth/metrics/exp"
colorable "github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
"github.com/urfave/cli"
)
@ -140,18 +137,9 @@ var (
glogger *log.GlogHandler
)
func init() {
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor))
glogger = log.NewGlogHandler(ostream)
}
func SetupCobra(cmd *cobra.Command) error {
flags := cmd.Flags()
flags := cmd.PersistentFlags()
dbg, err := flags.GetBool(debugFlag.Name)
if err != nil {
return err
@ -160,7 +148,6 @@ func SetupCobra(cmd *cobra.Command) error {
if err != nil {
return err
}
vmodule, err := flags.GetString(vmoduleFlag.Name)
if err != nil {
return err
@ -170,20 +157,8 @@ func SetupCobra(cmd *cobra.Command) error {
return err
}
// logging
ostream, glogger = log.SetupDefaultTerminalLogger(log.Lvl(lvl), vmodule, backtrace)
log.PrintOrigins(dbg)
glogger.Verbosity(log.Lvl(lvl))
err = glogger.Vmodule(vmodule)
if err != nil {
return err
}
if backtrace != "" {
err = glogger.BacktraceAt(backtrace)
if err != nil {
return err
}
}
log.Root().SetHandler(glogger)
memprofilerate, err := flags.GetInt(memprofilerateFlag.Name)
if err != nil {
@ -263,10 +238,11 @@ func SetupCobra(cmd *cobra.Command) error {
func Setup(ctx *cli.Context) error {
// logging
log.PrintOrigins(ctx.GlobalBool(debugFlag.Name))
glogger.Verbosity(log.Lvl(ctx.GlobalInt(verbosityFlag.Name)))
glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name))
glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name))
log.Root().SetHandler(glogger)
ostream, glogger = log.SetupDefaultTerminalLogger(
log.Lvl(ctx.GlobalInt(verbosityFlag.Name)),
ctx.GlobalString(vmoduleFlag.Name),
ctx.GlobalString(backtraceAtFlag.Name),
)
// profiling, tracing
if ctx.GlobalIsSet(legacyMemprofilerateFlag.Name) {

View File

@ -2,6 +2,8 @@ package log
import (
"fmt"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"io"
"net"
"os"
@ -357,3 +359,45 @@ func (m muster) FileHandler(path string, fmtr Format) Handler {
func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
return must(NetHandler(network, addr, fmtr))
}
// verbosityPerModule sets the glog verbosity pattern.
//
// The syntax of the argument is a comma-separated list of pattern=N, where the
// pattern is a literal file name or "glob" pattern matching and N is a V level:
// pattern="gopher.go=3"
// sets the V level to 3 in all Go files named "gopher.go"
//
// pattern="foo=3"
// sets V to 3 in all files of any packages whose import path ends in "foo"
//
// pattern="foo/*=3"
// sets V to 3 in all files of any packages whose import path contains "foo"
//
//
//
// backtraceAt sets the glog backtrace location. When set to a file and line
// number holding a logging statement, a stack trace will be written to the Info
// log whenever execution hits that statement.
// Unlike verbosityPerModule, the ".go" must be present.
//
func SetupDefaultTerminalLogger(lvl Lvl, verbosityPerModule string, backtraceAt string) (ostream Handler, glogger *GlogHandler) {
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(os.Stderr)
if usecolor {
output = colorable.NewColorableStderr()
}
ostream = StreamHandler(output, TerminalFormat(usecolor))
glogger = NewGlogHandler(ostream)
Root().SetHandler(glogger)
glogger.Verbosity(lvl)
if err := glogger.Vmodule(verbosityPerModule); err != nil {
panic(err)
}
if backtraceAt != "" {
if err := glogger.BacktraceAt(backtraceAt); err != nil {
panic(err)
}
}
return ostream, glogger
}

View File

@ -29,8 +29,6 @@ import (
"path/filepath"
"time"
"github.com/pkg/errors"
"github.com/ledgerwatch/turbo-geth/accounts/keystore"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/fdlimit"
@ -193,7 +191,7 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
err = stack.Start()
return stack, ethBackend, err
}); err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("cannot register stress test miner. config %v", ethConfig))
return nil, fmt.Errorf("cannot register stress test miner. config %v", ethConfig)
}
// Start the node and return if successful
return stack, stack.Start()

1
rpc/helpers.go Normal file
View File

@ -0,0 +1 @@
package rpc

View File

@ -28,7 +28,6 @@ import (
"github.com/ledgerwatch/turbo-geth/crypto"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/pkg/errors"
)
var (
@ -327,7 +326,7 @@ func (t *Trie) UpdateAccountCode(key []byte, code codeNode) error {
accNode, gotValue := t.getAccount(t.root, hex, 0)
if accNode == nil || !gotValue {
return errors.Wrapf(ethdb.ErrKeyNotFound, "account not found with key: %x", key)
return fmt.Errorf("account not found with key: %x, %w", key, ethdb.ErrKeyNotFound)
}
actualCodeHash := crypto.Keccak256(code)
@ -356,7 +355,7 @@ func (t *Trie) UpdateAccountCodeSize(key []byte, codeSize int) error {
accNode, gotValue := t.getAccount(t.root, hex, 0)
if accNode == nil || !gotValue {
return errors.Wrapf(ethdb.ErrKeyNotFound, "account not found with key: %x", key)
return fmt.Errorf("account not found with key: %x, %w", key, ethdb.ErrKeyNotFound)
}
accNode.codeSize = codeSize

View File

@ -0,0 +1,24 @@
package adapter
import (
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/types"
)
func NewBlockGetter(dbReader rawdb.DatabaseReader) *blockGetter {
return &blockGetter{dbReader}
}
type blockGetter struct {
dbReader rawdb.DatabaseReader
}
func (g *blockGetter) GetBlockByHash(hash common.Hash) *types.Block {
return rawdb.ReadBlockByHash(g.dbReader, hash)
}
func (g *blockGetter) GetBlock(hash common.Hash, number uint64) *types.Block {
return rawdb.ReadBlock(g.dbReader, hash, number)
}

View File

@ -0,0 +1,77 @@
package adapter
import (
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/consensus"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"math/big"
)
type chainContext struct {
db rawdb.DatabaseReader
}
func NewChainContext(db rawdb.DatabaseReader) *chainContext {
return &chainContext{
db: db,
}
}
type powEngine struct {
}
func (c *powEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
panic("must not be called")
}
func (c *powEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (func(), <-chan error) {
panic("must not be called")
}
func (c *powEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
panic("must not be called")
}
func (c *powEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
panic("must not be called")
}
func (c *powEngine) Finalize(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction, uncles []*types.Header) {
panic("must not be called")
}
func (c *powEngine) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
panic("must not be called")
}
func (c *powEngine) Seal(_ consensus.Cancel, chain consensus.ChainHeaderReader, block *types.Block, results chan<- consensus.ResultWithContext, stop <-chan struct{}) error {
panic("must not be called")
}
func (c *powEngine) SealHash(header *types.Header) common.Hash {
panic("must not be called")
}
func (c *powEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
panic("must not be called")
}
func (c *powEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
panic("must not be called")
}
func (c *powEngine) Close() error {
panic("must not be called")
}
func (c *powEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
func (c *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
return rawdb.ReadHeader(c.db, hash, number)
}
func (c *chainContext) Engine() consensus.Engine {
return &powEngine{}
}

View File

@ -0,0 +1,50 @@
package ethapi
// This file stores proxy-objects for `internal` package
import (
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
)
// This package provides copy-paste and proxy objects to "internal/ethapi" package
func NewRevertError(result *core.ExecutionResult) *RevertError {
return &RevertError{ethapi.NewRevertError(result)}
}
type RevertError struct {
*ethapi.RevertError
}
type CallArgs struct {
*ethapi.CallArgs
}
type ExecutionResult struct {
*ethapi.ExecutionResult
}
//nolint
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
return ethapi.RPCMarshalHeader(head)
}
//nolint
func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool, additional map[string]interface{}) (map[string]interface{}, error) {
fields, err := ethapi.RPCMarshalBlock(b, inclTx, fullTx)
if err != nil {
return nil, err
}
for k, v := range additional {
fields[k] = v
}
return fields, err
}
//nolint
type RPCTransaction struct {
*ethapi.RPCTransaction
}

View File

@ -1,33 +1,22 @@
package commands
package adapter
import (
"bytes"
"context"
"encoding/binary"
"errors"
"fmt"
"github.com/holiman/uint256"
"github.com/petar/GoLLRB/llrb"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/crypto"
"github.com/ledgerwatch/turbo-geth/ethdb"
"golang.org/x/net/context"
"github.com/petar/GoLLRB/llrb"
)
type storageItem struct {
key, seckey common.Hash
value uint256.Int
}
func (a *storageItem) Less(b llrb.Item) bool {
bi := b.(*storageItem)
return bytes.Compare(a.key[:], bi.key[:]) < 0
}
type StateReader struct {
accountReads map[common.Address]struct{}
storageReads map[common.Address]map[common.Hash]struct{}
@ -238,3 +227,23 @@ func (r *StateReader) ForEachStorage(addr common.Address, start []byte, cb func(
})
return nil
}
type storageItem struct {
key, seckey common.Hash
value uint256.Int
}
func (a *storageItem) Less(b llrb.Item) bool {
bi := b.(*storageItem)
return bytes.Compare(a.key[:], bi.key[:]) < 0
}
// computeIntraBlockState retrieves the state database associated with a certain block.
// If no state is locally available for the given block, a number of blocks are
// attempted to be reexecuted to generate the desired state.
func ComputeIntraBlockState(chainKV ethdb.KV, block *types.Block) (*state.IntraBlockState, *StateReader) {
// If we have the state fully available, use that
reader := NewStateReader(chainKV, block.NumberU64())
statedb := state.New(reader)
return statedb, reader
}

53
turbo/rpchelper/helper.go Normal file
View File

@ -0,0 +1,53 @@
package rpchelper
import (
"fmt"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/adapter"
)
func GetBlockNumber(blockNrOrHash rpc.BlockNumberOrHash, dbReader rawdb.DatabaseReader) (uint64, common.Hash, error) {
var blockNumber uint64
var err error
hash, ok := blockNrOrHash.Hash()
if !ok {
blockNumber = uint64(blockNrOrHash.BlockNumber.Int64())
hash, err = GetHashByNumber(blockNumber, blockNrOrHash.RequireCanonical, dbReader)
if err != nil {
return 0, common.Hash{}, err
}
} else {
block := rawdb.ReadBlockByHash(dbReader, hash)
if block == nil {
return 0, common.Hash{}, fmt.Errorf("block %x not found", hash)
}
blockNumber = block.NumberU64()
if blockNrOrHash.RequireCanonical && rawdb.ReadCanonicalHash(dbReader, blockNumber) != hash {
return 0, common.Hash{}, fmt.Errorf("hash %q is not currently canonical", hash.String())
}
}
return blockNumber, hash, nil
}
func GetAccount(chainKV ethdb.KV, blockNumber uint64, address common.Address) (*accounts.Account, error) {
reader := adapter.NewStateReader(chainKV, blockNumber)
return reader.ReadAccountData(address)
}
func GetHashByNumber(blockNumber uint64, requireCanonical bool, dbReader rawdb.DatabaseReader) (common.Hash, error) {
if requireCanonical {
return rawdb.ReadCanonicalHash(dbReader, blockNumber), nil
}
block := rawdb.ReadBlockByNumber(dbReader, blockNumber)
if block == nil {
return common.Hash{}, fmt.Errorf("block %d not found", blockNumber)
}
return block.Hash(), nil
}

146
turbo/transactions/call.go Normal file
View File

@ -0,0 +1,146 @@
package transactions
import (
"context"
"fmt"
"github.com/holiman/uint256"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/params"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/rpchelper"
"math/big"
"time"
)
const callTimeout = 5 * time.Second
func DoCall(ctx context.Context, args ethapi.CallArgs, kv ethdb.KV, dbReader rawdb.DatabaseReader, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account, GasCap uint64) (*core.ExecutionResult, error) {
// todo: Pending state is only known by the miner
/*
if blockNrOrHash.BlockNumber != nil && *blockNrOrHash.BlockNumber == rpc.PendingBlockNumber {
block, state, _ := b.eth.miner.Pending()
return state, block.Header(), nil
}
*/
blockNumber, hash, err := rpchelper.GetBlockNumber(blockNrOrHash, dbReader)
if err != nil {
return nil, err
}
ds := state.NewPlainDBState(kv, blockNumber)
state := state.New(ds)
if state == nil {
return nil, fmt.Errorf("can't get the state for %d", blockNumber)
}
header := rawdb.ReadHeader(dbReader, hash, blockNumber)
if header == nil {
return nil, fmt.Errorf("block %d(%x) not found", blockNumber, hash)
}
// Override the fields of specified contracts before execution.
if overrides != nil {
for addr, account := range *overrides {
// Override account nonce.
if account.Nonce != nil {
state.SetNonce(addr, uint64(*account.Nonce))
}
// Override account(contract) code.
if account.Code != nil {
state.SetCode(addr, *account.Code)
}
// Override account balance.
if account.Balance != nil {
balance, _ := uint256.FromBig((*big.Int)(*account.Balance))
state.SetBalance(addr, balance)
}
if account.State != nil && account.StateDiff != nil {
return nil, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
}
// Replace entire state if caller requires.
if account.State != nil {
state.SetStorage(addr, *account.State)
}
// Apply state diff into specified accounts.
if account.StateDiff != nil {
for key, value := range *account.StateDiff {
key := key
state.SetState(addr, &key, value)
}
}
}
}
// 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 callTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, callTimeout)
} 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.
msg := args.ToMessage(GasCap)
evmCtx := GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, dbReader)
evm := vm.NewEVM(evmCtx, state, params.MainnetChainConfig, vm.Config{})
// 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()
}()
gp := new(core.GasPool).AddGas(msg.Gas())
result, err := core.ApplyMessage(evm, msg, gp)
if 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)", callTimeout)
}
return result, nil
}
func GetEvmContext(msg core.Message, header *types.Header, requireCanonical bool, dbReader rawdb.DatabaseReader) vm.Context {
return vm.Context{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: getHashGetter(requireCanonical, dbReader),
Origin: msg.From(),
Coinbase: header.Coinbase,
BlockNumber: new(big.Int).Set(header.Number),
Time: new(big.Int).SetUint64(header.Time),
Difficulty: new(big.Int).Set(header.Difficulty),
GasLimit: header.GasLimit,
GasPrice: msg.GasPrice().ToBig(),
}
}
func getHashGetter(requireCanonical bool, dbReader rawdb.DatabaseReader) func(uint64) common.Hash {
return func(n uint64) common.Hash {
hash, err := rpchelper.GetHashByNumber(n, requireCanonical, dbReader)
if err != nil {
log.Debug("can't get block hash by number", "number", n, "only-canonical", requireCanonical)
}
return hash
}
}

View File

@ -0,0 +1,141 @@
package transactions
import (
"context"
"errors"
"fmt"
"time"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/eth"
"github.com/ledgerwatch/turbo-geth/eth/tracers"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/params"
state2 "github.com/ledgerwatch/turbo-geth/turbo/adapter"
)
const (
// defaultTraceTimeout is the amount of time a single transaction can execute
// by default before being forcefully aborted.
defaultTraceTimeout = 5 * time.Second
)
type BlockGetter interface {
// GetBlockByHash retrieves a block from the database by hash, caching it if found.
GetBlockByHash(hash common.Hash) *types.Block
// GetBlock retrieves a block from the database by hash and number,
// caching it if found.
GetBlock(hash common.Hash, number uint64) *types.Block
}
// computeTxEnv returns the execution environment of a certain transaction.
func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainKV ethdb.KV, blockHash common.Hash, txIndex uint64) (core.Message, vm.Context, *state.IntraBlockState, *state2.StateReader, error) {
// Create the parent state database
block := blockGetter.GetBlockByHash(blockHash)
if block == nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("block %x not found", blockHash)
}
parent := blockGetter.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("parent %x not found", block.ParentHash())
}
statedb, reader := state2.ComputeIntraBlockState(chainKV, parent)
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.Context{}, statedb, reader, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(cfg, block.Number())
for idx, tx := range block.Transactions() {
select {
default:
case <-ctx.Done():
return nil, vm.Context{}, nil, nil, ctx.Err()
}
statedb.Prepare(tx.Hash(), blockHash, idx)
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer)
EVMcontext := core.NewEVMContext(msg, block.Header(), chain, nil)
if idx == int(txIndex) {
return msg, EVMcontext, statedb, reader, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(EVMcontext, statedb, cfg, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction %x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
_ = statedb.FinalizeTx(vmenv.ChainConfig().WithEIPsFlags(context.Background(), block.Number()), reader)
}
return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %x", txIndex, blockHash)
}
// 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 TraceTx(ctx context.Context, message core.Message, vmctx vm.Context, ibs vm.IntraBlockState, config *eth.TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.Tracer
err error
)
switch {
case config != nil && config.Tracer != nil:
// Define a meaningful timeout of a single transaction trace
timeout := defaultTraceTimeout
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); err != nil {
return nil, err
}
// Handle timeouts and RPC cancellations
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
go func() {
<-deadlineCtx.Done()
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
}()
defer cancel()
case config == nil:
tracer = vm.NewStructLogger(nil)
default:
tracer = vm.NewStructLogger(config.LogConfig)
}
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, ibs, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.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:
return &ethapi.ExecutionResult{
Gas: result.UsedGas,
Failed: result.Failed(),
ReturnValue: fmt.Sprintf("%x", result.Return()),
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
case *tracers.Tracer:
return tracer.GetResult()
default:
panic(fmt.Sprintf("bad tracer type %T", tracer))
}
}