For our next script, we will use the datum and redeemer arguments instead of ignoring them. We will still ignore the third argument, the transaction context, for now. The goal of this script is to create a guessing game, where the UTxO sitting at the script address is unlocked if the submitting transaction sends a redeemer that matches the datum present at that UTxO. It is quite a simple re-rewrite from our first script - we just need to add a bit of logic to the mkValidator function and replace the function names accordingly. Create a new file src/GuessingGame.hs for this validator and paste the code from SimplestSuccess.hs into it. Our extensions and imports stay exactly the same, let's just remove the qualified from the PlutusTx.Prelude import so that we do not have to prefix every Prelude function with Prelude.:
import PlutusTx.Prelude
Writing the validator
We rename our module and exposed functions. Let's remove the script name from the exposed generic functions for serialising and writing the script to disk and just call them scriptSerialised and writeSerialisedScript:
{-# INLINABLE mkValidator #-}mkValidator::BuiltinData->BuiltinData->BuiltinData->()mkValidator datum redeemer _ =if datum == redeemer then()else error ()
We use the comparison function to check whether the received redeemer matches the datum sitting at the UTxO. If that's the case, we return () as a sign of successful validation. Otherwise, we use the error () from Prelude to signify failed validation. Our validator and script functions stay exactly the same, but we need to update the names of generic functions for serialising and writing the script to disk, and set the write filename to GuessingGame.plutus:
Every time we want to automatically load a module we write when launching a cabal repl, we can add them to our .cabal file in the exposed-modules field.
To test this script, we could use the compiled unit.json as our datum, but let's instead create a more interesting one. Again launch the cabal repl and load the Utils module. Let's say we want to create a secret in the String format. We can try:
Prelude>:l src/helpers/Utils.hs Ok, one module loaded.Prelude Utils> writeJSONData "compiled/assets/secretGuess.json" "I am a secret"
But we will get the following error:
<interactive>:3:1: error:• No instance for (PlutusTx.IsData.Class.ToData Char) arising from a use of‘writeJSONData’
It seems that PlutusTx.toData class does not implement an instance for the String type. Indeed, if we check the documentation, we see that only a ToData BuiltinByteString is defined when it comes to string-like values. So we need to convert our Haskell String to a Plutus BuiltinByteString. Again we need to look through the documentation to find the function we need (located in the PlutusTx.Builtins.Class module):
Let's load up this module and apply this function to our string before serialising it:
Prelude Utils> import PlutusTx.Builtins.ClassPrelude PlutusTx.Builtins.Class Utils> writeJSONData "compiled/assets/secretGuess.json"$ stringToBuiltinByteString "I am a secret"
No error message, and our datum is compiled under compiled/assets/secretGuess.json. It looks like this:
{"bytes":"4920616d206120736563726574"}
We are now ready to test the validator! Create a new directory testnet/GuessingGame for this purpose.
Let's compile the validator as well.
-- The following line is not necessary if the module was added to exposed-modules in the .cabal filePrelude>:l src/GuessingGame.hsPrelude> GuessingGame.writeSerialisedScriptRight ()
Now, we need to create an address for this validator like before:
Again, we will see existing UTxOs present on the script address, as someone has already compiled and used it. Let's send some value to the script along with our secret datum.
If we check the UTxOs again, we'll see a new UTxO sitting at the script address. Only transactions with the matching redeemer can spend it. Let's try to first spend it with an invalid redeemer. Since the datum of the UTxO we are trying to spend needs to be specified in the transaction regardless, this is slightly pointless. But to show that the validator works as it should, let's specify the correct datum, but the wrong redeemer:
Trying to execute it gives us a script execution failure:
./spend-script-utxo-invalid.shCommandfailed:transactionbuildError:Thefollowingscriptshaveexecutionfailures:thescriptfortransactioninput0 (in ascendingorderoftheTxIds) failed with: ThePlutusscriptevaluationfailed:Anerrorhasoccurred:Usererror:Themachineterminatedbecauseofanerror,eitherfromabuilt-infunctionorfromanexplicituseof'error'.Scriptdebugginglogs:
The logs are empty as we have not configured any logging, nor did we give an error message. But we still know that the script failed to execute successfully for this transaction because the redeemer does not match the datum. Let's create a valid transaction this time. We just need to change the --tx-in-redeemer-file line to point to our secret guess: