Queue executable speed and peer-to-peer sharing plans with tested patches and harnesses
This commit is contained in:
22
plans/harness/device-db-check.mjs
Normal file
22
plans/harness/device-db-check.mjs
Normal file
@@ -0,0 +1,22 @@
|
||||
// Plan 012 harness check. Needs playwright (npm i --no-save playwright) and a
|
||||
// Chromium: CHROMIUM=/path/to/chrome (default: the cloud image's /opt/pw-browsers one).
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({ executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome' });
|
||||
const p = await b.newPage();
|
||||
p.on('console', (m) => console.log('console:', m.text()));
|
||||
await p.goto('http://localhost:8766/');
|
||||
const out = await p.evaluate(async () => {
|
||||
const sha = await (await fetch('/sha')).text();
|
||||
const good = await OPFS.downloadVideo('good', '/api/download/good');
|
||||
const bad = await OPFS.downloadVideo('bad', '/api/download/bad');
|
||||
const nohash = await OPFS.downloadVideo('nohash', '/api/download/nohash');
|
||||
const list = (await OPFS.listVideos()).map((x) => x.id).sort();
|
||||
await DeviceDB.putFile({ videoId: 'good', cid: good.sha256, size: good.size, state: 'verified' });
|
||||
const rec = await DeviceDB.getFile('good');
|
||||
const byCid = await DeviceDB.getByCid(good.sha256);
|
||||
await DeviceDB.deleteFile('good');
|
||||
const after = await DeviceDB.listFiles();
|
||||
return { shaOk: good.sha256 === sha, goodExpected: good.expectedSha === sha, bad, nohashSha: nohash.sha256 === sha, nohashExpected: nohash.expectedSha, list, rec: rec && rec.state, byCid: byCid && byCid.videoId, after: after.length };
|
||||
});
|
||||
console.log(JSON.stringify(out));
|
||||
await b.close();
|
||||
17
plans/harness/device-db-server.js
Normal file
17
plans/harness/device-db-server.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Plan 012 harness: serves ../../frontend plus fake /api/download/<id> responses
|
||||
// (good hash / wrong hash / no hash). Run: bun device-db-server.js
|
||||
import { createHash } from 'node:crypto';
|
||||
const FE = new URL('../../frontend/', import.meta.url).pathname;
|
||||
const body = new Uint8Array(3 * 1024 * 1024 + 123).map((_, i) => (i * 7) % 256);
|
||||
const sha = createHash('sha256').update(body).digest('hex');
|
||||
Bun.serve({ port: 8766, fetch(req) {
|
||||
const u = new URL(req.url);
|
||||
if (u.pathname === '/api/download/good') return new Response(body, { headers: { 'Content-Type': 'video/mp4', 'Content-Length': String(body.length), 'X-Content-SHA256': sha } });
|
||||
if (u.pathname === '/api/download/bad') return new Response(body, { headers: { 'Content-Type': 'video/mp4', 'Content-Length': String(body.length), 'X-Content-SHA256': 'f'.repeat(64) } });
|
||||
if (u.pathname === '/api/download/nohash') return new Response(body, { headers: { 'Content-Type': 'video/mp4', 'Content-Length': String(body.length) } });
|
||||
if (u.pathname === '/sha') return new Response(sha);
|
||||
if (u.pathname === '/') return new Response('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script>', { headers: { 'Content-Type': 'text/html' } });
|
||||
const f = Bun.file(FE + u.pathname.slice(1));
|
||||
return new Response(f, { headers: { 'Content-Type': 'text/javascript' } });
|
||||
} });
|
||||
console.log('ready');
|
||||
19
plans/harness/intake-check.mjs
Normal file
19
plans/harness/intake-check.mjs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Plan 016 harness check: an unknown saved file → contribute → accepted.
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({ executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome' });
|
||||
const p = await b.newPage();
|
||||
await p.goto('http://localhost:8769/');
|
||||
const out = await p.evaluate(async () => {
|
||||
const bytes = new Uint8Array(await (await fetch('/fixture.mp4')).arrayBuffer());
|
||||
const dir = await (await navigator.storage.getDirectory()).getDirectoryHandle('videos', { create: true });
|
||||
const w = await (await dir.getFileHandle('upAAAAAAAA9.mp4', { create: true })).createWritable();
|
||||
await w.write(bytes); await w.close();
|
||||
P2PClient.start({ getSettings: () => ({}) });
|
||||
const first = await P2PClient.sync();
|
||||
const unknown = await P2PClient.unknownVideos();
|
||||
const c = await P2PClient.contribute(unknown[0]);
|
||||
const second = await P2PClient.sync();
|
||||
return { firstUnknown: first.unknown.length, unknown, contribute: { ok: c.ok, adopted: c.adopted, cidOk: /^[0-9a-f]{64}$/.test(c.cid) }, secondAccepted: second.accepted.length, secondUnknown: second.unknown.length };
|
||||
});
|
||||
console.log(JSON.stringify(out));
|
||||
await b.close();
|
||||
29
plans/harness/intake-server.js
Normal file
29
plans/harness/intake-server.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Plan 016 harness: routes + intake with real validateMedia (needs ffmpeg).
|
||||
// Run from server/: bun ../plans/harness/intake-server.js
|
||||
const { Hono } = await import(process.cwd() + '/node_modules/hono/dist/index.js');
|
||||
import { mkdtempSync, readFileSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
const root = mkdtempSync(join(tmpdir(), 'ytp-h16-'));
|
||||
process.env.DB_PATH = join(root, 'h.db');
|
||||
const dbmod = await import(process.cwd() + '/db.js');
|
||||
const p2pDb = await import(process.cwd() + '/p2p-db.js');
|
||||
const { registerP2pRoutes } = await import(process.cwd() + '/p2p-routes.js');
|
||||
const { registerIntakeRoutes } = await import(process.cwd() + '/p2p-intake.js');
|
||||
const { admitFile } = await import(process.cwd() + '/p2p-admit.js');
|
||||
const { validateMedia } = await import(process.cwd() + '/media-cache.js');
|
||||
const FE = process.env.FE_DIR || new URL('../../frontend', import.meta.url).pathname;
|
||||
await dbmod.initDb(); await p2pDb.initP2pSchema();
|
||||
const fx = join(root, 'fx.mp4');
|
||||
spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'testsrc=size=320x180:rate=25:duration=4', '-f', 'lavfi', '-i', 'sine=frequency=440:duration=4',
|
||||
'-c:v', 'libx264', '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-shortest', '-movflags', '+faststart', fx]);
|
||||
const cfg = { enabled: true, malwareScan: false, scanCmd: 'true', staleDays: 7, intakeDir: join(root, 'intake'), intakeMaxBytes: 50e6 };
|
||||
const app = new Hono();
|
||||
const p2p = registerP2pRoutes(app, { cfg, p2pDb, fileForCid: async () => null, sha256Range: async () => '' });
|
||||
registerIntakeRoutes(app, { cfg, p2pDb, gate: p2p.gate, requireDevice: p2p.requireDevice, validateMedia, admitFile });
|
||||
app.get('/fixture.mp4', () => new Response(readFileSync(fx)));
|
||||
app.get('/', (c) => c.html('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script><script src="/p2p-client.js"></script>'));
|
||||
app.get('/:f', (c) => new Response(Bun.file(FE + '/' + c.req.param('f')), { headers: { 'Content-Type': 'text/javascript' } }));
|
||||
Bun.serve({ port: 8769, fetch: app.fetch });
|
||||
console.log('ready');
|
||||
26
plans/harness/p2p-client-check.mjs
Normal file
26
plans/harness/p2p-client-check.mjs
Normal file
@@ -0,0 +1,26 @@
|
||||
// Plan 013 harness check (see device-db-check.mjs for requirements).
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({ executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome' });
|
||||
const p = await b.newPage();
|
||||
p.on('console', (m) => { if (m.type() === 'error') console.log('console:', m.text()); });
|
||||
await p.goto('http://localhost:8767/');
|
||||
const out = await p.evaluate(async () => {
|
||||
const good = await OPFS.downloadVideo('goodAAAAAAA', '/api/download/goodAAAAAAA');
|
||||
// A legacy save: in OPFS but no DeviceDB record, unknown to the server.
|
||||
const dir = await (await navigator.storage.getDirectory()).getDirectoryHandle('videos', { create: true });
|
||||
const w = await (await dir.getFileHandle('legacyAAAAA.mp4', { create: true })).createWritable();
|
||||
await w.write(new Uint8Array(1000).fill(9)); await w.close();
|
||||
await DeviceDB.putFile({ videoId: 'goodAAAAAAA', cid: good.sha256, size: good.size, state: 'unverified' });
|
||||
await DeviceDB.putFile({ videoId: 'ghostAAAAAA', cid: 'e'.repeat(64), size: 5, state: 'verified' }); // no file
|
||||
P2PClient.start({ getSettings: () => ({}) });
|
||||
const res = await P2PClient.sync();
|
||||
const recs = (await DeviceDB.listFiles()).map((r) => [r.videoId, r.state, !!r.cid]).sort();
|
||||
const holders = await (await fetch('/debug/holders')).json();
|
||||
// Turn sharing off → full withdrawal.
|
||||
P2PClient.start({ getSettings: () => ({ p2pShare: false }) });
|
||||
await P2PClient.sync();
|
||||
const after = await (await fetch('/debug/holders')).json();
|
||||
return { accepted: res && res.accepted.length, unknown: res && res.unknown.length, challenges: res && res.challenges.length, recs, trust: holders.map((h) => h.trust), afterShareOff: after.length, device: !!P2PClient.device() };
|
||||
});
|
||||
console.log(JSON.stringify(out));
|
||||
await b.close();
|
||||
27
plans/harness/p2p-client-server.js
Normal file
27
plans/harness/p2p-client-server.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// Plan 013 harness: real p2p-routes + temp DB + the frontend files.
|
||||
// Run from server/: bun ../plans/harness/p2p-client-server.js
|
||||
const { Hono } = await import(process.cwd() + '/node_modules/hono/dist/index.js');
|
||||
import { mkdtempSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
const root = mkdtempSync(join(tmpdir(), 'ytp-h13-'));
|
||||
process.env.DB_PATH = join(root, 'h.db');
|
||||
const dbmod = await import(process.cwd() + '/db.js');
|
||||
const p2pDb = await import(process.cwd() + '/p2p-db.js');
|
||||
const { registerP2pRoutes } = await import(process.cwd() + '/p2p-routes.js');
|
||||
const { sha256Range } = await import(process.cwd() + '/hash.js');
|
||||
const FE = process.env.FE_DIR || new URL('../../frontend', import.meta.url).pathname;
|
||||
await dbmod.initDb(); await p2pDb.initP2pSchema();
|
||||
const body = new Uint8Array(3 * 1024 * 1024 + 123).map((_, i) => (i * 7) % 256);
|
||||
const sha = createHash('sha256').update(body).digest('hex');
|
||||
const file = join(root, 'good.mp4'); writeFileSync(file, body);
|
||||
await p2pDb.upsertContent({ cid: sha, videoId: 'goodAAAAAAA', size: body.length, origin: 'server', now: Date.now() });
|
||||
const app = new Hono();
|
||||
registerP2pRoutes(app, { cfg: { enabled: true, staleDays: 7 }, p2pDb, fileForCid: async (c) => (c === sha ? { path: file, size: body.length } : null), sha256Range });
|
||||
app.get('/api/download/:id', () => new Response(body, { headers: { 'Content-Type': 'video/mp4', 'Content-Length': String(body.length), 'X-Content-SHA256': sha } }));
|
||||
app.get('/debug/holders', async (c) => c.json(await p2pDb.listHolders(sha)));
|
||||
app.get('/', (c) => c.html('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script><script src="/p2p-client.js"></script>'));
|
||||
app.get('/:f', (c) => new Response(Bun.file(FE + '/' + c.req.param('f')), { headers: { 'Content-Type': 'text/javascript' } }));
|
||||
Bun.serve({ port: 8767, fetch: app.fetch });
|
||||
console.log('ready');
|
||||
21
plans/harness/presence-check.mjs
Normal file
21
plans/harness/presence-check.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Plan 014 harness check: two browsers, one signal (see device-db-check.mjs for requirements).
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({ executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome' });
|
||||
const pages = [];
|
||||
for (let i = 0; i < 2; i++) { const ctx = await b.newContext(); const p = await ctx.newPage(); await p.goto('http://localhost:8768/'); pages.push(p); }
|
||||
const peers = [];
|
||||
for (const p of pages) {
|
||||
peers.push(await p.evaluate(async () => {
|
||||
P2PClient.start({ getSettings: () => ({}) });
|
||||
await P2PClient.sync();
|
||||
for (let i = 0; i < 50 && !P2PClient.isConnected(); i++) await new Promise((r) => setTimeout(r, 100));
|
||||
window.__got = [];
|
||||
P2PClient.onMessage('signal', (m) => window.__got.push(m));
|
||||
return P2PClient.peer();
|
||||
}));
|
||||
}
|
||||
const sent = await pages[0].evaluate((to) => P2PClient.signal(to, { hi: 1 }), peers[1]);
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
const got = await pages[1].evaluate(() => window.__got);
|
||||
console.log(JSON.stringify({ peers: peers.map((x) => typeof x === 'string' && x.length === 12), distinct: peers[0] !== peers[1], sent, got: got.map((m) => ({ from: m.from === peers[0], data: m.data })) }));
|
||||
await b.close();
|
||||
24
plans/harness/presence-server.js
Normal file
24
plans/harness/presence-server.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// Plan 014 harness: p2p-routes + p2p-hub on a real Bun server (websocket).
|
||||
// Run from server/: bun ../plans/harness/presence-server.js
|
||||
const { Hono } = await import(process.cwd() + '/node_modules/hono/dist/index.js');
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
const root = mkdtempSync(join(tmpdir(), 'ytp-h14-'));
|
||||
process.env.DB_PATH = join(root, 'h.db');
|
||||
const dbmod = await import(process.cwd() + '/db.js');
|
||||
const p2pDb = await import(process.cwd() + '/p2p-db.js');
|
||||
const { registerP2pRoutes } = await import(process.cwd() + '/p2p-routes.js');
|
||||
const { createP2pHub } = await import(process.cwd() + '/p2p-hub.js');
|
||||
const FE = process.env.FE_DIR || new URL('../../frontend', import.meta.url).pathname;
|
||||
await dbmod.initDb(); await p2pDb.initP2pSchema();
|
||||
const app = new Hono();
|
||||
registerP2pRoutes(app, { cfg: { enabled: true, staleDays: 7 }, p2pDb, fileForCid: async () => null, sha256Range: async () => '' });
|
||||
const hub = createP2pHub({ getDevice: p2pDb.getDevice });
|
||||
app.get('/', (c) => c.html('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script><script src="/p2p-client.js"></script>'));
|
||||
app.get('/:f', (c) => new Response(Bun.file(FE + '/' + c.req.param('f')), { headers: { 'Content-Type': 'text/javascript' } }));
|
||||
Bun.serve({ port: 8768, fetch(req, server) {
|
||||
if (new URL(req.url).pathname === '/ws/p2p') return hub.upgrade(req, server);
|
||||
return app.fetch(req, server);
|
||||
}, websocket: hub.websocket });
|
||||
console.log('ready');
|
||||
21
plans/harness/rehydrate-check.mjs
Normal file
21
plans/harness/rehydrate-check.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Plan 018 harness check: a holder is asked to restore an evicted copy.
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({ executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome' });
|
||||
const p = await b.newPage();
|
||||
await p.goto('http://localhost:8771/');
|
||||
const out = await p.evaluate(async () => {
|
||||
const bytes = new Uint8Array(await (await fetch('/fixture.mp4')).arrayBuffer());
|
||||
const dir = await (await navigator.storage.getDirectory()).getDirectoryHandle('videos', { create: true });
|
||||
const w = await (await dir.getFileHandle('goneAAAAAAA.mp4', { create: true })).createWritable();
|
||||
await w.write(bytes); await w.close();
|
||||
P2PClient.start({ getSettings: () => ({}) });
|
||||
const rep = await P2PClient.sync(); // hashes the file, reports it → accepted (cid is verified content)
|
||||
for (let i = 0; i < 50 && !P2PClient.isConnected(); i++) await new Promise((r) => setTimeout(r, 100));
|
||||
const s1 = await (await fetch('/api/streams?v=goneAAAAAAA')).json();
|
||||
let dbg;
|
||||
for (let i = 0; i < 50; i++) { dbg = await (await fetch('/debug')).json(); if (dbg.adopted.length) break; await new Promise((r) => setTimeout(r, 200)); }
|
||||
const s2 = await (await fetch('/api/streams?v=goneAAAAAAA')).json();
|
||||
return { accepted: rep.accepted.length, first: s1, adopted: dbg.adopted.map((a) => [a.videoId, a.sha256 === dbg.cid]), second: s2 };
|
||||
});
|
||||
console.log(JSON.stringify(out));
|
||||
await b.close();
|
||||
49
plans/harness/rehydrate-server.js
Normal file
49
plans/harness/rehydrate-server.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Plan 018 harness: hub + intake + rehydrator + a stand-in /api/streams whose
|
||||
// source always fails and whose server copy is gone. Port 8771.
|
||||
// Run from server/: bun ../plans/harness/rehydrate-server.js (needs ffmpeg)
|
||||
const { Hono } = await import(process.cwd() + '/node_modules/hono/dist/index.js');
|
||||
import { mkdtempSync, readFileSync, unlinkSync } from 'node:fs';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
const root = mkdtempSync(join(tmpdir(), 'ytp-h18-'));
|
||||
process.env.DB_PATH = join(root, 'h.db');
|
||||
const dbmod = await import(process.cwd() + '/db.js');
|
||||
const p2pDb = await import(process.cwd() + '/p2p-db.js');
|
||||
const { registerP2pRoutes } = await import(process.cwd() + '/p2p-routes.js');
|
||||
const { registerIntakeRoutes } = await import(process.cwd() + '/p2p-intake.js');
|
||||
const { createP2pHub, createRehydrator } = await import(process.cwd() + '/p2p-hub.js');
|
||||
const { admitFile } = await import(process.cwd() + '/p2p-admit.js');
|
||||
const { validateMedia } = await import(process.cwd() + '/media-cache.js');
|
||||
const FE = process.env.FE_DIR || new URL('../../frontend', import.meta.url).pathname;
|
||||
await dbmod.initDb(); await p2pDb.initP2pSchema();
|
||||
const fx = join(root, 'fx.mp4');
|
||||
spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'testsrc=size=320x180:rate=25:duration=4', '-f', 'lavfi', '-i', 'sine=frequency=440:duration=4',
|
||||
'-c:v', 'libx264', '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-shortest', '-movflags', '+faststart', fx]);
|
||||
const bytes = readFileSync(fx);
|
||||
const cid = createHash('sha256').update(bytes).digest('hex');
|
||||
await p2pDb.upsertContent({ cid, videoId: 'goneAAAAAAA', size: bytes.length, origin: 'server', now: Date.now() }); // verified earlier, file since evicted
|
||||
const cfg = { enabled: true, malwareScan: false, scanCmd: 'true', staleDays: 7, intakeDir: join(root, 'intake'), intakeMaxBytes: 50e6 };
|
||||
const adopted = [];
|
||||
const app = new Hono();
|
||||
const p2p = registerP2pRoutes(app, { cfg, p2pDb, fileForCid: async () => null, sha256Range: async () => '' });
|
||||
const hub = createP2pHub({ getDevice: p2pDb.getDevice });
|
||||
registerIntakeRoutes(app, { cfg, p2pDb, gate: p2p.gate, requireDevice: p2p.requireDevice, validateMedia, admitFile,
|
||||
serverHasCid: async () => adopted.length > 0,
|
||||
adopt: async (videoId, path, info) => { adopted.push({ videoId, sha256: info.sha256 }); unlinkSync(path); return { adopted: true }; } });
|
||||
const rehydrate = createRehydrator({ p2pDb, hub, hasServerCopy: async () => adopted.length > 0 });
|
||||
app.get('/api/streams', async (c) => {
|
||||
const v = c.req.query('v');
|
||||
if (await rehydrate(v)) return c.json({ ok: false, restoring: true, error: 'restoring from a device' }, 503);
|
||||
return c.json({ ok: false, error: 'source unavailable' }, 500);
|
||||
});
|
||||
app.get('/debug', (c) => c.json({ adopted, cid }));
|
||||
app.get('/fixture.mp4', () => new Response(bytes));
|
||||
app.get('/', (c) => c.html('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script><script src="/p2p-client.js"></script>'));
|
||||
app.get('/:f', (c) => new Response(Bun.file(FE + '/' + c.req.param('f')), { headers: { 'Content-Type': 'text/javascript' } }));
|
||||
Bun.serve({ port: 8771, fetch(req, server) {
|
||||
if (new URL(req.url).pathname === '/ws/p2p') return hub.upgrade(req, server);
|
||||
return app.fetch(req, server);
|
||||
}, websocket: hub.websocket });
|
||||
console.log('ready');
|
||||
52
plans/harness/transfer-check.mjs
Normal file
52
plans/harness/transfer-check.mjs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Plan 017 harness check: device A holds a file; device B downloads it over WebRTC.
|
||||
import { chromium } from 'playwright';
|
||||
const b = await chromium.launch({
|
||||
executablePath: process.env.CHROMIUM || '/opt/pw-browsers/chromium-1194/chrome-linux/chrome',
|
||||
args: ['--disable-features=WebRtcHideLocalIpsWithMdns'],
|
||||
});
|
||||
const pages = [];
|
||||
for (let i = 0; i < 2; i++) { const ctx = await b.newContext(); const p = await ctx.newPage(); p.on('console', (m) => { if (m.type() === 'error') console.log('console', i, m.text()); }); await p.goto('http://localhost:8770/'); pages.push(p); }
|
||||
const setup = (p) => p.evaluate(async () => {
|
||||
P2PClient.start({ getSettings: () => ({}) });
|
||||
await P2PClient.sync();
|
||||
for (let i = 0; i < 50 && !P2PClient.isConnected(); i++) await new Promise((r) => setTimeout(r, 100));
|
||||
P2PTransfer.start({ iceServers: [] });
|
||||
return P2PClient.peer();
|
||||
});
|
||||
// Holder: write a 5 MB file + registry record.
|
||||
const held = await pages[0].evaluate(async () => {
|
||||
const bytes = new Uint8Array(5 * 1024 * 1024 + 777).map((_, i) => (i * 31 + 7) % 256);
|
||||
const dir = await (await navigator.storage.getDirectory()).getDirectoryHandle('videos', { create: true });
|
||||
const w = await (await dir.getFileHandle('heldAAAAAAA.mp4', { create: true })).createWritable();
|
||||
await w.write(bytes); await w.close();
|
||||
const cid = Sha256.hex(bytes);
|
||||
await DeviceDB.putFile({ videoId: 'heldAAAAAAA', cid, size: bytes.length, state: 'verified' });
|
||||
return { cid, size: bytes.length };
|
||||
});
|
||||
const peerA = await setup(pages[0]);
|
||||
await setup(pages[1]);
|
||||
const t0 = Date.now();
|
||||
const res = await pages[1].evaluate(async ({ peerA, held }) => {
|
||||
const good = await P2PTransfer.download({ videoId: 'gotAAAAAAAA', cid: held.cid, size: held.size, peers: [{ peer: peerA }] });
|
||||
const list = (await OPFS.listVideos()).map((x) => [x.id, x.size]);
|
||||
const bad = await P2PTransfer.download({ videoId: 'badAAAAAAAA', cid: 'f'.repeat(64), size: held.size, peers: [{ peer: peerA }] });
|
||||
const list2 = (await OPFS.listVideos()).map((x) => x.id);
|
||||
return { good, list, bad, list2 };
|
||||
}, { peerA, held });
|
||||
// Holder's file gets corrupted (same size, one byte flipped) but its record still claims the cid.
|
||||
await pages[0].evaluate(async () => {
|
||||
const dir = await (await navigator.storage.getDirectory()).getDirectoryHandle('videos');
|
||||
const fh = await dir.getFileHandle('heldAAAAAAA.mp4');
|
||||
const bytes = new Uint8Array(await (await fh.getFile()).arrayBuffer());
|
||||
bytes[12345] ^= 0xff;
|
||||
const w = await fh.createWritable(); await w.write(bytes); await w.close();
|
||||
});
|
||||
res.corrupt = await pages[1].evaluate(async ({ peerA, held }) => {
|
||||
const r = await P2PTransfer.download({ videoId: 'corAAAAAAAA', cid: held.cid, size: held.size, peers: [{ peer: peerA }] });
|
||||
await new Promise((x) => setTimeout(x, 300));
|
||||
const names = [];
|
||||
for await (const [n] of (await (await navigator.storage.getDirectory()).getDirectoryHandle('videos')).entries()) names.push(n);
|
||||
return { r, names: names.sort() };
|
||||
}, { peerA, held });
|
||||
console.log(JSON.stringify({ ...res, ms: Date.now() - t0 }));
|
||||
await b.close();
|
||||
24
plans/harness/transfer-server.js
Normal file
24
plans/harness/transfer-server.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// Plan 017 harness: p2p-routes + p2p-hub + the transfer scripts (port 8770).
|
||||
// Run from server/: bun ../plans/harness/transfer-server.js
|
||||
const { Hono } = await import(process.cwd() + '/node_modules/hono/dist/index.js');
|
||||
import { mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
const root = mkdtempSync(join(tmpdir(), 'ytp-h14-'));
|
||||
process.env.DB_PATH = join(root, 'h.db');
|
||||
const dbmod = await import(process.cwd() + '/db.js');
|
||||
const p2pDb = await import(process.cwd() + '/p2p-db.js');
|
||||
const { registerP2pRoutes } = await import(process.cwd() + '/p2p-routes.js');
|
||||
const { createP2pHub } = await import(process.cwd() + '/p2p-hub.js');
|
||||
const FE = process.env.FE_DIR || new URL('../../frontend', import.meta.url).pathname;
|
||||
await dbmod.initDb(); await p2pDb.initP2pSchema();
|
||||
const app = new Hono();
|
||||
registerP2pRoutes(app, { cfg: { enabled: true, staleDays: 7 }, p2pDb, fileForCid: async () => null, sha256Range: async () => '' });
|
||||
const hub = createP2pHub({ getDevice: p2pDb.getDevice });
|
||||
app.get('/', (c) => c.html('<!doctype html><script src="/opfs.js"></script><script src="/sha256.js"></script><script src="/device-db.js"></script><script src="/p2p-client.js"></script><script src="/p2p-transfer.js"></script>'));
|
||||
app.get('/:f', (c) => new Response(Bun.file(FE + '/' + c.req.param('f')), { headers: { 'Content-Type': 'text/javascript' } }));
|
||||
Bun.serve({ port: 8770, fetch(req, server) {
|
||||
if (new URL(req.url).pathname === '/ws/p2p') return hub.upgrade(req, server);
|
||||
return app.fetch(req, server);
|
||||
}, websocket: hub.websocket });
|
||||
console.log('ready');
|
||||
Reference in New Issue
Block a user