Repository navigation
fix(uplc): reject constr/case before 1.1.0 and constr tags of 2^64 or more - #702
Conversation
… more Plutus 1.72.0.0 decodeTerm (UntypedPlutusCore/Core/Instance/Flat.hs) fails on a constr or case term in a program below version 1.1.0, and reads the constr tag with dWord64, which fails past ten 7-bit chunks or a value of 2^64 or more. UPLC decoded and encoded both. The decoder now applies the same checks, and the encoder refuses to write such terms. Conformance cases constr-06, constr-08, constr-09 and case-07 are now rejected; no other conformance file changes outcome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Match dWord64's "Unexpected extra byte in unsigned integer", say in the changeset that an overlong tag is rejected even when its value fits, and assert the 2^64-1 tag with toMatchObject. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plutus encodes each Version component as a Flat Natural (PlutusCore/FlatInstances.hs),
but UPLC used one byte per component. A component of 256 or more was truncated, so
Program({ version: "257.0.0", body: constr 0 }) was written as a 1.0.0 constr program,
bypassing the new version gate; correctly encoded versions with a component of 128 or
more were misread. Components below 128 encode to the same bytes as before.
The changeset now separates what decoding rejects (overlong Word64 encodings too)
from what the encoder refuses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
solidsnakedev
left a comment
There was a problem hiding this comment.
Thanks, this matches Plutus. The version gate, the Word64 tag and the version naturals all hold against Flat.hs:167-184, Strict.hs:165-184 and FlatInstances.hs:177-179 at 1.72.0.0. Every program in the repo re-encodes to the same bytes. Of the 908 conformance files, only constr-06, constr-08, constr-09 and case-07 change, and all four are now rejected, as their expected output says.
One small change: encodeProgram adds a second program.version as SemVer cast (UPLC.ts:1078). Taking the version as a string in encodeTerm and allowsConstrCase removes it.
A follow-up, not for this PR: a var index is still read as an unbounded natural, while Plutus reads it as a Word64 (FlatInstances.hs:363), so (program 1.1.0 (lam (var 2^64))) decodes here and fails in Plutus.
It merges cleanly with #667, so I'll merge it right after that one.
|
Merged, thanks. The new |
Fixes #701.
Plutus 1.72.0.0
decodeTerm(UntypedPlutusCore/Core/Instance/Flat.hs,handleTerm 8line 167 andhandleTerm 9line 181) rejectsconstrandcasein a program below version 1.1.0. It reads theconstrtag as aWord64withdWord64(PlutusCore/Flat/Decoder/Strict.hsline 165), which allows at most ten 7-bit chunks with a tenth chunk of 0 or 1.UPLC.tschecked neither, in either direction.Changes
decodeTermandencodeTermtake the program version. Before reading or writingconstrorcase, they check it with Plutus's ordering (major, then minor, then patch) and fail with Plutus's message:'constr' is not allowed before version 1.1.0, this program has version: 1.0.0.constrtag is decoded with a newdecodeWord64that followsdWord64. The encoder refuses tags above 2^64-1. A negative tag was already refused byencodeNatural.decodeVersionandencodeVersionread and write each version component as a Flat natural, as Plutus'sinstance Flat Versiondoes (PlutusCore/FlatInstances.hs), instead of one byte. Without this, the encoder's version check could be bypassed:257.0.0was truncated to1.0.0. Versions with every component below 128 encode to the same bytes as before.@evolution-sdk/evolution.Tests
New tests in
packages/evolution/test/UPLC.test.ts:constr/constr-06.flat,constr/constr-08.flat,constr/constr-09.flatandcase/case-07.flat(bytes copied in) now fails to decode with aUPLCError. Forconstr-09, the error is the version one.constr/constr-01.flat,case/case-01.flatandconstr/constr-07.flat(tag 2^64-1) decode and re-encode byte for byte.constris accepted in a 2.0.0 program, and a tag spread over exactly ten chunks is accepted.257.0.0encodes as810200008001and decodes back, and(program 0.255.0 (constr 0))(00ff01008001) is rejected with the version error.constrnested under a lambda in a 1.0.0 program, and encodingconstr/caseat 1.0.0 or a tag of 2^64.The new rejection tests fail on
mainand pass with the fix. With each guard reverted on its own (decode or encode version gate, Word64 decode, Word64 encode), at least one test fails.Across all 908
.flatfiles inplutus-conformance/test-cases/uplc(Plutus 1.72.0.0), decode then re-encode changes outcome for exactly these four files, from accepted to rejected.🤖 Generated with Claude Code