mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-11 14:54:12 +00:00
f70ff38b53
Some upstream repos still need fixes, but this gets us close enough that style hints can be enabled by default. In general, "canonical" spellings are preferred even if they violate nep-1 - this applies in particular to spec-related stuff like `genesis_validators_root` which appears throughout the codebase.
257 lines
8.6 KiB
Nim
257 lines
8.6 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018-2021 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
import
|
|
std/[options, sequtils],
|
|
stew/byteutils,
|
|
chronicles,
|
|
json_rpc/servers/httpserver,
|
|
eth/p2p/discoveryv5/enr,
|
|
libp2p/[multiaddress, multicodec, peerstore],
|
|
../beacon_node, ../version,
|
|
../networking/[eth2_network, peer_pool],
|
|
../sync/sync_manager,
|
|
../spec/datatypes/base,
|
|
./rpc_utils
|
|
|
|
logScope: topics = "nodeapi"
|
|
|
|
type
|
|
RpcServer = RpcHttpServer
|
|
|
|
proc validateState(state: Option[seq[string]]): Option[set[ConnectionState]] =
|
|
var res: set[ConnectionState]
|
|
if state.isSome():
|
|
let states = state.get()
|
|
for item in states:
|
|
case item
|
|
of "disconnected":
|
|
if ConnectionState.Disconnected notin res:
|
|
res.incl(ConnectionState.Disconnected)
|
|
else:
|
|
# `state` values should be unique
|
|
return none(set[ConnectionState])
|
|
of "connecting":
|
|
if ConnectionState.Disconnected notin res:
|
|
res.incl(ConnectionState.Connecting)
|
|
else:
|
|
# `state` values should be unique
|
|
return none(set[ConnectionState])
|
|
of "connected":
|
|
if ConnectionState.Connected notin res:
|
|
res.incl(ConnectionState.Connected)
|
|
else:
|
|
# `state` values should be unique
|
|
return none(set[ConnectionState])
|
|
of "disconnecting":
|
|
if ConnectionState.Disconnecting notin res:
|
|
res.incl(ConnectionState.Disconnecting)
|
|
else:
|
|
# `state` values should be unique
|
|
return none(set[ConnectionState])
|
|
else:
|
|
# Found incorrect `state` string value
|
|
return none(set[ConnectionState])
|
|
|
|
if res == {}:
|
|
res = {ConnectionState.Connecting, ConnectionState.Connected,
|
|
ConnectionState.Disconnecting, ConnectionState.Disconnected}
|
|
some(res)
|
|
|
|
proc validateDirection(direction: Option[seq[string]]): Option[set[PeerType]] =
|
|
var res: set[PeerType]
|
|
if direction.isSome():
|
|
let directions = direction.get()
|
|
for item in directions:
|
|
case item
|
|
of "inbound":
|
|
if PeerType.Incoming notin res:
|
|
res.incl(PeerType.Incoming)
|
|
else:
|
|
# `direction` values should be unique
|
|
return none(set[PeerType])
|
|
of "outbound":
|
|
if PeerType.Outgoing notin res:
|
|
res.incl(PeerType.Outgoing)
|
|
else:
|
|
# `direction` values should be unique
|
|
return none(set[PeerType])
|
|
else:
|
|
# Found incorrect `direction` string value
|
|
return none(set[PeerType])
|
|
|
|
if res == {}:
|
|
res = {PeerType.Incoming, PeerType.Outgoing}
|
|
some(res)
|
|
|
|
proc toString(state: ConnectionState): string =
|
|
case state
|
|
of ConnectionState.Disconnected:
|
|
"disconnected"
|
|
of ConnectionState.Connecting:
|
|
"connecting"
|
|
of ConnectionState.Connected:
|
|
"connected"
|
|
of ConnectionState.Disconnecting:
|
|
"disconnecting"
|
|
else:
|
|
""
|
|
|
|
proc toString(direction: PeerType): string =
|
|
case direction:
|
|
of PeerType.Incoming:
|
|
"inbound"
|
|
of PeerType.Outgoing:
|
|
"outbound"
|
|
|
|
proc getLastSeenAddress(node: BeaconNode, id: PeerId): string =
|
|
# TODO (cheatfate): We need to provide filter here, which will be able to
|
|
# filter such multiaddresses like `/ip4/0.0.0.0` or local addresses or
|
|
# addresses with peer ids.
|
|
let addrs = node.network.switch.peerStore.addressBook.get(id).toSeq()
|
|
if len(addrs) > 0:
|
|
$addrs[len(addrs) - 1]
|
|
else:
|
|
""
|
|
|
|
proc getDiscoveryAddresses(node: BeaconNode): Option[seq[string]] =
|
|
let restr = node.network.enrRecord().toTypedRecord()
|
|
if restr.isErr():
|
|
return none[seq[string]]()
|
|
let respa = restr.get().toPeerAddr(udpProtocol)
|
|
if respa.isErr():
|
|
return none[seq[string]]()
|
|
let pa = respa.get()
|
|
let mpa = MultiAddress.init(multiCodec("p2p"), pa.peerId)
|
|
if mpa.isErr():
|
|
return none[seq[string]]()
|
|
var addresses = newSeqOfCap[string](len(pa.addrs))
|
|
for item in pa.addrs:
|
|
let resa = concat(item, mpa.get())
|
|
if resa.isOk():
|
|
addresses.add($(resa.get()))
|
|
return some(addresses)
|
|
|
|
proc getP2PAddresses(node: BeaconNode): Option[seq[string]] =
|
|
let pinfo = node.network.switch.peerInfo
|
|
let mpa = MultiAddress.init(multiCodec("p2p"), pinfo.peerId)
|
|
if mpa.isErr():
|
|
return none[seq[string]]()
|
|
var addresses = newSeqOfCap[string](len(pinfo.addrs))
|
|
for item in pinfo.addrs:
|
|
let resa = concat(item, mpa.get())
|
|
if resa.isOk():
|
|
addresses.add($(resa.get()))
|
|
return some(addresses)
|
|
|
|
proc installNodeApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
|
|
raises: [Defect, CatchableError].} =
|
|
rpcServer.rpc("get_v1_node_identity") do () -> RpcNodeIdentity:
|
|
let discoveryAddresses =
|
|
block:
|
|
let res = node.getDiscoveryAddresses()
|
|
if res.isSome():
|
|
res.get()
|
|
else:
|
|
newSeq[string](0)
|
|
|
|
let p2pAddresses =
|
|
block:
|
|
let res = node.getP2PAddresses()
|
|
if res.isSome():
|
|
res.get()
|
|
else:
|
|
newSeq[string]()
|
|
|
|
return (
|
|
peer_id: $node.network.peerId(),
|
|
enr: node.network.enrRecord().toURI(),
|
|
p2p_addresses: p2pAddresses,
|
|
discovery_addresses: discoveryAddresses,
|
|
metadata: (node.network.metadata.seq_number,
|
|
to0xHex(node.network.metadata.attnets.bytes))
|
|
)
|
|
|
|
rpcServer.rpc("get_v1_node_peers") do (state: Option[seq[string]],
|
|
direction: Option[seq[string]]) -> seq[RpcNodePeer]:
|
|
var res = newSeq[RpcNodePeer]()
|
|
let rstates = validateState(state)
|
|
if rstates.isNone():
|
|
raise newException(CatchableError, "Incorrect state parameter")
|
|
let rdirs = validateDirection(direction)
|
|
if rdirs.isNone():
|
|
raise newException(CatchableError, "Incorrect direction parameter")
|
|
let states = rstates.get()
|
|
let dirs = rdirs.get()
|
|
for peer in node.network.peers.values():
|
|
if (peer.connectionState in states) and (peer.direction in dirs):
|
|
let resPeer = (
|
|
peer_id: $peer.peerId,
|
|
enr: if peer.enr.isSome(): peer.enr.get().toURI() else: "",
|
|
last_seen_p2p_address: getLastSeenAddress(node, peer.peerId),
|
|
state: peer.connectionState.toString(),
|
|
direction: peer.direction.toString(),
|
|
agent: node.network.switch.peerStore.agentBook.get(peer.peerId), # Fields `agent` and `proto` are not
|
|
proto: node.network.switch.peerStore.protoVersionBook.get(peer.peerId) # part of specification
|
|
)
|
|
res.add(resPeer)
|
|
return res
|
|
|
|
rpcServer.rpc("get_v1_node_peer_count") do () -> RpcNodePeerCount:
|
|
var res: RpcNodePeerCount
|
|
for item in node.network.peers.values():
|
|
case item.connectionState
|
|
of ConnectionState.Connecting:
|
|
inc(res.connecting)
|
|
of ConnectionState.Connected:
|
|
inc(res.connected)
|
|
of ConnectionState.Disconnecting:
|
|
inc(res.disconnecting)
|
|
of ConnectionState.Disconnected:
|
|
inc(res.disconnected)
|
|
of ConnectionState.None:
|
|
discard
|
|
return res
|
|
|
|
rpcServer.rpc("get_v1_node_peers_peerId") do (
|
|
peer_id: string) -> RpcNodePeer:
|
|
let pres = PeerId.init(peer_id)
|
|
if pres.isErr():
|
|
raise newException(CatchableError,
|
|
"The peer ID supplied could not be parsed")
|
|
let pid = pres.get()
|
|
let peer = node.network.peers.getOrDefault(pid)
|
|
if isNil(peer):
|
|
raise newException(CatchableError, "Peer not found")
|
|
|
|
return (
|
|
peer_id: $peer.peerId,
|
|
enr: if peer.enr.isSome(): peer.enr.get().toURI() else: "",
|
|
last_seen_p2p_address: getLastSeenAddress(node, peer.peerId),
|
|
state: peer.connectionState.toString(),
|
|
direction: peer.direction.toString(),
|
|
agent: node.network.switch.peerStore.agentBook.get(peer.peerId), # Fields `agent` and `proto` are not
|
|
proto: node.network.switch.peerStore.protoVersionBook.get(peer.peerId) # part of specification
|
|
)
|
|
|
|
rpcServer.rpc("get_v1_node_version") do () -> JsonNode:
|
|
return %*{"version": "Nimbus/" & fullVersionStr}
|
|
|
|
rpcServer.rpc("get_v1_node_syncing") do () -> RpcSyncInfo:
|
|
return node.syncManager.getInfo()
|
|
|
|
rpcServer.rpc("get_v1_node_health") do () -> JsonNode:
|
|
# TODO: There currently no way to situation when we node has issues, so
|
|
# its impossible to return HTTP ERROR 503 according to specification.
|
|
if node.syncManager.inProgress:
|
|
# We need to return HTTP ERROR 206 according to specification
|
|
return %*{"health": 206}
|
|
else:
|
|
return %*{"health": 200}
|