Initial commit: Naut-Torrent — from-scratch 10 GbE BitTorrent client
A maintainable, extensible BitTorrent client (C11, Linux/io_uring) targeting 10 GbE saturation. All torrent functionality is built from scratch; liburing is the only linked third-party dependency on the data path. Implements Phases 1-7 of the roadmap: - core: page-aligned buffer pool, MPMC/Treiber queues, bitfields, worker pool - crypto: SHA-1/256 (SHA-NI + scalar), Merkle (BEP-52), RC4 (MSE) - bencode/metainfo: zero-copy parser, v1/v2/hybrid .torrent + magnet - peer: sans-IO wire codec, MSE/PE handshake state machine, BEP-10, ut_metadata, PEX - piece/storage: block-level multi-peer engine, rarest-first + endgame, per-file completion events + single-file relocate (move-as-you-finish) - tracker/dht: HTTP + UDP (BEP-15) trackers, BEP-5 KRPC iterative lookup - platform: io_uring reactor (SQPOLL, registered buffers, SEND_ZC) - surface: versioned RPC, native plugin ABI, sandboxed Lua scripting, nautd/nautctl Verified against libtorrent (single/multi/hybrid, MSE, magnet-via-DHT, swarm); unit + interop tests green; ASan/UBSan/TSan clean. Scripting reference in docs/scripting.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
2178d6a70c
121 changed files with 12644 additions and 0 deletions
166
tests/unit/test_picker.c
Normal file
166
tests/unit/test_picker.c
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/* Exercises the multi-peer swarm logic with a synthetic torrent (real SHA-1
|
||||
* piece hashes, so verification actually runs): rarest-first selection, two
|
||||
* peers collaborating on one piece, duplicate-block dedup, unrequest, endgame,
|
||||
* and a full multi-peer completion. */
|
||||
#include "naut/piece.h"
|
||||
#include "naut/metainfo.h"
|
||||
#include "naut/storage.h"
|
||||
#include "naut/hash.h"
|
||||
#include "naut/bitfield.h"
|
||||
#include "test.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NP 10
|
||||
#define PLEN 32768 /* 2 blocks per piece */
|
||||
#define TOTAL ((uint64_t)NP * PLEN)
|
||||
|
||||
typedef struct {
|
||||
uint32_t piece[4], begin[4];
|
||||
size_t count;
|
||||
} active_requests;
|
||||
|
||||
static bool is_active(void *ctx, uint32_t piece, uint32_t begin) {
|
||||
active_requests *a = ctx;
|
||||
for (size_t i = 0; i < a->count; i++)
|
||||
if (a->piece[i] == piece && a->begin[i] == begin) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_active(active_requests *a, uint32_t piece, uint32_t begin) {
|
||||
a->piece[a->count] = piece;
|
||||
a->begin[a->count] = begin;
|
||||
a->count++;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint8_t *data = malloc(TOTAL);
|
||||
for (uint64_t i = 0; i < TOTAL; i++) data[i] = (uint8_t)(i * 1103515245u + 12345u);
|
||||
uint8_t hashes[NP * NAUT_SHA1_LEN];
|
||||
for (int p = 0; p < NP; p++) naut_sha1(data + (uint64_t)p * PLEN, PLEN, hashes + p * NAUT_SHA1_LEN);
|
||||
|
||||
naut_file files[1] = { { (char *)"data.bin", (int64_t)TOTAL } };
|
||||
naut_metainfo mi; memset(&mi, 0, sizeof mi);
|
||||
mi.has_v1 = true; mi.num_pieces = NP; mi.piece_length = PLEN; mi.total_length = TOTAL;
|
||||
mi.piece_hashes = hashes; mi.files = files; mi.num_files = 1; mi.name = (char *)"t";
|
||||
|
||||
char tmpl[] = "/tmp/naut_pick_XXXXXX"; char *root = mkdtemp(tmpl);
|
||||
naut_err err;
|
||||
naut_storage *st = naut_storage_open(files, 1, root, &err);
|
||||
CHECK(st && err == NAUT_OK);
|
||||
naut_download *d = naut_download_create(&mi, st);
|
||||
CHECK(d != NULL);
|
||||
|
||||
/* peer A has pieces 0..8, peer B has all 0..9 -> piece 9 is rarest (avail 1) */
|
||||
naut_bitfield ha, hb;
|
||||
naut_bitfield_init(&ha, NP); naut_bitfield_init(&hb, NP);
|
||||
for (int p = 0; p < NP; p++) { naut_bitfield_set(&hb, p); if (p < 9) naut_bitfield_set(&ha, p); }
|
||||
naut_download_add_bitfield(d, &ha);
|
||||
naut_download_add_bitfield(d, &hb);
|
||||
|
||||
/* rarest-first: B's first pick must be the unique piece 9 */
|
||||
uint32_t idx, begin, len;
|
||||
CHECK(naut_download_pick(d, &hb, &idx, &begin, &len));
|
||||
CHECK_EQ(idx, 9);
|
||||
CHECK_EQ(begin, 0);
|
||||
|
||||
/* collaboration: next pick for B finishes piece 9's second block */
|
||||
CHECK(naut_download_pick(d, &hb, &idx, &begin, &len));
|
||||
CHECK(idx == 9 && begin == NAUT_BLOCK);
|
||||
/* both blocks of piece 9 now requested; A (lacks 9) gets a different piece */
|
||||
uint32_t ia, ba, la;
|
||||
CHECK(naut_download_pick(d, &ha, &ia, &ba, &la));
|
||||
CHECK(ia != 9);
|
||||
|
||||
/* unrequest releases a block for re-pick */
|
||||
naut_download_unrequest(d, ia, ba);
|
||||
uint32_t ia2, ba2, la2;
|
||||
CHECK(naut_download_pick(d, &ha, &ia2, &ba2, &la2));
|
||||
CHECK(ia2 == ia && ba2 == ba);
|
||||
|
||||
/* duplicate block is ignored */
|
||||
bool done = false;
|
||||
uint64_t g9 = (uint64_t)9 * PLEN;
|
||||
CHECK(naut_download_on_block(d, 9, 0, data + g9, NAUT_BLOCK, &done) == NAUT_OK);
|
||||
CHECK(naut_download_on_block(d, 9, 0, data + g9, NAUT_BLOCK, &done) == NAUT_OK); /* dup */
|
||||
CHECK(!done);
|
||||
|
||||
/* endgame flag is off this early (20 blocks, only a couple received) */
|
||||
CHECK(!naut_download_in_endgame(d));
|
||||
|
||||
naut_download_destroy(d);
|
||||
|
||||
/* full multi-peer download: alternate peers, all pieces verify */
|
||||
d = naut_download_create(&mi, st);
|
||||
naut_download_add_bitfield(d, &hb); /* one peer that has everything */
|
||||
int guard = 0;
|
||||
while (!naut_download_complete(d) && guard++ < 10000) {
|
||||
if (!naut_download_pick(d, &hb, &idx, &begin, &len)) break;
|
||||
uint64_t g = (uint64_t)idx * PLEN + begin;
|
||||
CHECK(naut_download_on_block(d, idx, begin, data + g, len, &done) == NAUT_OK);
|
||||
}
|
||||
CHECK(naut_download_complete(d));
|
||||
CHECK(naut_download_in_endgame(d)); /* must have passed through endgame near the end */
|
||||
CHECK_EQ((long long)naut_download_bytes_done(d), (long long)TOTAL);
|
||||
|
||||
naut_download_destroy(d);
|
||||
|
||||
/* Endgame races each block on at most two distinct peers. Releasing one
|
||||
* peer's copy must not erase the other peer's outstanding request. */
|
||||
uint8_t small_hash[NAUT_SHA1_LEN];
|
||||
naut_sha1(data, PLEN, small_hash);
|
||||
naut_file small_file[1] = { { (char *)"small.bin", PLEN } };
|
||||
naut_metainfo small; memset(&small, 0, sizeof small);
|
||||
small.has_v1 = true; small.num_pieces = 1; small.piece_length = PLEN;
|
||||
small.total_length = PLEN; small.piece_hashes = small_hash;
|
||||
small.files = small_file; small.num_files = 1; small.name = (char *)"small";
|
||||
d = naut_download_create(&small, st);
|
||||
CHECK(d != NULL);
|
||||
naut_bitfield one;
|
||||
naut_bitfield_init(&one, 1);
|
||||
naut_bitfield_set(&one, 0);
|
||||
naut_download_add_bitfield(d, &one);
|
||||
naut_download_add_bitfield(d, &one);
|
||||
|
||||
active_requests pa = {0}, pb = {0};
|
||||
CHECK(naut_download_pick_for_peer(d, &one, is_active, &pa,
|
||||
&idx, &begin, &len));
|
||||
add_active(&pa, idx, begin);
|
||||
CHECK(naut_download_pick_for_peer(d, &one, is_active, &pa,
|
||||
&idx, &begin, &len));
|
||||
add_active(&pa, idx, begin);
|
||||
CHECK(!naut_download_pick_for_peer(d, &one, is_active, &pa,
|
||||
&idx, &begin, &len));
|
||||
|
||||
CHECK(naut_download_pick_for_peer(d, &one, is_active, &pb,
|
||||
&idx, &begin, &len));
|
||||
add_active(&pb, idx, begin);
|
||||
CHECK(naut_download_pick_for_peer(d, &one, is_active, &pb,
|
||||
&idx, &begin, &len));
|
||||
add_active(&pb, idx, begin);
|
||||
CHECK(!naut_download_pick_for_peer(d, &one, is_active, &pb,
|
||||
&idx, &begin, &len));
|
||||
|
||||
naut_download_unrequest(d, pa.piece[0], pa.begin[0]);
|
||||
CHECK(!naut_download_pick_for_peer(d, &one, is_active, &pb,
|
||||
&idx, &begin, &len));
|
||||
pa.piece[0] = pa.piece[1]; pa.begin[0] = pa.begin[1]; pa.count = 1;
|
||||
CHECK(naut_download_pick_for_peer(d, &one, is_active, &pa,
|
||||
&idx, &begin, &len));
|
||||
CHECK_EQ(begin, 0);
|
||||
|
||||
naut_bitfield_free(&one);
|
||||
naut_storage_sync(st);
|
||||
/* on-disk bytes match the source */
|
||||
uint8_t *rb = malloc(TOTAL);
|
||||
CHECK(naut_storage_read(st, 0, rb, TOTAL) == NAUT_OK);
|
||||
CHECK(memcmp(rb, data, TOTAL) == 0);
|
||||
free(rb);
|
||||
|
||||
naut_download_destroy(d);
|
||||
naut_storage_close(st);
|
||||
naut_bitfield_free(&ha); naut_bitfield_free(&hb);
|
||||
free(data);
|
||||
char cmd[256]; snprintf(cmd, sizeof cmd, "rm -rf '%s'", root); if (system(cmd)) {}
|
||||
TEST_MAIN_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue