Summary
Redeemers.toScriptDataHash(redeemers, costModels, datums, options) applies options to the redeemers but always encodes the datums with CML_DATA_DEFAULT_OPTIONS. Asked for a canonical hash, it returns the hash over indefinite-length datums:
const d = Data.fromCBORHex("d8799f019f02ffff")
Data.toCBORHex(d, CBOR.CANONICAL_OPTIONS) // d87982018102, the bytes in the witness set
Redeemers.toScriptDataHash(empty, costModels, [d], CBOR.CANONICAL_OPTIONS)
// 80dbaeaf…, the hash over d8799f019f02ffff; expected ac4817df…
The ledger hashes the datums exactly as they appear in the witness set: cardano-ledger Alonzo/Tx.hs L318-321 builds the script integrity bytes from originalBytes of the redeemers and datums. A transaction whose witness set carries canonical datums therefore needs the hash over canonical datums.
Affected
packages/evolution/src/Redeemers.ts
encodeDatumsTaggedSet (L548-555) defaults to CML_DATA_DEFAULT_OPTIONS
toScriptDataHash calls it without options in both branches: datums with no redeemers (L600) and redeemers with datums (L612)
The builder is not affected today: it computes the hash with default options and does not add witness datums.
Fix
Pass the caller's options through to encodeDatumsTaggedSet when the caller gives them. When options is left at its default, keep encoding datums with CML_DATA_DEFAULT_OPTIONS, so today's default hashes do not change.
Regression test
Oracle is the node.
- given: datum
d8799f019f02ffff, no redeemers, CANONICAL_OPTIONS
- before fix:
80dbaeaf…
- after fix:
ac4817df…, blake2b-256 of a0 d9010281 d87982018102 a0
- same check for the branch with redeemers and datums
- control: with default options the hash is unchanged
Devnet, raw submission through Ogmios. The witness set carries d87982018102, and the output holds its datum hash:
- script data hash from
toScriptDataHash(..., CANONICAL_OPTIONS): rejected with 3113, provided 80dbaeaf…, computed ac4817df…
- script data hash
ac4817df…: accepted
Must FAIL on main today and PASS after the fix.
Summary
Redeemers.toScriptDataHash(redeemers, costModels, datums, options)appliesoptionsto the redeemers but always encodes the datums withCML_DATA_DEFAULT_OPTIONS. Asked for a canonical hash, it returns the hash over indefinite-length datums:The ledger hashes the datums exactly as they appear in the witness set: cardano-ledger
Alonzo/Tx.hsL318-321 builds the script integrity bytes fromoriginalBytesof the redeemers and datums. A transaction whose witness set carries canonical datums therefore needs the hash over canonical datums.Affected
packages/evolution/src/Redeemers.ts
encodeDatumsTaggedSet(L548-555) defaults toCML_DATA_DEFAULT_OPTIONStoScriptDataHashcalls it withoutoptionsin both branches: datums with no redeemers (L600) and redeemers with datums (L612)The builder is not affected today: it computes the hash with default options and does not add witness datums.
Fix
Pass the caller's options through to
encodeDatumsTaggedSetwhen the caller gives them. Whenoptionsis left at its default, keep encoding datums withCML_DATA_DEFAULT_OPTIONS, so today's default hashes do not change.Regression test
Oracle is the node.
d8799f019f02ffff, no redeemers,CANONICAL_OPTIONS80dbaeaf…ac4817df…, blake2b-256 ofa0 d9010281 d87982018102 a0Devnet, raw submission through Ogmios. The witness set carries
d87982018102, and the output holds its datum hash:toScriptDataHash(..., CANONICAL_OPTIONS): rejected with 3113, provided80dbaeaf…, computedac4817df…ac4817df…: acceptedMust FAIL on main today and PASS after the fix.