From b4763b4b66ee952cc367ad732e2bad16323c4389 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Thu, 24 Oct 2024 09:34:46 +0800 Subject: [PATCH] a basic telemetry rpc (#186) * a basic telemetry rpc * fix test * add rpcs * fix * fix * fix * fix --- Networking/Sources/Networking/Peer.swift | 5 +++ .../Node/NetworkingProtocol/Network.swift | 4 +++ .../NetworkingProtocol/NetworkManager.swift | 4 +++ Node/Sources/Node/Node.swift | 4 ++- Node/Sources/Node/NodeDataSource.swift | 35 +++++++++++++++++++ Node/Sources/Node/ValidatorNode.swift | 4 +-- .../DataSource/Blockchain+DataSource.swift | 16 --------- RPC/Sources/RPC/DataSource/DataSource.swift | 3 +- RPC/Sources/RPC/Handlers/ChainHandler.swift | 28 +++++++++++++-- RPC/Sources/RPC/Handlers/SystemHandler.swift | 5 +++ .../RPC/Handlers/TelemetryHandler.swift | 26 ++++++++++++++ RPC/Sources/RPC/Server.swift | 1 + RPC/Tests/RPCTests/MockDataSource.swift | 25 ------------- 13 files changed, 113 insertions(+), 47 deletions(-) create mode 100644 Node/Sources/Node/NodeDataSource.swift delete mode 100644 RPC/Sources/RPC/DataSource/Blockchain+DataSource.swift create mode 100644 RPC/Sources/RPC/Handlers/TelemetryHandler.swift delete mode 100644 RPC/Tests/RPCTests/MockDataSource.swift diff --git a/Networking/Sources/Networking/Peer.swift b/Networking/Sources/Networking/Peer.swift index 61513ad1..15a50c3b 100644 --- a/Networking/Sources/Networking/Peer.swift +++ b/Networking/Sources/Networking/Peer.swift @@ -157,6 +157,11 @@ public final class Peer: Sendable { } } } + + // there should be only one connection per peer + public func getPeersCount() -> Int { + impl.connections.value.byId.values.count + } } final class PeerImpl: Sendable { diff --git a/Node/Sources/Node/NetworkingProtocol/Network.swift b/Node/Sources/Node/NetworkingProtocol/Network.swift index 6a94abb2..5a0ee275 100644 --- a/Node/Sources/Node/NetworkingProtocol/Network.swift +++ b/Node/Sources/Node/NetworkingProtocol/Network.swift @@ -77,6 +77,10 @@ public final class Network: Sendable { public func listenAddress() throws -> NetAddr { try peer.listenAddress() } + + public func getPeersCount() -> Int { + peer.getPeersCount() + } } struct HandlerDef: StreamHandler { diff --git a/Node/Sources/Node/NetworkingProtocol/NetworkManager.swift b/Node/Sources/Node/NetworkingProtocol/NetworkManager.swift index e43cbc6c..9e515786 100644 --- a/Node/Sources/Node/NetworkingProtocol/NetworkManager.swift +++ b/Node/Sources/Node/NetworkingProtocol/NetworkManager.swift @@ -104,6 +104,10 @@ public final class NetworkManager: Sendable { ) } } + + public func getPeersCount() -> Int { + network.getPeersCount() + } } struct HandlerImpl: NetworkProtocolHandler { diff --git a/Node/Sources/Node/Node.swift b/Node/Sources/Node/Node.swift index c62904b9..b6b17107 100644 --- a/Node/Sources/Node/Node.swift +++ b/Node/Sources/Node/Node.swift @@ -55,8 +55,10 @@ public class Node { devPeers: Set(config.peers) ) + let nodeDataSource = NodeDataSource(blockchain: blockchain, chainDataProvider: dataProvider, networkManager: network) + rpcServer = try config.rpc.map { - try Server(config: $0, source: blockchain) + try Server(config: $0, source: nodeDataSource) } } diff --git a/Node/Sources/Node/NodeDataSource.swift b/Node/Sources/Node/NodeDataSource.swift new file mode 100644 index 00000000..3fdd3a93 --- /dev/null +++ b/Node/Sources/Node/NodeDataSource.swift @@ -0,0 +1,35 @@ +import Blockchain +import RPC +import Utils + +public final class NodeDataSource: DataSource { + public let blockchain: Blockchain + public let chainDataProvider: BlockchainDataProvider + public let networkManager: NetworkManager + + public init(blockchain: Blockchain, chainDataProvider: BlockchainDataProvider, networkManager: NetworkManager) { + self.blockchain = blockchain + self.chainDataProvider = chainDataProvider + self.networkManager = networkManager + } + + public func importBlock(_ block: BlockRef) async throws { + try await blockchain.importBlock(block) + } + + public func getBestBlock() async throws -> BlockRef { + try await chainDataProvider.getBlock(hash: chainDataProvider.bestHead.hash) + } + + public func getBlock(hash: Data32) async throws -> BlockRef? { + try await chainDataProvider.getBlock(hash: hash) + } + + public func getState(hash: Data32) async throws -> StateRef? { + try await chainDataProvider.getState(hash: hash) + } + + public func getPeersCount() async throws -> Int { + networkManager.getPeersCount() + } +} diff --git a/Node/Sources/Node/ValidatorNode.swift b/Node/Sources/Node/ValidatorNode.swift index b0a0fb6b..752297c6 100644 --- a/Node/Sources/Node/ValidatorNode.swift +++ b/Node/Sources/Node/ValidatorNode.swift @@ -20,8 +20,8 @@ public class ValidatorNode: Node { dataProvider: dataProvider ) - let genesisState = try await blockchain.getState(hash: blockchain.dataProvider.genesisBlockHash) + let genesisState = try await dataProvider.getState(hash: blockchain.dataProvider.genesisBlockHash) - await validator.on(genesis: genesisState!) + await validator.on(genesis: genesisState) } } diff --git a/RPC/Sources/RPC/DataSource/Blockchain+DataSource.swift b/RPC/Sources/RPC/DataSource/Blockchain+DataSource.swift deleted file mode 100644 index 8326b89e..00000000 --- a/RPC/Sources/RPC/DataSource/Blockchain+DataSource.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Blockchain -import Utils - -extension Blockchain: DataSource { - public func getBestBlock() async throws -> BlockRef { - try await dataProvider.getBlock(hash: dataProvider.bestHead.hash) - } - - public func getBlock(hash: Data32) async throws -> BlockRef? { - try await dataProvider.getBlock(hash: hash) - } - - public func getState(hash: Data32) async throws -> StateRef? { - try await dataProvider.getState(hash: hash) - } -} diff --git a/RPC/Sources/RPC/DataSource/DataSource.swift b/RPC/Sources/RPC/DataSource/DataSource.swift index 00fffdce..d3de016b 100644 --- a/RPC/Sources/RPC/DataSource/DataSource.swift +++ b/RPC/Sources/RPC/DataSource/DataSource.swift @@ -4,6 +4,7 @@ import Utils public protocol DataSource: Sendable { func getBestBlock() async throws -> BlockRef func getBlock(hash: Data32) async throws -> BlockRef? - func importBlock(_: BlockRef) async throws + func getState(hash: Data32) async throws -> StateRef? + func getPeersCount() async throws -> Int } diff --git a/RPC/Sources/RPC/Handlers/ChainHandler.swift b/RPC/Sources/RPC/Handlers/ChainHandler.swift index 08c7b4e9..0e33a9fa 100644 --- a/RPC/Sources/RPC/Handlers/ChainHandler.swift +++ b/RPC/Sources/RPC/Handlers/ChainHandler.swift @@ -10,6 +10,7 @@ struct ChainHandler { return [ "chain_getBlock": handler.getBlock, + "chain_getState": handler.getState, ] } @@ -20,10 +21,33 @@ struct ChainHandler { throw JSONError(code: -32602, message: "Invalid block hash") } let block = try await source.getBlock(hash: data32) - return block.map { ["hash": $0.hash.description, "parentHash": $0.header.parentHash.description] } + return block } else { let block = try await source.getBestBlock() - return ["hash": block.hash.description, "parentHash": block.header.parentHash.description] + return block + } + } + + func getState(request: JSONRequest) async throws -> any Encodable { + let hash = request.params?["hash"] as? String + if let hash { + guard let data = Data(fromHexString: hash), let data32 = Data32(data) else { + throw JSONError(code: -32602, message: "Invalid block hash") + } + let state = try await source.getState(hash: data32) + // return state root for now + return [ + "stateRoot": state?.stateRoot.description, + "blockHash": hash.description, + ] + } else { + // return best block state by default + let block = try await source.getBestBlock() + let state = try await source.getState(hash: block.hash) + return [ + "stateRoot": state?.stateRoot.description, + "blockHash": block.hash.description, + ] } } } diff --git a/RPC/Sources/RPC/Handlers/SystemHandler.swift b/RPC/Sources/RPC/Handlers/SystemHandler.swift index 6673c06e..e81f658b 100644 --- a/RPC/Sources/RPC/Handlers/SystemHandler.swift +++ b/RPC/Sources/RPC/Handlers/SystemHandler.swift @@ -4,10 +4,15 @@ struct SystemHandler { return [ "system_health": handler.health, + "system_name": handler.name, ] } func health(request _: JSONRequest) async throws -> any Encodable { true } + + func name(request _: JSONRequest) async throws -> any Encodable { + "Boka" + } } diff --git a/RPC/Sources/RPC/Handlers/TelemetryHandler.swift b/RPC/Sources/RPC/Handlers/TelemetryHandler.swift new file mode 100644 index 00000000..d0c21f82 --- /dev/null +++ b/RPC/Sources/RPC/Handlers/TelemetryHandler.swift @@ -0,0 +1,26 @@ +import Blockchain +import Foundation +import Utils + +struct TelemetryHandler { + let source: DataSource + + static func getHandlers(source: DataSource) -> [String: JSONRPCHandler] { + let handler = TelemetryHandler(source: source) + + return [ + "telemetry_getUpdate": handler.getUpdate, + ] + } + + func getUpdate(request _: JSONRequest) async throws -> any Encodable { + let block = try await source.getBestBlock() + let peerCount = try await source.getPeersCount() + return [ + "name": "Boka", + "chainHead": block.header.timeslot.description, + "blockHash": block.hash.description, + "peerCount": peerCount.description, + ] + } +} diff --git a/RPC/Sources/RPC/Server.swift b/RPC/Sources/RPC/Server.swift index 5540be93..f5692a7b 100644 --- a/RPC/Sources/RPC/Server.swift +++ b/RPC/Sources/RPC/Server.swift @@ -29,6 +29,7 @@ public class Server { var handlers: [String: JSONRPCHandler] = SystemHandler.getHandlers() handlers.merge(ChainHandler.getHandlers(source: source)) { _, new in new } + handlers.merge(TelemetryHandler.getHandlers(source: source)) { _, new in new } // Register routes let rpcController = JSONRPCController(handlers: handlers) diff --git a/RPC/Tests/RPCTests/MockDataSource.swift b/RPC/Tests/RPCTests/MockDataSource.swift deleted file mode 100644 index b7e21356..00000000 --- a/RPC/Tests/RPCTests/MockDataSource.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Blockchain -import RPC -import Utils - -actor MockDataSource: DataSource, @unchecked Sendable { - var bestBlock: BlockRef - var blocks: [Data32: BlockRef] = [:] - var importedBlocks: [BlockRef] = [] - - init(bestBlock: BlockRef) { - self.bestBlock = bestBlock - } - - func getBestBlock() async throws -> BlockRef { - bestBlock - } - - func getBlock(hash: Data32) async throws -> BlockRef? { - blocks[hash] - } - - func importBlock(_: BlockRef) async throws { - importedBlocks.append(bestBlock) - } -}