diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 93 |
1 files changed, 92 insertions, 1 deletions
diff --git a/src/net.cpp b/src/net.cpp index 1d552cf..08a3348 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -414,6 +414,93 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | |||
| 414 | } | 414 | } |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | // | ||
| 418 | // Compiled-in bootstrap seeds. | ||
| 419 | // | ||
| 420 | // A node with no cache has nothing but the Nostr relays, and if those are down, | ||
| 421 | // blocked or simply slow, a first run has no way into the network at all. Seeds | ||
| 422 | // are the floor under that: a handful of long-lived peers baked into the binary. | ||
| 423 | // | ||
| 424 | // A seed entry is deliberately just an address and an encryption key -- no | ||
| 425 | // meeting node. The rendezvous relay pairs on the service's public key, and a | ||
| 426 | // `.btf` address *is* that key, so a client can find a seed by trying the known | ||
| 427 | // relays in turn. Recording which relay a seed was on would rot the moment it | ||
| 428 | // failed over, which is the exact failure this project spent a long time | ||
| 429 | // chasing: advertising a rendezvous somebody is not registered at. | ||
| 430 | // | ||
| 431 | // Seeds are ordinary nodes with no special authority. They hand out signed, | ||
| 432 | // self-certifying descriptors like any peer, so a hostile seed can stall a | ||
| 433 | // bootstrap but cannot forge a peer or feed a false chain. | ||
| 434 | // | ||
| 435 | struct BtfSeed | ||
| 436 | { | ||
| 437 | const char* btfAddr; | ||
| 438 | const char* encHex; // 64 hex chars, the peer's x25519 public key | ||
| 439 | }; | ||
| 440 | |||
| 441 | static const BtfSeed pszBtfSeeds[] = | ||
| 442 | { | ||
| 443 | // Dedicated bootstrap node, Almaty. Runs beside a rendezvous relay on the | ||
| 444 | // same host, holds no wallet balance and does not mine -- it exists only to | ||
| 445 | // answer a first dial. It is trusted for nothing: it serves the same signed | ||
| 446 | // descriptors any peer does. | ||
| 447 | { "ygffz37jczlmrkzicxok6chobauyratdexc7hfgwzjdabzvb2nkngqy.btf", | ||
| 448 | "de8284b9d4effa2132e7981b566c1a297c39c1857d9c128e8ae093ef511d3200" }, | ||
| 449 | }; | ||
| 450 | static const size_t nBtfSeeds = ARRAYLEN(pszBtfSeeds); | ||
| 451 | |||
| 452 | // Extra seeds from the command line (/btfseed=ADDRESS:ENCHEX, repeatable). | ||
| 453 | vector<pair<string, string> > vBtfExtraSeeds; | ||
| 454 | |||
| 455 | static int TryBtfSeeds() | ||
| 456 | { | ||
| 457 | vector<pair<string, string> > seeds; | ||
| 458 | for (size_t i = 0; i < nBtfSeeds; i++) | ||
| 459 | seeds.push_back(make_pair(string(pszBtfSeeds[i].btfAddr), | ||
| 460 | string(pszBtfSeeds[i].encHex))); | ||
| 461 | foreach(const PAIRTYPE(string, string)& s, vBtfExtraSeeds) | ||
| 462 | seeds.push_back(s); | ||
| 463 | |||
| 464 | if (seeds.empty()) | ||
| 465 | return 0; | ||
| 466 | |||
| 467 | vector<string> relays = BtfAllRelays(); | ||
| 468 | if (relays.empty()) | ||
| 469 | return 0; | ||
| 470 | |||
| 471 | LogPrint("net", "btfseed: trying %zu seed(s) across %zu relay(s)\n", | ||
| 472 | seeds.size(), relays.size()); | ||
| 473 | |||
| 474 | int nConnected = 0; | ||
| 475 | foreach(const PAIRTYPE(string, string)& s, seeds) | ||
| 476 | { | ||
| 477 | if (fShutdown) return nConnected; | ||
| 478 | unsigned char enc[32]; | ||
| 479 | if (!HexToBytes(s.second, enc, 32)) | ||
| 480 | { | ||
| 481 | LogPrint("net", "btfseed: %s has a malformed encryption key, skipped\n", | ||
| 482 | s.first.c_str()); | ||
| 483 | continue; | ||
| 484 | } | ||
| 485 | // We do not know which relay this seed is registered at, so walk them. | ||
| 486 | foreach(const string& strRelay, relays) | ||
| 487 | { | ||
| 488 | if (fShutdown) return nConnected; | ||
| 489 | if (ConnectNodeBtfResolved(s.first, strRelay, enc)) | ||
| 490 | { | ||
| 491 | LogPrint("net", "btfseed: reached %s via %s\n", | ||
| 492 | s.first.c_str(), strRelay.c_str()); | ||
| 493 | nConnected++; | ||
| 494 | break; // found it; no need to try this seed's other relays | ||
| 495 | } | ||
| 496 | } | ||
| 497 | if (nConnected >= 4) | ||
| 498 | break; // enough of a foothold; the rest comes from discovery | ||
| 499 | } | ||
| 500 | LogPrint("net", "btfseed: %d seed(s) answered\n", nConnected); | ||
| 501 | return nConnected; | ||
| 502 | } | ||
| 503 | |||
| 417 | // Dial everything we reached last time, before the relays have said anything. | 504 | // Dial everything we reached last time, before the relays have said anything. |
| 418 | void ThreadReconnectCachedBtfPeers(void* parg) | 505 | void ThreadReconnectCachedBtfPeers(void* parg) |
| 419 | { | 506 | { |
| @@ -421,7 +508,11 @@ void ThreadReconnectCachedBtfPeers(void* parg) | |||
| 421 | LoadCachedBtfPeers(peers); | 508 | LoadCachedBtfPeers(peers); |
| 422 | if (peers.empty()) | 509 | if (peers.empty()) |
| 423 | { | 510 | { |
| 424 | LogPrint("net", "btfpeers: no cache yet, waiting on relay discovery\n"); | 511 | // First run, or the cache aged out. Seeds are the only way in that does |
| 512 | // not depend on a relay answering. | ||
| 513 | LogPrint("net", "btfpeers: no cache yet, falling back to seeds\n"); | ||
| 514 | if (TryBtfSeeds() == 0) | ||
| 515 | LogPrint("net", "btfpeers: no seed answered, waiting on relay discovery\n"); | ||
| 425 | return; | 516 | return; |
| 426 | } | 517 | } |
| 427 | LogPrint("net", "btfpeers: trying %zu remembered peer(s) before discovery\n", | 518 | LogPrint("net", "btfpeers: trying %zu remembered peer(s) before discovery\n", |