|
| 1 | +// Peerstore: a node's local record of other peers. This drives the store |
| 2 | +// directly — no dialing — using a second node's real PeerInfo: add the peer, |
| 3 | +// list it, read it back, then delete it. Calls are made blocking via common.h. |
| 4 | +#include "common.h" |
| 5 | + |
| 6 | +// TransportType / MuxerType ordinals, mirrored from libp2p_ffi/config.nim. |
| 7 | +static const int64_t TransportTcp = 1; |
| 8 | +static const int64_t MuxerMplex = 0; |
| 9 | + |
| 10 | +static const char *Proto = "/cbind/peerstore/1.0.0"; |
| 11 | + |
| 12 | +// get_peers replies with a PeersResponse of peer-id strings. |
| 13 | +#define MAX_PEERS 16 |
| 14 | +typedef struct { |
| 15 | + atomic_int done; |
| 16 | + int err_code; |
| 17 | + char err[256]; |
| 18 | + char peerIds[MAX_PEERS][128]; |
| 19 | + size_t n; |
| 20 | +} PeersWaiter; |
| 21 | + |
| 22 | +static void on_peers(int ec, const PeersResponse *reply, const char *em, |
| 23 | + void *ud) { |
| 24 | + PeersWaiter *w = (PeersWaiter *)ud; |
| 25 | + w->err_code = ec; |
| 26 | + if (reply) { |
| 27 | + w->n = reply->peerIds.len < MAX_PEERS ? reply->peerIds.len : MAX_PEERS; |
| 28 | + for (size_t i = 0; i < w->n; i++) |
| 29 | + if (reply->peerIds.data[i].data) |
| 30 | + snprintf(w->peerIds[i], sizeof(w->peerIds[i]), "%s", |
| 31 | + reply->peerIds.data[i].data); |
| 32 | + } |
| 33 | + if (em) |
| 34 | + snprintf(w->err, sizeof(w->err), "%s", em); |
| 35 | + atomic_store(&w->done, 1); |
| 36 | +} |
| 37 | + |
| 38 | +static bool get_peers(LibP2PCtx *ctx, PeersWaiter *w) { |
| 39 | + memset(w, 0, sizeof(*w)); |
| 40 | + libp2p_ctx_peerstore_get_peers(ctx, on_peers, w); |
| 41 | + if (!wait_done(&w->done) || w->err_code != 0) { |
| 42 | + fprintf(stderr, "get_peers: %s\n", w->err[0] ? w->err : "unknown"); |
| 43 | + return false; |
| 44 | + } |
| 45 | + return true; |
| 46 | +} |
| 47 | + |
| 48 | +static bool holds(const PeersWaiter *w, const char *peerId) { |
| 49 | + for (size_t i = 0; i < w->n; i++) |
| 50 | + if (strcmp(w->peerIds[i], peerId) == 0) |
| 51 | + return true; |
| 52 | + return false; |
| 53 | +} |
| 54 | + |
| 55 | +// get_peer_info replies with the full entry; we only report its counts here. |
| 56 | +typedef struct { |
| 57 | + atomic_int done; |
| 58 | + int err_code; |
| 59 | + char err[256]; |
| 60 | + size_t naddrs, nprotocols; |
| 61 | +} EntryWaiter; |
| 62 | + |
| 63 | +static void on_entry(int ec, const PeerStoreEntryResponse *reply, |
| 64 | + const char *em, void *ud) { |
| 65 | + EntryWaiter *w = (EntryWaiter *)ud; |
| 66 | + w->err_code = ec; |
| 67 | + if (reply) { |
| 68 | + w->naddrs = reply->addrs.len; |
| 69 | + w->nprotocols = reply->protocols.len; |
| 70 | + } |
| 71 | + if (em) |
| 72 | + snprintf(w->err, sizeof(w->err), "%s", em); |
| 73 | + atomic_store(&w->done, 1); |
| 74 | +} |
| 75 | + |
| 76 | +static LibP2PCtx *createNode(const char *addr, const char *label) { |
| 77 | + NimFfiStr slot = nimffi_str(addr); |
| 78 | + Libp2pConfig cfg; |
| 79 | + memset(&cfg, 0, sizeof(cfg)); |
| 80 | + cfg.addrs.data = &slot; |
| 81 | + cfg.addrs.len = 1; |
| 82 | + cfg.muxer = MuxerMplex; |
| 83 | + cfg.transport = TransportTcp; |
| 84 | + return await_create(&cfg, label); |
| 85 | +} |
| 86 | + |
| 87 | +int main(void) { |
| 88 | + int status = 1; |
| 89 | + BoolWaiter bw; |
| 90 | + |
| 91 | + // `local` owns the peerstore we drive; `other` just hands us a real peer id |
| 92 | + // and listen addresses to store. |
| 93 | + LibP2PCtx *local = createNode("/ip4/127.0.0.1/tcp/5051", "local"); |
| 94 | + LibP2PCtx *other = createNode("/ip4/127.0.0.1/tcp/5052", "other"); |
| 95 | + if (!local || !other) { |
| 96 | + libp2p_ctx_destroy(local); |
| 97 | + libp2p_ctx_destroy(other); |
| 98 | + return 1; |
| 99 | + } |
| 100 | + |
| 101 | + // Start `other` so its listen addresses are bound and reported by peer_info. |
| 102 | + if (!AWAIT_BOOL(bw, libp2p_ctx_start(other, on_bool, &bw), "start other")) |
| 103 | + goto cleanup; |
| 104 | + PeerInfoWaiter oi; |
| 105 | + if (!await_peerinfo(other, &oi, "other peerinfo")) |
| 106 | + goto cleanup; |
| 107 | + printf("Peer to store: %s\n", oi.peerId); |
| 108 | + |
| 109 | + // add_peer: merge the peer's addresses and a protocol into the store. |
| 110 | + NimFfiStr addrs[MAX_ADDRS]; |
| 111 | + for (size_t i = 0; i < oi.naddrs; i++) |
| 112 | + addrs[i] = nimffi_str(oi.addrs[i]); |
| 113 | + NimFfiStr proto = nimffi_str(Proto); |
| 114 | + AddPeerRequest addReq = { |
| 115 | + nimffi_str(oi.peerId), {addrs, oi.naddrs}, {&proto, 1}}; |
| 116 | + if (!AWAIT_BOOL(bw, |
| 117 | + libp2p_ctx_peerstore_add_peer(local, &addReq, on_bool, &bw), |
| 118 | + "add_peer")) |
| 119 | + goto cleanup; |
| 120 | + |
| 121 | + // get_peers + get_peer_info: the peer is now listed and readable. |
| 122 | + PeersWaiter peers; |
| 123 | + if (!get_peers(local, &peers)) |
| 124 | + goto cleanup; |
| 125 | + EntryWaiter entry; |
| 126 | + memset(&entry, 0, sizeof(entry)); |
| 127 | + libp2p_ctx_peerstore_get_peer_info(local, nimffi_str(oi.peerId), on_entry, |
| 128 | + &entry); |
| 129 | + if (!wait_done(&entry.done) || entry.err_code != 0) { |
| 130 | + fprintf(stderr, "get_peer_info: %s\n", |
| 131 | + entry.err[0] ? entry.err : "unknown"); |
| 132 | + goto cleanup; |
| 133 | + } |
| 134 | + printf("Stored: %zu peer(s), entry has %zu address(es) and %zu protocol(s)\n", |
| 135 | + peers.n, entry.naddrs, entry.nprotocols); |
| 136 | + if (!holds(&peers, oi.peerId) || entry.naddrs == 0 || entry.nprotocols == 0) { |
| 137 | + fprintf(stderr, "Error: peer was not stored correctly\n"); |
| 138 | + goto cleanup; |
| 139 | + } |
| 140 | + |
| 141 | + // delete_peer: the store no longer holds it. |
| 142 | + if (!AWAIT_BOOL(bw, |
| 143 | + libp2p_ctx_peerstore_delete_peer(local, nimffi_str(oi.peerId), |
| 144 | + on_bool, &bw), |
| 145 | + "delete_peer")) |
| 146 | + goto cleanup; |
| 147 | + if (!get_peers(local, &peers)) |
| 148 | + goto cleanup; |
| 149 | + if (holds(&peers, oi.peerId)) { |
| 150 | + fprintf(stderr, "Error: peer still present after delete_peer\n"); |
| 151 | + goto cleanup; |
| 152 | + } |
| 153 | + printf("Deleted %s\n", oi.peerId); |
| 154 | + status = 0; |
| 155 | + |
| 156 | +cleanup: |
| 157 | + AWAIT_BOOL(bw, libp2p_ctx_stop(other, on_bool, &bw), "stop other"); |
| 158 | + libp2p_ctx_destroy(other); |
| 159 | + libp2p_ctx_destroy(local); |
| 160 | + return status; |
| 161 | +} |
0 commit comments