fix: \uint256ToBytesLittleEndian\ silently truncates above 2³²−1 (#294)
Expand the output buffer from 4 bytes to 32 bytes and iterate all 32 positions, so values ≥ 2³² are encoded correctly instead of silently dropped. Update tests to assert 32-byte output and add coverage for 2³², 2¹²⁸, and max uint256 roundtrips. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9341673a1a
commit
6eacfe1975
3 changed files with 45 additions and 17 deletions
|
|
@ -9,8 +9,8 @@ export function bytesToUint256LittleEndian(bytes: Uint8Array): bigint {
|
|||
}
|
||||
|
||||
export function uint256ToBytesLittleEndian(value: bigint): Uint8Array {
|
||||
const bytes = new Uint8Array(4);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const bytes = new Uint8Array(32);
|
||||
for (let i = 0; i < 32; i++) {
|
||||
bytes[i] = Number((value >> (8n * BigInt(i))) & 0xffn);
|
||||
}
|
||||
return bytes;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from '../subgr
|
|||
describe('BigInt Conversion Functions', () => {
|
||||
test('converts uint256 to bytes and back (little endian)', () => {
|
||||
const mockId = uint256ToBytesLittleEndian(3n);
|
||||
expect(mockId).toEqual(new Uint8Array([3, 0, 0, 0]));
|
||||
const expected = new Uint8Array(32);
|
||||
expected[0] = 3;
|
||||
expect(mockId).toEqual(expected);
|
||||
expect(bytesToUint256LittleEndian(mockId)).toEqual(3n);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,39 +2,57 @@ import { describe, expect, test } from 'vitest';
|
|||
import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from '../subgraph.js';
|
||||
|
||||
describe('uint256ToBytesLittleEndian', () => {
|
||||
test('converts zero to four zero bytes', () => {
|
||||
expect(uint256ToBytesLittleEndian(0n)).toEqual(new Uint8Array([0, 0, 0, 0]));
|
||||
test('converts zero to 32 zero bytes', () => {
|
||||
expect(uint256ToBytesLittleEndian(0n)).toEqual(new Uint8Array(32));
|
||||
});
|
||||
|
||||
test('converts 1 to [1, 0, 0, 0]', () => {
|
||||
expect(uint256ToBytesLittleEndian(1n)).toEqual(new Uint8Array([1, 0, 0, 0]));
|
||||
test('converts 1 to [1, ...31 zeros]', () => {
|
||||
const expected = new Uint8Array(32);
|
||||
expected[0] = 1;
|
||||
expect(uint256ToBytesLittleEndian(1n)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('converts 3 to [3, 0, 0, 0]', () => {
|
||||
test('converts 3 to [3, ...31 zeros]', () => {
|
||||
const result = uint256ToBytesLittleEndian(3n);
|
||||
let hexString = '0x';
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
hexString += result[i].toString(16).padStart(2, '0');
|
||||
}
|
||||
expect(hexString).toEqual('0x03000000');
|
||||
expect(hexString).toEqual('0x03' + '00'.repeat(31));
|
||||
});
|
||||
|
||||
test('converts 256 to [0, 1, 0, 0] (second byte)', () => {
|
||||
expect(uint256ToBytesLittleEndian(256n)).toEqual(new Uint8Array([0, 1, 0, 0]));
|
||||
test('converts 256 to [0, 1, ...30 zeros] (second byte)', () => {
|
||||
const expected = new Uint8Array(32);
|
||||
expected[1] = 1;
|
||||
expect(uint256ToBytesLittleEndian(256n)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('converts 65536 to [0, 0, 1, 0] (third byte)', () => {
|
||||
expect(uint256ToBytesLittleEndian(65536n)).toEqual(new Uint8Array([0, 0, 1, 0]));
|
||||
test('converts 65536 to [0, 0, 1, ...29 zeros] (third byte)', () => {
|
||||
const expected = new Uint8Array(32);
|
||||
expected[2] = 1;
|
||||
expect(uint256ToBytesLittleEndian(65536n)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('converts 16777216 (2^24) to [0, 0, 0, 1] (fourth byte)', () => {
|
||||
expect(uint256ToBytesLittleEndian(16777216n)).toEqual(new Uint8Array([0, 0, 0, 1]));
|
||||
test('converts 16777216 (2^24) to [0, 0, 0, 1, ...28 zeros] (fourth byte)', () => {
|
||||
const expected = new Uint8Array(32);
|
||||
expected[3] = 1;
|
||||
expect(uint256ToBytesLittleEndian(16777216n)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('returns a 4-byte Uint8Array', () => {
|
||||
test('converts 2^32 to [0, 0, 0, 0, 1, ...27 zeros] (fifth byte)', () => {
|
||||
const expected = new Uint8Array(32);
|
||||
expected[4] = 1;
|
||||
expect(uint256ToBytesLittleEndian(2n ** 32n)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('converts max uint256 to all 0xff bytes', () => {
|
||||
expect(uint256ToBytesLittleEndian(2n ** 256n - 1n)).toEqual(new Uint8Array(32).fill(255));
|
||||
});
|
||||
|
||||
test('returns a 32-byte Uint8Array', () => {
|
||||
const result = uint256ToBytesLittleEndian(12345n);
|
||||
expect(result).toBeInstanceOf(Uint8Array);
|
||||
expect(result.length).toBe(4);
|
||||
expect(result.length).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -69,4 +87,12 @@ describe('roundtrip: uint256ToBytesLittleEndian -> bytesToUint256LittleEndian',
|
|||
expect(bytesToUint256LittleEndian(bytes)).toBe(val);
|
||||
}
|
||||
});
|
||||
|
||||
test('roundtrip preserves values >= 2^32', () => {
|
||||
const values = [2n ** 32n, 2n ** 32n + 1n, 2n ** 128n, 2n ** 256n - 1n];
|
||||
for (const val of values) {
|
||||
const bytes = uint256ToBytesLittleEndian(val);
|
||||
expect(bytesToUint256LittleEndian(bytes)).toBe(val);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue