6.3 KiB
Tutorial 6: Kademlia Provider Records
In the previous tutorial, we stored and retrieved raw key-value pairs in the DHT. But what if you want to advertise that your node has a piece of content, rather than storing the content itself?
This is where provider records come in. Instead of putting a value directly, you announce that your node provides a given Content ID (CID), and other peers can discover who provides that content.
Provider Records vs Key-Value Store
| Feature | Key-Value Store | Provider Records |
|---|---|---|
| What's stored | The value itself | The list of provider peer IDs |
| Use case | Small configs, peer info | File sharing, content discovery |
| Data size | Unlimited (but impractical for large data) | Metadata only |
| Finding | kadGetValue(key) |
kadGetProviders(cid) |
Content IDs (CIDs)
CIDs are self-describing content hashes used in IPFS and IPLD. The
toCid() function converts a string key into a CID that can be used
with the provider API.
#include <cstdio>
#include <string>
#include <vector>
#include "plugin.h"
int main()
{
printf("=== Tutorial 6: Kademlia Provider Records ===\n\n");
setLogLevel("fatal");
Step 1: Create two peers
Libp2pModuleOptions optsA, optsB;
optsA.addrs = {"/ip4/127.0.0.1/tcp/9490"};
optsA.mountKad = true;
Libp2pModuleImpl nodeA(optsA);
StdLogosResult startARes = nodeA.start();
if (!startARes.success) {
fprintf(stderr, "Node A failed: %s\n", startARes.error.c_str());
return 1;
}
StdLogosResult infoARes = nodeA.peerInfo();
if (!infoARes.success) {
fprintf(stderr, "Failed to get Node A info: %s\n",
infoARes.error.c_str());
return 1;
}
auto infoA = infoARes.value;
std::string peerIdA = infoA["peerId"].get<std::string>();
std::vector<std::string> addrsA;
for (const auto& a : infoA["addrs"])
addrsA.push_back(a.get<std::string>());
// Bootstrap and connect Node B
optsB.addrs = {"/ip4/127.0.0.1/tcp/9491"};
optsB.mountKad = true;
optsB.bootstrapNodes = {{peerIdA, addrsA}};
Libp2pModuleImpl nodeB(optsB);
StdLogosResult startBRes = nodeB.start();
if (!startBRes.success) {
fprintf(stderr, "Node B failed: %s\n", startBRes.error.c_str());
return 1;
}
StdLogosResult connectRes = nodeB.connectPeer(peerIdA, addrsA, 5000);
if (!connectRes.success) {
fprintf(stderr, "Failed to connect: %s\n",
connectRes.error.c_str());
return 1;
}
printf("Nodes connected\n");
Step 2: Convert a content key to a CID
The toCid() function takes a string and produces a CID (Content ID)
that can be used with Kademlia's provider API.
std::string contentKey = "my-awesome-file.txt";
printf("Converting \"%s\" to CID...\n", contentKey.c_str());
StdLogosResult cidRes = nodeA.toCid(contentKey);
if (!cidRes.success) {
fprintf(stderr, "Failed to create CID: %s\n",
cidRes.error.c_str());
return 1;
}
std::string cid = cidRes.value.get<std::string>();
printf("CID: %s\n", cid.c_str());
Step 3: Node A starts providing the CID
This advertises to the DHT that Node A has this content.
printf("\nNode A starting to provide CID...\n");
StdLogosResult startProvidingRes = nodeA.kadStartProviding(cid);
if (!startProvidingRes.success) {
fprintf(stderr, "kadStartProviding failed: %s\n",
startProvidingRes.error.c_str());
return 1;
}
printf("Node A is now a provider for: %s\n", cid.c_str());
Step 4: Node B discovers providers
Node B queries the DHT to find who provides this CID.
printf("\nNode B looking up providers...\n");
StdLogosResult provRes = nodeB.kadGetProviders(cid);
if (!provRes.success) {
fprintf(stderr, "kadGetProviders failed: %s\n",
provRes.error.c_str());
return 1;
}
auto providers = provRes.value;
printf("Node B found %zu provider(s):\n", providers.size());
bool foundNodeA = false;
for (const auto& p : providers) {
std::string providerId = p["peerId"].get<std::string>();
if (providerId == peerIdA) {
foundNodeA = true;
}
printf(" Peer: %s\n", providerId.c_str());
for (const auto& addr : p["addrs"]) {
printf(" Address: %s\n",
addr.get<std::string>().c_str());
}
}
if (!foundNodeA) {
fprintf(stderr, "Node A was not found as a provider for %s\n",
cid.c_str());
return 1;
}
Step 5: Find a specific node in the DHT
We can also use kadFindNode() to locate a specific peer in the
DHT routing table.
printf("\nNode B finding Node A in the DHT...\n");
StdLogosResult findRes = nodeB.kadFindNode(peerIdA);
if (!findRes.success) {
fprintf(stderr, "kadFindNode failed: %s\n",
findRes.error.c_str());
return 1;
}
printf("Closest peers to Node A:\n");
for (const auto& p : findRes.value) {
printf(" %s\n", p.get<std::string>().c_str());
}
Step 6: Stop providing
When Node A no longer has the content, it can stop advertising.
printf("\nNode A stopping providing...\n");
if (!nodeA.kadStopProviding(cid).success) {
fprintf(stderr, "kadStopProviding failed\n");
return 1;
}
printf("Node A stopped providing %s\n", cid.c_str());
Step 7: Clean up
nodeA.stop();
nodeB.stop();
printf("\n=== Tutorial 6 Complete ===\n");
return 0;
}
Key Takeaways
- Provider records advertise content availability, not content itself
toCid()converts a key string to a CID for use with provider APIkadStartProviding()/kadStopProviding()manage provider announcementskadGetProviders()discovers who has contentkadFindNode()finds peers in the DHT routing table
Run tutorial
./build/tutorial/tutorial_6_kademlia_providers