upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp31
-rw-r--r--src/main_gui.cpp18
-rw-r--r--src/net.cpp115
-rw-r--r--src/net.h19
-rw-r--r--src/nostr.cpp30
-rw-r--r--src/nostr.h8
6 files changed, 2 insertions, 219 deletions
diff --git a/src/main.cpp b/src/main.cpp
index bb317d2..a75bf54 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1877,14 +1877,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1877 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0)); 1877 pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0));
1878 } 1878 }
1879 1879
1880 // Introduce ourselves and hand over the .btf peers that answered us.
1881 // Everything in here is self-certifying, so this is safe to send to a
1882 // node we know nothing about, and safe for it to act on.
1883 vector<string> vPexOut;
1884 BtfPexCollect(vPexOut);
1885 if (!vPexOut.empty())
1886 pfrom->PushMessage("btfpeers", vPexOut);
1887
1888 if (LogAcceptsCategory("net")) printf("version addrMe = %s\n", addrMe.ToString().c_str()); 1880 if (LogAcceptsCategory("net")) printf("version addrMe = %s\n", addrMe.ToString().c_str());
1889 } 1881 }
1890 1882
@@ -1896,29 +1888,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
1896 } 1888 }
1897 1889
1898 1890
1899 else if (strCommand == "btfpeers")
1900 {
1901 // Peer exchange. Nothing here is taken on trust: BtfPexAccept checks
1902 // every descriptor's signature against the key its .btf address
1903 // decodes to, and drops the rest.
1904 int64 nNow = GetTime();
1905 if (nNow - pfrom->nLastPexRecv < PEX_MIN_INTERVAL)
1906 {
1907 if (LogAcceptsCategory("net"))
1908 printf("btfpeers: ignoring, %s is sending them too fast\n",
1909 pfrom->addr.ToString().c_str());
1910 return true;
1911 }
1912 pfrom->nLastPexRecv = nNow;
1913
1914 vector<string> vDesc;
1915 vRecv >> vDesc;
1916 int nKept = BtfPexAccept(vDesc);
1917 LogPrint("net", "btfpeers: kept %d of %zu descriptor(s) from %s\n",
1918 nKept, vDesc.size(), pfrom->addr.ToString().c_str());
1919 }
1920
1921
1922 else if (strCommand == "inv") 1891 else if (strCommand == "inv")
1923 { 1892 {
1924 vector<CInv> vInv; 1893 vector<CInv> vInv;
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index edc772d..842b0d5 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -58,7 +58,6 @@ static void PrintUsage()
58 printf(" /connectbtf=PEER_BTF_ADDRESS\n"); 58 printf(" /connectbtf=PEER_BTF_ADDRESS\n");
59 printf(" /rvrelay=HOST:PORT\n"); 59 printf(" /rvrelay=HOST:PORT\n");
60 printf(" /announcerelay=HOST:PORT\n"); 60 printf(" /announcerelay=HOST:PORT\n");
61 printf(" /port=N (P2P listen port, default 8433)\n");
62 printf("\n"); 61 printf("\n");
63 printf("Each option also accepts '-' instead of '/'.\n"); 62 printf("Each option also accepts '-' instead of '/'.\n");
64} 63}
@@ -114,23 +113,6 @@ static void ParseStartupArguments(int argc, char* argv[])
114 string announceRelay = argval2(argc, argv, "/announcerelay", "-announcerelay"); 113 string announceRelay = argval2(argc, argv, "/announcerelay", "-announcerelay");
115 if (!announceRelay.empty()) 114 if (!announceRelay.empty())
116 strBtfAnnounceRelay = announceRelay; 115 strBtfAnnounceRelay = announceRelay;
117
118 // net.cpp has claimed nListenPort was "tunable via /port" since it was
119 // written, but nothing ever read the option. Without it a second node
120 // cannot start on a machine that already runs one -- which is exactly what
121 // testing peer exchange needs.
122 string strPort = argval2(argc, argv, "/port", "-port");
123 if (!strPort.empty())
124 {
125 int nPort = atoi(strPort.c_str());
126 if (nPort <= 0 || nPort > 65535)
127 fprintf(stderr, "Ignoring /port=%s: not a port number\n", strPort.c_str());
128 else
129 {
130 nListenPort = htons((unsigned short)nPort);
131 addrLocalHost.port = nListenPort; // or we advertise a port we never bound
132 }
133 }
134} 116}
135 117
136int main(int argc, char* argv[]) 118int main(int argc, char* argv[])
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{
diff --git a/src/net.h b/src/net.h
index 7d70721..f028c3b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -29,21 +29,6 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting
29void ThreadBtfAccept(void* parg); 29void ThreadBtfAccept(void* parg);
30void ThreadBtfConnect(void* parg); 30void ThreadBtfConnect(void* parg);
31extern string strBtfConnect; 31extern string strBtfConnect;
32
33// --- .btf peer exchange ---------------------------------------------------
34// Peers gossip signed descriptors so discovery survives a relay outage. Both
35// caps apply to a single "btfpeers" message; the generic header limit is 256 MB,
36// which is no protection at all for something a stranger can send unprompted.
37static const unsigned int MAX_PEX_DESCRIPTORS = 20;
38static const unsigned int MAX_PEX_DESCRIPTOR_BYTES = 1024;
39// Ignore a peer's exchange more often than this. One announcement per side per
40// connection is the intended traffic; anything faster is someone else's idea.
41static const int64 PEX_MIN_INTERVAL = 60;
42
43// Descriptors to hand a peer: ours first, then peers that answered us.
44void BtfPexCollect(std::vector<std::string>& vDescOut);
45// Verify descriptors a peer sent and remember the good ones. Returns how many.
46int BtfPexAccept(const std::vector<std::string>& vDesc);
47void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); 32void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1);
48bool AnySubscribed(unsigned int nChannel); 33bool AnySubscribed(unsigned int nChannel);
49void ThreadBitcoinMiner(void* parg); 34void ThreadBitcoinMiner(void* parg);
@@ -482,9 +467,6 @@ public:
482 // publish and subscription 467 // publish and subscription
483 vector<char> vfSubscribe; 468 vector<char> vfSubscribe;
484 469
485 // Last "btfpeers" we accepted from this node, to rate-limit the exchange.
486 int64 nLastPexRecv;
487
488 470
489 CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) 471 CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false)
490 { 472 {
@@ -501,7 +483,6 @@ public:
501 fDisconnect = false; 483 fDisconnect = false;
502 nRefCount = 0; 484 nRefCount = 0;
503 nReleaseTime = 0; 485 nReleaseTime = 0;
504 nLastPexRecv = 0;
505 vfSubscribe.assign(256, false); 486 vfSubscribe.assign(256, false);
506 487
507 // Push a version message 488 // Push a version message
diff --git a/src/nostr.cpp b/src/nostr.cpp
index 482a835..ec02c2e 100644
--- a/src/nostr.cpp
+++ b/src/nostr.cpp
@@ -342,36 +342,6 @@ std::string BtfLocalAddress()
342 return g_nostrKey.BtfAddress(); 342 return g_nostrKey.BtfAddress();
343} 343}
344 344
345// This node's own descriptor, signed and self-certifying -- the same blob
346// PublishDescriptor puts on Nostr, but handed straight to a peer instead.
347//
348// Reads strBtfActiveRelay directly rather than going through BtfActiveRelay():
349// only a rendezvous we actually registered at is worth announcing. Handing out
350// a meeting node we are not reachable at would make every peer that receives
351// it burn a dial, and over peer exchange that mistake travels further than it
352// does over Nostr.
353std::string BtfLocalDescriptor()
354{
355 if (!EnsureNostrKey())
356 return "";
357 string meeting;
358 CRITICAL_BLOCK(cs_activeRelay)
359 meeting = strBtfActiveRelay;
360 if (meeting.empty())
361 return "";
362 return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey,
363 g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime());
364}
365
366// The shared secp256k1 context, so net.cpp can verify descriptors that arrive
367// from peers. NULL if the identity could not be loaded.
368void* BtfSecpContext()
369{
370 if (!EnsureNostrKey())
371 return NULL;
372 return (void*)g_nostrKey.ctx;
373}
374
375int GetDiscoveredPeerCount() 345int GetDiscoveredPeerCount()
376{ 346{
377 int n = 0; 347 int n = 0;
diff --git a/src/nostr.h b/src/nostr.h
index b5eb5e3..04149e3 100644
--- a/src/nostr.h
+++ b/src/nostr.h
@@ -82,14 +82,6 @@ bool BtfGetIdentity(unsigned char pubkey[32], unsigned char enc_sk[32]);
82// This node's printable .btf address, or "" if the identity failed to load. 82// This node's printable .btf address, or "" if the identity failed to load.
83std::string BtfLocalAddress(); 83std::string BtfLocalAddress();
84 84
85// This node's own signed descriptor, ready to hand to a peer over the wire.
86// "" until ThreadBtfAccept has registered at a rendezvous.
87std::string BtfLocalDescriptor();
88
89// The shared secp256k1 context, for btf::VerifyDescriptor (which takes it as
90// void* to keep headers free of secp). NULL if the identity failed to load.
91void* BtfSecpContext();
92
93// Resolve a .btf address via the public discovery relays: fetch the owner's 85// Resolve a .btf address via the public discovery relays: fetch the owner's
94// self-certified descriptor and return its meeting node ("host:port") and 86// self-certified descriptor and return its meeting node ("host:port") and
95// x25519 public key for the end-to-end channel. 87// x25519 public key for the end-to-end channel.