From a9324e973a5f059b160e40842db0447d814ea758 Mon Sep 17 00:00:00 2001 From: Giuliano Mega Date: Mon, 22 Jun 2026 17:10:00 -0300 Subject: [PATCH] chore: unified REST and module debug info (#1468) --- .../requests/node_debug_request.nim | 15 +---- openapi.yaml | 13 ++-- storage/rest/api.nim | 26 ++------ storage/rest/json.nim | 61 +++++++++++++++++++ storage/storage.nim | 2 + storage/utils/json.nim | 10 ++- 6 files changed, 87 insertions(+), 40 deletions(-) diff --git a/library/storage_thread_requests/requests/node_debug_request.nim b/library/storage_thread_requests/requests/node_debug_request.nim index 8bf3106c..a60b34bf 100644 --- a/library/storage_thread_requests/requests/node_debug_request.nim +++ b/library/storage_thread_requests/requests/node_debug_request.nim @@ -49,19 +49,8 @@ proc destroyShared(self: ptr NodeDebugRequest) = proc getDebug( storage: ptr StorageServer ): Future[Result[string, string]] {.async: (raises: []).} = - let node = storage[].node - let table = RestRoutingTable.init(node.discovery.protocol.routingTable) - - let json = %*{ - "id": $node.switch.peerInfo.peerId, - "addrs": node.switch.peerInfo.addrs.mapIt($it), - "spr": - if node.discovery.dhtRecord.isSome: node.discovery.dhtRecord.get.toURI else: "", - "announceAddresses": node.discovery.announceAddrs, - "table": table, - } - - return ok($json) + let nodeInfo = %DebugInfo.init(storage[].node) + return ok($nodeInfo) proc getPeer( storage: ptr StorageServer, peerId: cstring diff --git a/openapi.yaml b/openapi.yaml index 041127ee..490b989d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -103,9 +103,9 @@ components: required: - id - addrs - - repo - spr - announceAddresses + - libp2pPubKey - table - storage properties: @@ -115,15 +115,20 @@ components: type: array items: $ref: "#/components/schemas/MultiAddress" - repo: - type: string - description: Path of the data repository where all nodes data are stored spr: $ref: "#/components/schemas/SPR" + providerRecord: + $ref: "#/components/schemas/SPR" announceAddresses: type: array items: $ref: "#/components/schemas/MultiAddress" + libp2pPubKey: + type: string + description: Hex-encoded libp2p public key of the node + mixPubKey: + type: string + description: Hex-encoded mix public key (present only for nodes that support mix) table: $ref: "#/components/schemas/PeersTable" storage: diff --git a/storage/rest/api.nim b/storage/rest/api.nim index af13d71f..9b4e0ef1 100644 --- a/storage/rest/api.nim +++ b/storage/rest/api.nim @@ -557,7 +557,7 @@ proc initNodeApi(node: StorageNodeRef, conf: StorageConf, router: var RestRouter return RestApiResponse.error(Http500, "Unknown error dialling peer", headers = headers) -proc initDebugApi(node: StorageNodeRef, conf: StorageConf, router: var RestRouter) = +proc initDebugApi(node: StorageNodeRef, router: var RestRouter) = let allowedOrigin = router.allowedOrigin router.api(MethodGet, "/api/storage/v1/debug/info") do() -> RestApiResponse: @@ -566,27 +566,11 @@ proc initDebugApi(node: StorageNodeRef, conf: StorageConf, router: var RestRoute var headers = buildCorsHeaders("GET", allowedOrigin) try: - let table = RestRoutingTable.init(node.discovery.protocol.routingTable) - - let json = %*{ - "id": $node.switch.peerInfo.peerId, - "addrs": node.switch.peerInfo.addrs.mapIt($it), - "repo": $conf.dataDir, - "spr": - if node.discovery.dhtRecord.isSome: node.discovery.dhtRecord.get.toURI else: "", - "providerRecord": - if node.discovery.providerRecord.isSome: - node.discovery.providerRecord.get.toURI - else: - "", - "announceAddresses": node.discovery.announceAddrs, - "table": table, - "storage": {"version": $storageVersion, "revision": $storageRevision}, - } - # return pretty json for human readability return RestApiResponse.response( - json.pretty(), contentType = "application/json", headers = headers + DebugInfo.init(node).toJson(pretty = true), + contentType = "application/json", + headers = headers, ) except CatchableError as exc: trace "Excepting processing request", exc = exc.msg @@ -648,6 +632,6 @@ proc initRestApi*( initDataApi(node, repoStore, router) initNodeApi(node, conf, router) - initDebugApi(node, conf, router) + initDebugApi(node, router) return router diff --git a/storage/rest/json.nim b/storage/rest/json.nim index 6a65b7b3..e35aa688 100644 --- a/storage/rest/json.nim +++ b/storage/rest/json.nim @@ -1,7 +1,14 @@ +import std/importutils + import pkg/questionable import pkg/libp2p +import pkg/libp2p_mix +import pkg/libp2p_mix/curve25519 import pkg/codexdht/discv5/node as dn import pkg/codexdht/discv5/routing_table as rt +import pkg/stew/byteutils +import ../node +import ../conf import ../utils/json import ../manifest import ../units @@ -41,6 +48,27 @@ type quotaUsedBytes* {.serialize.}: NBytes quotaReservedBytes* {.serialize.}: NBytes + VersionInfo* = object + version* {.serialize.}: string + revision* {.serialize.}: string + + DebugInfo* = object # Peer's ID + id* {.serialize.}: PeerId + # peer addresses known by the libp2p switch + addrs* {.serialize.}: seq[MultiAddress] + # signed peer record (URI form) + spr* {.serialize.}: Option[SignedPeerRecord] + # provider record (the one we announce for content we provide) + providerRecord* {.serialize.}: Option[SignedPeerRecord] + # addresses contained in the provider record + announceAddresses* {.serialize.}: seq[MultiAddress] + # libp2p public key + libp2pPubKey* {.serialize.}: string + # mix public key (for nodes that support mix) + mixPubKey* {.serialize.}: Option[FieldElement] + table* {.serialize.}: RestRoutingTable + storage* {.serialize.}: VersionInfo + proc init*(_: type RestContentList, content: seq[RestContent]): RestContentList = RestContentList(content: content) @@ -74,3 +102,36 @@ proc init*(_: type RestNodeId, id: NodeId): RestNodeId = proc `%`*(obj: RestNodeId): JsonNode = % $obj.id + +proc init*(_: type DebugInfo, node: StorageNodeRef): DebugInfo = + let + peerInfo = node.switch.peerInfo + peerId = peerInfo.peerId + libp2pPubKeyBytes = peerInfo.publicKey.getBytes() + + # Cause there's no canonical way to get your own key from MixProtocol + # that I'm aware of. + privateAccess(MixProtocol) + + DebugInfo( + id: peerId, + addrs: peerInfo.addrs, + spr: node.discovery.dhtRecord, + providerRecord: node.discovery.providerRecord, + announceAddresses: node.discovery.announceAddrs, + table: RestRoutingTable.init(node.discovery.protocol.routingTable), + storage: VersionInfo(version: $storageVersion, revision: $storageRevision), + # Serialization has no error contract in nim-serde, so we need to + # handle this here. + libp2pPubKey: + if libp2pPubKeyBytes.isErr: + $libp2pPubKeyBytes.error + else: + byteutils.toHex(libp2pPubKeyBytes.get), + mixPubKey: + if node.discovery.mixProto.isNil(): + none(FieldElement) + else: + # It's a bug if we don't have the peer in the pool, so let it throw an exception. + some(node.discovery.mixProto.mixNodeInfo.mixPubKey), + ) diff --git a/storage/storage.nim b/storage/storage.nim index 49382baa..f794a2f5 100644 --- a/storage/storage.nim +++ b/storage/storage.nim @@ -103,6 +103,8 @@ proc start*(s: StorageServer) {.async.} = raise newException(StorageError, "Failed to load Mix relay pool: " & error.msg) mixProto = MixProtocol.new(mixNodeInfo, switch) + info "Starting node with Mix relay pool", count = relayPool.len + for info in relayPool.values: mixProto.nodePool.add(info) diff --git a/storage/utils/json.nim b/storage/utils/json.nim index 1ee7147c..3450713e 100644 --- a/storage/utils/json.nim +++ b/storage/utils/json.nim @@ -3,7 +3,10 @@ from pkg/libp2p import Cid, PeerId, SignedPeerRecord, MultiAddress, AddressInfo, init, `$` import pkg/contractabi import pkg/codexdht/discv5/node as dn +import pkg/codexdht/discv5/spr as spr +import pkg/libp2p_mix/curve25519 import pkg/serde/json +import pkg/stew/byteutils import pkg/questionable/results import ../errors @@ -19,8 +22,8 @@ func `%`*(cid: Cid): JsonNode = func `%`*(obj: PeerId): JsonNode = % $obj -func `%`*(obj: SignedPeerRecord): JsonNode = - % $obj +proc `%`*(obj: SignedPeerRecord): JsonNode = + %obj.toURI func `%`*(obj: dn.Address): JsonNode = % $obj @@ -30,3 +33,6 @@ func `%`*(obj: AddressInfo): JsonNode = func `%`*(obj: MultiAddress): JsonNode = % $obj + +func `%`*(obj: FieldElement): JsonNode = + %byteutils.toHex(fieldElementToBytes(obj))