diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -185,6 +185,9 @@ uint64_t getSlotCount() const { return slotCount; } uint64_t getFragmentation() const { return fragmentation; } + void getPeers(std::vector &vpeers) const; + void getNodes(std::vector &vnodes) const; + private: PeerSet::iterator fetchOrCreatePeer(const Proof &proof); bool addNodeToPeer(const PeerSet::iterator &it); diff --git a/src/avalanche/peermanager.cpp b/src/avalanche/peermanager.cpp --- a/src/avalanche/peermanager.cpp +++ b/src/avalanche/peermanager.cpp @@ -438,4 +438,16 @@ return NO_PEER; } +void PeerManager::getPeers(std::vector &vpeers) const { + for (auto &it : peers.get<0>()) { + vpeers.emplace_back(it); + } +} + +void PeerManager::getNodes(std::vector &vnodes) const { + for (auto &it : nodes.get<0>()) { + vnodes.emplace_back(it); + } +} + } // namespace avalanche diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -6,6 +6,7 @@ #define BITCOIN_AVALANCHE_PROCESSOR_H #include +#include #include #include #include @@ -280,6 +281,9 @@ CPubKey getSessionPubKey() const; bool sendHello(CNode *pfrom) const; + void getPeers(std::vector &vpeers) const; + void getNodes(std::vector &vnodes) const; + bool startEventLoop(CScheduler &scheduler); bool stopEventLoop(); diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -587,4 +587,14 @@ } while (nodeid != NO_NODE); } +void Processor::getPeers(std::vector &vpeers) const { + LOCK(cs_peerManager); + peerManager->getPeers(vpeers); +} + +void Processor::getNodes(std::vector &vnodes) const { + LOCK(cs_peerManager); + peerManager->getNodes(vnodes); +} + } // namespace avalanche diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -3,10 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include #include +#include #include #include #include @@ -203,6 +205,118 @@ return HexStr(ss); } +static UniValue getavalanchepeerinfo(const Config &config, + const JSONRPCRequest &request) { + RPCHelpMan{ + "getavlanchepeerinfo", + "Returns data about each connected avalanche peer as a json array of " + "objects.\n", + {}, + RPCResult{ + RPCResult::Type::ARR, + "", + "", + {{ + RPCResult::Type::OBJ, + "", + "", + {{ + {RPCResult::Type::NUM, "peerid", "The peer id"}, + {RPCResult::Type::STR_HEX, "proof", + "The avalanche proof used by this peer"}, + {RPCResult::Type::NUM, "sequence", "The proof's sequence"}, + {RPCResult::Type::NUM_TIME, "expiration", + "The proof's expiration timestamp"}, + {RPCResult::Type::STR_HEX, "master", + "The proof's master public key"}, + { + RPCResult::Type::ARR, + "stakes", + "", + {{ + RPCResult::Type::OBJ, + "", + "", + {{ + {RPCResult::Type::STR_HEX, "txid", ""}, + {RPCResult::Type::NUM, "vout", ""}, + {RPCResult::Type::STR_AMOUNT, "amount", + "The amount in this UTXO"}, + {RPCResult::Type::NUM, "height", + "The height at which this UTXO was mined"}, + {RPCResult::Type::BOOL, "iscoinbase", + "Indicate wether the UTXO is a coinbase"}, + {RPCResult::Type::STR_HEX, "pubkey", ""}, + }}, + }}, + }, + {RPCResult::Type::ARR, + "nodes", + "", + { + {RPCResult::Type::NUM, "nodeid", + "Node id, as returned by getpeerinfo"}, + }}, + }}, + }}, + }, + RPCExamples{HelpExampleCli("getavalanchepeerinfo", "") + + HelpExampleRpc("getavalanchepeerinfo", "")}, + } + .Check(request); + + if (!g_avalanche) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Avalanche is not initialized"); + } + + std::vector vpeers; + g_avalanche->getPeers(vpeers); + + std::vector vnodes; + g_avalanche->getNodes(vnodes); + + UniValue ret(UniValue::VARR); + + for (avalanche::Peer &peer : vpeers) { + UniValue obj(UniValue::VOBJ); + + CDataStream serproof(SER_NETWORK, PROTOCOL_VERSION); + serproof << peer.proof; + + obj.pushKV("peerid", uint64_t(peer.peerid)); + obj.pushKV("proof", HexStr(serproof)); + obj.pushKV("sequence", peer.proof.getSequence()); + obj.pushKV("expiration", peer.proof.getExpirationTime()); + obj.pushKV("master", HexStr(peer.proof.getMaster())); + + UniValue stakes(UniValue::VARR); + for (const auto &s : peer.proof.getStakes()) { + UniValue stake(UniValue::VOBJ); + stake.pushKV("txid", s.getStake().getUTXO().GetTxId().GetHex()); + stake.pushKV("vout", uint64_t(s.getStake().getUTXO().GetN())); + stake.pushKV("amount", ValueFromAmount(s.getStake().getAmount())); + stake.pushKV("height", uint64_t(s.getStake().getHeight())); + stake.pushKV("iscoinbase", s.getStake().isCoinbase()); + stake.pushKV("pubkey", HexStr(s.getStake().getPubkey())); + stakes.push_back(stake); + } + obj.pushKV("stakes", stakes); + + UniValue nodes(UniValue::VARR); + for (const auto &n : vnodes) { + if (n.peerid == peer.peerid) { + nodes.push_back(n.nodeid); + } + } + obj.pushKV("nodes", nodes); + obj.pushKV("nodecount", uint64_t(peer.node_count)); + + ret.push_back(obj); + } + + return ret; +} + void RegisterAvalancheRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = { @@ -211,6 +325,7 @@ { "avalanche", "getavalanchekey", getavalanchekey, {}}, { "avalanche", "addavalanchenode", addavalanchenode, {"nodeid"}}, { "avalanche", "buildavalancheproof", buildavalancheproof, {"sequence", "expiration", "master", "stakes"}}, + { "avalanche", "getavalanchepeerinfo", getavalanchepeerinfo, {}}, }; // clang-format on