Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/uplc-bool-js-boolean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@evolution-sdk/evolution": patch
---

Fix the UPLC flat encoder encoding a Bool constant given as a plain JS boolean `false` as true. `ConstantValue` permits a JS boolean, but the Bool encoder read `.index` off the value, which a boolean does not have, so `con bool false` and `con bool true` produced identical flat bytes. The encoder now handles the boolean form directly; the decoded `{ index }` form encodes exactly as before.
7 changes: 5 additions & 2 deletions packages/evolution/src/UPLC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,8 +950,11 @@ const encodeData = (state: EncodeState, dataType: DataType, value: ConstantValue
} else if (dataType === "Unit") {
// No encoding needed
} else if (dataType === "Bool") {
const boolVal = value as { index: bigint }
pushBit(state, boolVal.index === 0n ? 0 : 1)
// ConstantValue permits a plain JS boolean as well as the decoded
// { index } form; a boolean has no .index, so reading it would
// encode false as true.
const bit = typeof value === "boolean" ? (value ? 1 : 0) : (value as { index: bigint }).index === 0n ? 0 : 1
pushBit(state, bit)
} else if (dataType === "Data") {
encodeByteString(state, value as Uint8Array)
} else if ("list" in dataType) {
Expand Down
45 changes: 45 additions & 0 deletions packages/evolution/test/UPLC.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,51 @@ describe("UPLC Module", () => {
const decoded2 = UPLC.fromFlatBytes(reencoded)
expect(decoded2.version).toBe(program.version)
})

describe("Bool constants given as plain JS booleans", () => {
const boolProgram = (value: UPLC.ConstantValue) =>
new UPLC.Program({ version: "1.0.0", body: UPLC.constantTerm("Bool", value) })

const decodedConstant = (bytes: Uint8Array): UPLC.ConstantValue => {
const decoded = UPLC.fromFlatBytes(bytes)
if (decoded.body.type !== "Constant") throw new Error("expected a Constant term")
return decoded.body.value
}

it("encodes JS false as False, not True", () => {
const falseBytes = UPLC.toFlatBytes(boolProgram(false))
const trueBytes = UPLC.toFlatBytes(boolProgram(true))
expect(Bytes.toHex(falseBytes)).not.toBe(Bytes.toHex(trueBytes))
expect(decodedConstant(falseBytes)).toEqual({ index: 0n, fields: [] })
})

it("encodes JS true as True", () => {
const trueBytes = UPLC.toFlatBytes(boolProgram(true))
expect(decodedConstant(trueBytes)).toEqual({ index: 1n, fields: [] })
})

it("matches the { index } representation byte for byte", () => {
expect(Bytes.toHex(UPLC.toFlatBytes(boolProgram(false)))).toBe(
Bytes.toHex(UPLC.toFlatBytes(boolProgram({ index: 0n, fields: [] })))
)
expect(Bytes.toHex(UPLC.toFlatBytes(boolProgram(true)))).toBe(
Bytes.toHex(UPLC.toFlatBytes(boolProgram({ index: 1n, fields: [] })))
)
})

it("roundtrips JS booleans nested in a list of Bool", () => {
const program = new UPLC.Program({
version: "1.0.0",
body: UPLC.constantTerm({ list: "Bool" }, { items: [false, true] })
})
expect(decodedConstant(UPLC.toFlatBytes(program))).toEqual({
items: [
{ index: 0n, fields: [] },
{ index: 1n, fields: [] }
]
})
})
})
})

describe("Schema Transform - FromFlatBytes", () => {
Expand Down