Skip to content

Commit 81ad117

Browse files
author
Your Name
committed
these were created while reviewing the bounty submissions.
1 parent c2f0629 commit 81ad117

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

test-libp2p-handshake.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
const { FrameDecoder, FrameEncoder, encodeFrame } = require('./src/FrameCodec');
2+
const { once } = require('events');
3+
4+
// Simulate libp2p multistream-select handshake protocol messages
5+
const MULTISTREAM_PROTOCOL = '/multistream/1.0.0\n';
6+
const NOISE_PROTOCOL = '/noise\n';
7+
const YAMUX_PROTOCOL = '/yamux/1.0.0\n';
8+
9+
async function simulateLibp2pHandshake() {
10+
console.log('=== Simulating libp2p multistream-select handshake ===\n');
11+
12+
const encoder = new FrameEncoder();
13+
const decoder = new FrameDecoder();
14+
15+
const received = [];
16+
decoder.on('data', (frame) => {
17+
const msg = frame.toString();
18+
console.log(`Received frame (${frame.length} bytes): ${JSON.stringify(msg)}`);
19+
received.push(msg);
20+
});
21+
22+
// Encode libp2p protocol negotiation messages
23+
const messages = [
24+
Buffer.from(MULTISTREAM_PROTOCOL),
25+
Buffer.from(NOISE_PROTOCOL),
26+
Buffer.from(YAMUX_PROTOCOL)
27+
];
28+
29+
console.log('Encoding messages as varint-prefixed frames:');
30+
const frames = [];
31+
for (const msg of messages) {
32+
const frame = encodeFrame(msg);
33+
console.log(` "${msg.toString().trim()}" -> ${frame.length} bytes (varint: ${frame[0]}, payload: ${msg.length})`);
34+
frames.push(frame);
35+
}
36+
37+
const wire = Buffer.concat(frames);
38+
console.log(`\nWire format: ${wire.length} bytes total\n`);
39+
40+
// Simulate TCP fragmentation - split at arbitrary boundaries
41+
const fragments = [
42+
wire.slice(0, 5), // Split in middle of first message
43+
wire.slice(5, 12), // Split across varint boundary
44+
wire.slice(12, 20), // Partial second message
45+
wire.slice(20, 25), // Fragment
46+
wire.slice(25) // Rest
47+
];
48+
49+
console.log('Simulating TCP fragmentation:');
50+
fragments.forEach((frag, i) => {
51+
console.log(` Fragment ${i}: ${frag.length} bytes`);
52+
});
53+
console.log();
54+
55+
// Feed fragmented chunks to decoder
56+
console.log('Feeding fragments to decoder:\n');
57+
for (const fragment of fragments) {
58+
decoder.write(fragment);
59+
}
60+
decoder.end();
61+
62+
await once(decoder, 'end');
63+
64+
console.log(`\n=== Results ===`);
65+
console.log(`Expected ${messages.length} messages, received ${received.length}`);
66+
console.log(`Frame reconstruction: ${received.length === messages.length ? 'PASS ✓' : 'FAIL ✗'}`);
67+
68+
for (let i = 0; i < messages.length; i++) {
69+
const expected = messages[i].toString();
70+
const actual = received[i] || '';
71+
const match = expected === actual;
72+
console.log(` Message ${i}: ${match ? 'PASS ✓' : 'FAIL ✗'} ${match ? '' : `(expected "${expected.trim()}", got "${actual.trim()}")`}`);
73+
}
74+
75+
if (received.length === messages.length && received.every((msg, i) => msg === messages[i].toString())) {
76+
console.log('\n🎉 libp2p multistream-select handshake would succeed!');
77+
console.log(' No UnexpectedEOFError - frames reconstructed correctly across fragmentation');
78+
} else {
79+
console.log('\n❌ Handshake would fail - frame reconstruction error');
80+
}
81+
}
82+
83+
simulateLibp2pHandshake().catch(console.error);

test-tcp.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { FrameDecoder, encodeFrame } = require('./src/FrameCodec');
2+
const { once } = require('events');
3+
const crypto = require('crypto');
4+
5+
let _seed = 0xdeadbeef;
6+
function seededRandom() {
7+
_seed = (_seed * 1664525 + 1013904223) >>> 0;
8+
return _seed / 0x100000000;
9+
}
10+
11+
function randomInt(min, max) {
12+
return Math.floor(seededRandom() * (max - min + 1)) + min;
13+
}
14+
15+
async function decodeWithFrameDecoder(chunks) {
16+
const decoder = new FrameDecoder();
17+
const frames = [];
18+
decoder.on('data', (frame) => frames.push(Buffer.from(frame)));
19+
const errorPromise = once(decoder, 'error').then(([err]) => { throw err; });
20+
const endPromise = once(decoder, 'end').then(() => frames);
21+
(async () => {
22+
for (const chunk of chunks) {
23+
if (!decoder.write(chunk)) await once(decoder, 'drain');
24+
}
25+
decoder.end();
26+
})().catch((err) => decoder.emit('error', err));
27+
return Promise.race([endPromise, errorPromise]);
28+
}
29+
30+
(async () => {
31+
const iterations = 10000;
32+
const start = Date.now();
33+
for (let i = 0; i < iterations; i++) {
34+
const frameCount = randomInt(1, 12);
35+
const frames = [];
36+
for (let j = 0; j < frameCount; j++) frames.push(crypto.randomBytes(randomInt(0, 512)));
37+
const wire = Buffer.concat(frames.map((frame) => encodeFrame(frame)));
38+
const chunks = [];
39+
let offset = 0;
40+
while (offset < wire.length) {
41+
const chunkLen = randomInt(1, Math.min(64, wire.length - offset));
42+
chunks.push(wire.slice(offset, offset + chunkLen));
43+
offset += chunkLen;
44+
}
45+
const decoded = await decodeWithFrameDecoder(chunks);
46+
if (decoded.length !== frames.length) throw new Error(`Round ${i}: expected ${frames.length}, got ${decoded.length}`);
47+
for (let k = 0; k < frames.length; k++) {
48+
if (!decoded[k].equals(frames[k])) throw new Error(`Round ${i}: frame ${k} corruption`);
49+
}
50+
}
51+
console.log(`${iterations} rounds: ${Date.now() - start}ms`);
52+
})();

0 commit comments

Comments
 (0)