mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 17:15:03 -07:00
250 lines
8.4 KiB
TypeScript
250 lines
8.4 KiB
TypeScript
import { BufferReader, BufferWriter } from './sv2-binary-codec';
|
|
|
|
describe('SV2 Binary Codec', () => {
|
|
// ── Round-trip Tests ────────────────────────────────────────────────
|
|
|
|
describe('BufferWriter + BufferReader round-trips', () => {
|
|
it('should round-trip Bool', () => {
|
|
const w = new BufferWriter();
|
|
w.writeBool(true);
|
|
w.writeBool(false);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readBool()).toBe(true);
|
|
expect(r.readBool()).toBe(false);
|
|
});
|
|
|
|
it('should round-trip U8', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU8(0);
|
|
w.writeU8(255);
|
|
w.writeU8(42);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU8()).toBe(0);
|
|
expect(r.readU8()).toBe(255);
|
|
expect(r.readU8()).toBe(42);
|
|
});
|
|
|
|
it('should round-trip U16', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU16(0);
|
|
w.writeU16(65535);
|
|
w.writeU16(1234);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU16()).toBe(0);
|
|
expect(r.readU16()).toBe(65535);
|
|
expect(r.readU16()).toBe(1234);
|
|
});
|
|
|
|
it('should round-trip U24', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU24(0);
|
|
w.writeU24(0xffffff);
|
|
w.writeU24(123456);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU24()).toBe(0);
|
|
expect(r.readU24()).toBe(0xffffff);
|
|
expect(r.readU24()).toBe(123456);
|
|
});
|
|
|
|
it('should round-trip U32', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU32(0);
|
|
w.writeU32(0xffffffff);
|
|
w.writeU32(305419896);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU32()).toBe(0);
|
|
expect(r.readU32()).toBe(0xffffffff);
|
|
expect(r.readU32()).toBe(305419896);
|
|
});
|
|
|
|
it('should round-trip U64', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU64(0n);
|
|
w.writeU64(0xffffffffffffffffn);
|
|
w.writeU64(123456789012345678n);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU64()).toBe(0n);
|
|
expect(r.readU64()).toBe(0xffffffffffffffffn);
|
|
expect(r.readU64()).toBe(123456789012345678n);
|
|
});
|
|
|
|
it('should round-trip U256', () => {
|
|
const val = Buffer.alloc(32);
|
|
for (let i = 0; i < 32; i++) val[i] = i;
|
|
const w = new BufferWriter();
|
|
w.writeU256(val);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU256()).toEqual(val);
|
|
});
|
|
|
|
it('should round-trip F32', () => {
|
|
const w = new BufferWriter();
|
|
w.writeF32(3.14);
|
|
w.writeF32(0);
|
|
w.writeF32(-1.5);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readF32()).toBeCloseTo(3.14, 2);
|
|
expect(r.readF32()).toBe(0);
|
|
expect(r.readF32()).toBe(-1.5);
|
|
});
|
|
|
|
it('should round-trip Str0_255', () => {
|
|
const w = new BufferWriter();
|
|
w.writeStr0_255('');
|
|
w.writeStr0_255('hello');
|
|
w.writeStr0_255('mining/pool/1.0');
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readStr0_255()).toBe('');
|
|
expect(r.readStr0_255()).toBe('hello');
|
|
expect(r.readStr0_255()).toBe('mining/pool/1.0');
|
|
});
|
|
|
|
it('should round-trip B0_32', () => {
|
|
const val = Buffer.from([1, 2, 3, 4]);
|
|
const w = new BufferWriter();
|
|
w.writeB0_32(Buffer.alloc(0));
|
|
w.writeB0_32(val);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readB0_32()).toEqual(Buffer.alloc(0));
|
|
expect(r.readB0_32()).toEqual(val);
|
|
});
|
|
|
|
it('should round-trip B0_255', () => {
|
|
const val = Buffer.alloc(100, 0xab);
|
|
const w = new BufferWriter();
|
|
w.writeB0_255(val);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readB0_255()).toEqual(val);
|
|
});
|
|
|
|
it('should round-trip B0_64K', () => {
|
|
const val = Buffer.alloc(1000, 0xcd);
|
|
const w = new BufferWriter();
|
|
w.writeB0_64K(val);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readB0_64K()).toEqual(val);
|
|
});
|
|
|
|
it('should round-trip Seq0_255', () => {
|
|
const items = [10, 20, 30];
|
|
const w = new BufferWriter();
|
|
w.writeSeq0_255(items, (wr, v) => wr.writeU32(v));
|
|
const r = new BufferReader(w.toBuffer());
|
|
const result = r.readSeq0_255((rd) => rd.readU32());
|
|
expect(result).toEqual(items);
|
|
});
|
|
|
|
it('should round-trip Seq0_64K', () => {
|
|
const items = ['alpha', 'beta'];
|
|
const w = new BufferWriter();
|
|
w.writeSeq0_64K(items, (wr, v) => wr.writeStr0_255(v));
|
|
const r = new BufferReader(w.toBuffer());
|
|
const result = r.readSeq0_64K((rd) => rd.readStr0_255());
|
|
expect(result).toEqual(items);
|
|
});
|
|
});
|
|
|
|
// ── Little-endian Verification ──────────────────────────────────────
|
|
|
|
describe('little-endian encoding', () => {
|
|
it('U16 should be little-endian', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU16(0x0102);
|
|
const buf = w.toBuffer();
|
|
expect(buf[0]).toBe(0x02); // low byte first
|
|
expect(buf[1]).toBe(0x01);
|
|
});
|
|
|
|
it('U32 should be little-endian', () => {
|
|
const w = new BufferWriter();
|
|
w.writeU32(0x01020304);
|
|
const buf = w.toBuffer();
|
|
expect(buf[0]).toBe(0x04);
|
|
expect(buf[1]).toBe(0x03);
|
|
expect(buf[2]).toBe(0x02);
|
|
expect(buf[3]).toBe(0x01);
|
|
});
|
|
});
|
|
|
|
// ── Bounds Checking ─────────────────────────────────────────────────
|
|
|
|
describe('bounds checking', () => {
|
|
it('BufferReader should throw on underflow', () => {
|
|
const r = new BufferReader(Buffer.alloc(1));
|
|
r.readU8(); // consumes the one byte
|
|
expect(() => r.readU8()).toThrow(RangeError);
|
|
});
|
|
|
|
it('BufferReader should throw when reading U32 from 2-byte buffer', () => {
|
|
const r = new BufferReader(Buffer.alloc(2));
|
|
expect(() => r.readU32()).toThrow(RangeError);
|
|
});
|
|
|
|
it('B0_32 should reject payloads > 32 bytes', () => {
|
|
const w = new BufferWriter();
|
|
expect(() => w.writeB0_32(Buffer.alloc(33))).toThrow(RangeError);
|
|
});
|
|
|
|
it('B0_32 reader should reject length > 32', () => {
|
|
// Craft a buffer with length prefix = 33
|
|
const buf = Buffer.alloc(34);
|
|
buf[0] = 33;
|
|
const r = new BufferReader(buf);
|
|
expect(() => r.readB0_32()).toThrow(RangeError);
|
|
});
|
|
|
|
it('U256 writer should reject non-32-byte buffers', () => {
|
|
const w = new BufferWriter();
|
|
expect(() => w.writeU256(Buffer.alloc(31))).toThrow(RangeError);
|
|
});
|
|
|
|
it('Str0_255 should reject strings > 255 bytes', () => {
|
|
const w = new BufferWriter();
|
|
expect(() => w.writeStr0_255('a'.repeat(256))).toThrow(RangeError);
|
|
});
|
|
|
|
it('remaining should track consumed bytes', () => {
|
|
const r = new BufferReader(Buffer.alloc(10));
|
|
expect(r.remaining).toBe(10);
|
|
r.readU32();
|
|
expect(r.remaining).toBe(6);
|
|
expect(r.position).toBe(4);
|
|
});
|
|
});
|
|
|
|
// ── Edge Cases ──────────────────────────────────────────────────────
|
|
|
|
describe('edge cases', () => {
|
|
it('empty buffer reader has 0 remaining', () => {
|
|
const r = new BufferReader(Buffer.alloc(0));
|
|
expect(r.remaining).toBe(0);
|
|
});
|
|
|
|
it('empty Seq0_255 round-trips', () => {
|
|
const w = new BufferWriter();
|
|
w.writeSeq0_255([], (wr, v) => wr.writeU8(v as number));
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readSeq0_255((rd) => rd.readU8())).toEqual([]);
|
|
});
|
|
|
|
it('maximum B0_32 (32 bytes) round-trips', () => {
|
|
const val = Buffer.alloc(32, 0xff);
|
|
const w = new BufferWriter();
|
|
w.writeB0_32(val);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readB0_32()).toEqual(val);
|
|
});
|
|
|
|
it('writeU32 handles signed int32 from bitcoinjs block.version', () => {
|
|
// bitcoinjs-lib stores block.version as signed Int32 (readInt32).
|
|
// Version rolling or BIP9 signaling can set bit 31, producing
|
|
// a negative JS number. writeU32 must handle this via >>> 0.
|
|
const signedVersion = -1554660091; // 0xA355C505 unsigned
|
|
const w = new BufferWriter();
|
|
w.writeU32(signedVersion);
|
|
const r = new BufferReader(w.toBuffer());
|
|
expect(r.readU32()).toBe(0xA355C505);
|
|
});
|
|
});
|
|
});
|