upleb.uk

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

summaryrefslogtreecommitdiff
path: root/relay/install-bitflash-relay.sh
diff options
context:
space:
mode:
authorBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
committerBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
commit51d617e70f90a972185917ea0096c1ecef2bfa8b (patch)
tree9fbbcd867de9de9d0776afdd02f594ba312c46eb /relay/install-bitflash-relay.sh
Bitflash: fair-launch release
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
Diffstat (limited to 'relay/install-bitflash-relay.sh')
-rw-r--r--relay/install-bitflash-relay.sh326
1 files changed, 326 insertions, 0 deletions
diff --git a/relay/install-bitflash-relay.sh b/relay/install-bitflash-relay.sh
new file mode 100644
index 0000000..77a46b2
--- /dev/null
+++ b/relay/install-bitflash-relay.sh
@@ -0,0 +1,326 @@
1#!/usr/bin/env bash
2# Bitflash rendezvous relay -- one-shot installer for Linux.
3# Anyone can run this on a machine with a PUBLIC IP (a cheap VPS) to become a
4# meeting node that lets Bitflash users behind CGNAT reach each other.
5# The relay holds NO keys and reads NOTHING -- it only forwards ciphertext.
6#
7# Usage:
8# sudo bash install-bitflash-relay.sh [PORT]
9# PORT defaults to 8434. Remember to open that port in your firewall/panel.
10
11set -e
12PORT="${1:-8434}"
13DIR="$HOME/bitflash-relay"
14mkdir -p "$DIR" && cd "$DIR"
15
16echo ">> writing sources to $DIR"
17
18cat > btfrv.h <<'BTFRV_H_EOF'
19// Copyright (c) 2026 Bitflash developers
20// Distributed under the MIT/X11 software license.
21#ifndef BITFLASH_BTFRV_H
22#define BITFLASH_BTFRV_H
23#include <stdint.h>
24namespace btf
25{
26typedef intptr_t RvSocket;
27static const RvSocket RV_INVALID = (RvSocket)-1;
28bool RvInit();
29bool RvRelayRun(unsigned short port);
30void RvRelayStop();
31RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
32 const unsigned char my_pubkey[32]);
33RvSocket RvClientConnect(const char* relay_host, unsigned short port,
34 const unsigned char target_pubkey[32]);
35bool RvReadN(RvSocket s, void* buf, int n);
36bool RvWriteN(RvSocket s, const void* buf, int n);
37void RvClose(RvSocket s);
38} // namespace btf
39#endif
40BTFRV_H_EOF
41
42cat > btfrv.cpp <<'BTFRV_CPP_EOF'
43// Copyright (c) 2026 Bitflash developers
44// Distributed under the MIT/X11 software license.
45#include "btfrv.h"
46#ifdef _WIN32
47#include <winsock2.h>
48#include <ws2tcpip.h>
49#define CLOSESOCK closesocket
50#else
51#include <sys/socket.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include <netdb.h>
55#include <unistd.h>
56#include <csignal>
57typedef int SOCKET;
58#define INVALID_SOCKET (-1)
59#define SOCKET_ERROR (-1)
60#define CLOSESOCK ::close
61#ifndef SD_BOTH
62#define SD_BOTH SHUT_RDWR
63#endif
64#endif
65#include <cstring>
66#include <cstdio>
67#include <string>
68#include <map>
69#include <thread>
70#include <mutex>
71#include <atomic>
72namespace btf
73{
74static std::atomic<bool> g_stop(false);
75static SOCKET g_listen = INVALID_SOCKET;
76static std::map<std::string, SOCKET> g_waiting;
77static std::mutex g_mtx;
78bool RvInit()
79{
80 static bool done = false;
81 if (done) return true;
82#ifdef _WIN32
83 WSADATA wsa;
84 if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return false;
85#else
86 signal(SIGPIPE, SIG_IGN);
87#endif
88 done = true;
89 return true;
90}
91static bool ReadN(SOCKET s, void* buf, int n)
92{
93 char* p = (char*)buf;
94 int off = 0;
95 while (off < n)
96 {
97 int r = recv(s, p + off, n - off, 0);
98 if (r <= 0) return false;
99 off += r;
100 }
101 return true;
102}
103static bool WriteN(SOCKET s, const void* buf, int n)
104{
105 const char* p = (const char*)buf;
106 int off = 0;
107 while (off < n)
108 {
109 int r = send(s, p + off, n - off, 0);
110 if (r <= 0) return false;
111 off += r;
112 }
113 return true;
114}
115static SOCKET ConnectTo(const char* host, unsigned short port)
116{
117 struct addrinfo hints, *res = NULL;
118 memset(&hints, 0, sizeof(hints));
119 hints.ai_family = AF_INET;
120 hints.ai_socktype = SOCK_STREAM;
121 char portstr[16]; sprintf(portstr, "%u", (unsigned)port);
122 if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) return INVALID_SOCKET;
123 SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
124 if (s == INVALID_SOCKET) { freeaddrinfo(res); return INVALID_SOCKET; }
125 if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0)
126 {
127 CLOSESOCK(s); freeaddrinfo(res); return INVALID_SOCKET;
128 }
129 freeaddrinfo(res);
130 return s;
131}
132static void Forward(SOCKET from, SOCKET to)
133{
134 char buf[32 * 1024];
135 for (;;)
136 {
137 int r = recv(from, buf, sizeof(buf), 0);
138 if (r <= 0) break;
139 if (!WriteN(to, buf, r)) break;
140 }
141 shutdown(to, SD_BOTH);
142 CLOSESOCK(from);
143}
144static void HandleIncoming(SOCKET s)
145{
146 unsigned char hdr[33];
147 if (!ReadN(s, hdr, 33)) { CLOSESOCK(s); return; }
148 char role = (char)hdr[0];
149 std::string pubkey((char*)hdr + 1, 32);
150 if (role == 'S')
151 {
152 std::lock_guard<std::mutex> lk(g_mtx);
153 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey);
154 if (it != g_waiting.end()) CLOSESOCK(it->second);
155 g_waiting[pubkey] = s;
156 }
157 else if (role == 'C')
158 {
159 SOCKET svc = INVALID_SOCKET;
160 {
161 std::lock_guard<std::mutex> lk(g_mtx);
162 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey);
163 if (it != g_waiting.end()) { svc = it->second; g_waiting.erase(it); }
164 }
165 if (svc == INVALID_SOCKET)
166 {
167 unsigned char no = 0x00;
168 WriteN(s, &no, 1);
169 CLOSESOCK(s);
170 return;
171 }
172 unsigned char ok = 0x01;
173 bool a = WriteN(svc, &ok, 1);
174 bool b = WriteN(s, &ok, 1);
175 if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; }
176 std::thread(Forward, s, svc).detach();
177 std::thread(Forward, svc, s).detach();
178 }
179 else
180 {
181 CLOSESOCK(s);
182 }
183}
184bool RvRelayRun(unsigned short port)
185{
186 if (!RvInit()) return false;
187 g_stop = false;
188 g_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
189 if (g_listen == INVALID_SOCKET) return false;
190 int yes = 1;
191 setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
192 struct sockaddr_in addr;
193 memset(&addr, 0, sizeof(addr));
194 addr.sin_family = AF_INET;
195 addr.sin_addr.s_addr = INADDR_ANY;
196 addr.sin_port = htons(port);
197 if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; }
198 if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; }
199 while (!g_stop)
200 {
201 struct sockaddr_in cli;
202 socklen_t len = sizeof(cli);
203 SOCKET s = accept(g_listen, (struct sockaddr*)&cli, &len);
204 if (s == INVALID_SOCKET)
205 {
206 if (g_stop) break;
207 continue;
208 }
209 std::thread(HandleIncoming, s).detach();
210 }
211 CLOSESOCK(g_listen);
212 g_listen = INVALID_SOCKET;
213 return true;
214}
215void RvRelayStop()
216{
217 g_stop = true;
218 if (g_listen != INVALID_SOCKET)
219 CLOSESOCK(g_listen);
220}
221RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
222 const unsigned char my_pubkey[32])
223{
224 if (!RvInit()) return RV_INVALID;
225 SOCKET s = ConnectTo(relay_host, port);
226 if (s == INVALID_SOCKET) return RV_INVALID;
227 unsigned char hdr[33];
228 hdr[0] = (unsigned char)'S';
229 memcpy(hdr + 1, my_pubkey, 32);
230 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
231 unsigned char paired = 0;
232 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; }
233 return (RvSocket)s;
234}
235RvSocket RvClientConnect(const char* relay_host, unsigned short port,
236 const unsigned char target_pubkey[32])
237{
238 if (!RvInit()) return RV_INVALID;
239 SOCKET s = ConnectTo(relay_host, port);
240 if (s == INVALID_SOCKET) return RV_INVALID;
241 unsigned char hdr[33];
242 hdr[0] = (unsigned char)'C';
243 memcpy(hdr + 1, target_pubkey, 32);
244 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
245 unsigned char paired = 0;
246 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; }
247 return (RvSocket)s;
248}
249bool RvReadN(RvSocket s, void* buf, int n) { return ReadN((SOCKET)s, buf, n); }
250bool RvWriteN(RvSocket s, const void* buf, int n) { return WriteN((SOCKET)s, buf, n); }
251void RvClose(RvSocket s) { if (s != RV_INVALID) CLOSESOCK((SOCKET)s); }
252} // namespace btf
253BTFRV_CPP_EOF
254
255cat > bitflashd.cpp <<'BITFLASHD_EOF'
256// Copyright (c) 2026 Bitflash developers
257// Distributed under the MIT/X11 software license.
258#include "btfrv.h"
259#include <cstdio>
260#include <cstdlib>
261#include <cstring>
262int main(int argc, char* argv[])
263{
264 unsigned short port = 8434;
265 if (argc >= 2)
266 port = (unsigned short)atoi(argv[1]);
267 printf("bitflashd -- Bitflash rendezvous relay\n");
268 printf("listening on 0.0.0.0:%u (open this port in your firewall)\n", port);
269 fflush(stdout);
270 if (!btf::RvInit())
271 {
272 fprintf(stderr, "bitflashd: socket init failed\n");
273 return 1;
274 }
275 if (!btf::RvRelayRun(port))
276 {
277 fprintf(stderr, "bitflashd: could not bind/listen on port %u\n", port);
278 return 1;
279 }
280 return 0;
281}
282BITFLASHD_EOF
283
284# g++ (Debian/Ubuntu: build-essential; RHEL/Alma: gcc-c++)
285if ! command -v g++ >/dev/null 2>&1; then
286 echo ">> installing g++"
287 if command -v apt-get >/dev/null 2>&1; then apt-get update && apt-get install -y g++
288 elif command -v dnf >/dev/null 2>&1; then dnf install -y gcc-c++
289 elif command -v yum >/dev/null 2>&1; then yum install -y gcc-c++
290 else echo "!! install a C++ compiler (g++) manually and re-run"; exit 1
291 fi
292fi
293
294echo ">> building bitflashd"
295g++ -std=gnu++14 -O2 -pthread bitflashd.cpp btfrv.cpp -o bitflashd
296echo ">> build OK: $DIR/bitflashd"
297
298# systemd service so it survives reboots (skipped if systemd is absent)
299if command -v systemctl >/dev/null 2>&1 && [ -d /etc/systemd/system ]; then
300 cat > /etc/systemd/system/bitflashd.service <<EOF
301[Unit]
302Description=Bitflash rendezvous relay
303After=network.target
304
305[Service]
306ExecStart=$DIR/bitflashd $PORT
307Restart=always
308User=$(id -un)
309
310[Install]
311WantedBy=multi-user.target
312EOF
313 systemctl daemon-reload
314 systemctl enable --now bitflashd
315 sleep 1
316 systemctl status bitflashd --no-pager || true
317 echo ">> relay running as a service on port $PORT"
318else
319 echo ">> systemd not found; run it manually: $DIR/bitflashd $PORT"
320fi
321
322echo
323echo "==================================================================="
324echo " DONE. Open TCP port $PORT in your firewall / cloud panel."
325echo " Your relay address for Bitflash users: <YOUR_PUBLIC_IP>:$PORT"
326echo "==================================================================="