forked from cerc-io/plugeth
Merge pull request #19700 from karalabe/cleanup-graphql
cmd, graphql, node: graphql flag polishes, les integration
This commit is contained in:
commit
c94068a8bb
@ -30,7 +30,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/dashboard"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/graphql"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
@ -177,14 +176,10 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
}
|
||||
utils.RegisterShhService(stack, &cfg.Shh)
|
||||
}
|
||||
|
||||
// Configure GraphQL if required
|
||||
// Configure GraphQL if requested
|
||||
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
|
||||
if err := graphql.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts); err != nil {
|
||||
utils.Fatalf("Failed to register the Ethereum service: %v", err)
|
||||
}
|
||||
utils.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts)
|
||||
}
|
||||
|
||||
// Add the Ethereum Stats daemon if requested.
|
||||
if cfg.Ethstats.URL != "" {
|
||||
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
||||
|
@ -156,20 +156,25 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
{
|
||||
Name: "API AND CONSOLE",
|
||||
Flags: []cli.Flag{
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCPathFlag,
|
||||
utils.RPCEnabledFlag,
|
||||
utils.RPCListenAddrFlag,
|
||||
utils.RPCPortFlag,
|
||||
utils.RPCApiFlag,
|
||||
utils.RPCGlobalGasCap,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.RPCVirtualHostsFlag,
|
||||
utils.WSEnabledFlag,
|
||||
utils.WSListenAddrFlag,
|
||||
utils.WSPortFlag,
|
||||
utils.WSApiFlag,
|
||||
utils.WSAllowedOriginsFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCPathFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.RPCVirtualHostsFlag,
|
||||
utils.GraphQLEnabledFlag,
|
||||
utils.GraphQLListenAddrFlag,
|
||||
utils.GraphQLPortFlag,
|
||||
utils.GraphQLCORSDomainFlag,
|
||||
utils.GraphQLVirtualHostsFlag,
|
||||
utils.JSpathFlag,
|
||||
utils.ExecFlag,
|
||||
utils.PreloadJSFlag,
|
||||
|
@ -20,6 +20,7 @@ package utils
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
@ -45,6 +46,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethstats"
|
||||
"github.com/ethereum/go-ethereum/graphql"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
@ -57,6 +59,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
pcsclite "github.com/gballet/go-libpcsclite"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
@ -474,6 +477,14 @@ var (
|
||||
Usage: "Disables db compaction after import",
|
||||
}
|
||||
// RPC settings
|
||||
IPCDisabledFlag = cli.BoolFlag{
|
||||
Name: "ipcdisable",
|
||||
Usage: "Disable the IPC-RPC server",
|
||||
}
|
||||
IPCPathFlag = DirectoryFlag{
|
||||
Name: "ipcpath",
|
||||
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
||||
}
|
||||
RPCEnabledFlag = cli.BoolFlag{
|
||||
Name: "rpc",
|
||||
Usage: "Enable the HTTP-RPC server",
|
||||
@ -488,30 +499,6 @@ var (
|
||||
Usage: "HTTP-RPC server listening port",
|
||||
Value: node.DefaultHTTPPort,
|
||||
}
|
||||
GraphQLEnabledFlag = cli.BoolFlag{
|
||||
Name: "graphql",
|
||||
Usage: "Enable the GraphQL server",
|
||||
}
|
||||
GraphQLListenAddrFlag = cli.StringFlag{
|
||||
Name: "graphql.addr",
|
||||
Usage: "GraphQL server listening interface",
|
||||
Value: node.DefaultGraphQLHost,
|
||||
}
|
||||
GraphQLPortFlag = cli.IntFlag{
|
||||
Name: "graphql.port",
|
||||
Usage: "GraphQL server listening port",
|
||||
Value: node.DefaultGraphQLPort,
|
||||
}
|
||||
GraphQLCORSDomainFlag = cli.StringFlag{
|
||||
Name: "graphql.rpccorsdomain",
|
||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||
Value: "",
|
||||
}
|
||||
GraphQLVirtualHostsFlag = cli.StringFlag{
|
||||
Name: "graphql.rpcvhosts",
|
||||
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
||||
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
|
||||
}
|
||||
RPCCORSDomainFlag = cli.StringFlag{
|
||||
Name: "rpccorsdomain",
|
||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||
@ -527,14 +514,6 @@ var (
|
||||
Usage: "API's offered over the HTTP-RPC interface",
|
||||
Value: "",
|
||||
}
|
||||
IPCDisabledFlag = cli.BoolFlag{
|
||||
Name: "ipcdisable",
|
||||
Usage: "Disable the IPC-RPC server",
|
||||
}
|
||||
IPCPathFlag = DirectoryFlag{
|
||||
Name: "ipcpath",
|
||||
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
||||
}
|
||||
WSEnabledFlag = cli.BoolFlag{
|
||||
Name: "ws",
|
||||
Usage: "Enable the WS-RPC server",
|
||||
@ -559,6 +538,30 @@ var (
|
||||
Usage: "Origins from which to accept websockets requests",
|
||||
Value: "",
|
||||
}
|
||||
GraphQLEnabledFlag = cli.BoolFlag{
|
||||
Name: "graphql",
|
||||
Usage: "Enable the GraphQL server",
|
||||
}
|
||||
GraphQLListenAddrFlag = cli.StringFlag{
|
||||
Name: "graphql.addr",
|
||||
Usage: "GraphQL server listening interface",
|
||||
Value: node.DefaultGraphQLHost,
|
||||
}
|
||||
GraphQLPortFlag = cli.IntFlag{
|
||||
Name: "graphql.port",
|
||||
Usage: "GraphQL server listening port",
|
||||
Value: node.DefaultGraphQLPort,
|
||||
}
|
||||
GraphQLCORSDomainFlag = cli.StringFlag{
|
||||
Name: "graphql.corsdomain",
|
||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||
Value: "",
|
||||
}
|
||||
GraphQLVirtualHostsFlag = cli.StringFlag{
|
||||
Name: "graphql.vhosts",
|
||||
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
||||
Value: strings.Join(node.DefaultConfig.GraphQLVirtualHosts, ","),
|
||||
}
|
||||
ExecFlag = cli.StringFlag{
|
||||
Name: "exec",
|
||||
Usage: "Execute JavaScript statement",
|
||||
@ -874,7 +877,6 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
||||
cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
|
||||
}
|
||||
@ -916,7 +918,6 @@ func setWS(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
||||
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
||||
}
|
||||
@ -1546,12 +1547,33 @@ func RegisterEthStatsService(stack *node.Node, url string) {
|
||||
var lesServ *les.LightEthereum
|
||||
ctx.Service(&lesServ)
|
||||
|
||||
// Let ethstats use whichever is not nil
|
||||
return ethstats.New(url, ethServ, lesServ)
|
||||
}); 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, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) {
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
// Try to construct the GraphQL service backed by a full node
|
||||
var ethServ *eth.Ethereum
|
||||
if err := ctx.Service(ðServ); err == nil {
|
||||
return graphql.New(ethServ.APIBackend, endpoint, cors, vhosts, timeouts)
|
||||
}
|
||||
// Try to construct the GraphQL service backed by a light node
|
||||
var lesServ *les.LightEthereum
|
||||
if err := ctx.Service(&lesServ); err == nil {
|
||||
return graphql.New(lesServ.ApiBackend, endpoint, cors, vhosts, timeouts)
|
||||
}
|
||||
// Well, this should not have happened, bail out
|
||||
return nil, errors.New("no Ethereum service")
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the GraphQL service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func SetupMetrics(ctx *cli.Context) {
|
||||
if metrics.Enabled {
|
||||
log.Info("Enabling metrics collection")
|
||||
|
@ -60,11 +60,11 @@ var graphiql = []byte(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.css"/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.13.0/graphiql.css"/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/3.0.0/fetch.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.5/umd/react.production.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.5/umd/react-dom.production.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.13.0/graphiql.min.js"></script>
|
||||
</head>
|
||||
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
|
||||
<div id="graphiql" style="height: 100vh;">Loading...</div>
|
||||
|
@ -20,9 +20,6 @@ package graphql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
@ -32,16 +29,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
graphqlgo "github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
)
|
||||
|
||||
var OnlyOnMainChainError = errors.New("This operation is only available for blocks on the canonical chain.")
|
||||
@ -49,7 +40,7 @@ var BlockInvariantError = errors.New("Block objects must be instantiated with at
|
||||
|
||||
// Account represents an Ethereum account at a particular block.
|
||||
type Account struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
address common.Address
|
||||
blockNumber rpc.BlockNumber
|
||||
}
|
||||
@ -102,7 +93,7 @@ func (a *Account) Storage(ctx context.Context, args struct{ Slot common.Hash })
|
||||
|
||||
// Log represents an individual log message. All arguments are mandatory.
|
||||
type Log struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
transaction *Transaction
|
||||
log *types.Log
|
||||
}
|
||||
@ -134,7 +125,7 @@ func (l *Log) Data(ctx context.Context) hexutil.Bytes {
|
||||
// Transaction represents an Ethereum transaction.
|
||||
// backend and hash are mandatory; all others will be fetched when required.
|
||||
type Transaction struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
hash common.Hash
|
||||
tx *types.Transaction
|
||||
block *Block
|
||||
@ -349,7 +340,7 @@ const (
|
||||
// backend, and either num or hash are mandatory. All other fields are lazily fetched
|
||||
// when required.
|
||||
type Block struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
num *rpc.BlockNumber
|
||||
hash common.Hash
|
||||
header *types.Header
|
||||
@ -745,7 +736,7 @@ type BlockFilterCriteria struct {
|
||||
|
||||
// runFilter accepts a filter and executes it, returning all its results as
|
||||
// `Log` objects.
|
||||
func runFilter(ctx context.Context, be *eth.EthAPIBackend, filter *filters.Filter) ([]*Log, error) {
|
||||
func runFilter(ctx context.Context, be ethapi.Backend, filter *filters.Filter) ([]*Log, error) {
|
||||
logs, err := filter.Logs(ctx)
|
||||
if err != nil || logs == nil {
|
||||
return nil, err
|
||||
@ -888,7 +879,7 @@ func (b *Block) EstimateGas(ctx context.Context, args struct {
|
||||
}
|
||||
|
||||
type Pending struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
}
|
||||
|
||||
func (p *Pending) TransactionCount(ctx context.Context) (int32, error) {
|
||||
@ -947,7 +938,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct {
|
||||
|
||||
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||
type Resolver struct {
|
||||
backend *eth.EthAPIBackend
|
||||
backend ethapi.Backend
|
||||
}
|
||||
|
||||
func (r *Resolver) Block(ctx context.Context, args struct {
|
||||
@ -1088,7 +1079,6 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria
|
||||
|
||||
// Construct the range filter
|
||||
filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics)
|
||||
|
||||
return runFilter(ctx, r.backend, filter)
|
||||
}
|
||||
|
||||
@ -1145,89 +1135,3 @@ func (r *Resolver) Syncing() (*SyncState, error) {
|
||||
// Otherwise gather the block sync stats
|
||||
return &SyncState{progress}, nil
|
||||
}
|
||||
|
||||
// NewHandler returns a new `http.Handler` that will answer GraphQL queries.
|
||||
// It additionally exports an interactive query browser on the / endpoint.
|
||||
func NewHandler(be *eth.EthAPIBackend) (http.Handler, error) {
|
||||
q := Resolver{be}
|
||||
|
||||
s, err := graphqlgo.ParseSchema(schema, &q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h := &relay.Handler{Schema: s}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", GraphiQL{})
|
||||
mux.Handle("/graphql", h)
|
||||
mux.Handle("/graphql/", h)
|
||||
return mux, nil
|
||||
}
|
||||
|
||||
// Service encapsulates a GraphQL service.
|
||||
type Service struct {
|
||||
endpoint string // The host:port endpoint for this service.
|
||||
cors []string // Allowed CORS domains
|
||||
vhosts []string // Recognised vhosts
|
||||
timeouts rpc.HTTPTimeouts // Timeout settings for HTTP requests.
|
||||
backend *eth.EthAPIBackend // The backend that queries will operate onn.
|
||||
handler http.Handler // The `http.Handler` used to answer queries.
|
||||
listener net.Listener // The listening socket.
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols exported by this service.
|
||||
func (s *Service) Protocols() []p2p.Protocol { return nil }
|
||||
|
||||
// APIs returns the list of APIs exported by this service.
|
||||
func (s *Service) APIs() []rpc.API { return nil }
|
||||
|
||||
// Start is called after all services have been constructed and the networking
|
||||
// layer was also initialized to spawn any goroutines required by the service.
|
||||
func (s *Service) Start(server *p2p.Server) error {
|
||||
var err error
|
||||
s.handler, err = NewHandler(s.backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.listener, err = net.Listen("tcp", s.endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go rpc.NewHTTPServer(s.cors, s.vhosts, s.timeouts, s.handler).Serve(s.listener)
|
||||
log.Info("GraphQL endpoint opened", "url", fmt.Sprintf("http://%s", s.endpoint))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop terminates all goroutines belonging to the service, blocking until they
|
||||
// are all terminated.
|
||||
func (s *Service) Stop() error {
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
log.Info("GraphQL endpoint closed", "url", fmt.Sprintf("http://%s", s.endpoint))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewService constructs a new service instance.
|
||||
func NewService(backend *eth.EthAPIBackend, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) (*Service, error) {
|
||||
return &Service{
|
||||
endpoint: endpoint,
|
||||
cors: cors,
|
||||
vhosts: vhosts,
|
||||
timeouts: timeouts,
|
||||
backend: backend,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RegisterGraphQLService is a utility function to construct a new service and register it against a node.
|
||||
func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) error {
|
||||
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
var ethereum *eth.Ethereum
|
||||
if err := ctx.Service(ðereum); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewService(ethereum.APIBackend, endpoint, cors, vhosts, timeouts)
|
||||
})
|
||||
}
|
||||
|
@ -22,8 +22,7 @@ import (
|
||||
|
||||
func TestBuildSchema(t *testing.T) {
|
||||
// Make sure the schema can be parsed and matched up to the object model.
|
||||
_, err := NewHandler(nil)
|
||||
if err != nil {
|
||||
if _, err := newHandler(nil); err != nil {
|
||||
t.Errorf("Could not construct GraphQL handler: %v", err)
|
||||
}
|
||||
}
|
||||
|
103
graphql/service.go
Normal file
103
graphql/service.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
)
|
||||
|
||||
// Service encapsulates a GraphQL service.
|
||||
type Service struct {
|
||||
endpoint string // The host:port endpoint for this service.
|
||||
cors []string // Allowed CORS domains
|
||||
vhosts []string // Recognised vhosts
|
||||
timeouts rpc.HTTPTimeouts // Timeout settings for HTTP requests.
|
||||
backend ethapi.Backend // The backend that queries will operate onn.
|
||||
handler http.Handler // The `http.Handler` used to answer queries.
|
||||
listener net.Listener // The listening socket.
|
||||
}
|
||||
|
||||
// New constructs a new GraphQL service instance.
|
||||
func New(backend ethapi.Backend, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) (*Service, error) {
|
||||
return &Service{
|
||||
endpoint: endpoint,
|
||||
cors: cors,
|
||||
vhosts: vhosts,
|
||||
timeouts: timeouts,
|
||||
backend: backend,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols exported by this service.
|
||||
func (s *Service) Protocols() []p2p.Protocol { return nil }
|
||||
|
||||
// APIs returns the list of APIs exported by this service.
|
||||
func (s *Service) APIs() []rpc.API { return nil }
|
||||
|
||||
// Start is called after all services have been constructed and the networking
|
||||
// layer was also initialized to spawn any goroutines required by the service.
|
||||
func (s *Service) Start(server *p2p.Server) error {
|
||||
var err error
|
||||
s.handler, err = newHandler(s.backend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.listener, err = net.Listen("tcp", s.endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
go rpc.NewHTTPServer(s.cors, s.vhosts, s.timeouts, s.handler).Serve(s.listener)
|
||||
log.Info("GraphQL endpoint opened", "url", fmt.Sprintf("http://%s", s.endpoint))
|
||||
return nil
|
||||
}
|
||||
|
||||
// newHandler returns a new `http.Handler` that will answer GraphQL queries.
|
||||
// It additionally exports an interactive query browser on the / endpoint.
|
||||
func newHandler(backend ethapi.Backend) (http.Handler, error) {
|
||||
q := Resolver{backend}
|
||||
|
||||
s, err := graphql.ParseSchema(schema, &q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h := &relay.Handler{Schema: s}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", GraphiQL{})
|
||||
mux.Handle("/graphql", h)
|
||||
mux.Handle("/graphql/", h)
|
||||
return mux, nil
|
||||
}
|
||||
|
||||
// Stop terminates all goroutines belonging to the service, blocking until they
|
||||
// are all terminated.
|
||||
func (s *Service) Stop() error {
|
||||
if s.listener != nil {
|
||||
s.listener.Close()
|
||||
s.listener = nil
|
||||
log.Info("GraphQL endpoint closed", "url", fmt.Sprintf("http://%s", s.endpoint))
|
||||
}
|
||||
return nil
|
||||
}
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@ -47,9 +48,10 @@ type Backend interface {
|
||||
ExtRPCEnabled() bool
|
||||
RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection
|
||||
|
||||
// BlockChain API
|
||||
// Blockchain API
|
||||
SetHead(number uint64)
|
||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
||||
HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
|
||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
||||
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||
@ -60,7 +62,7 @@ type Backend interface {
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||
|
||||
// TxPool API
|
||||
// Transaction pool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||
GetPoolTransactions() (types.Transactions, error)
|
||||
@ -70,6 +72,13 @@ type Backend interface {
|
||||
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||
|
||||
// Filter API
|
||||
BloomStatus() (uint64, uint64)
|
||||
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
|
||||
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
|
||||
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
||||
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
|
||||
|
||||
ChainConfig() *params.ChainConfig
|
||||
CurrentBlock() *types.Block
|
||||
}
|
||||
|
@ -38,13 +38,15 @@ const (
|
||||
|
||||
// DefaultConfig contains reasonable default settings.
|
||||
var DefaultConfig = Config{
|
||||
DataDir: DefaultDataDir(),
|
||||
HTTPPort: DefaultHTTPPort,
|
||||
HTTPModules: []string{"net", "web3"},
|
||||
HTTPVirtualHosts: []string{"localhost"},
|
||||
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
|
||||
WSPort: DefaultWSPort,
|
||||
WSModules: []string{"net", "web3"},
|
||||
DataDir: DefaultDataDir(),
|
||||
HTTPPort: DefaultHTTPPort,
|
||||
HTTPModules: []string{"net", "web3"},
|
||||
HTTPVirtualHosts: []string{"localhost"},
|
||||
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
|
||||
WSPort: DefaultWSPort,
|
||||
WSModules: []string{"net", "web3"},
|
||||
GraphQLPort: DefaultGraphQLPort,
|
||||
GraphQLVirtualHosts: []string{"localhost"},
|
||||
P2P: p2p.Config{
|
||||
ListenAddr: ":30303",
|
||||
MaxPeers: 50,
|
||||
|
Loading…
Reference in New Issue
Block a user