mirror of
https://github.com/logos-co/logos-libp2p-module.git
synced 2026-08-27 16:01:12 +00:00
192 lines
6.2 KiB
C++
192 lines
6.2 KiB
C++
/// # Tutorial 6: Kademlia Provider Records
|
|
///
|
|
/// In the [previous tutorial](tutorial_5_kademlia_basics.md), 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 API
|
|
/// - `kadStartProviding()` / `kadStopProviding()` manage provider announcements
|
|
/// - `kadGetProviders()` discovers who has content
|
|
/// - `kadFindNode()` finds peers in the DHT routing table
|
|
|
|
/// ## Run tutorial
|
|
///
|
|
/// ```bash
|
|
/// ./build/tutorial/tutorial_6_kademlia_providers
|
|
/// ```
|