upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/test_btfrv.cpp
diff options
context:
space:
mode:
authorbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
committerbitflash-coin <bookofshamers@outlook.com>2026-08-04 06:34:32 -0700
commit5ce650382352c1503f40abf08129ada69402af55 (patch)
tree6a18198901f1e2c4933f1ac53c9365eec512bf63 /src/test_btfrv.cpp
parentf34113291da7e5e3dc7d2468d4f9c212c05c584b (diff)
public commit
Diffstat (limited to 'src/test_btfrv.cpp')
-rw-r--r--src/test_btfrv.cpp116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/test_btfrv.cpp b/src/test_btfrv.cpp
new file mode 100644
index 0000000..291e660
--- /dev/null
+++ b/src/test_btfrv.cpp
@@ -0,0 +1,116 @@
1// Standalone loopback test of the rendezvous transport + echan on top.
2// A relay, a service and a client all run in one process on 127.0.0.1 (a unit
3// test of the protocol logic -- NOT a network/LAN test). Proves: service
4// registers, client pairs by pubkey through the relay, and they exchange data
5// end-to-end encrypted so the relay only ever forwards ciphertext.
6//
7// Build (UCRT64):
8// g++ -std=gnu++14 test_btfrv.cpp btfrv.cpp btfchan.cpp -lsodium -lws2_32 -o test_btfrv.exe
9
10#include "btfrv.h"
11#include "btfchan.h"
12#include <cstdio>
13#include <cstring>
14#include <string>
15#include <vector>
16#include <thread>
17#include <chrono>
18
19using namespace btf;
20static int g_fail = 0;
21#define CHECK(cond, name) do { \
22 if (cond) { printf(" ok %s\n", name); } \
23 else { printf(" FAIL %s\n", name); g_fail++; } } while(0)
24
25static const unsigned short PORT = 18443;
26
27// echan framing over a rendezvous socket
28static bool WriteFrame(RvSocket s, const unsigned char shared[32], const std::string& pt)
29{
30 std::vector<unsigned char> f = ChanEncryptFrame(shared, (const unsigned char*)pt.data(), pt.size());
31 return RvWriteN(s, f.data(), (int)f.size());
32}
33static bool ReadFrame(RvSocket s, const unsigned char shared[32], std::string& out)
34{
35 unsigned char len[4];
36 if (!RvReadN(s, len, 4)) return false;
37 size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3];
38 if (n == 0 || n > (1u<<20)) return false;
39 std::vector<unsigned char> body(n);
40 if (!RvReadN(s, body.data(), (int)n)) return false;
41 std::vector<unsigned char> pt;
42 if (!ChanDecryptBody(shared, body.data(), (int)n, pt)) return false;
43 out.assign((char*)pt.data(), pt.size());
44 return true;
45}
46
47static std::string g_svcGot;
48static bool g_svcOk = false;
49
50static void ServiceThread(std::vector<unsigned char> pairing_id, std::vector<unsigned char> svc_enc_sk)
51{
52 RvSocket s = RvServiceRegister("127.0.0.1", PORT, pairing_id.data());
53 if (s == RV_INVALID) return;
54 // read the client's ephemeral x25519 public key
55 unsigned char eph[32];
56 if (!RvReadN(s, eph, 32)) { RvClose(s); return; }
57 unsigned char shared[32];
58 if (!ChanEndpointHandshake(eph, svc_enc_sk.data(), shared)) { RvClose(s); return; }
59 std::string msg;
60 if (!ReadFrame(s, shared, msg)) { RvClose(s); return; }
61 g_svcGot = msg;
62 WriteFrame(s, shared, std::string("block accepted: ") + msg);
63 g_svcOk = true;
64 RvClose(s);
65}
66
67int main()
68{
69 if (!ChanInit() || !RvInit()) { printf("init failed\n"); return 2; }
70
71 // service identity: a 32-byte .btf pairing id + an x25519 enc keypair
72 unsigned char svc_enc_pk[32], svc_enc_sk[32];
73 ChanKeypair(svc_enc_pk, svc_enc_sk);
74 std::vector<unsigned char> pairing_id(32);
75 for (int i = 0; i < 32; i++) pairing_id[i] = (unsigned char)(i * 7 + 1);
76
77 printf("rendezvous_relay_pairs_and_forwards_encrypted\n");
78
79 std::thread relay([]{ RvRelayRun(PORT); });
80 std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let it bind
81
82 std::thread svc(ServiceThread, pairing_id, std::vector<unsigned char>(svc_enc_sk, svc_enc_sk+32));
83 std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let service register
84
85 // client dials the pairing id through the relay
86 RvSocket c = RvClientConnect("127.0.0.1", PORT, pairing_id.data());
87 CHECK(c != RV_INVALID, "client paired to service via relay");
88 if (c != RV_INVALID)
89 {
90 unsigned char eph_pub[32], shared_client[32];
91 CHECK(ChanClientHandshake(svc_enc_pk, eph_pub, shared_client), "client handshake");
92 CHECK(RvWriteN(c, eph_pub, 32), "client sends ephemeral pubkey");
93 CHECK(WriteFrame(c, shared_client, "hello one-cpu-one-vote"), "client sends encrypted frame");
94 std::string reply;
95 CHECK(ReadFrame(c, shared_client, reply), "client reads encrypted reply");
96 CHECK(reply == "block accepted: hello one-cpu-one-vote", "end-to-end message round-trips through relay");
97 RvClose(c);
98 }
99
100 svc.join();
101 CHECK(g_svcOk && g_svcGot == "hello one-cpu-one-vote", "service received the decrypted plaintext");
102
103 printf("unknown_pubkey_is_rejected\n");
104 {
105 unsigned char other[32];
106 for (int i = 0; i < 32; i++) other[i] = 0xEE;
107 RvSocket bad = RvClientConnect("127.0.0.1", PORT, other);
108 CHECK(bad == RV_INVALID, "client for unregistered .btf is rejected");
109 }
110
111 RvRelayStop();
112 relay.join();
113
114 printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail);
115 return g_fail == 0 ? 0 : 1;
116}