upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-29 13:28:50 -0300
committerGitHub <noreply@github.com>2026-07-29 13:28:50 -0300
commit997e511e120272e8f2ddd29716b486e597410f02 (patch)
tree42c816df51d837bcac9f806864d677c2d9a9de0c
parent6e836ed5cb5f41391e335b4f508dc6c0e8915ba5 (diff)
rendezvous: register and wait for a dial as separate steps (#30)
A node could not become reachable. RvServiceRegister connected, sent its 'S'+pubkey header, and then blocked reading the relay's 0x01 byte -- which the relay only writes when a client pairs with the service. So the call did not return when the node was registered; it returned when somebody had already dialled it. ThreadBtfAccept called BtfSetActiveRelay after that. The result is a deadlock: - to be dialled, a node must publish a descriptor naming its meeting node - to publish, BtfActiveRelay() must be set - it is only set once somebody has dialled Before #26 this was masked. BtfActiveRelay() fell back to vBtfMeetingRelays[0], so a node always published something, and ThreadBtfAccept picks its relay at random -- so it worked whenever the two happened to match, roughly one time in the number of relays. That is the flakiness this project has been chasing: not a bad relay, an advertisement that usually named the wrong one. #26 removed the fallback, which was right on its own terms and turned an unreliable heuristic into a reliable deadlock. This is the other half of that change and they belong together. RvServiceRegister now returns as soon as the relay has the service listed. RvServiceWaitPaired does the blocking read. ThreadBtfAccept registers, advertises, then waits. RvClientConnect is untouched -- a dialer blocking until it is paired is correct. Registration failure also had no log line at all, which is why this stayed invisible: a node that never registered looked identical to one waiting quietly for a peer. There are now lines for registering, failing over, and being dropped. Verified: a fresh node reports `registered at 90.156.222.107:8434, waiting for a dial` and then self-resolves with `meeting_node=90.156.222.107:8434` -- the relay it is actually on. Two nodes on one machine reach each other.
-rw-r--r--src/btfrv.cpp13
-rw-r--r--src/btfrv.h13
-rw-r--r--src/net.cpp25
3 files changed, 44 insertions, 7 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 0d02ca8..63e092e 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -250,11 +250,20 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
250 hdr[0] = (unsigned char)'S'; 250 hdr[0] = (unsigned char)'S';
251 memcpy(hdr + 1, my_pubkey, 32); 251 memcpy(hdr + 1, my_pubkey, 32);
252 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } 252 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
253 unsigned char paired = 0; 253 // Return here: registered, not yet paired. The relay lists the service as
254 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } 254 // soon as it reads this header. The 0x01 that used to be consumed below
255 // only arrives when somebody dials us, so blocking on it made "registered"
256 // and "already in use" indistinguishable to the caller -- and left the node
257 // unable to advertise a meeting node until after it had been reached there.
255 return (RvSocket)s; 258 return (RvSocket)s;
256} 259}
257 260
261bool RvServiceWaitPaired(RvSocket s)
262{
263 unsigned char paired = 0;
264 return ReadN((SOCKET)s, &paired, 1) && paired == 0x01;
265}
266
258RvSocket RvClientConnect(const char* relay_host, unsigned short port, 267RvSocket RvClientConnect(const char* relay_host, unsigned short port,
259 const unsigned char target_pubkey[32]) 268 const unsigned char target_pubkey[32])
260{ 269{
diff --git a/src/btfrv.h b/src/btfrv.h
index c776201..9d6756e 100644
--- a/src/btfrv.h
+++ b/src/btfrv.h
@@ -37,11 +37,20 @@ bool RvRelayRun(unsigned short port);
37void RvRelayStop(); 37void RvRelayStop();
38 38
39// ---- service side (the node being reached by its .btf address) ---- 39// ---- service side (the node being reached by its .btf address) ----
40// Register at the relay under `my_pubkey` (32 bytes) and block until a client is 40// Register at the relay under `my_pubkey` (32 bytes). Returns as soon as the
41// paired to us. Returns the paired socket, or RV_INVALID on error. 41// relay has us listed, WITHOUT waiting for anyone to dial -- the caller needs
42// that moment to start advertising this relay as its meeting node. Waiting for
43// the dial was folded into this call before, which deadlocked discovery: a node
44// could not be advertised until it had been dialled, and could not be dialled
45// until it was advertised. Returns RV_INVALID on error.
42RvSocket RvServiceRegister(const char* relay_host, unsigned short port, 46RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
43 const unsigned char my_pubkey[32]); 47 const unsigned char my_pubkey[32]);
44 48
49// Block on a registered service socket until a client is paired to us. Returns
50// false if the relay drops us or errors, in which case the caller should close
51// the socket and register again.
52bool RvServiceWaitPaired(RvSocket s);
53
45// ---- client side (the node dialing a .btf address) ---- 54// ---- client side (the node dialing a .btf address) ----
46// Connect through the relay to the service registered under `target_pubkey`. 55// Connect through the relay to the service registered under `target_pubkey`.
47// Returns the paired socket, or RV_INVALID on error. 56// Returns the paired socket, or RV_INVALID on error.
diff --git a/src/net.cpp b/src/net.cpp
index 7b2ef83..b5d12d0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -554,7 +554,10 @@ void ThreadBtfAccept(void* parg)
554 string strHost = strMeeting.substr(0, colon); 554 string strHost = strMeeting.substr(0, colon);
555 int nPort = atoi(strMeeting.substr(colon + 1).c_str()); 555 int nPort = atoi(strMeeting.substr(colon + 1).c_str());
556 556
557 // Blocks until a client is paired to us (or the relay drops us) 557 // Returns as soon as the relay has us listed -- it does not wait for a
558 // dial. That distinction is the whole point: we have to be advertised
559 // before anyone can dial us, so registering and waiting cannot be the
560 // same call.
558 btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk); 561 btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk);
559 if (fShutdown) 562 if (fShutdown)
560 { 563 {
@@ -564,13 +567,29 @@ void ThreadBtfAccept(void* parg)
564 } 567 }
565 if (rv == btf::RV_INVALID) 568 if (rv == btf::RV_INVALID)
566 { 569 {
570 LogPrint("net", "rendezvous: could not register at %s, trying another\n",
571 strMeeting.c_str());
567 iRelay++; // this relay is down/attacked -> try the next one 572 iRelay++; // this relay is down/attacked -> try the next one
568 Sleep(3000); 573 Sleep(3000);
569 continue; 574 continue;
570 } 575 }
571 // Registered OK: advertise THIS relay in our descriptor so clients dial 576
572 // us here, and keep using it (iRelay unchanged) until it fails. 577 // Registered. Advertise THIS relay now, while we are listed and before
578 // anybody dials -- a descriptor naming it is what makes a dial possible
579 // at all. Keep using it (iRelay unchanged) until it fails.
573 BtfSetActiveRelay(strMeeting); 580 BtfSetActiveRelay(strMeeting);
581 LogPrint("net", "rendezvous: registered at %s, waiting for a dial\n",
582 strMeeting.c_str());
583
584 // Now block for someone to arrive.
585 if (!btf::RvServiceWaitPaired(rv))
586 {
587 btf::RvClose(rv);
588 LogPrint("net", "rendezvous: %s dropped us before any dial, re-registering\n",
589 strMeeting.c_str());
590 continue;
591 }
592
574 btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); 593 btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk);
575 if (hSocket == INVALID_SOCKET) 594 if (hSocket == INVALID_SOCKET)
576 continue; 595 continue;