Skip to content

Give Plutus data its own encoder #614

Description

@solidsnakedev

Summary

Plutus data has no encoder of its own. Data.toCBORBytes turns a Data value into a generic CBOR tree and hands it to the CBOR encoder, which applies one set of options to the whole tree. Some Plutus data rules do not fit that model, so they are missing or live in CBOR.ts:

Rule (plutus PlutusCore/Data.hs encodeData) SDK today
Tag 102 writes its [index, fields] pair as a definite 2-element array; only the fields follow the list encoding the pair follows the list options (#601)
A bignum payload over 64 bytes is chunked like any bounded byte string written as one byte string (#530)
Integers from -2^64 to 2^64-1 use major types 0 and 1; only values outside use tags 2 and 3 -2^64 is written as tag 3 (#395)
Byte strings over 64 bytes are chunked at 64 done by CBOR.BoundedBytes, a Plutus-specific node in the generic CBOR type

The node splits this the same way: the ledger delegates Plutus data to the Plutus encoder (cardano-ledger Plutus/Data.hs L86-87, encCBOR (PlutusData d) = fromPlainEncoding $ Cborg.encode d).

Proposal

Give Data its own encoder that walks a Data value straight to bytes:

  • Constructor, list, map, integer and byte-string rules as in encodeData, including the four rows above.
  • Lists, maps and constructor fields follow the codec options, so PLUTUS_DATA_OPTIONS, CML_DATA_DEFAULT_OPTIONS, CML_DATA_DEFINITE_OPTIONS and CANONICAL_OPTIONS keep their meaning.
  • Data.toCBORBytes, Data.toCBORHex, Data.toDatumHash and the Data encode schemas use it. The transaction encoders already join datum and redeemer bytes from Data.toCBORBytes (feat(cbor): write witness datums and redeemer data with TxCodecOptions #610), inline datums do too (DatumOption.ts), so they pick it up without changes.
  • Decoding stays as it is: the generic decoder already accepts every form the ledger accepts, and decoded datums keep their original bytes (fix(datum): keep the original bytes of decoded inline datums #603, format replay).
  • After the switch, deprecate CBOR.BoundedBytes, so CBOR holds no Plutus data rules.

Order

  1. Add the encoder next to the current path and prove it byte-identical to today's output for every data preset on random data, except the cases in the table.
  2. Switch Data to it. This fixes Data: constructors with an index above 127 write the tag 102 pair with the wrong header #601 and CBOR: bignum payloads bypass bounded_bytes chunking above 64 bytes #530 and the -2^64 boundary from CBOR: align Data map encoding default and fix negative int bignum boundary #395.
  3. Deprecate CBOR.BoundedBytes.
  4. Then move the data default to PLUTUS_DATA_OPTIONS (CBOR: align Data map encoding default and fix negative int bignum boundary #395).

Regression test

  • Byte identity with the current encoder for every data preset on seeded random Data, outside the table's cases.
  • Constr 128 [1] and Constr 1000 [1] under PLUTUS_DATA_OPTIONS equal the cardano-cli 10.5.1 hash-script-data bytes in Data: constructors with an index above 127 write the tag 102 pair with the wrong header #601.
  • A 65-byte bignum, -2^64 and 2^64 match cardano-cli and Aiken v1.1.24 serialise_data.
  • Devnet: datum hash spends and redeemers built from these values are accepted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions