nimbus-eth1/nimbus/rpc.nim

312 lines
9.8 KiB
Nim

# Nimbus
# Copyright (c) 2022-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
chronicles,
websock/websock,
json_rpc/rpcserver,
graphql/httpserver,
./rpc/common,
#./rpc/debug,
./rpc/engine_api,
./rpc/p2p,
./rpc/jwt_auth,
./rpc/cors,
./rpc/rpc_server,
./rpc/experimental,
./rpc/oracle,
./rpc/server_api,
./nimbus_desc,
./graphql/ethapi
export
common,
debug,
engine_api,
p2p,
jwt_auth,
cors,
rpc_server,
experimental,
oracle,
server_api
{.push gcsafe, raises: [].}
const DefaultChunkSize = 8192
func serverEnabled(conf: NimbusConf): bool =
conf.httpServerEnabled or
conf.engineApiServerEnabled
func combinedServer(conf: NimbusConf): bool =
conf.httpServerEnabled and
conf.shareServerWithEngineApi
proc installRPC(server: RpcServer,
nimbus: NimbusNode,
conf: NimbusConf,
com: CommonRef,
oracle: Oracle,
flags: set[RpcFlag]) =
setupCommonRpc(nimbus.ethNode, conf, server)
if RpcFlag.Eth in flags:
setupEthRpc(nimbus.ethNode, nimbus.ctx, com, nimbus.txPool, oracle, server)
# # Tracer is currently disabled
# if RpcFlag.Debug in flags:
# setupDebugRpc(com, nimbus.txPool, server)
if RpcFlag.Exp in flags:
setupExpRpc(com, server)
server.rpc("admin_quit") do() -> string:
{.gcsafe.}:
nimbus.state = NimbusState.Stopping
result = "EXITING"
proc newRpcWebsocketHandler(): RpcWebSocketHandler =
let rng = HmacDrbgContext.new()
RpcWebSocketHandler(
wsserver: WSServer.new(rng = rng),
)
proc newRpcHttpHandler(): RpcHttpHandler =
RpcHttpHandler(
maxChunkSize: DefaultChunkSize,
)
proc addHandler(handlers: var seq[RpcHandlerProc],
server: RpcHttpHandler) =
proc handlerProc(request: HttpRequestRef):
Future[RpcHandlerResult] {.async: (raises: []).} =
try:
let res = await server.serveHTTP(request)
if res.isNil:
return RpcHandlerResult(status: RpcHandlerStatus.Skip)
else:
return RpcHandlerResult(status: RpcHandlerStatus.Response, response: res)
except CancelledError:
return RpcHandlerResult(status: RpcHandlerStatus.Error)
handlers.add handlerProc
proc addHandler(handlers: var seq[RpcHandlerProc],
server: RpcWebSocketHandler) =
proc handlerProc(request: HttpRequestRef):
Future[RpcHandlerResult] {.async: (raises: []).} =
if not request.headers.contains("Sec-WebSocket-Version"):
return RpcHandlerResult(status: RpcHandlerStatus.Skip)
let stream = websock.AsyncStream(
reader: request.connection.mainReader,
writer: request.connection.mainWriter,
)
let req = websock.HttpRequest(
meth: request.meth,
uri: request.uri,
version: request.version,
headers: request.headers,
stream: stream,
)
try:
await server.serveHTTP(req)
return RpcHandlerResult(status: RpcHandlerStatus.KeepConnection)
except CancelledError:
return RpcHandlerResult(status: RpcHandlerStatus.Error)
handlers.add handlerProc
proc addHandler(handlers: var seq[RpcHandlerProc],
server: GraphqlHttpHandlerRef) =
proc handlerProc(request: HttpRequestRef):
Future[RpcHandlerResult] {.async: (raises: []).} =
try:
let res = await server.serveHTTP(request)
if res.isNil:
return RpcHandlerResult(status: RpcHandlerStatus.Skip)
else:
return RpcHandlerResult(status: RpcHandlerStatus.Response, response: res)
except CatchableError:
return RpcHandlerResult(status: RpcHandlerStatus.Error)
handlers.add handlerProc
proc addHttpServices(handlers: var seq[RpcHandlerProc],
nimbus: NimbusNode, conf: NimbusConf,
com: CommonRef, oracle: Oracle,
protocols: set[ProtocolFlag],
address: TransportAddress) =
# The order is important: graphql, ws, rpc
# graphql depends on /graphl path
# ws depends on Sec-WebSocket-Version header
# json-rpc have no reliable identification
if conf.graphqlEnabled:
let ctx = setupGraphqlContext(com, nimbus.ethNode, nimbus.txPool)
let server = GraphqlHttpHandlerRef.new(ctx)
handlers.addHandler(server)
info "GraphQL API enabled", url = "http://" & $address
if conf.wsEnabled:
let server = newRpcWebsocketHandler()
var rpcFlags = conf.getWsFlags()
if ProtocolFlag.Eth in protocols: rpcFlags.incl RpcFlag.Eth
installRPC(server, nimbus, conf, com, oracle, rpcFlags)
handlers.addHandler(server)
info "JSON-RPC WebSocket API enabled", url = "ws://" & $address
if conf.rpcEnabled:
let server = newRpcHttpHandler()
var rpcFlags = conf.getRpcFlags()
if ProtocolFlag.Eth in protocols: rpcFlags.incl RpcFlag.Eth
installRPC(server, nimbus, conf, com, oracle, rpcFlags)
handlers.addHandler(server)
info "JSON-RPC API enabled", url = "http://" & $address
proc addEngineApiServices(handlers: var seq[RpcHandlerProc],
nimbus: NimbusNode, conf: NimbusConf,
com: CommonRef, oracle: Oracle,
address: TransportAddress) =
# The order is important: ws, rpc
if conf.engineApiWsEnabled:
let server = newRpcWebsocketHandler()
setupEngineAPI(nimbus.beaconEngine, server)
installRPC(server, nimbus, conf, com, oracle, {RpcFlag.Eth})
handlers.addHandler(server)
info "Engine WebSocket API enabled", url = "ws://" & $address
if conf.engineApiEnabled:
let server = newRpcHttpHandler()
setupEngineAPI(nimbus.beaconEngine, server)
installRPC(server, nimbus, conf, com, oracle, {RpcFlag.Eth})
handlers.addHandler(server)
info "Engine API enabled", url = "http://" & $address
proc addServices(handlers: var seq[RpcHandlerProc],
nimbus: NimbusNode, conf: NimbusConf,
com: CommonRef, oracle: Oracle, protocols: set[ProtocolFlag],
address: TransportAddress) =
# The order is important: graphql, ws, rpc
if conf.graphqlEnabled:
let ctx = setupGraphqlContext(com, nimbus.ethNode, nimbus.txPool)
let server = GraphqlHttpHandlerRef.new(ctx)
handlers.addHandler(server)
info "GraphQL API enabled", url = "http://" & $address
if conf.wsEnabled or conf.engineApiWsEnabled:
let server = newRpcWebsocketHandler()
if conf.engineApiWsEnabled:
setupEngineAPI(nimbus.beaconEngine, server)
if not conf.wsEnabled:
installRPC(server, nimbus, conf, com, oracle, {RpcFlag.Eth})
info "Engine WebSocket API enabled", url = "ws://" & $address
if conf.wsEnabled:
var rpcFlags = conf.getWsFlags()
if ProtocolFlag.Eth in protocols: rpcFlags.incl RpcFlag.Eth
installRPC(server, nimbus, conf, com, oracle, rpcFlags)
info "JSON-RPC WebSocket API enabled", url = "ws://" & $address
handlers.addHandler(server)
if conf.rpcEnabled or conf.engineApiEnabled:
let server = newRpcHttpHandler()
if conf.engineApiEnabled:
setupEngineAPI(nimbus.beaconEngine, server)
if not conf.rpcEnabled:
installRPC(server, nimbus, conf, com, oracle, {RpcFlag.Eth})
info "Engine API enabled", url = "http://" & $address
if conf.rpcEnabled:
var rpcFlags = conf.getRpcFlags()
if ProtocolFlag.Eth in protocols: rpcFlags.incl RpcFlag.Eth
installRPC(server, nimbus, conf, com, oracle, rpcFlags)
info "JSON-RPC API enabled", url = "http://" & $address
handlers.addHandler(server)
proc setupRpc*(nimbus: NimbusNode, conf: NimbusConf,
com: CommonRef, protocols: set[ProtocolFlag]) =
if not conf.engineApiEnabled:
warn "Engine API disabled, the node will not respond to consensus client updates (enable with `--engine-api`)"
if not conf.serverEnabled:
return
# Provide JWT authentication handler for rpcHttpServer
let jwtKey = block:
# Create or load shared secret
let rc = nimbus.ctx.rng.jwtSharedSecret(conf)
if rc.isErr:
fatal "Failed create or load shared secret",
msg = $(rc.unsafeError) # avoid side effects
quit(QuitFailure)
rc.value
let
allowedOrigins = conf.getAllowedOrigins()
jwtAuthHook = httpJwtAuth(jwtKey)
corsHook = httpCors(allowedOrigins)
oracle = Oracle.new(com)
if conf.combinedServer:
let hooks: seq[RpcAuthHook] = @[jwtAuthHook, corsHook]
var handlers: seq[RpcHandlerProc]
let address = initTAddress(conf.httpAddress, conf.httpPort)
handlers.addServices(nimbus, conf, com, oracle, protocols, address)
let res = newHttpServerWithParams(address, hooks, handlers)
if res.isErr:
fatal "Cannot create RPC server", msg=res.error
quit(QuitFailure)
nimbus.httpServer = res.get
nimbus.httpServer.start()
return
if conf.httpServerEnabled:
let hooks = @[corsHook]
var handlers: seq[RpcHandlerProc]
let address = initTAddress(conf.httpAddress, conf.httpPort)
handlers.addHttpServices(nimbus, conf, com, oracle, protocols, address)
let res = newHttpServerWithParams(address, hooks, handlers)
if res.isErr:
fatal "Cannot create RPC server", msg=res.error
quit(QuitFailure)
nimbus.httpServer = res.get
nimbus.httpServer.start()
if conf.engineApiServerEnabled:
let hooks = @[jwtAuthHook, corsHook]
var handlers: seq[RpcHandlerProc]
let address = initTAddress(conf.engineApiAddress, conf.engineApiPort)
handlers.addEngineApiServices(nimbus, conf, com, oracle, address)
let res = newHttpServerWithParams(address, hooks, handlers)
if res.isErr:
fatal "Cannot create RPC server", msg=res.error
quit(QuitFailure)
nimbus.engineApiServer = res.get
nimbus.engineApiServer.start()