diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bc6a0dd6..91a382b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Next version - Calls to `publish` a message on `wakunode2` now `await` instead of `discard` dispatched [`WakuRelay`](https://github.com/vacp2p/specs/blob/master/specs/waku/v2/waku-relay.md) procedures +- Added JSON-RPC Admin API to retrieve information about peers registered on the `wakunode2` ## 2020-11-30 v0.1 diff --git a/tests/v2/test_jsonrpc_waku.nim b/tests/v2/test_jsonrpc_waku.nim index c6b23a46c..0be838a08 100644 --- a/tests/v2/test_jsonrpc_waku.nim +++ b/tests/v2/test_jsonrpc_waku.nim @@ -2,8 +2,7 @@ import std/[unittest, options, sets, tables, os, strutils, sequtils], stew/shims/net as stewNet, json_rpc/[rpcserver, rpcclient], - libp2p/standard_setup, - libp2p/switch, + libp2p/[standard_setup, switch, multiaddress], libp2p/protobuf/minprotobuf, libp2p/stream/[bufferstream, connection], libp2p/crypto/crypto, @@ -11,10 +10,11 @@ import libp2p/protocols/pubsub/rpc/message, ../../waku/v2/waku_types, ../../waku/v2/node/wakunode2, - ../../waku/v2/node/jsonrpc/[jsonrpc_types,store_api,relay_api,debug_api,filter_api], + ../../waku/v2/node/jsonrpc/[jsonrpc_types,store_api,relay_api,debug_api,filter_api,admin_api], ../../waku/v2/protocol/message_notifier, ../../waku/v2/protocol/waku_filter, ../../waku/v2/protocol/waku_store/waku_store, + ../../waku/v2/protocol/waku_swap/waku_swap, ../test_helpers template sourceDir*: string = currentSourcePath.rsplit(DirSep, 1)[0] @@ -359,3 +359,55 @@ procSuite "Waku v2 JSON-RPC API": server.stop() server.close() waitfor node.stop() + + asyncTest "Admin API: get peer information": + const cTopic = ContentTopic(1) + + waitFor node.start() + + # RPC server setup + let + rpcPort = Port(8545) + ta = initTAddress(bindIp, rpcPort) + server = newRpcHttpServer([ta]) + + installAdminApiHandlers(node, server) + server.start() + + let client = newRpcHttpClient() + await client.connect("127.0.0.1", rpcPort) + + node.mountFilter() + node.mountSwap() + node.mountStore() + + # Create and set some peers + let + locationAddr = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet() + + filterKey = wakunode2.PrivateKey.random(ECDSA, rng[]).get() + filterPeer = PeerInfo.init(filterKey, @[locationAddr]) + + swapKey = wakunode2.PrivateKey.random(ECDSA, rng[]).get() + swapPeer = PeerInfo.init(swapKey, @[locationAddr]) + + storeKey = wakunode2.PrivateKey.random(ECDSA, rng[]).get() + storePeer = PeerInfo.init(storeKey, @[locationAddr]) + + node.wakuFilter.setPeer(filterPeer) + node.wakuSwap.setPeer(swapPeer) + node.wakuStore.setPeer(storePeer) + + let response = await client.get_waku_v2_admin_v1_peers() + + check: + response.len == 3 + # Check filter peer + (response.filterIt(it.protocol == WakuFilterCodec)[0]).multiaddr == constructMultiaddrStr(filterPeer) + # Check swap peer + (response.filterIt(it.protocol == WakuSwapCodec)[0]).multiaddr == constructMultiaddrStr(swapPeer) + # Check store peer + (response.filterIt(it.protocol == WakuStoreCodec)[0]).multiaddr == constructMultiaddrStr(storePeer) + + server.close() + waitfor node.stop() diff --git a/waku/v2/node/jsonrpc/admin_api.nim b/waku/v2/node/jsonrpc/admin_api.nim new file mode 100644 index 000000000..4c5873c03 --- /dev/null +++ b/waku/v2/node/jsonrpc/admin_api.nim @@ -0,0 +1,54 @@ +{.push raises: [Exception, Defect].} + +import + std/[options,sequtils], + json_rpc/rpcserver, + libp2p/[peerinfo, switch], + ../../waku_types, + ../../protocol/waku_store/[waku_store_types, waku_store], + ../../protocol/waku_swap/[waku_swap_types, waku_swap], + ../../protocol/waku_filter, + ../wakunode2, + ./jsonrpc_types, ./jsonrpc_utils + +proc constructMultiaddrStr*(peerInfo: PeerInfo): string = + # Constructs a multiaddress with both location address and p2p identity + $peerInfo.addrs[0] & "/p2p/" & $peerInfo.peerId + +proc installAdminApiHandlers*(node: WakuNode, rpcsrv: RpcServer) = + + ## Admin API version 1 definitions + + rpcsrv.rpc("get_waku_v2_admin_v1_peers") do() -> seq[WakuPeer]: + ## Returns history for a list of content topics with optional paging + debug "get_waku_v2_admin_v1_peers" + + # Create a single list of peers from mounted protocols. + # @TODO since the switch does not expose its connections, retrieving the connected peers requires a peer store/peer management + + var wPeers: seq[WakuPeer] = @[] + + if not node.wakuSwap.isNil: + # Map WakuSwap peers to WakuPeers and add to return list + wPeers.insert(node.wakuSwap.peers.mapIt(WakuPeer(multiaddr: constructMultiaddrStr(it.peerInfo), + protocol: WakuSwapCodec, + connected: node.switch.isConnected(it.peerInfo))), + wPeers.len) # Append to the end of the sequence + + if not node.wakuFilter.isNil: + # Map WakuFilter peers to WakuPeers and add to return list + wPeers.insert(node.wakuFilter.peers.mapIt(WakuPeer(multiaddr: constructMultiaddrStr(it.peerInfo), + protocol: WakuFilterCodec, + connected: node.switch.isConnected(it.peerInfo))), + wPeers.len) # Append to the end of the sequence + + if not node.wakuStore.isNil: + # Map WakuStore peers to WakuPeers and add to return list + wPeers.insert(node.wakuStore.peers.mapIt(WakuPeer(multiaddr: constructMultiaddrStr(it.peerInfo), + protocol: WakuStoreCodec, + connected: node.switch.isConnected(it.peerInfo))), + wPeers.len) # Append to the end of the sequence + + + # @TODO filter output on protocol/connected-status + return wPeers diff --git a/waku/v2/node/jsonrpc/jsonrpc_callsigs.nim b/waku/v2/node/jsonrpc/jsonrpc_callsigs.nim index 35dfff228..75b9bf7d7 100644 --- a/waku/v2/node/jsonrpc/jsonrpc_callsigs.nim +++ b/waku/v2/node/jsonrpc/jsonrpc_callsigs.nim @@ -1,3 +1,7 @@ +# Admin API + +proc get_waku_v2_admin_v1_peers(): seq[WakuPeer] + # Debug API proc get_waku_v2_debug_v1_info(): WakuInfo diff --git a/waku/v2/node/jsonrpc/jsonrpc_types.nim b/waku/v2/node/jsonrpc/jsonrpc_types.nim index 9571a75cd..9d8604046 100644 --- a/waku/v2/node/jsonrpc/jsonrpc_types.nim +++ b/waku/v2/node/jsonrpc/jsonrpc_types.nim @@ -16,3 +16,8 @@ type WakuRelayMessage* = object payload*: seq[byte] contentTopic*: Option[ContentTopic] + + WakuPeer* = object + multiaddr*: string + protocol*: string + connected*: bool