Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce strict equality check for PlutusTx Value #709

Merged
merged 4 commits into from
Feb 8, 2023

Conversation

v0d1ch
Copy link
Contributor

@v0d1ch v0d1ch commented Feb 7, 2023

Why

We found out that equality check (==) on Value is expensive in PlutusTx. Values are implemented as map of maps and map here is just an associative list and this check cares to be able to find the appropriate value for the key anywhere in this list.

Since we know that two values and their order should be the same we can leverage this to serialize the data
and then do equality check on BuiltinBytestring which is cheaper.

What

Introduce a new function to do the strict equality check between serialized representation of two Values.

To check before merging:

  • CHANGELOG is up to date
  • Up to date with master

@v0d1ch v0d1ch self-assigned this Feb 7, 2023
@v0d1ch v0d1ch requested a review from ch1bo February 7, 2023 11:48
@github-actions
Copy link

github-actions bot commented Feb 7, 2023

Transactions Costs

Sizes and execution budgets for Hydra protocol transactions. Note that unlisted parameters are currently using arbitrary values and results are not fully deterministic and comparable to previous runs.

Metadata
Generated at 2023-02-08 10:01:29.174335602 UTC
Max. memory units 14000000
Max. CPU units 10000000000
Max. tx size (kB) 16384

Script summary

Name Hash Size (Bytes)
νInitial 6e4ce2bd32260424babefe087c2a5ee0a414745fe0281d5608ae9bf8 5530
νCommit 03f2e29fd352f8c2961ad2f61d87a48f88bf01ea6c079b46a3184aee 2547
νHead 13a1c9e1bf086b3de264fb6d411a0691bfb557d4439bd625c28fea80 9722

Cost of Init Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 5016 9.81 3.87 0.48
2 5222 12.76 5.04 0.52
3 5426 16.34 6.46 0.57
5 5835 18.18 7.11 0.61
10 6861 28.40 11.06 0.76
45 14038 97.99 37.88 1.83

Cost of Commit Transaction

Currently only one UTxO per commit allowed (this is about to change soon)

UTxO Tx size % max Mem % max CPU Min fee ₳
1 633 21.18 8.56 0.41

Cost of CollectCom Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 13005 22.61 9.01 0.97
2 13356 37.83 15.23 1.15
3 13708 55.36 22.47 1.36
4 14210 79.86 32.57 1.66

Cost of Close Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 10323 15.32 6.54 0.78
2 10488 16.81 7.35 0.80
3 10688 18.87 8.55 0.83
5 11013 22.09 10.26 0.89
10 11867 30.15 14.70 1.02
50 15206 69.21 31.73 1.61

Cost of Contest Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 10346 16.24 6.87 0.79
2 10546 18.00 7.95 0.82
3 10711 19.44 8.74 0.84
5 11039 22.76 10.49 0.90
10 11898 30.42 14.77 1.03
36 16233 70.21 36.57 1.70

Cost of Abort Transaction

Some variation because of random mixture of still initial and already committed outputs.

Parties Tx size % max Mem % max CPU Min fee ₳
1 14851 28.50 12.05 1.12
2 15204 47.03 20.13 1.34
3 15557 68.98 29.75 1.61
4 15284 77.17 32.81 1.68
5 15179 89.43 37.78 1.81

Cost of FanOut Transaction

Involves spending head output and burning head tokens. Uses ada-only UTxO for better comparability.

UTxO Tx size % max Mem % max CPU Min fee ₳
1 14732 11.40 4.91 0.93
2 14768 12.68 5.69 0.94
3 14739 13.82 6.42 0.96
5 14875 17.25 8.33 1.00
10 15054 25.18 12.85 1.11
20 15414 40.44 21.66 1.31
51 16335 86.34 48.44 1.92

@github-actions
Copy link

github-actions bot commented Feb 7, 2023

Test Results

274 tests  ±0   268 ✔️ ±0   13m 5s ⏱️ +50s
  93 suites ±0       6 💤 ±0 
    4 files   ±0       0 ±0 

Results for commit 7591c87. ± Comparison against base commit 6632086.

♻️ This comment has been updated with latest results.

Copy link
Member

@ch1bo ch1bo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks good. But want all core contributors on Hydra to see and approve this (it's important to know)

-- lists and Map equality is implemented. Instead we can be more strict and
-- require EXACTLY the same value and compare using the serialised bytes.
(===!) :: Value -> Value -> Bool
(===!) val val' =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is === or ==! already taken?

Copy link
Contributor Author

@v0d1ch v0d1ch Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I first went with '===' but then thought to add extra sprinkle using '!' because this is a strict check 😀

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intuition would not have associated the ! with strictness here. Besides it has nothing to do with evaluation strictness. Let's call it more an exact equality. Also, other readers might confuse ===! with != from other languages. So I would remove the ! and just stick with ===

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine by me, renamed!

Copy link

@manupadillaph manupadillaph Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello!

I was trying to compare results using (===!):

(===!) :: LedgerApiV2.Value -> LedgerApiV2.Value -> Bool    

(===!) val val' =    
  TxBuiltins.serialiseData (LedgerApiV2.toBuiltinData val) == TxBuiltins.serialiseData (LedgerApiV2.toBuiltinData val')    

But got an error:

Eval Error: CodecError (DeserialiseFailure 2663 "BadEncoding (0x0000004231c2aa77,S {currPtr = 0x0000004231c2a04e, usedBits = 3}) \"Forbidden builtin function: (builtin serialiseData)\"")

Any idea?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to run it using protocol version >= 7.0. The serialiseData builtin was enabled in version 7 of the ledger/plutus interface.

Copy link

@manupadillaph manupadillaph Mar 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @ch1bo !
Im still trying to build and run with the serialiseData to compare results of used resources, but I'm getting the same error "Forbidden builtin function: (builtin serialiseData)".
I just updated Plutus Apps to 1.1.0, the last release and updated cabal and project file according to the files in Plutus Apps.
What else I need to Update?
And also, I let a question for you in cardano stakoverflow... I would be happy if you can help me with that too
Thank you!!!!!

PD: I think I figured it out... you are using your own plutus: hydra-plutus... Im sorry I got this so late! :p

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not "an own plutus", but we are not using plutus-apps. I don't know where you need to do the setting there, but you want to use protocol version >= 7 when you run the scripts by your plutus interpreter (e.g. in a cardano-ledger or a simulator or so).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you brother!
I just realize that basic stuff about the protocol version I was using to do the evaluation. Now it works!
The method with serialiseData is the best I tested by now. With 20 different tokens, using your way, ExMemory 7294670, mine: ExMemory 9089316, and with (==), ExMemory 13382024. As you can see it serialiseData is almost the half of the normal equality.
Im about to use your way of comparing in a production contracts Im developing. I really need the extra free mem I getting now.
But still I dont really get in full clarity in what kind of scenarios VALUES are not normalized, not ordered and with cs or tn repeated and this way of comparing can fail.
Im using off chain code to create the transactions with lucid framework in a frontend.
Where comes the part that creates the list of values, normalized or not?
I need to understand a little more that to feel more safe in using your way or mine.
Thank you so much

-- require EXACTLY the same value and compare using the serialised bytes.
(===!) :: Value -> Value -> Bool
(===!) val val' =
serialiseData (toBuiltinData val) == serialiseData (toBuiltinData val')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you also tried comparing the hashes or doing an explicit loop through the value elements?

Asked differently, do we know if this is the cheapest way to do it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried it but will do and compare the results.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the logbook entry here and it shows that serializing data is our best bet. Perhaps I should try alternative implementation where I don't rely on plutusTx functions like flattenValue and valueOf but I didn't see anything that would make them slower then they need be.

@v0d1ch v0d1ch merged commit c37f6b4 into master Feb 8, 2023
@v0d1ch v0d1ch deleted the fix-plutus-map-equality-check branch February 8, 2023 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants