IntersectMBO / plutus

The Plutus language implementation and tools
Apache License 2.0
1.56k stars 473 forks source link

Apply params to validator loaded from file fails #5414

Open KristianBalaj opened 1 year ago

KristianBalaj commented 1 year ago

Summary

I'm trying to apply parameters to Aiken written validator that is loaded from file in Plutus. When the validator is non-parametrized, it works fine, but the problem is when applying parameters to a validator.

The problem seems to be that the encoding of parameter application doesn't seem to match but I'm not sure what is the format Plutus is using to apply parameters and whether it could be customised.

Steps to reproduce the behavior

The Aiken's validator is a simple always pass validator that accesses the script parameters.

The following is the Aiken code for reference:

type FooScriptParams {
    a: Int,
}

validator(params: FooScriptParams) {
  fn spend(datum: Void, redeemer: Void, ctx: ScriptContext) -> Bool {
    let FooScriptParams { a } = params
    a == a
  }
}

Then using the following transformation to named deBruijn:

  1. aiken build --uplc
  2. aiken uplc encode --to named-debruijn artifacts/foo.spend.uplc > foo.namedDeBruijn

I'm using the following revision of Aiken https://github.com/aiken-lang/aiken/commit/699467a876f54232833e034a0b21deea4f3a69af

The following is the loading of the validator encoded in named deBruijn from foo.namedDeBruijn into Plutus:

data FooScriptParams = FooScriptParams
  { a :: Integer
  }

PlutusTx.makeLift ''FooScriptParams
PlutusTx.makeIsDataIndexed ''FooScriptParams [('FooScriptParams, 0)]

compiledFooUnapplied :: CompiledCode (FooScriptParams -> UntypedValidator)
compiledFooUnapplied = $$(PlutusTx.loadFromFile "some_path/foo.namedDeBruijn")

compiledFoo :: CompiledCode UntypedValidator
compiledFoo = compiledFooUnapplied `PlutusTx.applyCode` PlutusTx.liftCode FooScriptParams {a = 100}

Actual Result

Now when executing the compiledFoo from the steps to reproduce, I get the following error:

Could not unlift a builtin:
Not a constant
Caused by: (delay (\i_i0 -> i_i1 100))) []

Where the (delay (\i_i0 -> i_i1 100))) [] seems to be the encoded FooScriptParams. So I'm expecting the problem is in the application of parameters.

Expected Result

Expected is that the parameters get properly applied to the validator and the validator validates.

Describe the approach you would take to fix this

No response

System info

Plutus revision: https://github.com/input-output-hk/plutus/commit/f003b096dbc5abca9dbb74fbff847914dd39fa6b

effectfully commented 1 year ago

aiken uplc encode --to named-debruijn artifacts/foo.spend.uplc > foo.namedDeBruijn

Please attach the result of this command (as a file or under a spoiler).

(delay (\i_i0 -> i_i1 100))) []

What evaluator do you use? This doesn't seem like the syntax that we use in those errors, although I might be wrong about that.

Anyway, the error message says that a function is applied to the Scott-encoded FooScriptParams (and it's encoded correctly) while expecting some constant. What function? I can't tell without seeing the whole program.

... but actually, thinking about it, wasn't Aiken encoding all data types using Data instead of replicating our old (we now have SOPs) Scott-encoding? If so, then that's your error: the function implemented in Aiken wants you to provide a Data-encoded value of FooScriptParams and you provide it a Scott-encoded one.

I'm going to remove the bug label for now and I'll return it later if it turns out that the bug is indeed on the Plutus side.

KristianBalaj commented 1 year ago

aiken uplc encode --to named-debruijn artifacts/foo.spend.uplc > foo.namedDeBruijn

Please attach the result of this command (as a file or under a spoiler).

Here's the Aiken's named debruijn of the always pass code listed above via hexdump:

Hexdump ``` 00000000 01 00 00 32 01 03 69 5f 30 00 00 32 01 03 69 5f |...2..i_0..2..i_| 00000010 31 00 00 32 01 03 69 5f 32 00 00 32 01 03 69 5f |1..2..i_2..2..i_| 00000020 33 00 00 32 01 03 69 5f 34 00 00 32 01 03 69 5f |3..2..i_4..2..i_| 00000030 35 00 00 32 01 03 69 5f 36 00 00 32 01 03 69 5f |5..2..i_6..2..i_| 00000040 37 00 00 21 03 69 5f 38 00 00 21 03 69 5f 39 00 |7..!.i_8..!.i_9.| 00000050 00 21 04 69 5f 31 30 00 00 21 04 69 5f 31 31 00 |.!.i_10..!.i_11.| 00000060 00 32 01 04 69 5f 31 32 00 00 32 01 04 69 5f 31 |.2..i_12..2..i_1| 00000070 33 00 00 53 33 01 03 69 5f 34 00 0a 32 01 04 69 |3..S3..i_4..2..i| 00000080 5f 31 34 00 00 32 01 04 69 5f 31 35 00 00 33 70 |_14..2..i_15..3p| 00000090 e1 04 69 5f 31 35 00 01 01 04 69 5f 31 35 00 01 |..i_15....i_15..| 000000a0 37 5a 61 03 69 5f 31 00 0e 01 04 69 5f 31 34 00 |7Za.i_1....i_14.| 000000b0 01 30 01 03 69 5f 32 00 0c 37 54 01 03 69 5f 38 |.0..i_2..7T..i_8| 000000c0 00 06 14 98 59 4c cc 01 03 69 5f 34 00 09 33 70 |....YL...i_4..3p| 000000d0 e9 00 01 81 03 69 5f 33 00 0a 37 54 01 03 69 5f |.....i_3..7T..i_| 000000e0 39 00 04 14 98 59 4c cc 01 03 69 5f 34 00 08 33 |9....YL...i_4..3| 000000f0 70 e9 00 01 81 03 69 5f 33 00 09 37 54 01 04 69 |p.....i_3..7T..i| 00000100 5f 31 30 00 02 14 98 58 cc 01 03 69 5f 36 00 01 |_10....X...i_6..| 00000110 01 03 69 5f 36 00 01 48 00 09 04 69 5f 31 36 00 |..i_6..H...i_16.| 00000120 00 21 04 69 5f 31 37 00 00 21 04 69 5f 31 38 00 |.!.i_17..!.i_18.| 00000130 00 21 04 69 5f 31 39 00 00 33 33 01 03 69 5f 34 |.!.i_19..33..i_4| 00000140 00 06 33 70 e1 04 69 5f 31 39 00 01 01 04 69 5f |..3p..i_19....i_| 00000150 31 37 00 03 01 03 69 5f 31 00 09 21 04 69 5f 32 |17....i_1..!.i_2| 00000160 30 00 00 33 33 01 04 69 5f 31 36 00 05 01 04 69 |0..33..i_16....i| 00000170 5f 31 36 00 05 33 70 01 04 69 5f 31 37 00 04 48 |_16..3p..i_17..H| 00000180 00 8c 01 03 69 5f 30 00 0b 01 04 69 5f 32 30 00 |....i_0....i_20.| 00000190 01 01 04 69 5f 31 39 00 02 01 04 69 5f 31 38 00 |...i_19....i_18.| 000001a0 02 21 04 69 5f 32 31 00 00 30 01 03 69 5f 33 00 |.!.i_21..0..i_3.| 000001b0 03 37 54 01 04 69 5f 32 31 00 01 57 34 aa e7 55 |.7T..i_21..W4..U| 000001c0 5c f2 ba 15 74 41 |\...tA| 000001c6 ```

Here's the same but from the stdout in terminal:

Terminal stdout ``` 2i_02i_12i_22i_32i_42i_52i_62i_7!i_8!i_9!i_10!i_112i_122i_13S3i_4 2i_142i_153p�i_15i_157Zai_1i_140i_2 7Ti_8�YL�i_4 3p��i_3 7Ti_9�YL�i_3p��i_3 7Ti_10�X�i_6i_6H i_16!i_17!i_18!i_1933i_43p�i_19i_17i_1 !i_2033i_16i_163pi_17H�i_0 i_20i_19i_18!i_210i_37Ti_21W4��U\�tA% ```

And here's the binary file (the upload expires in 6 days) - https://filebin.net/uv538dwurqn2pp0j

What evaluator do you use? This doesn't seem like the syntax that we use in those errors, although I might be wrong about that.

I'm using the following tool to run the validator https://github.com/mlabs-haskell/plutus-simple-model I'm not sure what evaluator does it use, but my guess would be the Cardano ledger one.

If so, then that's your error: the function implemented in Aiken wants you to provide a Data-encoded value of FooScriptParams and you provide it a Scott-encoded one.

That sounds like the possible problem. Is there a way how to modify the parameter application to be data-encoded instead of the scott encoded one?

compiledFoo :: CompiledCode UntypedValidator
compiledFoo = compiledFooUnapplied `PlutusTx.applyCode` PlutusTx.liftCode FooScriptParams {a = 100}
effectfully commented 1 year ago

Is there a way how to modify the parameter application to be data-encoded instead of the scott encoded one?

No, we do not support Data-encoded data in any way, it's Aiken's thing. I know @michaelpj has strong opinions about it. Basically, it's just super extra inefficient to encode data using Data.

If you really need such a thing, your best bet is probably to compile to SOPs using our most recent release and then convert SOPs to Data, shouldn't be too hard. But I don't know how Aiken encodes data, so perhaps it makes sense for you to request such functionality from Aiken people.

I'll take a look at the provided file shortly, thanks for sharing.

effectfully commented 1 year ago

So this is the decoded program:

program ``` (program 1.0.0 ((\i_0 -> (\i_1 -> (\i_2 -> (\i_3 -> (\i_4 -> (\i_5 -> (\i_6 -> (\i_7 i_8 i_9 i_10 i_11 -> (\i_12 -> (\i_13 -> force (i_4 ((\i_14 -> (\i_15 -> equalsInteger i_15 i_15) (unIData (i_1 i_14))) (i_2 (unConstrData i_8))) (delay ()) (delay error))) (force (i_4 (equalsInteger 0 (i_3 (unConstrData i_9))) (delay ()) (delay error)))) (force (i_4 (equalsInteger 0 (i_3 (unConstrData i_10))) (delay ()) (delay error)))) (i_6 i_6 0)) (\i_16 i_17 i_18 i_19 -> i_4 (equalsInteger i_19 i_17) i_1 (\i_20 -> i_16 i_16 (addInteger i_17 1) (i_0 i_20) i_19) i_18)) (\i_21 -> i_3 (unConstrData i_21))) (force ifThenElse)) (force (force fstPair))) (force (force sndPair))) (force headList)) (force tailList))) ```

The lambda that you apply is the one binding i_8. i_8 is expected to be a Data object with an Integer in its single constructor, i.e. this all confirms my hypothesis.

I do not see what the Plutus team can do here, I don't think we're going to support converting SOPs to Data in any observable future (we might decide to do that if we end up implementing a fancy ScriptContext encoding or something). @michaelpj please verify this when you're back.

Hence I'm closing the issue, do feel free to reopen if you disagree.

KristianBalaj commented 1 year ago

Thank you very much for your insights again 🙌 I really appreciate it.

effectfully commented 1 year ago

Happy to help!

michaelpj commented 1 year ago

No, we do not support Data-encoded data in any way, it's Aiken's thing. I know @michaelpj has strong opinions about it. Basically, it's just super extra inefficient to encode data using Data.

I mean, it's stupid and very slow, but I think we could support this as a datatype compilation mode now. Although I would mostly use it to show how bad it is :joy:

effectfully commented 1 year ago

I mean, it's stupid and very slow, but I think we could support this as a datatype compilation mode now. Although I would mostly use it to show how bad it is joy

OK, if there's a serious consideration for us to do it, then I'm reopening this issue.