Skip to content

fix(evolution): preserve Plutus data encodings on re-encode (#576) - #635

Open
Kshot3000 wants to merge 1 commit into
IntersectMBO:mainfrom
Kshot3000:fix/plutus-data-encodings
Open

Kshot3000 wants to merge 1 commit into
IntersectMBO:mainfrom
Kshot3000:fix/plutus-data-encodings

Conversation

@Kshot3000

Copy link
Copy Markdown

Summary

Fixes #576: six encodings of Plutus data that the ledger accepts came back from Transaction.fromCBORHex → Transaction.toCBORHex in different bytes, with nothing changed. Because the ledger hashes the original bytes of every datum and redeemer, a changed witness datum failed with PPViewHashesDontMatch and a changed inline datum changed the body and the transaction id.

This also adds the regression case for #580 part 2 (an empty indefinite witness set written back as a0) and fixes #580 part 3 (the CBOR guide's "canonical by default" comment). Part 1 of #580 shipped separately.

The bug, reproduced first

New regression tests failed on main for five of the six encodings, in both positions the issue names — as a witness datum (a1 04 d90102 81 [datum]) and as an inline datum in a map-form output:

The sixth encoding, c24101 (1 as a bignum), already round-trips on main — the captured-tag path in encodeUintSync handles it — so it is included in the tests as a guard rather than claimed as part of this fix. The control d8799f01ff round-tripped before and still does.

Fix

Tests

  • Transaction-with-format.test.ts: each encoding round-trips byte for byte as a witness datum and as an inline datum (plain decode/encode path), the body hash of a chunked-inline-datum transaction is unchanged (verified via CML), and the empty indefinite witness set round-trips.
  • CBOR-with-format.test.ts: the four generic encodings join the round-trip vectors; hand-crafted formats pin the empty array/map behaviour; BoundedBytes tests pin the kept header width, the kept chunk layout, and both stale-layout refusals (sizes that do not add up, a chunk over 64 bytes).
  • Full package suite: 1709 passed / 75 skipped / 0 failed, test files 101 passed / 1 skipped of 102 (baseline on main: 1678 passed / 75 skipped; the delta is exactly the new tests). tsc --noEmit and eslint are clean; a patch changeset is included.

I'm an independent builder working on Cardano tooling — not affiliated with the Cardano Foundation, IOG or Intersect.

Support my work: ADA addr1q8hnl6vl5a6k3rw3n5g3jtte696zcl76kfatzv7gpswa9r0dj7fma6klq55y4ffm7tf0em09udnyhuk4ah92pl5x9jpqjae44v · X: @kshot9000 · GitHub: Kshot3000

cc @cardano-foundation @IntersectMBO

…tMBO#576)

Empty arrays and maps returned 80/a0 before the captured format was
read, so an empty indefinite list or map and a wider length header were
lost; the BoundedBytes branch dropped the captured format entirely, so
a datum's byte-string header width and chunk layout were lost. The
ledger hashes the original datum and redeemer bytes, so each change
broke the script data hash or the transaction id.

The empty fast paths now honour a captured indefinite length or header
width, and encodeBoundedBytesSync receives the format: it keeps the
header width and replays a chunk layout only when the chunk sizes add
up to the value's length and no chunk is over 64 bytes, falling back
to the default bounded encoding otherwise. Also adds the regression
case for IntersectMBO#580 part 2 (empty indefinite witness set) and corrects the
CBOR guide's 'canonical by default' comment (IntersectMBO#580 part 3).

Tests: 1709 passed / 75 skipped / 0 failed (main baseline 1678 + 31 new).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant