8.5 KiB
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 <cstdio>
#include <string>
#include <vector>
#include "plugin.h"
int main()
{
printf("=== Tutorial 10: Peer Store Management ===\n\n");
setLogLevel(LogLevel::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<std::string>();
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::string>();
std::vector<std::string> remoteAddrs;
for (const auto& a : remoteInfo["addrs"]) {
remoteAddrs.push_back(a.get<std::string>());
}
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<std::string>().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<std::string>().c_str());
printf(" Public key: %s\n",
remoteStoreInfo.value["publicKey"].get<std::string>().c_str());
printf(" Addresses:\n");
for (const auto& a : remoteStoreInfo.value["addrs"]) {
printf(" %s\n", a.get<std::string>().c_str());
}
printf(" Protocols:\n");
for (const auto& p : remoteStoreInfo.value["protocols"]) {
printf(" %s\n", p.get<std::string>().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<std::string> manualAddrs = {
"/ip4/192.168.1.100/tcp/9000",
"/ip4/10.0.0.50/tcp/9001",
};
std::vector<std::string> 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<std::string> 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<std::string>().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<std::string>().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 IDspeerstoreGetPeerInfo()returns detailed peer metadatapeerstoreAddPeer()adds peers manually (with addresses + protocols)peerstoreSetPeerAddresses()update existing entriespeerstoreDeletePeer()removes peers- A populated peer store speeds up reconnection and reduces network overhead
Run tutorial
./build/tutorial/tutorial_10_peerstore
← Service Discovery | Circuit Relay – Connecting Through Firewalls →