From 22b8dfb76fb93f687a027f28c5cf27d352571c25 Mon Sep 17 00:00:00 2001 From: Kyle Cox Date: Fri, 9 Oct 2026 09:43:46 +0000 Subject: [PATCH] fix(serializer): read a boolean as true for any non-zero byte --- .changeset/tidy-bools-truthy.md | 5 +++++ .../serializer/src/coders/sigmaByteReader.spec.ts | 11 +++++++++++ packages/serializer/src/coders/sigmaByteReader.ts | 4 +++- packages/serializer/src/sigmaConstant.spec.ts | 10 ++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/tidy-bools-truthy.md diff --git a/.changeset/tidy-bools-truthy.md b/.changeset/tidy-bools-truthy.md new file mode 100644 index 00000000..42160419 --- /dev/null +++ b/.changeset/tidy-bools-truthy.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/serializer": patch +--- + +Fix `SigmaByteReader.readBool()` reading any boolean byte other than `0x01` as false. The reference implementation (sigmastate's `CoreDataSerializer`) reads a boolean as `getUByte() != 0`, so a non-canonical byte such as `0x02` or `0xff` in an `SBool` constant is true; it was previously parsed as false, silently flipping the value. diff --git a/packages/serializer/src/coders/sigmaByteReader.spec.ts b/packages/serializer/src/coders/sigmaByteReader.spec.ts index 79108891..7615a8ef 100644 --- a/packages/serializer/src/coders/sigmaByteReader.spec.ts +++ b/packages/serializer/src/coders/sigmaByteReader.spec.ts @@ -62,6 +62,17 @@ describe("Sigma byte reader", () => { expect(reader.match(u8a([0x04, 0x05, 0x06, 0x07]), 3)).toBe(false); }); + it("should read a boolean as false only for a zero byte", () => { + // Reference (sigmastate CoreDataSerializer): `r.getUByte() != 0` — + // any non-zero byte is true, not just 0x01. + expect(new SigmaByteReader(u8a([0x00])).readBool()).toBe(false); + expect(new SigmaByteReader(u8a([0x01])).readBool()).toBe(true); + expect(new SigmaByteReader(u8a([0x02])).readBool()).toBe(true); + expect(new SigmaByteReader(u8a([0x7f])).readBool()).toBe(true); + expect(new SigmaByteReader(u8a([0x80])).readBool()).toBe(true); + expect(new SigmaByteReader(u8a([0xff])).readBool()).toBe(true); + }); + it("should read remaining bytes", () => { const bytes = u8a([0x01, 0x02, 0x03, 0x04]); const reader = new SigmaByteReader(bytes); diff --git a/packages/serializer/src/coders/sigmaByteReader.ts b/packages/serializer/src/coders/sigmaByteReader.ts index 6795e433..7cb18a84 100644 --- a/packages/serializer/src/coders/sigmaByteReader.ts +++ b/packages/serializer/src/coders/sigmaByteReader.ts @@ -38,7 +38,9 @@ export class SigmaByteReader { } readBool(): boolean { - return this.readByte() === 0x01; + // Reference (sigmastate CoreDataSerializer): a boolean is `getUByte() != 0`, + // so any non-zero byte is true — not just the canonical 0x01. + return this.readByte() !== 0; } readBits(length: number): ArrayLike { diff --git a/packages/serializer/src/sigmaConstant.spec.ts b/packages/serializer/src/sigmaConstant.spec.ts index 0d2fcf6d..b4258f6e 100644 --- a/packages/serializer/src/sigmaConstant.spec.ts +++ b/packages/serializer/src/sigmaConstant.spec.ts @@ -59,6 +59,16 @@ describe("Primitive types serialization and parsing", () => { expect(SConstant.from(tv.hex).data).to.be.equal(tv.value); }); + it("Should parse a non-canonical SBool byte as true, like the reference", () => { + // sigmastate CoreDataSerializer reads SBoolean as `getUByte() != 0`, and + // sigma-rust does the same, so a bool constant byte of 0x02 or 0xff is + // true — only 0x00 is false. Canonical encodings (0x00/0x01) are unchanged. + expect(SConstant.from("0100").data).to.be.equal(false); + expect(SConstant.from("0101").data).to.be.equal(true); + expect(SConstant.from("0102").data).to.be.equal(true); + expect(SConstant.from("01ff").data).to.be.equal(true); + }); + it.each(byteVectors)("Should road-trip SByte($value)", (tv) => { const sconst = SByte(tv.value);