Skip to content

Commit c18b8e5

Browse files
authored
Merge pull request #164 from fleet-sdk/arobsn/i163
Fix `SInt` serialization
2 parents 1638062 + 7bf9ee3 commit c18b8e5

File tree

12 files changed

+264
-169
lines changed

12 files changed

+264
-169
lines changed

.changeset/angry-pumpkins-itch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@fleet-sdk/serializer": patch
3+
---
4+
5+
Fix `ZigZag` encoding for 32-big integers
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@fleet-sdk/serializer": patch
3+
---
4+
5+
Fix signed `SByte` parsing

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
"explorer.fileNesting.enabled": true,
33
"explorer.fileNesting.patterns": {
44
"*.ts": "${capture}.*.ts, ${capture}.js"
5+
},
6+
"[typescript]": {
7+
"editor.defaultFormatter": "biomejs.biome"
58
}
69
}

packages/serializer/src/_test-vectors/constantVectors.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ export const boolVectors: ConstantTestVector<boolean>[] = [
3333
export const byteVectors: ConstantTestVector<number>[] = [
3434
{ hex: "0201", value: 1 },
3535
{ hex: "0202", value: 2 },
36-
{ hex: "024c", value: 76 }
36+
{ hex: "024c", value: 76 },
37+
{ hex: "027f", value: 127 }, // max i8
38+
{ hex: "0280", value: -128 } // min i8
3739
];
3840

3941
export const shortVectors: ConstantTestVector<number>[] = [
4042
{ hex: "0302", value: 1 },
4143
{ hex: "0303", value: -2 },
42-
{ hex: "0322", value: 17 }
44+
{ hex: "0322", value: 17 },
45+
{ hex: "03feff03", value: 32767 }, // max i16
46+
{ hex: "03ffff03", value: -32768 } // min i16
4347
];
4448

4549
export const intVectors: ConstantTestVector<number>[] = [
@@ -51,7 +55,9 @@ export const intVectors: ConstantTestVector<number>[] = [
5155
{ hex: "042d", value: -23 },
5256
{ hex: "04800f", value: 960 },
5357
{ hex: "04808008", value: 65536 },
54-
{ hex: "04808023", value: 286720 }
58+
{ hex: "04808023", value: 286720 },
59+
{ hex: "04feffffffffffffffff01", value: 2147483647 }, // max i32
60+
{ hex: "04ffffffffffffffffff01", value: -2147483648 } // min i32
5561
];
5662

5763
export const longVectors: ConstantTestVector<bigint | string>[] = [
@@ -75,7 +81,9 @@ export const longVectors: ConstantTestVector<bigint | string>[] = [
7581
{ hex: "0580809d80d0bf9901", value: 337543627513856n },
7682
{ hex: "058080cba684a68201", value: 286526435581952n },
7783
{ hex: "058080b4ccd4dfc603", value: 1000000000000000n },
78-
{ hex: "058080a0f6f4acdbe01b", value: 1000000000000000000n }
84+
{ hex: "058080a0f6f4acdbe01b", value: 1000000000000000000n },
85+
{ hex: "05feffffffffffffffff01", value: 9223372036854775807n }, // max i64
86+
{ hex: "05ffffffffffffffffff01", value: -9223372036854775808n } // min i64
7987
];
8088

8189
export const bigintVectors: ConstantTestVector<string>[] = [
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export const MIN_U8 = 0x00;
2+
export const MAX_U8 = 0xff;
3+
4+
export const MIN_I8 = -0x80;
5+
export const MAX_I8 = 0x7f;
6+
7+
export const MIN_I16 = -0x8000;
8+
export const MAX_I16 = 0x7fff;
9+
10+
export const MIN_I32 = -0x80000000;
11+
export const MAX_I32 = 0x7fffffff;
12+
13+
export const MIN_I64 = -BigInt("0x8000000000000000");
14+
export const MAX_I64 = BigInt("0x7fffffffffffffff");
15+
16+
export const MIN_I256 = -BigInt(
17+
"0x8000000000000000000000000000000000000000000000000000000000000000"
18+
);
19+
20+
export const MAX_I256 = BigInt(
21+
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
22+
);

packages/serializer/src/coders/sigmaByteReader.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { isEmpty } from "@fleet-sdk/common";
22
import { type ByteInput, ensureBytes, hex } from "@fleet-sdk/crypto";
33
import { hexToBigInt } from "./bigint";
44
import { readBigVLQ, readVLQ } from "./vlq";
5-
import { zigZagDecode, zigZagDecodeBigInt } from "./zigZag";
5+
import { zigZag32, zigZag64 } from "./zigZag";
6+
import { MAX_I8, MAX_U8 } from "./numRanges";
67

78
export class SigmaByteReader {
89
readonly #bytes: Uint8Array;
@@ -17,7 +18,7 @@ export class SigmaByteReader {
1718
this.#cursor = 0;
1819
}
1920

20-
public readBoolean(): boolean {
21+
public readBool(): boolean {
2122
return this.readByte() === 0x01;
2223
}
2324

@@ -52,19 +53,24 @@ export class SigmaByteReader {
5253
return readVLQ(this);
5354
}
5455

55-
public readShort(): number {
56-
return Number(zigZagDecode(readVLQ(this)));
56+
public readI8(): number {
57+
const byte = this.readByte();
58+
return byte > MAX_I8 ? byte - (MAX_U8 + 1) : byte;
5759
}
5860

59-
public readInt(): number {
60-
return Number(this.readLong());
61+
public readI16(): number {
62+
return zigZag32.decode(readBigVLQ(this));
6163
}
6264

63-
public readLong(): bigint {
64-
return zigZagDecodeBigInt(readBigVLQ(this));
65+
public readI32(): number {
66+
return zigZag32.decode(readBigVLQ(this));
6567
}
6668

67-
public readBigInt(): bigint {
69+
public readI64(): bigint {
70+
return zigZag64.decode(readBigVLQ(this));
71+
}
72+
73+
public readI256(): bigint {
6874
const len = readVLQ(this);
6975
return hexToBigInt(hex.encode(this.readBytes(len)));
7076
}

packages/serializer/src/coders/sigmaByteWriter.spec.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import { blake2b256, hex, sha256 } from "@fleet-sdk/crypto";
33
import { describe, expect, it } from "vitest";
44
import { MAX_CONSTANT_LENGTH } from "../sigmaConstant";
55
import { SigmaByteWriter } from "./sigmaByteWriter";
6+
import {
7+
MIN_I16,
8+
MAX_I16,
9+
MIN_I32,
10+
MAX_I32,
11+
MIN_I64,
12+
MAX_I64,
13+
MIN_I256,
14+
MAX_I256
15+
} from "./numRanges";
616

717
describe("Sigma Writer", () => {
818
it("Should put a single byte at time", () => {
@@ -38,8 +48,8 @@ describe("Sigma Writer", () => {
3848

3949
it("Should put a boolean", () => {
4050
const sigmaBuffer = new SigmaByteWriter(MAX_CONSTANT_LENGTH);
41-
sigmaBuffer.writeBoolean(true);
42-
sigmaBuffer.writeBoolean(false);
51+
sigmaBuffer.writeBool(true);
52+
sigmaBuffer.writeBool(false);
4353

4454
expect(sigmaBuffer).toHaveLength(2);
4555
expect(sigmaBuffer.toBytes()).toEqual(Uint8Array.from([0x01, 0x00]));
@@ -71,15 +81,39 @@ describe("Sigma Writer", () => {
7181

7282
const all = new SigmaByteWriter(MAX_CONSTANT_LENGTH);
7383
for (const tv of testVectors) {
74-
all.writeShort(tv.int);
75-
expect(new SigmaByteWriter(tv.hex.length).writeShort(tv.int).encode(hex)).toBe(
84+
all.writeI16(tv.int);
85+
expect(new SigmaByteWriter(tv.hex.length).writeI16(tv.int).encode(hex)).toBe(
7686
tv.hex
7787
);
7888
}
7989

8090
expect(all.encode(hex)).toEqual(testVectors.map((x) => x.hex).join(""));
8191
});
8292

93+
it("Should fail for out of range values", () => {
94+
const sigmaBuffer = new SigmaByteWriter(MAX_CONSTANT_LENGTH);
95+
96+
// Short
97+
const i16Err = "out of range for a 16-bit integer";
98+
expect(() => sigmaBuffer.writeI16(MIN_I16 - 1)).to.throw(i16Err);
99+
expect(() => sigmaBuffer.writeI16(MAX_I16 + 1)).to.throw(i16Err);
100+
101+
// Int
102+
const i32Err = "out of range for a 32-bit integer";
103+
expect(() => sigmaBuffer.writeI32(MIN_I32 - 1)).to.throw(i32Err);
104+
expect(() => sigmaBuffer.writeI32(MAX_I32 + 1)).to.throw(i32Err);
105+
106+
// Long
107+
const i64Err = "out of range for a 64-bit integer";
108+
expect(() => sigmaBuffer.writeI64(MIN_I64 - 1n)).to.throw(i64Err);
109+
expect(() => sigmaBuffer.writeI64(MAX_I64 + 1n)).to.throw(i64Err);
110+
111+
// BigInt
112+
const i256Err = "out of range for a 256-bit integer";
113+
expect(() => sigmaBuffer.writeI256(MIN_I256 - 1n)).to.throw(i256Err);
114+
expect(() => sigmaBuffer.writeI256(MAX_I256 + 1n)).to.throw(i256Err);
115+
});
116+
83117
it("Should write a checksum based on the current stream content", () => {
84118
const bytes = Uint8Array.from([0x00, 0x00, 0x21, 0xff, 0x15, 0x0c]);
85119
const blakeHash = blake2b256(bytes);

packages/serializer/src/coders/sigmaByteWriter.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import { blake2b256, type Coder, hex } from "@fleet-sdk/crypto";
22
import { bigIntToHex } from "./bigint";
33
import { writeBigVLQ, writeVLQ } from "./vlq";
4-
import { zigZagEncode, zigZagEncodeBigInt } from "./zigZag";
4+
import { zigZag32, zigZag64 } from "./zigZag";
5+
import {
6+
MIN_I16,
7+
MAX_I16,
8+
MIN_I32,
9+
MAX_I32,
10+
MIN_I64,
11+
MAX_I64,
12+
MIN_I256,
13+
MAX_I256
14+
} from "./numRanges";
515

616
export class SigmaByteWriter {
717
readonly #bytes: Uint8Array;
@@ -16,7 +26,7 @@ export class SigmaByteWriter {
1626
this.#cursor = 0;
1727
}
1828

19-
public writeBoolean(value: boolean): SigmaByteWriter {
29+
public writeBool(value: boolean): SigmaByteWriter {
2030
this.write(value === true ? 0x01 : 0x00);
2131

2232
return this;
@@ -30,19 +40,38 @@ export class SigmaByteWriter {
3040
return writeBigVLQ(this, value);
3141
}
3242

33-
public writeShort(value: number): SigmaByteWriter {
34-
this.writeVLQ(zigZagEncode(value));
43+
public writeI16(value: number): SigmaByteWriter {
44+
if (value < MIN_I16 || value > MAX_I16) {
45+
throw new RangeError(`Value ${value} is out of range for a 16-bit integer`);
46+
}
47+
48+
this.writeBigVLQ(zigZag32.encode(value));
3549
return this;
3650
}
3751

38-
public writeInt(value: number): SigmaByteWriter {
39-
this.writeLong(BigInt(value));
40-
return this;
52+
public writeI32(value: number): SigmaByteWriter {
53+
if (value < MIN_I32 || value > MAX_I32) {
54+
throw new RangeError(`Value ${value} is out of range for a 32-bit integer`);
55+
}
56+
57+
return this.writeBigVLQ(zigZag32.encode(value));
4158
}
4259

43-
public writeLong(value: bigint): SigmaByteWriter {
44-
this.writeBigVLQ(zigZagEncodeBigInt(value));
45-
return this;
60+
public writeI64(value: bigint): SigmaByteWriter {
61+
if (value < MIN_I64 || value > MAX_I64) {
62+
throw new RangeError(`Value ${value} is out of range for a 64-bit integer`);
63+
}
64+
65+
return this.writeBigVLQ(zigZag64.encode(value));
66+
}
67+
68+
public writeI256(value: bigint): SigmaByteWriter {
69+
if (value < MIN_I256 || value > MAX_I256) {
70+
throw new RangeError(`Value ${value} is out of range for a 256-bit integer`);
71+
}
72+
73+
const hex = bigIntToHex(value);
74+
return this.writeVLQ(hex.length / 2).writeHex(hex);
4675
}
4776

4877
public write(byte: number): SigmaByteWriter {
@@ -81,11 +110,6 @@ export class SigmaByteWriter {
81110
return this;
82111
}
83112

84-
public writeBigInt(value: bigint): SigmaByteWriter {
85-
const hex = bigIntToHex(value);
86-
return this.writeVLQ(hex.length / 2).writeHex(hex);
87-
}
88-
89113
public writeChecksum(length = 4, hashFn = blake2b256): SigmaByteWriter {
90114
const hash = hashFn(this.toBytes());
91115
return this.writeBytes(length ? hash.subarray(0, length) : hash);

0 commit comments

Comments
 (0)