tweag / cooked-validators

MIT License
39 stars 11 forks source link

Misleading comments about circluar dependencies in the Auction contract #153

Closed carlhammann closed 1 year ago

carlhammann commented 2 years ago

The first substantial comment in the source code of the auction contract example is misleading at best, and false at worst (in any case, it's confusing). It talks of breaking a circular dependency between a minting policy and a certain validator: In order for the contract to work, each has to know the other in order to ensure that

That circle is broken by the code in the sense that, by submitting the hash of the validator as a redemer to the minting policy, the necessary information can be shared between the two scripts. It is not ensured, however, that the information in that redeemer is correct. In order to achieve this, each script's hash would need to be a parameter to the other script -- not a redeemer -- which is patently impossible. It's unclear to me if this problem has a solution.

We should:

mmontin commented 2 years ago

A possible fix to that, that is somewhat generic, is to make any initial posting (paying to a script) a two steps process, since the first one is basically unchecked (except for whatever check the minting policy makes, that cannot be enough due to the circular dependency issue).

The idea is that instead of minting tokens whenever the script is paid to, these tokens are only minted later on, in an additional transaction (with an additional "Check" redeemer), which takes as input any "unchecked" utxo (the ones that have been directly paid by users) and produced a "checked" one. In addition, this transaction can make any relevant checks that are required over the input utxo, regarding its value and datum, and refuse to process any such invalid utxo.

This solves this issue because the validator itself knows its address and can thus ensure that the minted token are given to itself and cannot be redirect to any random public key by an attacker.

A question arises : how to differentiate "checked" to "unchecked" utxos ? This is solved by having an additional field in the datum of the script, essentially a boolean flag, which keeps track of whether this utxo has already been checked. Calling the "Check" redeemer to consume this utxo would just fail or do nothing on already "checked" utxos, and it would do the checking for "unchecked" ones, assigning this new field to "True".

This raises yet another question : how to prevent users from paying to the script with the new datum field set to True ? This cannot be prevented of course, but there are ways around that : first, it's questionable whether this would benefit the user since the utxo would not contain any of the necessary minted tokens, and would not be changed by the "Check" action, second, checks could be done by any other action consuming such a utxo that could verify the following : the flag must be True, and whatever tokens are required must be present. In that case, any utxo which has the flag set to True and does not contain any token would just be locked forever and a total loss for the attacker.

mmontin commented 2 years ago

Here is an updated version of the description of the fix that we did for our latest report:

Cardano blockchain and associated scripts suffer from a well-known circular dependency issue, where a minting policy for instance would need to have a static parameter corresponding to the address of a script, while said script would need the hash of said minting policy as a static parameter as well. This, of course, is impossible. A possible fix would be to allow multi-purpose scripts, but this is a design choice that is not up to us to make.

There is, however, one way to go around this issue. The idea is to turn any initial payment to a script which induces a minting of tokens into a two-transaction process. The first transaction does not involve any checks at all, does not mint any tokens that should be locked in the script, and creates unchecked UTxOs at the recipient script. The second transaction consumes unchecked UTxOs (with an additional Check redeemer), mints the required tokens, and pays a checked UTxO back to the same script, which contains the newly minted tokens as a proof of their soundness. Since the second transaction uses a redeemer, it can make whatever checks are needed to ensure the tokens are minted correctly and paid to the correct script.

This should solve the issue because the validator itself knows its own address and can thus ensure that the minted tokens are given to itself and cannot be redirected to any random public key by an attacker. Any further transaction consuming one such UTxO would have to rely on the presence of the minted tokens to assess its correctness.