Subscribe GetNodes
Since we are connected successfully to the Watchtower service, we can stream down available nodes from the HCS. See the protobuf definition.
Payload Static Reference
export interface FileID { 'shardNum'?: (number | string | Long); 'realmNum'?: (number | string | Long); 'fileNum'?: (number | string | Long);}export interface GetNodesQuery { 'fileId'?: (FileID | null); 'limit'?: (number);}export interface SubscribeGetNodesPayload { subscribe: string; body: GetNodesQuery;}const subscribeTopicRoute = `/com.hedera.mirror.api.proto.NetworkService/getNodes`;const payloadBody: GetNodesQuery = { fileId: { fileNum: '101' // The ID of the address book file on the network. Can be either 101 or 102. }, limit: 100 // The maximum number of node addresses to receive before stopping. If not set or set to zero it will return all node addresses in the database.}const payload: SubscribeGetNodesPayload = { subscribe: subscribeTopicRoute, body: payloadBody}
Payload breakdown
To subscribe this stream, our payload should match reference protobuf definition.
View Definition
/** * Request object to query an address book for its list of nodes */message AddressBookQuery { /** * The ID of the address book file on the network. Can be either 0.0.101 or 0.0.102. */ .proto.FileID file_id = 1; /** * The maximum number of node addresses to receive before stopping. If not set or set to zero it will return all node addresses in the database. */ int32 limit = 2;}
By looking into the defination, we can see that we need to provide a FileID
and limit
(optional) to get the latest messages.
For forming the payload, we should also be aware of basic_types.proto
used in the defination.
View Definition
//** * The ID for a file */message FileID { /** * The shard number (nonnegative) */ int64 shardNum = 1; /** * The realm number (nonnegative) */ int64 realmNum = 2; /** * A nonnegative File number unique within its realm */ int64 fileNum = 3;}
All we need is to build a payload as below:
- React.js
- Node.js
- HTML/Javascript
import React, { useEffect, useState } from "react";import io from 'socket.io-client';function StreamingInit () { // Make sure you match the url for testnet/mainnet const demoFileId = `102`; // The ID of the address book file on the network. Can be either 101 or 102. const watchtowerURL = `wss://<WATCHTOWER_URL>?api_key=<ARKHIA_API_KEY>`; const socket = io(watchtowerURL); const [socketStatus, setSocketStatus] = useState(``); const [socketConnect, setSocketConnect] = useState(false); // Available services in page 2 of these docs const subscribeGetNodesRoute = `/com.hedera.mirror.api.proto.NetworkService/getNodes`; const getSubscriptionPayload = (file_id: string, limit: string) => { const subscriptionPayload = { subscribe: subscribeGetNodesRoute, body: { fileId: { fileNum: file_id // The ID of the address book file on the network. Can be either 101 or 102. }, limit // The maximum number of node addresses to receive before stopping. If not set or set to zero it will return all node addresses in the database. } }; }; const subscribeGetNodes = () => { const requestPayload = getSubscriptionPayload(demoFileId , `100`); // get latest 100 socket.emit(`subscribe`, requestPayload, (msg: any) => { console.log(`GetNodes streaming down...`); socket.on(msg.listeners.data, function (message: any) { console.log(message) }); socket.on(msg.listeners.error, function (message: any) { setSocketError(message); console.log(`Error`); console.log(message) }); }); }; useEffect(() => { socket.on(`connect`, () => { setSocketConnect(true); socket.on(`status`, (msg) => { console.log(`status`, msg); setSocketStatus(`Watchtower is successfully connected`); }); socket.emit(`list-services`, (services: any) => { console.log(`Listing available services`, services); }); }); socket.on(`disconnect`, (msg: any) => { setSocketConnect(false); setSocketStatus(`Watchtower is disconnected.`) console.log(`Disconnected.`) }); socket.on(`error`, (message: any) => { setSocketConnect(false); console.error(message); setSocketStatus(message) }) }, [socket]); return ( <> <div> { socketStatus && ( <> <h6>Watchtower status</h6> <div>{socketStatus}</div> </> ) } { socketConnect && ( <> <button onClick={subscribeGetNodes}> </> ) } </div> </> );}export default StreamingInit;
const io = require('socket.io-client');// Make sure you match the url for testnet/mainnet const demoFileId = `102`; // The ID of the address book file on the network. Can be either 101 or 102.const watchtowerURL = `wss://<WATCHTOWER_URL>?api_key=<ARKHIA_API_KEY>`;const socket = io(watchtowerURL);// Available services in page 2 of these docsconst subscribeGetNodesRoute = `/com.hedera.mirror.api.proto.NetworkService/getNodes`;const getSubscriptionPayload = (file_id, limit) => { return { subscribe: subscribeGetNodesRoute, body: { fileId: { fileNum: file_id // The ID of the address book file on the network. Can be either 101 or 102. }, limit // The maximum number of node addresses to receive before stopping. If not set or set to zero it will return all node addresses in the database. } };};const subscribeGetNodes = () => { const requestPayload = getSubscriptionPayload(demoFileId, `100`); // get latest 100 socket.emit(`subscribe`, requestPayload, (msg) => { console.log(`GetNodes streaming down...`); socket.on(msg.listeners.data, function (message) { console.log(message) }); socket.on(msg.listeners.error, function (message) { console.log(`Error`); console.log(message) }); });}socket.on(`connect`, () => { socket.on(`status`, (msg) => { console.log(`Watchtower is successfully connected`); console.log(`status`, msg); }); subscribeGetNodes();});socket.on(`disconnect`, (msg) => { console.log(`Watchtower is disconnected.`) console.log(`Disconnected.`)});socket.on(`error`, (message) => { console.error(message); console.log(message)})
<!DOCTYPE html><html><head> <style> body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } #messages { list-style-type: none; margin: 0; padding: 0; } #messages>li { padding: 0.5rem 1rem; background: #868484; } #messages>li:nth-child(odd) { background: #efefef; } </style></head><body> <ul id="messages"></ul></body><script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.min.js"></script><script> // Make sure you match the url for testnet/mainnet const demoFileId = `102`; // The ID of the address book file on the network. Can be either 101 or 102. const watchtowerURL = `wss://<WATCHTOWER_URL>?api_key=<ARKHIA_API_KEY>`; const socket = io(watchtowerURL); // Available services in page 2 of these docs const subscribeGetNodesRoute = `/com.hedera.mirror.api.proto.NetworkService/getNodes`; socket.on('connect', function () { socket.on('status', (msg) => { console.log('status', msg); addMessage('status', msg); }); socket.emit('list-services', function (services) { console.log('services', services); // addMessage('services', services); }) subscribeGetNodes(); }); const subscribeGetNodes = () => { const requestPayload = getSubscriptionPayload(demoFileId, `100`); // get latest 100 socket.emit(`subscribe`, requestPayload, (msg) => { addMessage(`Message from: ${demoFileId}`, `GetNodes streaming down...`); socket.on(msg.listeners.data, function (message) { addMessage(`Message from: ${demoFileId}`, message); }); socket.on(msg.listeners.error, function (message) { console.log(`Error`); addMessage(`Error from: ${demoFileId}`, { error: message }); }); }); } const getSubscriptionPayload = (file_id, limit) => { return { subscribe: subscribeGetNodesRoute, body: { fileId: { fileNum: file_id // The ID of the address book file on the network. Can be either 101 or 102. }, limit // The maximum number of node addresses to receive before stopping. If not set or set to zero it will return all node addresses in the database. } }; }; function addMessage(event, message) { var item = document.createElement('li'); item.innerHTML = `` messages.appendChild(item); window.scrollTo(0, document.body.scrollHeight); }</script></html>