cmd, graphql, node: graphql flag polishes, les integration
This commit is contained in:
parent
2b54666018
commit
e3ec77f50e
@ -30,7 +30,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/dashboard"
|
"github.com/ethereum/go-ethereum/dashboard"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/graphql"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
@ -177,14 +176,10 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
|||||||
}
|
}
|
||||||
utils.RegisterShhService(stack, &cfg.Shh)
|
utils.RegisterShhService(stack, &cfg.Shh)
|
||||||
}
|
}
|
||||||
|
// Configure GraphQL if requested
|
||||||
// Configure GraphQL if required
|
|
||||||
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
|
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.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts)
|
||||||
utils.Fatalf("Failed to register the Ethereum service: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the Ethereum Stats daemon if requested.
|
// Add the Ethereum Stats daemon if requested.
|
||||||
if cfg.Ethstats.URL != "" {
|
if cfg.Ethstats.URL != "" {
|
||||||
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
||||||
|
@ -156,20 +156,25 @@ var AppHelpFlagGroups = []flagGroup{
|
|||||||
{
|
{
|
||||||
Name: "API AND CONSOLE",
|
Name: "API AND CONSOLE",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
utils.IPCDisabledFlag,
|
||||||
|
utils.IPCPathFlag,
|
||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.RPCApiFlag,
|
||||||
utils.RPCGlobalGasCap,
|
utils.RPCGlobalGasCap,
|
||||||
|
utils.RPCCORSDomainFlag,
|
||||||
|
utils.RPCVirtualHostsFlag,
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
utils.WSApiFlag,
|
utils.WSApiFlag,
|
||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.GraphQLEnabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.GraphQLListenAddrFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.GraphQLPortFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
|
utils.GraphQLVirtualHostsFlag,
|
||||||
utils.JSpathFlag,
|
utils.JSpathFlag,
|
||||||
utils.ExecFlag,
|
utils.ExecFlag,
|
||||||
utils.PreloadJSFlag,
|
utils.PreloadJSFlag,
|
||||||
|
@ -20,6 +20,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -45,6 +46,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
|
"github.com/ethereum/go-ethereum/graphql"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
@ -57,6 +59,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
pcsclite "github.com/gballet/go-libpcsclite"
|
pcsclite "github.com/gballet/go-libpcsclite"
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
@ -474,6 +477,14 @@ var (
|
|||||||
Usage: "Disables db compaction after import",
|
Usage: "Disables db compaction after import",
|
||||||
}
|
}
|
||||||
// RPC settings
|
// 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{
|
RPCEnabledFlag = cli.BoolFlag{
|
||||||
Name: "rpc",
|
Name: "rpc",
|
||||||
Usage: "Enable the HTTP-RPC server",
|
Usage: "Enable the HTTP-RPC server",
|
||||||
@ -488,30 +499,6 @@ var (
|
|||||||
Usage: "HTTP-RPC server listening port",
|
Usage: "HTTP-RPC server listening port",
|
||||||
Value: node.DefaultHTTPPort,
|
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{
|
RPCCORSDomainFlag = cli.StringFlag{
|
||||||
Name: "rpccorsdomain",
|
Name: "rpccorsdomain",
|
||||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
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",
|
Usage: "API's offered over the HTTP-RPC interface",
|
||||||
Value: "",
|
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{
|
WSEnabledFlag = cli.BoolFlag{
|
||||||
Name: "ws",
|
Name: "ws",
|
||||||
Usage: "Enable the WS-RPC server",
|
Usage: "Enable the WS-RPC server",
|
||||||
@ -559,6 +538,30 @@ var (
|
|||||||
Usage: "Origins from which to accept websockets requests",
|
Usage: "Origins from which to accept websockets requests",
|
||||||
Value: "",
|
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{
|
ExecFlag = cli.StringFlag{
|
||||||
Name: "exec",
|
Name: "exec",
|
||||||
Usage: "Execute JavaScript statement",
|
Usage: "Execute JavaScript statement",
|
||||||
@ -874,7 +877,6 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
|||||||
cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
|
cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
||||||
cfg.HTTPPort = ctx.GlobalInt(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)
|
cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
||||||
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
||||||
}
|
}
|
||||||
@ -1546,12 +1547,33 @@ func RegisterEthStatsService(stack *node.Node, url string) {
|
|||||||
var lesServ *les.LightEthereum
|
var lesServ *les.LightEthereum
|
||||||
ctx.Service(&lesServ)
|
ctx.Service(&lesServ)
|
||||||
|
|
||||||
|
// Let ethstats use whichever is not nil
|
||||||
return ethstats.New(url, ethServ, lesServ)
|
return ethstats.New(url, ethServ, lesServ)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
Fatalf("Failed to register the Ethereum Stats service: %v", err)
|
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) {
|
func SetupMetrics(ctx *cli.Context) {
|
||||||
if metrics.Enabled {
|
if metrics.Enabled {
|
||||||
log.Info("Enabling metrics collection")
|
log.Info("Enabling metrics collection")
|
||||||
|
@ -60,11 +60,11 @@ var graphiql = []byte(`
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.css"/>
|
<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/2.0.3/fetch.min.js"></script>
|
<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.2.0/umd/react.production.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.2.0/umd/react-dom.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.11.11/graphiql.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.13.0/graphiql.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
|
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
|
||||||
<div id="graphiql" style="height: 100vh;">Loading...</div>
|
<div id="graphiql" style="height: 100vh;">Loading...</div>
|
||||||
|
@ -20,9 +20,6 @@ package graphql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
@ -32,16 +29,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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/eth/filters"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"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/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"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.")
|
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.
|
// Account represents an Ethereum account at a particular block.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
address common.Address
|
address common.Address
|
||||||
blockNumber rpc.BlockNumber
|
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.
|
// Log represents an individual log message. All arguments are mandatory.
|
||||||
type Log struct {
|
type Log struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
transaction *Transaction
|
transaction *Transaction
|
||||||
log *types.Log
|
log *types.Log
|
||||||
}
|
}
|
||||||
@ -134,7 +125,7 @@ func (l *Log) Data(ctx context.Context) hexutil.Bytes {
|
|||||||
// Transaction represents an Ethereum transaction.
|
// Transaction represents an Ethereum transaction.
|
||||||
// backend and hash are mandatory; all others will be fetched when required.
|
// backend and hash are mandatory; all others will be fetched when required.
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
tx *types.Transaction
|
tx *types.Transaction
|
||||||
block *Block
|
block *Block
|
||||||
@ -349,7 +340,7 @@ const (
|
|||||||
// backend, and either num or hash are mandatory. All other fields are lazily fetched
|
// backend, and either num or hash are mandatory. All other fields are lazily fetched
|
||||||
// when required.
|
// when required.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
num *rpc.BlockNumber
|
num *rpc.BlockNumber
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
header *types.Header
|
header *types.Header
|
||||||
@ -745,7 +736,7 @@ type BlockFilterCriteria struct {
|
|||||||
|
|
||||||
// runFilter accepts a filter and executes it, returning all its results as
|
// runFilter accepts a filter and executes it, returning all its results as
|
||||||
// `Log` objects.
|
// `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)
|
logs, err := filter.Logs(ctx)
|
||||||
if err != nil || logs == nil {
|
if err != nil || logs == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -888,7 +879,7 @@ func (b *Block) EstimateGas(ctx context.Context, args struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Pending struct {
|
type Pending struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pending) TransactionCount(ctx context.Context) (int32, error) {
|
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.
|
// Resolver is the top-level object in the GraphQL hierarchy.
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
backend *eth.EthAPIBackend
|
backend ethapi.Backend
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) Block(ctx context.Context, args struct {
|
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
|
// Construct the range filter
|
||||||
filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics)
|
filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics)
|
||||||
|
|
||||||
return runFilter(ctx, r.backend, filter)
|
return runFilter(ctx, r.backend, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1145,89 +1135,3 @@ func (r *Resolver) Syncing() (*SyncState, error) {
|
|||||||
// Otherwise gather the block sync stats
|
// Otherwise gather the block sync stats
|
||||||
return &SyncState{progress}, nil
|
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) {
|
func TestBuildSchema(t *testing.T) {
|
||||||
// Make sure the schema can be parsed and matched up to the object model.
|
// Make sure the schema can be parsed and matched up to the object model.
|
||||||
_, err := NewHandler(nil)
|
if _, err := newHandler(nil); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Could not construct GraphQL handler: %v", err)
|
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/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
@ -47,9 +48,10 @@ type Backend interface {
|
|||||||
ExtRPCEnabled() bool
|
ExtRPCEnabled() bool
|
||||||
RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection
|
RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection
|
||||||
|
|
||||||
// BlockChain API
|
// Blockchain API
|
||||||
SetHead(number uint64)
|
SetHead(number uint64)
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
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)
|
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
||||||
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, 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
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||||
|
|
||||||
// TxPool API
|
// Transaction pool API
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
GetPoolTransactions() (types.Transactions, error)
|
GetPoolTransactions() (types.Transactions, error)
|
||||||
@ -70,6 +72,13 @@ type Backend interface {
|
|||||||
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
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
|
ChainConfig() *params.ChainConfig
|
||||||
CurrentBlock() *types.Block
|
CurrentBlock() *types.Block
|
||||||
}
|
}
|
||||||
|
@ -38,13 +38,15 @@ const (
|
|||||||
|
|
||||||
// DefaultConfig contains reasonable default settings.
|
// DefaultConfig contains reasonable default settings.
|
||||||
var DefaultConfig = Config{
|
var DefaultConfig = Config{
|
||||||
DataDir: DefaultDataDir(),
|
DataDir: DefaultDataDir(),
|
||||||
HTTPPort: DefaultHTTPPort,
|
HTTPPort: DefaultHTTPPort,
|
||||||
HTTPModules: []string{"net", "web3"},
|
HTTPModules: []string{"net", "web3"},
|
||||||
HTTPVirtualHosts: []string{"localhost"},
|
HTTPVirtualHosts: []string{"localhost"},
|
||||||
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
|
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
|
||||||
WSPort: DefaultWSPort,
|
WSPort: DefaultWSPort,
|
||||||
WSModules: []string{"net", "web3"},
|
WSModules: []string{"net", "web3"},
|
||||||
|
GraphQLPort: DefaultGraphQLPort,
|
||||||
|
GraphQLVirtualHosts: []string{"localhost"},
|
||||||
P2P: p2p.Config{
|
P2P: p2p.Config{
|
||||||
ListenAddr: ":30303",
|
ListenAddr: ":30303",
|
||||||
MaxPeers: 50,
|
MaxPeers: 50,
|
||||||
|
Loading…
Reference in New Issue
Block a user