# Validation Scripts

# Low Level, Untyped Validation Scripts

Lecture 2, Part 2

Lecture Video

There are 2 parts to a smart contract: an on-chain part and an off-chain part. The on-chain part is all about validation. The off-chain part lives in the user's wallet and constructs suitable transactions.

Normal UTxO model uses public key addresses to identify an output. An address can be used as an input as long as it's signed with the corresponding signature for that address.

The EUTxO model extends this and allows "script" addresses in addition to public key addresses. When a transaction uses a script as an input, the node runs the script and determines whether the transaction is valid or not.

A script can act on three pieces of information:

  • An arbitrary piece of data called the "datum"
  • A "redeemer" that is used in place of a signature for validation
  • The "context", which contains all inputs and outputs for the transaction

These 3 pieces use the same Haskell datatype at a low level: PlutusTx.BuiltinData

BuiltinData doesn't have a constructor. Instead you use a conversion from / to the PlutusTx.Data datatype.

# The Data Datatype

Data has multiple constructors:

  • Constr: Integer [Data]
  • Map: [(Data, Data)] for lists of key/value pairs
  • List: [Data]
  • I: Integer for numbers
  • B: ByteString for strings

It's very similar to JavaScript's JSON. You can see information about the type in the Cabal REPL. Follow the instructions from lecture 1 about building the example code, but replacing week 1 with week 2 where necessary. After following those steps, launch the REPL.

cabal repl

Import the PlutusTx module

import PlutusTx

Get information about Data

:i Data

You can view a structure with this type. Let's use the I constructor to create a Data with an integer.

:t I 42
I 42 :: Data

You can do the same with a string (or more accurately, a ByteString), but you'll need to enable overloaded strings in GHC first.

:set -XOverloadedStrings

View the type information for a Data structure created with the B bytestring constructor.

:t B "Haskell"
B "Haskell" :: Data

How about with key/value pairs?

:t Map [(I 42, B "Haskell"), (List [I 0], I 1000)]
Map [(I 42, B "Haskell"), (List [I 0], I 1000)] :: Data

# Gift.hs

Source Code

My re-written code with comments

While writing code in a .hs file, make sure you have a REPL running with the module loaded.

:l src/Week02/Gift.hs

And reload often.

:r

If you write too many lines, then try to reload and see a bunch of compilation errors, you'll have a much harder time debugging than if you only wrote a few lines of code and see one compilation error.

The most important line is the validator logic. This one ignores all 3 inputs and always passes.

mkValidator _ _ _ = ()

Once you've written the lines for valhash, you can reload the module in the REPL and view the hash

valHash
67f3314...

You can do the same with scrAddress

scrAddress
Address {
  addressCredential = ScriptCredential 67f3314...,
  addressStakingCredential = Nothing
}

The scripts credential is basically just its valHash.

Copy & paste the contents of Gift.hs to the playground to simulate it. Try things like 3 wallets, 2 give and then 1 grabs, etc.

# Burn.hs

Source Code

My re-written code with comments

Almost identical to Gift.hs. The difference is the validator always throws an error instead of always passing.

mkValidator _ _ _ = error ()

When using error such as mkValidator _ _ _ = error () you may think you are using the standard error type included in the Haskell Prelude. But since we are using the language extension NoImplicitPrelude none of the standard Haskell Prelude types are loaded. You are actually using PlutusTx.Prelude.error.

:t PlutusTx.Prelude.error
PlutusTx.Prelude.error :: () -> a

Even better is traceError

mkValidator _ _ _ = traceError "BURNT!"

# FortyTwo.hs

Now for a validator that doesn't ignore all its inputs.

Source Code

My re-written code with comments

In this case, the grab endpoint asks for an integer. If that integer is the hard-coded value 42 the transaction passes. Otherwise, it fails.