/// # Tutorial 10: Peer Store Management /// /// The **peer store** is libp2p's database of known peers. It maps peer IDs /// to their known addresses, protocols, public keys, and metadata. /// /// A well-maintained peer store helps: /// - Reconnect to known peers without re-discovery /// - Track protocol capabilities of known peers /// - Maintain a persistent view of the network /// /// In this tutorial we'll: /// - List known peers /// - Get detailed peer info /// - Manually add peers to the store /// - Update addresses and protocols /// - Delete peers from the store /// /// ----------- #include #include #include #include "plugin.h" int main() { printf("=== Tutorial 10: Peer Store Management ===\n\n"); setLogLevel("fatal"); /// ## Step 1: Create two nodes /// /// The peer store is automatically available on any started node. /// We use one main node and one remote peer so the main node's peerstore /// contains real peer data after a connection is established. Libp2pModuleOptions opts; opts.addrs = {"/ip4/127.0.0.1/tcp/9790"}; Libp2pModuleOptions remoteOpts; remoteOpts.addrs = {"/ip4/127.0.0.1/tcp/9791"}; Libp2pModuleImpl node(opts); Libp2pModuleImpl remote(remoteOpts); StdLogosResult startRes = node.start(); if (!startRes.success) { fprintf(stderr, "Node failed to start: %s\n", startRes.error.c_str()); return 1; } StdLogosResult remoteStartRes = remote.start(); if (!remoteStartRes.success) { fprintf(stderr, "Remote node failed to start: %s\n", remoteStartRes.error.c_str()); return 1; } StdLogosResult infoRes = node.peerInfo(); if (!infoRes.success) { fprintf(stderr, "Failed to get node info: %s\n", infoRes.error.c_str()); return 1; } auto info = infoRes.value; std::string nodePeerId = info["peerId"].get(); printf("Node started, peer ID: %s\n", nodePeerId.c_str()); StdLogosResult remoteInfoRes = remote.peerInfo(); if (!remoteInfoRes.success) { fprintf(stderr, "Failed to get remote node info: %s\n", remoteInfoRes.error.c_str()); return 1; } auto remoteInfo = remoteInfoRes.value; std::string remotePeerId = remoteInfo["peerId"].get(); std::vector remoteAddrs; for (const auto& a : remoteInfo["addrs"]) { remoteAddrs.push_back(a.get()); } printf("Remote node started, peer ID: %s\n", remotePeerId.c_str()); /// ## Step 2: List known peers /// /// Initially, the peer store may be empty because no remote peers have /// been discovered or added yet. /// `peerstoreGetPeers()` returns a JSON array of peer IDs. printf("\nListing known peers...\n"); StdLogosResult peersRes = node.peerstoreGetPeers(); if (!peersRes.success) { fprintf(stderr, "Failed to list peers: %s\n", peersRes.error.c_str()); return 1; } printf("Found %zu known peer(s):\n", peersRes.value.size()); for (const auto& p : peersRes.value) { printf(" %s\n", p.get().c_str()); } /// ## Step 3: Connect a remote peer and get detailed peer info /// /// `peerstoreGetPeerInfo()` returns a rich JSON object with addresses, /// protocols, and the public key. printf("\nConnecting to remote peer...\n"); StdLogosResult connectRes = node.connectPeer(remotePeerId, remoteAddrs, 5000); if (!connectRes.success) { fprintf(stderr, "Failed to connect to remote peer: %s\n", connectRes.error.c_str()); return 1; } printf("Connected to remote peer\n"); printf("\nGetting remote peer info from the store:\n"); StdLogosResult remoteStoreInfo = node.peerstoreGetPeerInfo(remotePeerId); if (!remoteStoreInfo.success) { fprintf(stderr, "Failed to get remote peer info: %s\n", remoteStoreInfo.error.c_str()); return 1; } printf(" Peer ID: %s\n", remoteStoreInfo.value["peerId"].get().c_str()); printf(" Public key: %s\n", remoteStoreInfo.value["publicKey"].get().c_str()); printf(" Addresses:\n"); for (const auto& a : remoteStoreInfo.value["addrs"]) { printf(" %s\n", a.get().c_str()); } printf(" Protocols:\n"); for (const auto& p : remoteStoreInfo.value["protocols"]) { printf(" %s\n", p.get().c_str()); } /// ## Step 4: Manually add a peer to the store /// /// You can manually register peers in the store. This is useful for /// peers discovered through out-of-band methods (e.g. a config file, /// QR code, or DNS). printf("\nManually adding a peer to the store...\n"); std::string manualPeerId = "16Uiu2HAkzM1nzU99VxRqzF9CjZAqjF3MZHQ2oRENL7SNgP1d8pRy"; std::vector manualAddrs = { "/ip4/192.168.1.100/tcp/9000", "/ip4/10.0.0.50/tcp/9001", }; std::vector manualProtos = { "/ipfs/ping/1.0.0", "/examples/echo/1.0.0", }; StdLogosResult addPeerRes = node.peerstoreAddPeer(manualPeerId, manualAddrs, manualProtos); if (!addPeerRes.success) { fprintf(stderr, "Failed to add peer: %s\n", addPeerRes.error.c_str()); return 1; } printf("Peer added: %s\n", manualPeerId.c_str()); /// Verify it was added: printf("\nVerifying: listing peers again...\n"); StdLogosResult peersAfter = node.peerstoreGetPeers(); if (!peersAfter.success) { fprintf(stderr, "Failed to list peers after add: %s\n", peersAfter.error.c_str()); return 1; } printf("Now have %zu known peer(s)\n", peersAfter.value.size()); if (peersAfter.value.size() != 2) { fprintf(stderr, "Expected to have 2 peers in the peer store after manually adding one"); return 1; } /// ## Step 5: Update peer info /// /// `peerstoreSetPeerAddresses()` to update a peer's stored data. printf("\nUpdating peer addresses...\n"); std::vector updatedAddrs = { "/ip4/192.168.1.100/tcp/9002", "/dns4/peer.example.com/tcp/9000", }; StdLogosResult updateAddrsRes = node.peerstoreSetPeerAddresses(manualPeerId, updatedAddrs); if (!updateAddrsRes.success) { fprintf(stderr, "Failed to update addresses: %s\n", updateAddrsRes.error.c_str()); return 1; } printf("Addresses updated\n"); // Check the updated info: StdLogosResult updatedInfo = node.peerstoreGetPeerInfo(manualPeerId); if (!updatedInfo.success) { fprintf(stderr, "Failed to get updated peer info: %s\n", updatedInfo.error.c_str()); return 1; } printf("Updated addresses:\n"); for (const auto& a : updatedInfo.value["addrs"]) { printf(" %s\n", a.get().c_str()); } /// ## Step 6: Delete a peer from the store /// /// `peerstoreDeletePeer()` removes a peer and all its metadata. printf("\nDeleting peer from store...\n"); StdLogosResult deletePeerRes = node.peerstoreDeletePeer(manualPeerId); if (!deletePeerRes.success) { fprintf(stderr, "Failed to delete peer: %s\n", deletePeerRes.error.c_str()); return 1; } printf("Peer deleted\n"); /// Verify deletion: StdLogosResult peersFinal = node.peerstoreGetPeers(); if (!peersFinal.success) { fprintf(stderr, "Failed to list peers after delete: %s\n", peersFinal.error.c_str()); return 1; } printf("Now have %zu known peer(s)\n", peersFinal.value.size()); for (const auto& p : peersFinal.value) { printf(" %s\n", p.get().c_str()); } /// ## Step 7: Clean up node.stop(); remote.stop(); printf("\n=== Tutorial 10 Complete ===\n"); return 0; } /// ## Key Takeaways /// /// - The peer store is a built-in database of known peers /// - `peerstoreGetPeers()` lists all known peer IDs /// - `peerstoreGetPeerInfo()` returns detailed peer metadata /// - `peerstoreAddPeer()` adds peers manually (with addresses + protocols) /// - `peerstoreSetPeerAddresses()` update existing entries /// - `peerstoreDeletePeer()` removes peers /// - A populated peer store speeds up reconnection and reduces /// network overhead /// ## Run tutorial /// /// ```bash /// ./build/tutorial/tutorial_10_peerstore /// ```