HeliosLang / compiler

Helios is a DSL for writing Cardano smart contracts. This library lets you compile Helios scripts and build Cardano transactions.
https://www.hyperion-bt.org/helios-book
BSD 3-Clause "New" or "Revised" License
143 stars 31 forks source link

Unhandled container type when trying to deserialize list of builtins #49

Closed nielstron closed 1 year ago

nielstron commented 1 year ago

I am trying to run the following code

import { ByteArray as HeliosByteArray, Int as HeliosInt, deserializeUplcBytes, hexToBytes, bytesToHex, CborData, textToBytes } from "@hyperionbt/helios"

const script = deserializeUplcBytes(
    CborData.decodeBytes(
        hexToBytes(
            "59029d01000022232498c8c8cccc00401492600300253300148901030013263357389201144e616d654572726f723a2076616c696461746f7200498c8c8c8894ccd5cd19b8f002488101030011003133004002001222232498c8c94ccd5cd19299800a4501020013263357389201164e616d654572726f723a207369675f70726573656e740049800440044c98cd5ce192491a5265717569726564207369676e6174757265206d697373696e6700001498c8c8894ccd5cd19b8f002489010200110031330040020013233223371e00466464600200244a666ae68d5d1800899b8b4800001454ccd5cd18019aba10011357420022660040046ae88004dc780100099191bae33323001001222533357346ae8c0044c98cd5ce24810a496e6465784572726f720049854ccd5cd19b87002480004d5d0800899980180199b8100248008d5d1000a40006aae78dd500099299800a4501000013263357389201104e616d654572726f723a20646174756d00498004004c8c8cc8c8c004004894ccd5cd1aba300114bd62099aba0300335742002660040046ae880048dd70009bac33323001001222533357346ae8c0044c98cd5ce2490a496e6465784572726f720049854ccd5cd19b87002480004d5d0800899980180199b8100248008d5d1000a40206aae78dd50009919199918008009112999ab9a35746002264c66ae712410a496e6465784572726f720049854ccd5cd19b87002480004d5d0800899980180199b8100248008d5d1000a40006aae78dd500099299800a4501010013263357389201124e616d654572726f723a20636f6e7465787400498004004004004004ccc888894ccd5cd19b8f0024890101001100315333573466e3c00922010104001100415333573466e3c0092201010000110051330060020010040030020012200101"
        )
    )
)
console.log(script)

And get this error:

error - Error: unhandled container type
    at UplcDeserializer.readTypedValue (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11843:12)
    at UplcDeserializer.readConstant (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11800:32)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11641:17)
    at UplcDeserializer.readDelay (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11857:19)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11635:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readForce (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11867:19)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11643:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readLambda (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11777:18)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11637:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11787:16)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11639:17)
    at UplcDeserializer.readCall (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11788:16) {
  page: '/'
}

I have tried more minimalistic programs, for them the deserialization works fine, so I am wondering if I am maybe using some weird uplc code that can not be deserialized by helios

The cbor is the result of compiling the eopsin sample gift contract with this uplc code

(program 1.0.0 (lam p0 (lam p1 (lam p2 [(lam _ (con unit ())) [(lam s [(lam g [[[[g p0] (lam _ (con unit ()))] p2] s]) (force [[s (con bytestring #03)] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "NameError: validator")] (con unit ())]])])]) [(lam s [(lam s [(lam a0 (lam x (lam def (force [[[(force (builtin ifThenElse)) [[(builtin equalsByteString) x] (con bytestring #03)]] (delay (delay a0))] (delay [[s x] def])])))) (lam p0 (lam p1 (lam p2 (lam s [(lam s (con unit ())) [(lam s [(lam s (force [[[(force (builtin ifThenElse)) [(lam s (force [[s (con bytestring #02)] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "NameError: sig_present")] (con unit ())]])])) s]] (delay s)] (delay [(lam _ (error)) [[(force (builtin trace)) [(lam s (con string "Required signature missing")) s]] (con unit ())]])])) [(lam s [(lam a0 (lam x (lam def (force [[[(force (builtin ifThenElse)) [[(builtin equalsByteString) x] (con bytestring #02)]] (delay (delay a0))] (delay [[s x] def])])))) [(lam s [[(lam x (lam y [[(builtin equalsByteString) x] [[(lam op [(lam g [g g]) (lam f (lam xs (force [[[(force (builtin ifThenElse)) [(force (builtin nullList)) xs]] (delay [[(builtin consByteString) (con integer 0)] x])] (delay (force [[[(force (builtin ifThenElse)) [op [(force (builtin headList)) xs]]] (delay [(force (builtin headList)) xs])] (delay [[f f] [(force (builtin tailList)) xs]])]))])))]) [(builtin equalsByteString) x]] y]])) [(lam s [(lam self [(builtin unBData) [[[(lam g [g g]) (lam f (lam i (lam xs (force [[[(force (builtin ifThenElse)) [(force (builtin nullList)) xs]] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "IndexError")] (con unit ())]])] (delay (force [[[(force (builtin ifThenElse)) [[(builtin equalsInteger) i] (con integer 0)]] (delay [(force (builtin headList)) xs])] (delay [[[f f] [[(builtin subtractInteger) i] (con integer 1)]] [(force (builtin tailList)) xs]])]))]))))] (con integer 0)] [(force (force (builtin sndPair))) [(builtin unConstrData) self]]]]) [(lam s (force [[s (con bytestring #00)] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "NameError: datum")] (con unit ())]])])) s]]) s]] [(lam s [(lam self [[(lam op [(lam g [g g]) (lam map (lam xs (force [[[(force (builtin ifThenElse)) [(force (builtin nullList)) xs]] (delay (con list<bytestring> []))] (delay [[(force (builtin mkCons)) [op [(force (builtin headList)) xs]]] [[map map] [(force (builtin tailList)) xs]]])])))]) (lam x [(builtin unBData) x])] [(builtin unListData) [[[(lam g [g g]) (lam f (lam i (lam xs (force [[[(force (builtin ifThenElse)) [(force (builtin nullList)) xs]] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "IndexError")] (con unit ())]])] (delay (force [[[(force (builtin ifThenElse)) [[(builtin equalsInteger) i] (con integer 0)]] (delay [(force (builtin headList)) xs])] (delay [[[f f] [[(builtin subtractInteger) i] (con integer 1)]] [(force (builtin tailList)) xs]])]))]))))] (con integer 8)] [(force (force (builtin sndPair))) [(builtin unConstrData) self]]]]]) [(lam s [(lam self [[[(lam g [g g]) (lam f (lam i (lam xs (force [[[(force (builtin ifThenElse)) [(force (builtin nullList)) xs]] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "IndexError")] (con unit ())]])] (delay (force [[[(force (builtin ifThenElse)) [[(builtin equalsInteger) i] (con integer 0)]] (delay [(force (builtin headList)) xs])] (delay [[[f f] [[(builtin subtractInteger) i] (con integer 1)]] [(force (builtin tailList)) xs]])]))]))))] (con integer 0)] [(force (force (builtin sndPair))) [(builtin unConstrData) self]]]) [(lam s (force [[s (con bytestring #01)] (delay [(lam _ (error)) [[(force (builtin trace)) (con string "NameError: context")] (con unit ())]])])) s]]) s]]) s]]) s]]) s]]) [[[(lam a0 (lam a1 (lam a2 (lam x (lam def (force [[[(force (builtin ifThenElse)) [[(builtin equalsByteString) x] (con bytestring #01)]] (delay (delay a2))] (delay (force [[[(force (builtin ifThenElse)) [[(builtin equalsByteString) x] (con bytestring #04)]] (delay (delay a1))] (delay (force [[[(force (builtin ifThenElse)) [[(builtin equalsByteString) x] (con bytestring #00)]] (delay (delay a0))] (delay [[s x] def])]))]))])))))) p0] p1] p2]]]))))]) s]) (lam x (lam def def))]]]))))
nielstron commented 1 year ago

I narrowed it down to this minimal example:

(program 1.0.0 (con list<integer> []))

Note this is the aiken syntax and concerns types that are possible but poorly documented by IOHK.

The corresponding JS

import { ByteArray as HeliosByteArray, Int as HeliosInt, deserializeUplcBytes, hexToBytes, bytesToHex, CborData, textToBytes } from "@hyperionbt/helios"

const script = deserializeUplcBytes(
    CborData.decodeBytes(
        hexToBytes(
            "460100004bd601"
        )
    )
)
console.log(script)

and error

error - Error: unhandled container type
    at UplcDeserializer.readTypedValue (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11843:12)
    at UplcDeserializer.readConstant (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11800:32)
    at UplcDeserializer.readTerm (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11641:17)
    at deserializeUplcBytes (file:///home/niels/git/wrappe.de/node_modules/@hyperionbt/helios/helios.js:11912:20)
    at eval (webpack-internal:///./src/config/contract.ts:16:103)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  page: '/'
nielstron commented 1 year ago

I guess the issue is precisely that "recursive types not handled yet" https://github.com/Hyperion-BT/helios/blob/3ac854f97dc7424917d9385e646ec1119299273c/helios.js#L11843

christianschmitz commented 1 year ago

Yes, it was only implemented up to point that it was needed for Helios itself. But for these decompilation cases, I guess it would be enough to have a "generic" container type

Thanks for creating an issue for this (might take a while to resolve though, I don't have much time currently)

nielstron commented 1 year ago

I am willing to work on this but need some guidance on it. Is there any reference/documentation on how the constant types are flattened/unflattened?

christianschmitz commented 1 year ago

The spec can be found here: https://github.com/input-output-hk/plutus/blob/master/doc/plutus-core-spec/plutus-core-specification.pdf

You would have to start making changes here: https://github.com/Hyperion-BT/helios/blob/3ac854f97dc7424917d9385e646ec1119299273c/src/uplc-program.js#L767-L782

Maybe making the UplcList/UplcMap/UplcPair classes generic is the best way to go.