upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp115
1 files changed, 2 insertions, 113 deletions
diff --git a/src/net.cpp b/src/net.cpp
index dfeb4a2..7b2ef83 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -285,13 +285,6 @@ struct CachedBtfPeer
285 string meeting; 285 string meeting;
286 string encHex; 286 string encHex;
287 int64 lastSeen; 287 int64 lastSeen;
288 // The peer's own signed descriptor, as it announced itself. Kept verbatim
289 // because that signature is what lets us hand this peer on to somebody
290 // else: the receiver checks it against the key the address decodes to and
291 // never has to take our word for anything. Empty for entries learned
292 // before peer exchange existed, or resolved through Nostr -- those are
293 // still dialable, just not relayable.
294 string desc;
295}; 288};
296 289
297static string BtfPeerCachePath() { return GetAppDir() + "/btfpeers.json"; } 290static string BtfPeerCachePath() { return GetAppDir() + "/btfpeers.json"; }
@@ -343,8 +336,6 @@ void LoadCachedBtfPeers(vector<CachedBtfPeer>& out)
343 p.meeting = o["meeting"].get<string>(); 336 p.meeting = o["meeting"].get<string>();
344 p.encHex = o["enc"].get<string>(); 337 p.encHex = o["enc"].get<string>();
345 p.lastSeen = o.value("seen", (int64)0); 338 p.lastSeen = o.value("seen", (int64)0);
346 if (o.contains("desc") && o["desc"].is_string())
347 p.desc = o["desc"].get<string>();
348 if (nNow - p.lastSeen > CACHED_BTF_PEER_TTL) continue; // long gone 339 if (nNow - p.lastSeen > CACHED_BTF_PEER_TTL) continue; // long gone
349 out.push_back(p); 340 out.push_back(p);
350 } 341 }
@@ -358,35 +349,22 @@ void LoadCachedBtfPeers(vector<CachedBtfPeer>& out)
358 349
359static CCriticalSection cs_btfPeerCache; 350static CCriticalSection cs_btfPeerCache;
360 351
361// strDesc is the peer's own signed descriptor when we have it (it announced
362// itself over peer exchange), "" when we only resolved it through Nostr. An
363// empty one never clears a descriptor already on file: dialing a peer we first
364// learned about by exchange must not cost us the ability to pass it on.
365static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, 352static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting,
366 const unsigned char enc_pub[32], 353 const unsigned char enc_pub[32])
367 const string& strDesc = string())
368{ 354{
369 CRITICAL_BLOCK(cs_btfPeerCache) 355 CRITICAL_BLOCK(cs_btfPeerCache)
370 { 356 {
371 vector<CachedBtfPeer> peers; 357 vector<CachedBtfPeer> peers;
372 LoadCachedBtfPeers(peers); 358 LoadCachedBtfPeers(peers);
373 359
374 string strKeepDesc = strDesc;
375 for (size_t i = 0; i < peers.size(); i++) 360 for (size_t i = 0; i < peers.size(); i++)
376 if (peers[i].btfAddr == strBtfAddr) 361 if (peers[i].btfAddr == strBtfAddr) { peers.erase(peers.begin() + i); break; }
377 {
378 if (strKeepDesc.empty())
379 strKeepDesc = peers[i].desc;
380 peers.erase(peers.begin() + i);
381 break;
382 }
383 362
384 CachedBtfPeer p; 363 CachedBtfPeer p;
385 p.btfAddr = strBtfAddr; 364 p.btfAddr = strBtfAddr;
386 p.meeting = strMeeting; 365 p.meeting = strMeeting;
387 p.encHex = BytesToHex(enc_pub, 32); 366 p.encHex = BytesToHex(enc_pub, 32);
388 p.lastSeen = GetTime(); 367 p.lastSeen = GetTime();
389 p.desc = strKeepDesc;
390 peers.insert(peers.begin(), p); // most recent first 368 peers.insert(peers.begin(), p); // most recent first
391 369
392 if (peers.size() > MAX_CACHED_BTF_PEERS) 370 if (peers.size() > MAX_CACHED_BTF_PEERS)
@@ -400,8 +378,6 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting,
400 o["meeting"] = q.meeting; 378 o["meeting"] = q.meeting;
401 o["enc"] = q.encHex; 379 o["enc"] = q.encHex;
402 o["seen"] = q.lastSeen; 380 o["seen"] = q.lastSeen;
403 if (!q.desc.empty())
404 o["desc"] = q.desc;
405 arr.push_back(o); 381 arr.push_back(o);
406 } 382 }
407 FILE* f = fopen(BtfPeerCachePath().c_str(), "w"); 383 FILE* f = fopen(BtfPeerCachePath().c_str(), "w");
@@ -443,93 +419,6 @@ void ThreadReconnectCachedBtfPeers(void* parg)
443 nConnected, peers.size()); 419 nConnected, peers.size());
444} 420}
445 421
446//
447// .btf peer exchange
448//
449// Until now discovery depended entirely on the Nostr relays: two connected
450// nodes never told each other who else existed, so a relay outage left a
451// running network unable to grow, and a node that found one stale peer had no
452// way to learn there was anything better.
453//
454// What travels here is each peer's own signed descriptor. The receiver checks
455// the Schnorr signature against the key the `.btf` address decodes to, so the
456// sender is trusted for nothing -- forging an entry would need somebody else's
457// secret key. That leaves flooding: valid descriptors for keys the sender
458// generated itself. The caps below bound one message, and the cache prefers
459// peers that actually answered, so a flood costs a Sybil more than it costs us.
460//
461
462void BtfPexCollect(vector<string>& vDescOut)
463{
464 vDescOut.clear();
465
466 // Ours first -- the node on the other end may know nobody but us.
467 string strMine = BtfLocalDescriptor();
468 if (!strMine.empty())
469 vDescOut.push_back(strMine);
470
471 vector<CachedBtfPeer> peers;
472 CRITICAL_BLOCK(cs_btfPeerCache)
473 LoadCachedBtfPeers(peers);
474
475 // Most recently seen first: these answered us, they are not names copied
476 // off a relay listing.
477 foreach(const CachedBtfPeer& p, peers)
478 {
479 if (vDescOut.size() >= MAX_PEX_DESCRIPTORS) break;
480 if (p.desc.empty()) continue; // nothing provable to pass on
481 if (p.desc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue;
482 vDescOut.push_back(p.desc);
483 }
484}
485
486int BtfPexAccept(const vector<string>& vDesc)
487{
488 void* ctx = BtfSecpContext();
489 if (!ctx)
490 return 0;
491
492 string strSelf = BtfLocalAddress();
493 int nKept = 0;
494 unsigned int nSeen = 0;
495
496 foreach(const string& strDesc, vDesc)
497 {
498 if (++nSeen > MAX_PEX_DESCRIPTORS) break;
499 if (strDesc.empty() || strDesc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue;
500
501 // The descriptor names the key it belongs to, and taking that key from
502 // the blob is safe precisely because the signature must verify under
503 // it: nobody can produce a valid descriptor for a key they don't hold.
504 unsigned char pubkey[32];
505 try
506 {
507 nlohmann::json v = nlohmann::json::parse(strDesc);
508 if (!v.is_object() || !v.contains("pubkey") || !v["pubkey"].is_string())
509 continue;
510 if (!HexToBytes(v["pubkey"].get<string>(), pubkey, 32))
511 continue;
512 }
513 catch (...) { continue; } // malformed JSON from an untrusted peer
514
515 btf::Descriptor d;
516 if (!btf::VerifyDescriptor(ctx, strDesc, pubkey, d))
517 continue;
518
519 string strAddr = btf::Address(pubkey);
520 if (strAddr.empty() || strAddr == strSelf)
521 continue; // ourselves, nothing to learn
522
523 unsigned char enc[32];
524 if (!HexToBytes(d.enc, enc, 32))
525 continue;
526
527 RememberBtfPeer(strAddr, d.meeting_node, enc, strDesc);
528 nKept++;
529 }
530 return nKept;
531}
532
533static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], 422static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32],
534 const string& strMeeting, const unsigned char enc_pub[32]) 423 const string& strMeeting, const unsigned char enc_pub[32])
535{ 424{