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/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" ) // 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) { additionalFields := make(map[string]interface{}) block := rawdb.ReadBlockByNumber(api.dbReader, uint64(number.Int64())) if block == nil { return nil, fmt.Errorf("block not found: %d", number.Int64()) } additionalFields["totalDifficulty"] = rawdb.ReadTd(api.dbReader, block.Hash(), uint64(number.Int64())) response, err := api.rpcMarshalBlock(block, true, fullTx, additionalFields) if err == nil && number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil } } 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 { var rpcAPI []rpc.API dbReader := ethdb.NewObjectDatabase(db) chainContext := NewChainContext(dbReader) apiImpl := NewAPI(db, dbReader, chainContext, eth) dbgAPIImpl := NewPrivateDebugAPI(db, dbReader, chainContext) for _, enabledAPI := range enabledApis { switch enabledAPI { case "eth": rpcAPI = append(rpcAPI, rpc.API{ Namespace: "eth", Public: true, Service: EthAPI(apiImpl), Version: "1.0", }) case "debug": rpcAPI = append(rpcAPI, rpc.API{ Namespace: "debug", Public: true, Service: PrivateDebugAPI(dbgAPIImpl), Version: "1.0", }) default: 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) }