diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 115 |
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 | ||
| 297 | static string BtfPeerCachePath() { return GetAppDir() + "/btfpeers.json"; } | 290 | static 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 | ||
| 359 | static CCriticalSection cs_btfPeerCache; | 350 | static 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. | ||
| 365 | static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | 352 | static 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 | |||
| 462 | void 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 | |||
| 486 | int 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 | |||
| 533 | static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], | 422 | static 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 | { |