@webbuf/numbers
Version:
Fixed-sized numbers (unsigned, signed, float) optimized with Rust/WASM for the web, node.js, deno, and bun.
859 lines (745 loc) • 26.2 kB
text/typescript
/**
* Audit tests for @webbuf/numbers
*
* These tests verify correct byte ordering (endianness) and boundary values
* by comparing against DataView, which is the standard JavaScript API for
* reading/writing multi-byte numbers with explicit endianness.
*/
import { describe, it, expect } from "vitest";
import {
U8,
U16BE,
U16LE,
U32BE,
U32LE,
U64BE,
U64LE,
U128BE,
U128LE,
U256BE,
U256LE,
} from "../src/index.js";
describe("Audit: U8 boundary values", () => {
it("should handle minimum value (0)", () => {
const u8 = U8.fromN(0);
expect(u8.n).toBe(0);
expect(u8.toHex()).toBe("00");
});
it("should handle maximum value (255)", () => {
const u8 = U8.fromN(255);
expect(u8.n).toBe(255);
expect(u8.toHex()).toBe("ff");
});
it("should reject values above 255", () => {
expect(() => U8.fromN(256)).toThrow();
});
it("should reject negative values", () => {
expect(() => U8.fromN(-1)).toThrow();
});
it("should match DataView for all byte values", () => {
for (let i = 0; i <= 255; i++) {
const u8 = U8.fromN(i);
const buf = new Uint8Array(1);
buf[0] = i;
expect(u8.buf.buf[0]).toBe(buf[0]);
}
});
});
describe("Audit: U16 endianness verification", () => {
describe("U16BE (Big Endian)", () => {
it("should handle minimum value (0)", () => {
const u16 = U16BE.fromN(0);
expect(u16.n).toBe(0);
expect(u16.toHex()).toBe("0000");
});
it("should handle maximum value (65535)", () => {
const u16 = U16BE.fromN(65535);
expect(u16.n).toBe(65535);
expect(u16.toHex()).toBe("ffff");
});
it("should match DataView big-endian byte ordering", () => {
const testValues = [0, 1, 255, 256, 0x0102, 0x1234, 0xabcd, 65535];
for (const value of testValues) {
const u16 = U16BE.fromN(value);
const buf = new ArrayBuffer(2);
const view = new DataView(buf);
view.setUint16(0, value, false); // false = big-endian
expect(u16.buf.buf[0]).toBe(new Uint8Array(buf)[0]);
expect(u16.buf.buf[1]).toBe(new Uint8Array(buf)[1]);
}
});
it("should encode 0x0102 as [01, 02]", () => {
const u16 = U16BE.fromN(0x0102);
expect(u16.buf.buf[0]).toBe(0x01);
expect(u16.buf.buf[1]).toBe(0x02);
});
});
describe("U16LE (Little Endian)", () => {
it("should handle minimum value (0)", () => {
const u16 = U16LE.fromN(0);
expect(u16.n).toBe(0);
expect(u16.toHex()).toBe("0000");
});
it("should handle maximum value (65535)", () => {
const u16 = U16LE.fromN(65535);
expect(u16.n).toBe(65535);
expect(u16.toHex()).toBe("ffff");
});
it("should match DataView little-endian byte ordering", () => {
const testValues = [0, 1, 255, 256, 0x0102, 0x1234, 0xabcd, 65535];
for (const value of testValues) {
const u16 = U16LE.fromN(value);
const buf = new ArrayBuffer(2);
const view = new DataView(buf);
view.setUint16(0, value, true); // true = little-endian
expect(u16.buf.buf[0]).toBe(new Uint8Array(buf)[0]);
expect(u16.buf.buf[1]).toBe(new Uint8Array(buf)[1]);
}
});
it("should encode 0x0102 as [02, 01]", () => {
const u16 = U16LE.fromN(0x0102);
expect(u16.buf.buf[0]).toBe(0x02);
expect(u16.buf.buf[1]).toBe(0x01);
});
});
describe("U16BE vs U16LE byte order difference", () => {
it("should produce reversed byte order for same value", () => {
const value = 0x1234;
const be = U16BE.fromN(value);
const le = U16LE.fromN(value);
// BE: [12, 34], LE: [34, 12]
expect(be.buf.buf[0]).toBe(0x12);
expect(be.buf.buf[1]).toBe(0x34);
expect(le.buf.buf[0]).toBe(0x34);
expect(le.buf.buf[1]).toBe(0x12);
});
});
});
describe("Audit: U32 endianness verification", () => {
describe("U32BE (Big Endian)", () => {
it("should handle minimum value (0)", () => {
const u32 = U32BE.fromN(0);
expect(u32.n).toBe(0);
expect(u32.toHex()).toBe("00000000");
});
it("should handle maximum value (4294967295)", () => {
const u32 = U32BE.fromN(4294967295);
expect(u32.n).toBe(4294967295);
expect(u32.toHex()).toBe("ffffffff");
});
it("should match DataView big-endian byte ordering", () => {
const testValues = [
0,
1,
255,
256,
65535,
65536,
0x01020304,
0x12345678,
0xdeadbeef,
4294967295,
];
for (const value of testValues) {
const u32 = U32BE.fromN(value);
const buf = new ArrayBuffer(4);
const view = new DataView(buf);
view.setUint32(0, value, false); // false = big-endian
const expected = new Uint8Array(buf);
expect(u32.buf.buf[0]).toBe(expected[0]);
expect(u32.buf.buf[1]).toBe(expected[1]);
expect(u32.buf.buf[2]).toBe(expected[2]);
expect(u32.buf.buf[3]).toBe(expected[3]);
}
});
it("should encode 0x01020304 as [01, 02, 03, 04]", () => {
const u32 = U32BE.fromN(0x01020304);
expect(u32.buf.buf[0]).toBe(0x01);
expect(u32.buf.buf[1]).toBe(0x02);
expect(u32.buf.buf[2]).toBe(0x03);
expect(u32.buf.buf[3]).toBe(0x04);
});
});
describe("U32LE (Little Endian)", () => {
it("should handle minimum value (0)", () => {
const u32 = U32LE.fromN(0);
expect(u32.n).toBe(0);
expect(u32.toHex()).toBe("00000000");
});
it("should handle maximum value (4294967295)", () => {
const u32 = U32LE.fromN(4294967295);
expect(u32.n).toBe(4294967295);
expect(u32.toHex()).toBe("ffffffff");
});
it("should match DataView little-endian byte ordering", () => {
const testValues = [
0,
1,
255,
256,
65535,
65536,
0x01020304,
0x12345678,
0xdeadbeef,
4294967295,
];
for (const value of testValues) {
const u32 = U32LE.fromN(value);
const buf = new ArrayBuffer(4);
const view = new DataView(buf);
view.setUint32(0, value, true); // true = little-endian
const expected = new Uint8Array(buf);
expect(u32.buf.buf[0]).toBe(expected[0]);
expect(u32.buf.buf[1]).toBe(expected[1]);
expect(u32.buf.buf[2]).toBe(expected[2]);
expect(u32.buf.buf[3]).toBe(expected[3]);
}
});
it("should encode 0x01020304 as [04, 03, 02, 01]", () => {
const u32 = U32LE.fromN(0x01020304);
expect(u32.buf.buf[0]).toBe(0x04);
expect(u32.buf.buf[1]).toBe(0x03);
expect(u32.buf.buf[2]).toBe(0x02);
expect(u32.buf.buf[3]).toBe(0x01);
});
});
describe("U32BE vs U32LE byte order difference", () => {
it("should produce reversed byte order for same value", () => {
const value = 0x12345678;
const be = U32BE.fromN(value);
const le = U32LE.fromN(value);
// BE: [12, 34, 56, 78], LE: [78, 56, 34, 12]
expect(be.buf.buf[0]).toBe(0x12);
expect(be.buf.buf[1]).toBe(0x34);
expect(be.buf.buf[2]).toBe(0x56);
expect(be.buf.buf[3]).toBe(0x78);
expect(le.buf.buf[0]).toBe(0x78);
expect(le.buf.buf[1]).toBe(0x56);
expect(le.buf.buf[2]).toBe(0x34);
expect(le.buf.buf[3]).toBe(0x12);
});
});
});
describe("Audit: U64 endianness verification", () => {
describe("U64BE (Big Endian)", () => {
it("should handle minimum value (0)", () => {
const u64 = U64BE.fromBn(0n);
expect(u64.bn).toBe(0n);
expect(u64.toHex()).toBe("0000000000000000");
});
it("should handle maximum value (2^64 - 1)", () => {
const max = 2n ** 64n - 1n;
const u64 = U64BE.fromBn(max);
expect(u64.bn).toBe(max);
expect(u64.toHex()).toBe("ffffffffffffffff");
});
it("should match DataView big-endian byte ordering", () => {
const testValues = [
0n,
1n,
255n,
256n,
65535n,
65536n,
0x0102030405060708n,
0x123456789abcdef0n,
2n ** 64n - 1n,
];
for (const value of testValues) {
const u64 = U64BE.fromBn(value);
const buf = new ArrayBuffer(8);
const view = new DataView(buf);
view.setBigUint64(0, value, false); // false = big-endian
const expected = new Uint8Array(buf);
for (let i = 0; i < 8; i++) {
expect(u64.buf.buf[i]).toBe(expected[i]);
}
}
});
it("should encode 0x0102030405060708 correctly", () => {
const u64 = U64BE.fromBn(0x0102030405060708n);
expect(u64.buf.buf[0]).toBe(0x01);
expect(u64.buf.buf[1]).toBe(0x02);
expect(u64.buf.buf[2]).toBe(0x03);
expect(u64.buf.buf[3]).toBe(0x04);
expect(u64.buf.buf[4]).toBe(0x05);
expect(u64.buf.buf[5]).toBe(0x06);
expect(u64.buf.buf[6]).toBe(0x07);
expect(u64.buf.buf[7]).toBe(0x08);
});
});
describe("U64LE (Little Endian)", () => {
it("should handle minimum value (0)", () => {
const u64 = U64LE.fromBn(0n);
expect(u64.bn).toBe(0n);
expect(u64.toHex()).toBe("0000000000000000");
});
it("should handle maximum value (2^64 - 1)", () => {
const max = 2n ** 64n - 1n;
const u64 = U64LE.fromBn(max);
expect(u64.bn).toBe(max);
expect(u64.toHex()).toBe("ffffffffffffffff");
});
it("should match DataView little-endian byte ordering", () => {
const testValues = [
0n,
1n,
255n,
256n,
65535n,
65536n,
0x0102030405060708n,
0x123456789abcdef0n,
2n ** 64n - 1n,
];
for (const value of testValues) {
const u64 = U64LE.fromBn(value);
const buf = new ArrayBuffer(8);
const view = new DataView(buf);
view.setBigUint64(0, value, true); // true = little-endian
const expected = new Uint8Array(buf);
for (let i = 0; i < 8; i++) {
expect(u64.buf.buf[i]).toBe(expected[i]);
}
}
});
it("should encode 0x0102030405060708 correctly", () => {
const u64 = U64LE.fromBn(0x0102030405060708n);
expect(u64.buf.buf[0]).toBe(0x08);
expect(u64.buf.buf[1]).toBe(0x07);
expect(u64.buf.buf[2]).toBe(0x06);
expect(u64.buf.buf[3]).toBe(0x05);
expect(u64.buf.buf[4]).toBe(0x04);
expect(u64.buf.buf[5]).toBe(0x03);
expect(u64.buf.buf[6]).toBe(0x02);
expect(u64.buf.buf[7]).toBe(0x01);
});
});
describe("U64BE vs U64LE byte order difference", () => {
it("should produce reversed byte order for same value", () => {
const value = 0x0102030405060708n;
const be = U64BE.fromBn(value);
const le = U64LE.fromBn(value);
// Bytes should be reversed
for (let i = 0; i < 8; i++) {
expect(be.buf.buf[i]).toBe(le.buf.buf[7 - i]);
}
});
});
});
describe("Audit: U128 endianness verification", () => {
describe("U128BE (Big Endian)", () => {
it("should handle minimum value (0)", () => {
const u128 = U128BE.fromBn(0n);
expect(u128.bn).toBe(0n);
expect(u128.toHex()).toBe("00000000000000000000000000000000");
});
it("should handle maximum value (2^128 - 1)", () => {
const max = 2n ** 128n - 1n;
const u128 = U128BE.fromBn(max);
expect(u128.bn).toBe(max);
expect(u128.toHex()).toBe("ffffffffffffffffffffffffffffffff");
});
it("should encode known value correctly (big-endian)", () => {
// 0x0102030405060708090a0b0c0d0e0f10
const value = 0x0102030405060708090a0b0c0d0e0f10n;
const u128 = U128BE.fromBn(value);
expect(u128.buf.buf[0]).toBe(0x01);
expect(u128.buf.buf[1]).toBe(0x02);
expect(u128.buf.buf[2]).toBe(0x03);
expect(u128.buf.buf[3]).toBe(0x04);
expect(u128.buf.buf[4]).toBe(0x05);
expect(u128.buf.buf[5]).toBe(0x06);
expect(u128.buf.buf[6]).toBe(0x07);
expect(u128.buf.buf[7]).toBe(0x08);
expect(u128.buf.buf[8]).toBe(0x09);
expect(u128.buf.buf[9]).toBe(0x0a);
expect(u128.buf.buf[10]).toBe(0x0b);
expect(u128.buf.buf[11]).toBe(0x0c);
expect(u128.buf.buf[12]).toBe(0x0d);
expect(u128.buf.buf[13]).toBe(0x0e);
expect(u128.buf.buf[14]).toBe(0x0f);
expect(u128.buf.buf[15]).toBe(0x10);
});
it("should round-trip through hex encoding", () => {
const testValues = [
0n,
1n,
2n ** 64n,
2n ** 127n,
2n ** 128n - 1n,
0x123456789abcdef0123456789abcdef0n,
];
for (const value of testValues) {
const u128 = U128BE.fromBn(value);
const hex = u128.toHex();
const restored = U128BE.fromHex(hex);
expect(restored.bn).toBe(value);
}
});
});
describe("U128LE (Little Endian)", () => {
it("should handle minimum value (0)", () => {
const u128 = U128LE.fromBn(0n);
expect(u128.bn).toBe(0n);
expect(u128.toHex()).toBe("00000000000000000000000000000000");
});
it("should handle maximum value (2^128 - 1)", () => {
const max = 2n ** 128n - 1n;
const u128 = U128LE.fromBn(max);
expect(u128.bn).toBe(max);
expect(u128.toHex()).toBe("ffffffffffffffffffffffffffffffff");
});
it("should encode known value correctly (little-endian)", () => {
// 0x0102030405060708090a0b0c0d0e0f10
const value = 0x0102030405060708090a0b0c0d0e0f10n;
const u128 = U128LE.fromBn(value);
// Little-endian: least significant byte first
expect(u128.buf.buf[0]).toBe(0x10);
expect(u128.buf.buf[1]).toBe(0x0f);
expect(u128.buf.buf[2]).toBe(0x0e);
expect(u128.buf.buf[3]).toBe(0x0d);
expect(u128.buf.buf[4]).toBe(0x0c);
expect(u128.buf.buf[5]).toBe(0x0b);
expect(u128.buf.buf[6]).toBe(0x0a);
expect(u128.buf.buf[7]).toBe(0x09);
expect(u128.buf.buf[8]).toBe(0x08);
expect(u128.buf.buf[9]).toBe(0x07);
expect(u128.buf.buf[10]).toBe(0x06);
expect(u128.buf.buf[11]).toBe(0x05);
expect(u128.buf.buf[12]).toBe(0x04);
expect(u128.buf.buf[13]).toBe(0x03);
expect(u128.buf.buf[14]).toBe(0x02);
expect(u128.buf.buf[15]).toBe(0x01);
});
it("should round-trip through hex encoding", () => {
const testValues = [
0n,
1n,
2n ** 64n,
2n ** 127n,
2n ** 128n - 1n,
0x123456789abcdef0123456789abcdef0n,
];
for (const value of testValues) {
const u128 = U128LE.fromBn(value);
const hex = u128.toHex();
const restored = U128LE.fromHex(hex);
expect(restored.bn).toBe(value);
}
});
});
describe("U128BE vs U128LE byte order difference", () => {
it("should produce reversed byte order for same value", () => {
const value = 0x0102030405060708090a0b0c0d0e0f10n;
const be = U128BE.fromBn(value);
const le = U128LE.fromBn(value);
// Bytes should be reversed
for (let i = 0; i < 16; i++) {
expect(be.buf.buf[i]).toBe(le.buf.buf[15 - i]);
}
});
});
});
describe("Audit: U256 endianness verification", () => {
describe("U256BE (Big Endian)", () => {
it("should handle minimum value (0)", () => {
const u256 = U256BE.fromBn(0n);
expect(u256.bn).toBe(0n);
expect(u256.toHex()).toBe(
"0000000000000000000000000000000000000000000000000000000000000000",
);
});
it("should handle maximum value (2^256 - 1)", () => {
const max = 2n ** 256n - 1n;
const u256 = U256BE.fromBn(max);
expect(u256.bn).toBe(max);
expect(u256.toHex()).toBe(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
);
});
it("should encode known value correctly (big-endian)", () => {
// Create a value where each byte is different for easy verification
let value = 0n;
for (let i = 1; i <= 32; i++) {
value = (value << 8n) | BigInt(i);
}
const u256 = U256BE.fromBn(value);
for (let i = 0; i < 32; i++) {
expect(u256.buf.buf[i]).toBe(i + 1);
}
});
it("should handle powers of 2", () => {
const testCases = [
{ power: 0, expected: 1n },
{ power: 8, expected: 256n },
{ power: 16, expected: 65536n },
{ power: 32, expected: 4294967296n },
{ power: 64, expected: 2n ** 64n },
{ power: 128, expected: 2n ** 128n },
{ power: 255, expected: 2n ** 255n },
];
for (const { expected } of testCases) {
const u256 = U256BE.fromBn(expected);
expect(u256.bn).toBe(expected);
}
});
it("should round-trip through hex encoding", () => {
const testValues = [
0n,
1n,
2n ** 64n,
2n ** 128n,
2n ** 255n,
2n ** 256n - 1n,
];
for (const value of testValues) {
const u256 = U256BE.fromBn(value);
const hex = u256.toHex();
const restored = U256BE.fromHex(hex);
expect(restored.bn).toBe(value);
}
});
});
describe("U256LE (Little Endian)", () => {
it("should handle minimum value (0)", () => {
const u256 = U256LE.fromBn(0n);
expect(u256.bn).toBe(0n);
expect(u256.toHex()).toBe(
"0000000000000000000000000000000000000000000000000000000000000000",
);
});
it("should handle maximum value (2^256 - 1)", () => {
const max = 2n ** 256n - 1n;
const u256 = U256LE.fromBn(max);
expect(u256.bn).toBe(max);
expect(u256.toHex()).toBe(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
);
});
it("should encode known value correctly (little-endian)", () => {
// Create a value where each byte is different for easy verification
let value = 0n;
for (let i = 1; i <= 32; i++) {
value = (value << 8n) | BigInt(i);
}
const u256 = U256LE.fromBn(value);
// Little-endian: bytes should be reversed
for (let i = 0; i < 32; i++) {
expect(u256.buf.buf[i]).toBe(32 - i);
}
});
it("should round-trip through hex encoding", () => {
const testValues = [
0n,
1n,
2n ** 64n,
2n ** 128n,
2n ** 255n,
2n ** 256n - 1n,
];
for (const value of testValues) {
const u256 = U256LE.fromBn(value);
const hex = u256.toHex();
const restored = U256LE.fromHex(hex);
expect(restored.bn).toBe(value);
}
});
});
describe("U256BE vs U256LE byte order difference", () => {
it("should produce reversed byte order for same value", () => {
// Create a value where each byte is different
let value = 0n;
for (let i = 1; i <= 32; i++) {
value = (value << 8n) | BigInt(i);
}
const be = U256BE.fromBn(value);
const le = U256LE.fromBn(value);
// Bytes should be reversed
for (let i = 0; i < 32; i++) {
expect(be.buf.buf[i]).toBe(le.buf.buf[31 - i]);
}
});
});
});
describe("Audit: Cross-type consistency", () => {
it("should produce consistent encoding across sizes for same small value", () => {
const value = 0x12n;
const u8 = U8.fromN(Number(value));
const u16be = U16BE.fromN(Number(value));
const u32be = U32BE.fromN(Number(value));
const u64be = U64BE.fromBn(value);
const u128be = U128BE.fromBn(value);
const u256be = U256BE.fromBn(value);
// All should encode 0x12 in the least significant byte
expect(u8.buf.buf[0]).toBe(0x12);
expect(u16be.buf.buf[1]).toBe(0x12); // Big-endian: value in last byte
expect(u32be.buf.buf[3]).toBe(0x12);
expect(u64be.buf.buf[7]).toBe(0x12);
expect(u128be.buf.buf[15]).toBe(0x12);
expect(u256be.buf.buf[31]).toBe(0x12);
// Leading bytes should be zero
expect(u16be.buf.buf[0]).toBe(0x00);
expect(u32be.buf.buf[0]).toBe(0x00);
expect(u64be.buf.buf[0]).toBe(0x00);
expect(u128be.buf.buf[0]).toBe(0x00);
expect(u256be.buf.buf[0]).toBe(0x00);
});
it("should produce consistent little-endian encoding across sizes", () => {
const value = 0x12n;
const u16le = U16LE.fromN(Number(value));
const u32le = U32LE.fromN(Number(value));
const u64le = U64LE.fromBn(value);
const u128le = U128LE.fromBn(value);
const u256le = U256LE.fromBn(value);
// Little-endian: value in first byte
expect(u16le.buf.buf[0]).toBe(0x12);
expect(u32le.buf.buf[0]).toBe(0x12);
expect(u64le.buf.buf[0]).toBe(0x12);
expect(u128le.buf.buf[0]).toBe(0x12);
expect(u256le.buf.buf[0]).toBe(0x12);
// Trailing bytes should be zero
expect(u16le.buf.buf[1]).toBe(0x00);
expect(u32le.buf.buf[3]).toBe(0x00);
expect(u64le.buf.buf[7]).toBe(0x00);
expect(u128le.buf.buf[15]).toBe(0x00);
expect(u256le.buf.buf[31]).toBe(0x00);
});
});
describe("Audit: Overflow protection", () => {
it("should reject U8 overflow", () => {
expect(() => U8.fromN(256)).toThrow();
expect(() => U8.fromBn(256n)).toThrow();
});
it("should reject U16 overflow", () => {
expect(() => U16BE.fromN(65536)).toThrow();
expect(() => U16LE.fromN(65536)).toThrow();
expect(() => U16BE.fromBn(65536n)).toThrow();
expect(() => U16LE.fromBn(65536n)).toThrow();
});
it("should reject U32 overflow", () => {
expect(() => U32BE.fromN(4294967296)).toThrow();
expect(() => U32LE.fromN(4294967296)).toThrow();
expect(() => U32BE.fromBn(4294967296n)).toThrow();
expect(() => U32LE.fromBn(4294967296n)).toThrow();
});
it("should reject U64 overflow", () => {
const overflow = 2n ** 64n;
expect(() => U64BE.fromBn(overflow)).toThrow();
expect(() => U64LE.fromBn(overflow)).toThrow();
});
it("should reject U128 overflow", () => {
const overflow = 2n ** 128n;
expect(() => U128BE.fromBn(overflow)).toThrow();
expect(() => U128LE.fromBn(overflow)).toThrow();
});
it("should reject U256 overflow", () => {
const overflow = 2n ** 256n;
expect(() => U256BE.fromBn(overflow)).toThrow();
expect(() => U256LE.fromBn(overflow)).toThrow();
});
it("should reject negative values", () => {
expect(() => U8.fromN(-1)).toThrow();
expect(() => U16BE.fromN(-1)).toThrow();
expect(() => U32BE.fromN(-1)).toThrow();
expect(() => U64BE.fromBn(-1n)).toThrow();
expect(() => U128BE.fromBn(-1n)).toThrow();
expect(() => U256BE.fromBn(-1n)).toThrow();
});
});
describe("Audit: Known test vectors", () => {
describe("Bitcoin-style values", () => {
it("should correctly encode Bitcoin genesis block timestamp (U32LE)", () => {
// Bitcoin genesis block timestamp: 1231006505 = 0x495FAB29
// In little-endian storage (LSB first): [29, AB, 5F, 49]
// toHex() returns bytes as stored, so: "29ab5f49" would be BE
// But since U32LE stores as little-endian: "495fab29"
const timestamp = U32LE.fromN(1231006505);
// Verify the numeric value round-trips correctly
expect(timestamp.n).toBe(1231006505);
// Verify byte order: 0x495FAB29 -> LE storage [0x29, 0xAB, 0x5F, 0x49]
expect(timestamp.buf.buf[0]).toBe(0x29);
expect(timestamp.buf.buf[1]).toBe(0xab);
expect(timestamp.buf.buf[2]).toBe(0x5f);
expect(timestamp.buf.buf[3]).toBe(0x49);
});
it("should correctly encode common satoshi amounts (U64LE)", () => {
// 1 BTC = 100,000,000 satoshis
const oneBtc = U64LE.fromBn(100000000n);
// 100000000 = 0x05F5E100, little-endian
expect(oneBtc.buf.buf[0]).toBe(0x00);
expect(oneBtc.buf.buf[1]).toBe(0xe1);
expect(oneBtc.buf.buf[2]).toBe(0xf5);
expect(oneBtc.buf.buf[3]).toBe(0x05);
// 21 million BTC cap = 2,100,000,000,000,000 satoshis
const maxBtc = U64LE.fromBn(2100000000000000n);
expect(maxBtc.bn).toBe(2100000000000000n);
});
});
describe("Ethereum-style values", () => {
it("should correctly encode wei amounts (U256BE)", () => {
// 1 ETH = 10^18 wei
const oneEth = 1000000000000000000n;
const u256 = U256BE.fromBn(oneEth);
expect(u256.bn).toBe(oneEth);
// Verify it round-trips
const restored = U256BE.fromHex(u256.toHex());
expect(restored.bn).toBe(oneEth);
});
});
});
describe("Audit: Buffer conversion consistency", () => {
it("should convert BE to LE buffer correctly for U16", () => {
const value = 0x1234;
const be = U16BE.fromN(value);
const leBuf = be.toLEBuf();
// BE buffer: [12, 34]
// LE buffer should be: [34, 12]
expect(leBuf.buf[0]).toBe(0x34);
expect(leBuf.buf[1]).toBe(0x12);
});
it("should convert LE to BE buffer correctly for U16", () => {
const value = 0x1234;
const le = U16LE.fromN(value);
const beBuf = le.toBEBuf();
// LE buffer: [34, 12]
// BE buffer should be: [12, 34]
expect(beBuf.buf[0]).toBe(0x12);
expect(beBuf.buf[1]).toBe(0x34);
});
it("should convert BE to LE buffer correctly for U32", () => {
const value = 0x12345678;
const be = U32BE.fromN(value);
const leBuf = be.toLEBuf();
expect(leBuf.buf[0]).toBe(0x78);
expect(leBuf.buf[1]).toBe(0x56);
expect(leBuf.buf[2]).toBe(0x34);
expect(leBuf.buf[3]).toBe(0x12);
});
it("should convert LE to BE buffer correctly for U32", () => {
const value = 0x12345678;
const le = U32LE.fromN(value);
const beBuf = le.toBEBuf();
expect(beBuf.buf[0]).toBe(0x12);
expect(beBuf.buf[1]).toBe(0x34);
expect(beBuf.buf[2]).toBe(0x56);
expect(beBuf.buf[3]).toBe(0x78);
});
it("should convert BE to LE buffer correctly for U64", () => {
const value = 0x0102030405060708n;
const be = U64BE.fromBn(value);
const leBuf = be.toLEBuf();
for (let i = 0; i < 8; i++) {
expect(leBuf.buf[i]).toBe(8 - i);
}
});
it("should convert LE to BE buffer correctly for U64", () => {
const value = 0x0102030405060708n;
const le = U64LE.fromBn(value);
const beBuf = le.toBEBuf();
for (let i = 0; i < 8; i++) {
expect(beBuf.buf[i]).toBe(i + 1);
}
});
});