From 1dcc2b141a90a43c60873d64b89b93ba614812b2 Mon Sep 17 00:00:00 2001 From: Alex Sharov Date: Wed, 19 Aug 2020 18:46:20 +0700 Subject: [PATCH] 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 --- cmd/abigen/main.go | 3 +- cmd/geth/config.go | 8 +- cmd/geth/consolecmd_test.go | 2 +- cmd/geth/main.go | 2 +- cmd/geth/retesteth.go | 22 +-- cmd/geth/retesteth_copypaste.go | 2 + cmd/hack/hack.go | 19 +- cmd/integration/commands/refetence_db.go | 5 +- cmd/integration/commands/reset_state.go | 5 +- cmd/integration/commands/root.go | 44 +---- cmd/integration/commands/stages.go | 15 +- cmd/integration/commands/state_stages.go | 3 +- cmd/integration/main.go | 19 +- cmd/restapi/commands/root.go | 32 +--- cmd/restapi/main.go | 23 +-- cmd/rpcdaemon/cli/config.go | 97 ++++++++++ cmd/rpcdaemon/commands/call.go | 142 +------------- cmd/rpcdaemon/commands/daemon.go | 180 ++---------------- cmd/rpcdaemon/commands/debug_api.go | 13 +- cmd/rpcdaemon/commands/eth_api.go | 26 +-- cmd/rpcdaemon/commands/get_balance.go | 50 +---- cmd/rpcdaemon/commands/get_receipts.go | 8 +- cmd/rpcdaemon/commands/root.go | 130 ------------- cmd/rpcdaemon/commands/storage_range.go | 4 +- cmd/rpcdaemon/commands/tracing.go | 150 +-------------- cmd/rpcdaemon/main.go | 40 ++-- cmd/rpcdaemon/service/service.go | 3 +- cmd/rpcdaemon/test.http | 16 ++ cmd/rpctest/main.go | 18 +- cmd/tester/main.go | 22 +-- cmd/utils/cmd.go | 34 ++++ cmd/utils/flags.go | 13 +- console/console_test.go | 6 +- eth/api.go | 72 +++---- eth/api_test.go | 51 ++--- eth/api_tracer.go | 56 ++---- eth/backend.go | 67 ++++--- eth/stagedsync/state_test.go | 2 +- ethstats/ethstats.go | 2 + go.mod | 1 - internal/debug/flags.go | 40 +--- log/handler.go | 44 +++++ miner/stresstest/stress_ethash.go | 4 +- rpc/helpers.go | 1 + trie/trie.go | 5 +- turbo/adapter/block_getter.go | 24 +++ turbo/adapter/chain_context.go | 77 ++++++++ turbo/adapter/ethapi/internal.go | 50 +++++ .../adapter/reader.go | 39 ++-- turbo/rpchelper/helper.go | 53 ++++++ turbo/transactions/call.go | 146 ++++++++++++++ turbo/transactions/tracing.go | 141 ++++++++++++++ 52 files changed, 975 insertions(+), 1056 deletions(-) create mode 100644 cmd/rpcdaemon/cli/config.go delete mode 100644 cmd/rpcdaemon/commands/root.go create mode 100644 rpc/helpers.go create mode 100644 turbo/adapter/block_getter.go create mode 100644 turbo/adapter/chain_context.go create mode 100644 turbo/adapter/ethapi/internal.go rename cmd/rpcdaemon/commands/state_reader.go => turbo/adapter/reader.go (92%) create mode 100644 turbo/rpchelper/helper.go create mode 100644 turbo/transactions/call.go create mode 100644 turbo/transactions/tracing.go diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 8d087e960..b0bf72030 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -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 ( diff --git a/cmd/geth/config.go b/cmd/geth/config.go index be82ca30d..ceb88da5f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -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 } diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 52c26ca42..a01541266 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -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" ) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5c5c6a8e6..8f4d4bca7 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -241,7 +241,7 @@ func init() { // See config.go dumpConfigCommand, // See retesteth.go - retestethCommand, + //retestethCommand, // See cmd/utils/flags_legacy.go utils.ShowDeprecated, } diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go index 57cfad122..3e76e1803 100644 --- a/cmd/geth/retesteth.go +++ b/cmd/geth/retesteth.go @@ -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 } +*/ diff --git a/cmd/geth/retesteth_copypaste.go b/cmd/geth/retesteth_copypaste.go index ec5af7db8..80cca1c29 100644 --- a/cmd/geth/retesteth_copypaste.go +++ b/cmd/geth/retesteth_copypaste.go @@ -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 } +*/ diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go index 21f1d6b50..596efa889 100644 --- a/cmd/hack/hack.go +++ b/cmd/hack/hack.go @@ -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) diff --git a/cmd/integration/commands/refetence_db.go b/cmd/integration/commands/refetence_db.go index fb4f9782c..456ff416f 100644 --- a/cmd/integration/commands/refetence_db.go +++ b/cmd/integration/commands/refetence_db.go @@ -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" } diff --git a/cmd/integration/commands/reset_state.go b/cmd/integration/commands/reset_state.go index d6a9af098..6ed49af32 100644 --- a/cmd/integration/commands/reset_state.go +++ b/cmd/integration/commands/reset_state.go @@ -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()) diff --git a/cmd/integration/commands/root.go b/cmd/integration/commands/root.go index 69b68752a..fec0ede32 100644 --- a/cmd/integration/commands/root.go +++ b/cmd/integration/commands/root.go @@ -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 } diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go index fc3a64729..6da7b3eea 100644 --- a/cmd/integration/commands/stages.go +++ b/cmd/integration/commands/stages.go @@ -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 diff --git a/cmd/integration/commands/state_stages.go b/cmd/integration/commands/state_stages.go index 324ccfc36..fd60d79b4 100644 --- a/cmd/integration/commands/state_stages.go +++ b/cmd/integration/commands/state_stages.go @@ -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 diff --git a/cmd/integration/main.go b/cmd/integration/main.go index a2979c353..ab4d4bd03 100644 --- a/cmd/integration/main.go +++ b/cmd/integration/main.go @@ -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) + } +} diff --git a/cmd/restapi/commands/root.go b/cmd/restapi/commands/root.go index 95873d4b3..bdeeac7f1 100644 --- a/cmd/restapi/commands/root.go +++ b/cmd/restapi/commands/root.go @@ -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 } diff --git a/cmd/restapi/main.go b/cmd/restapi/main.go index c130073d6..59523abb6 100644 --- a/cmd/restapi/main.go +++ b/cmd/restapi/main.go @@ -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() } diff --git a/cmd/rpcdaemon/cli/config.go b/cmd/rpcdaemon/cli/config.go new file mode 100644 index 000000000..0d628c848 --- /dev/null +++ b/cmd/rpcdaemon/cli/config.go @@ -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) +} diff --git a/cmd/rpcdaemon/commands/call.go b/cmd/rpcdaemon/commands/call.go index c12faea0c..65dd4b3bf 100644 --- a/cmd/rpcdaemon/commands/call.go +++ b/cmd/rpcdaemon/commands/call.go @@ -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 diff --git a/cmd/rpcdaemon/commands/daemon.go b/cmd/rpcdaemon/commands/daemon.go index d57904ff2..45a76f460 100644 --- a/cmd/rpcdaemon/commands/daemon.go +++ b/cmd/rpcdaemon/commands/daemon.go @@ -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...) } diff --git a/cmd/rpcdaemon/commands/debug_api.go b/cmd/rpcdaemon/commands/debug_api.go index 00fd88b5d..72eb5f5df 100644 --- a/cmd/rpcdaemon/commands/debug_api.go +++ b/cmd/rpcdaemon/commands/debug_api.go @@ -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 } diff --git a/cmd/rpcdaemon/commands/eth_api.go b/cmd/rpcdaemon/commands/eth_api.go index 4112ec47b..607f35056 100644 --- a/cmd/rpcdaemon/commands/eth_api.go +++ b/cmd/rpcdaemon/commands/eth_api.go @@ -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) -} diff --git a/cmd/rpcdaemon/commands/get_balance.go b/cmd/rpcdaemon/commands/get_balance.go index ddff360dd..26fddcc3a 100644 --- a/cmd/rpcdaemon/commands/get_balance.go +++ b/cmd/rpcdaemon/commands/get_balance.go @@ -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 -} diff --git a/cmd/rpcdaemon/commands/get_receipts.go b/cmd/rpcdaemon/commands/get_receipts.go index 127bc9eb8..6f19c2f9d 100644 --- a/cmd/rpcdaemon/commands/get_receipts.go +++ b/cmd/rpcdaemon/commands/get_receipts.go @@ -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() { diff --git a/cmd/rpcdaemon/commands/root.go b/cmd/rpcdaemon/commands/root.go deleted file mode 100644 index 86c838d4c..000000000 --- a/cmd/rpcdaemon/commands/root.go +++ /dev/null @@ -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 - } - } -} diff --git a/cmd/rpcdaemon/commands/storage_range.go b/cmd/rpcdaemon/commands/storage_range.go index bfe541713..db2f2e642 100644 --- a/cmd/rpcdaemon/commands/storage_range.go +++ b/cmd/rpcdaemon/commands/storage_range.go @@ -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) diff --git a/cmd/rpcdaemon/commands/tracing.go b/cmd/rpcdaemon/commands/tracing.go index d2b94b07a..64f243dca 100644 --- a/cmd/rpcdaemon/commands/tracing.go +++ b/cmd/rpcdaemon/commands/tracing.go @@ -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 ðapi.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) } diff --git a/cmd/rpcdaemon/main.go b/cmd/rpcdaemon/main.go index 84a7f7bb3..bc343103f 100644 --- a/cmd/rpcdaemon/main.go +++ b/cmd/rpcdaemon/main.go @@ -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) + } } diff --git a/cmd/rpcdaemon/service/service.go b/cmd/rpcdaemon/service/service.go index c30c62c0f..894a01a28 100644 --- a/cmd/rpcdaemon/service/service.go +++ b/cmd/rpcdaemon/service/service.go @@ -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) } diff --git a/cmd/rpcdaemon/test.http b/cmd/rpcdaemon/test.http index 3b071ad13..0383b442e 100644 --- a/cmd/rpcdaemon/test.http +++ b/cmd/rpcdaemon/test.http @@ -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 diff --git a/cmd/rpctest/main.go b/cmd/rpctest/main.go index c48880be4..49d77d9b0 100644 --- a/cmd/rpctest/main.go +++ b/cmd/rpctest/main.go @@ -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 diff --git a/cmd/tester/main.go b/cmd/tester/main.go index 4f0026829..19b8d8264 100644 --- a/cmd/tester/main.go +++ b/cmd/tester/main.go @@ -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) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index da825f6db..815c7bd84 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -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 +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5d1636633..b2954109b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -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) { diff --git a/console/console_test.go b/console/console_test.go index f41432ed6..645043548 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -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) } diff --git a/eth/api.go b/eth/api.go index 419dff870..b8a2d12b4 100644 --- a/eth/api.go +++ b/eth/api.go @@ -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 } +*/ diff --git a/eth/api_test.go b/eth/api_test.go index 3be330839..c8da29db4 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -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 . -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) { }) } } +*/ diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 9fbfed861..d859fd4c7 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -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) } +*/ diff --git a/eth/backend.go b/eth/backend.go index 1085428ec..4efe82cd2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -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, diff --git a/eth/stagedsync/state_test.go b/eth/stagedsync/state_test.go index 106d65c47..2bda7e664 100644 --- a/eth/stagedsync/state_test.go +++ b/eth/stagedsync/state_test.go @@ -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" ) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index c2d0c1036..065ffc149 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -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) } +*/ diff --git a/go.mod b/go.mod index 6a5c2e0e8..9ede158eb 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/internal/debug/flags.go b/internal/debug/flags.go index e8edb63b9..4a68eb833 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -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) { diff --git a/log/handler.go b/log/handler.go index 4ad433334..5e187b766 100644 --- a/log/handler.go +++ b/log/handler.go @@ -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 +} diff --git a/miner/stresstest/stress_ethash.go b/miner/stresstest/stress_ethash.go index d93a31e1e..ca9b902eb 100644 --- a/miner/stresstest/stress_ethash.go +++ b/miner/stresstest/stress_ethash.go @@ -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() diff --git a/rpc/helpers.go b/rpc/helpers.go new file mode 100644 index 000000000..9ab1e3e8e --- /dev/null +++ b/rpc/helpers.go @@ -0,0 +1 @@ +package rpc diff --git a/trie/trie.go b/trie/trie.go index 93498ac90..458dd50ea 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -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 diff --git a/turbo/adapter/block_getter.go b/turbo/adapter/block_getter.go new file mode 100644 index 000000000..160674d91 --- /dev/null +++ b/turbo/adapter/block_getter.go @@ -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) +} diff --git a/turbo/adapter/chain_context.go b/turbo/adapter/chain_context.go new file mode 100644 index 000000000..960580e3c --- /dev/null +++ b/turbo/adapter/chain_context.go @@ -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{} +} diff --git a/turbo/adapter/ethapi/internal.go b/turbo/adapter/ethapi/internal.go new file mode 100644 index 000000000..c7667ae2a --- /dev/null +++ b/turbo/adapter/ethapi/internal.go @@ -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 +} diff --git a/cmd/rpcdaemon/commands/state_reader.go b/turbo/adapter/reader.go similarity index 92% rename from cmd/rpcdaemon/commands/state_reader.go rename to turbo/adapter/reader.go index 00cd7ac61..c788b30bc 100644 --- a/cmd/rpcdaemon/commands/state_reader.go +++ b/turbo/adapter/reader.go @@ -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 +} diff --git a/turbo/rpchelper/helper.go b/turbo/rpchelper/helper.go new file mode 100644 index 000000000..043c0ba70 --- /dev/null +++ b/turbo/rpchelper/helper.go @@ -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 +} diff --git a/turbo/transactions/call.go b/turbo/transactions/call.go new file mode 100644 index 000000000..6e6c06bf0 --- /dev/null +++ b/turbo/transactions/call.go @@ -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 + } +} diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go new file mode 100644 index 000000000..4de655c67 --- /dev/null +++ b/turbo/transactions/tracing.go @@ -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 ðapi.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)) + } +}