2020-08-19 11:46:20 +00:00
package cli
import (
"context"
"fmt"
2020-09-18 10:23:35 +00:00
"net/http"
2020-10-10 06:06:54 +00:00
"time"
2020-09-18 10:23:35 +00:00
2020-08-19 11:46:20 +00:00
"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
2020-09-11 20:17:37 +00:00
TLSCertfile string
2020-09-19 14:16:04 +00:00
TLSCACert string
TLSKeyFile string
2020-08-19 11:46:20 +00:00
HttpPort int
HttpCORSDomain [ ] string
HttpVirtualHost [ ] string
API [ ] string
Gascap uint64
2020-08-29 15:50:24 +00:00
MaxTraces uint64
2020-09-25 12:12:36 +00:00
TraceType string
2020-09-01 16:00:47 +00:00
WebsocketEnabled bool
2020-08-19 11:46:20 +00:00
}
var rootCmd = & cobra . Command {
Use : "rpcdaemon" ,
Short : "rpcdaemon is JSON RPC server that connects to turbo-geth node for remote DB access" ,
2020-08-20 03:52:27 +00:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if err := utils . SetupCobra ( cmd ) ; err != nil {
return err
}
return nil
} ,
PersistentPostRunE : func ( cmd * cobra . Command , args [ ] string ) error {
utils . StopDebug ( )
return nil
} ,
2020-08-19 11:46:20 +00:00
}
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" )
2020-09-11 20:17:37 +00:00
rootCmd . PersistentFlags ( ) . StringVar ( & cfg . TLSCertfile , "tls.cert" , "" , "certificate for client side TLS handshake" )
2020-09-19 14:16:04 +00:00
rootCmd . PersistentFlags ( ) . StringVar ( & cfg . TLSKeyFile , "tls.key" , "" , "key file for client side TLS handshake" )
rootCmd . PersistentFlags ( ) . StringVar ( & cfg . TLSCACert , "tls.cacert" , "" , "CA certificate for client side TLS handshake" )
2020-08-19 11:46:20 +00:00
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" )
2020-08-29 15:50:24 +00:00
rootCmd . PersistentFlags ( ) . Uint64Var ( & cfg . MaxTraces , "trace.maxtraces" , 200 , "Sets a limit on traces that can be returned in trace_filter" )
2020-09-25 12:12:36 +00:00
rootCmd . PersistentFlags ( ) . StringVar ( & cfg . TraceType , "trace.type" , "parity" , "Specify the type of tracing [geth|parity*] (experimental)" )
2020-09-01 16:00:47 +00:00
rootCmd . PersistentFlags ( ) . BoolVar ( & cfg . WebsocketEnabled , "ws" , false , "Enable Websockets" )
2020-08-19 11:46:20 +00:00
return rootCmd , cfg
}
func OpenDB ( cfg Flags ) ( ethdb . KV , ethdb . Backend , error ) {
var db ethdb . KV
var txPool ethdb . Backend
var err error
2020-09-18 10:23:35 +00:00
// Do not change the order of these checks. Chaindata needs to be checked first, because PrivateApiAddr has default value which is not ""
// If PrivateApiAddr is checked first, the Chaindata option will never work
if cfg . Chaindata != "" {
2020-08-19 11:46:20 +00:00
if database , errOpen := ethdb . Open ( cfg . Chaindata ) ; errOpen == nil {
db = database . KV ( )
} else {
err = errOpen
}
2020-09-18 10:23:35 +00:00
} else if cfg . PrivateApiAddr != "" {
2020-10-10 12:24:56 +00:00
db , txPool , err = ethdb . NewRemote2 ( ) . Path ( cfg . PrivateApiAddr ) . Open ( cfg . TLSCertfile , cfg . TLSKeyFile , cfg . TLSCACert )
2020-09-18 10:23:35 +00:00
if err != nil {
return nil , nil , fmt . Errorf ( "could not connect to remoteDb: %w" , err )
}
2020-08-19 11:46:20 +00:00
} else {
2020-09-09 10:09:55 +00:00
return nil , nil , fmt . Errorf ( "either remote db or lmdb must be specified" )
2020-08-19 11:46:20 +00:00
}
if err != nil {
return nil , nil , fmt . Errorf ( "could not connect to remoteDb: %w" , err )
}
return db , txPool , err
}
2020-08-20 03:52:27 +00:00
func StartRpcServer ( ctx context . Context , cfg Flags , rpcAPI [ ] rpc . API ) error {
2020-08-19 11:46:20 +00:00
// register apis and create handler stack
httpEndpoint := fmt . Sprintf ( "%s:%d" , cfg . HttpListenAddress , cfg . HttpPort )
2020-09-02 05:56:48 +00:00
2020-08-19 11:46:20 +00:00
srv := rpc . NewServer ( )
if err := node . RegisterApisFromWhitelist ( rpcAPI , cfg . API , srv , false ) ; err != nil {
2020-08-20 03:52:27 +00:00
return fmt . Errorf ( "could not start register RPC apis: %w" , err )
2020-08-19 11:46:20 +00:00
}
2020-09-02 05:56:48 +00:00
2020-09-01 16:00:47 +00:00
var err error
2020-09-02 05:56:48 +00:00
httpHandler := node . NewHTTPHandlerStack ( srv , cfg . HttpCORSDomain , cfg . HttpVirtualHost )
var wsHandler http . Handler
2020-09-01 16:00:47 +00:00
if cfg . WebsocketEnabled {
2020-09-02 05:56:48 +00:00
wsHandler = srv . WebsocketHandler ( [ ] string { "*" } )
}
2020-09-09 20:21:19 +00:00
var handler http . Handler = http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2020-09-02 05:56:48 +00:00
if cfg . WebsocketEnabled && r . Method == "GET" {
wsHandler . ServeHTTP ( w , r )
2020-09-01 16:00:47 +00:00
}
2020-09-02 05:56:48 +00:00
httpHandler . ServeHTTP ( w , r )
} )
listener , _ , err := node . StartHTTPEndpoint ( httpEndpoint , rpc . DefaultHTTPTimeouts , handler )
if err != nil {
return fmt . Errorf ( "could not start RPC api: %w" , err )
2020-08-19 11:46:20 +00:00
}
2020-09-02 05:56:48 +00:00
2020-09-25 12:12:36 +00:00
if cfg . TraceType != "parity" {
log . Info ( "Tracing output type: " , cfg . TraceType )
}
2020-09-01 16:00:47 +00:00
log . Info ( "HTTP endpoint opened" , "url" , httpEndpoint , "ws" , cfg . WebsocketEnabled )
2020-08-19 11:46:20 +00:00
defer func ( ) {
2020-10-10 06:06:54 +00:00
srv . Stop ( )
shutdownCtx , cancel := context . WithTimeout ( context . Background ( ) , 5 * time . Second )
defer cancel ( )
_ = listener . Shutdown ( shutdownCtx )
2020-08-19 11:46:20 +00:00
log . Info ( "HTTP endpoint closed" , "url" , httpEndpoint )
} ( )
2020-10-10 06:06:54 +00:00
<- ctx . Done ( )
log . Info ( "Exiting..." )
2020-08-20 03:52:27 +00:00
return nil
2020-08-19 11:46:20 +00:00
}