mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import { sipHash24, shortTxId, sipHashKeyFromNonce } from './sv2-siphash';
|
|
|
|
describe('SipHash-2-4', () => {
|
|
// Official SipHash-2-4 test vectors from the reference paper
|
|
// Key: 00 01 02 ... 0f
|
|
// Input: empty, 00, 00 01, 00 01 02, ...
|
|
// Expected outputs (first few from Appendix A of the paper)
|
|
const testKey = Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
|
|
|
// Reference vectors from vectors.h: uint8_t vectors_sip64[64][8]
|
|
// These are the raw output bytes, so we compare directly with our output buffer.
|
|
const expectedOutputs = [
|
|
'310e0edd47db6f72', // empty input
|
|
'fd67dc93c539f874', // [0x00]
|
|
'5a4fa9d909806c0d', // [0x00, 0x01]
|
|
'2d7efbd796666785', // [0x00, 0x01, 0x02]
|
|
'b7877127e09427cf', // [0x00, 0x01, 0x02, 0x03]
|
|
'8da699cd64557618', // [0x00, ..., 0x04]
|
|
'cee3fe586e46c9cb', // [0x00, ..., 0x05]
|
|
'37d1018bf50002ab', // [0x00, ..., 0x06]
|
|
'6224939a79f5f593', // [0x00, ..., 0x07]
|
|
'b0e4a90bdf82009e', // [0x00, ..., 0x08]
|
|
'f3b9dd94c5bb5d7a', // [0x00, ..., 0x09]
|
|
'a7ad6b22462fb3f4', // [0x00, ..., 0x0a]
|
|
'fbe50e86bc8f1e75', // [0x00, ..., 0x0b]
|
|
'903d84c02756ea14', // [0x00, ..., 0x0c]
|
|
'eef27a8e90ca23f7', // [0x00, ..., 0x0d]
|
|
'e545be4961ca29a1', // [0x00, ..., 0x0e]
|
|
];
|
|
|
|
expectedOutputs.forEach((expected, i) => {
|
|
it(`matches test vector for ${i}-byte input`, () => {
|
|
const input = Buffer.from(Array.from({ length: i }, (_, j) => j));
|
|
const result = sipHash24(testKey, input);
|
|
// Compare raw output bytes directly
|
|
expect(result.toString('hex')).toBe(expected);
|
|
});
|
|
});
|
|
|
|
it('throws for invalid key length', () => {
|
|
expect(() => sipHash24(Buffer.alloc(8), Buffer.alloc(0))).toThrow('SipHash key must be 16 bytes');
|
|
expect(() => sipHash24(Buffer.alloc(32), Buffer.alloc(0))).toThrow('SipHash key must be 16 bytes');
|
|
});
|
|
|
|
it('produces 8-byte output', () => {
|
|
const result = sipHash24(testKey, Buffer.from('hello'));
|
|
expect(result.length).toBe(8);
|
|
});
|
|
|
|
it('produces consistent results', () => {
|
|
const data = Buffer.from('test data');
|
|
const r1 = sipHash24(testKey, data);
|
|
const r2 = sipHash24(testKey, data);
|
|
expect(r1).toEqual(r2);
|
|
});
|
|
|
|
it('different keys produce different results', () => {
|
|
const key2 = Buffer.alloc(16, 0xff);
|
|
const data = Buffer.from('hello');
|
|
const r1 = sipHash24(testKey, data);
|
|
const r2 = sipHash24(key2, data);
|
|
expect(r1).not.toEqual(r2);
|
|
});
|
|
|
|
describe('shortTxId', () => {
|
|
it('produces 6-byte output', () => {
|
|
const txHash = Buffer.alloc(32, 0xab);
|
|
const result = shortTxId(testKey, txHash);
|
|
expect(result.length).toBe(6);
|
|
});
|
|
|
|
it('is the first 6 bytes of sipHash24', () => {
|
|
const txHash = Buffer.alloc(32, 0xcd);
|
|
const full = sipHash24(testKey, txHash);
|
|
const short = shortTxId(testKey, txHash);
|
|
expect(short).toEqual(full.subarray(0, 6));
|
|
});
|
|
|
|
it('different tx hashes produce different short IDs', () => {
|
|
const tx1 = Buffer.alloc(32, 0x01);
|
|
const tx2 = Buffer.alloc(32, 0x02);
|
|
const s1 = shortTxId(testKey, tx1);
|
|
const s2 = shortTxId(testKey, tx2);
|
|
expect(s1).not.toEqual(s2);
|
|
});
|
|
});
|
|
|
|
describe('sipHashKeyFromNonce', () => {
|
|
it('produces 16-byte key', () => {
|
|
const key = sipHashKeyFromNonce(42n);
|
|
expect(key.length).toBe(16);
|
|
});
|
|
|
|
it('nonce is stored in first 8 bytes LE', () => {
|
|
const key = sipHashKeyFromNonce(0x0102030405060708n);
|
|
expect(key.readBigUInt64LE(0)).toBe(0x0102030405060708n);
|
|
// Second 8 bytes should be zero
|
|
expect(key.readBigUInt64LE(8)).toBe(0n);
|
|
});
|
|
|
|
it('produces valid SipHash key', () => {
|
|
const key = sipHashKeyFromNonce(12345n);
|
|
const result = sipHash24(key, Buffer.from('test'));
|
|
expect(result.length).toBe(8);
|
|
});
|
|
});
|
|
});
|