Plutus

Please note: This section is currently still Work In Progress, so you will probably find that it is a bit raw, and stops before it is finished. I'm building it now.

Overview

To understand Plutus you are going to have to understand a few general concepts first. I have created a few pages of my own that help give a background, such as my Lexicon which helps you with all the terms and general concepts, and I've written a great page about Blockchains which is an easy to read history lesson about where they came from and what they do.

This page dives a bit more deeply into Plutus, the programming language for Smart Contracts in Cardano. I have created it while I was absorbing the official Plutus Tutorial, so it follows the line of thought shown there. Hopefully I have helped to simplify and clarify some of it, but I recommend getting the extra detail from there.

Plutus is the engine which drives Marlowe, so a read of my Marlowe Tutorial is worthwhile as well. That page talks about Haskell, and Functional Languages, and shows how to construct simple code in the Marlowe environment. Here I will try to cover Plutus in more detail, which will mean understanding how transactions work, and how they are processed and controlled.

I have split the narrative up into sections, so you can skip forward if you just want a particular part. The sections are:

Transactions and UTXO

UTXO stands for Unspent Transaction Output. It is important that you understand this first because it defines the basic rules about how ADA moves around. I have written a summary of UTXO in my Lexicon, so I won't repeat myself, but I want to say a bit more.

In a UTXO system every transaction clears the balance of its source addresses to zero, so all the funds are moved to a list of destination addresses. So you can think of a transaction output as an address. A transaction takes a list of previous transaction outputs as its source, and creates a new list of outputs. The whole balance of the source transactions are always cleared, and the new outputs never add to a previous output record. They are controlled by the keys of the destination wallets. When you look at the balance in a wallet it is the sum of all the Transaction Outputs controlled by the wallet private key which are Unpent. When the value of a UTXO is higher than the amount you want to spend you move the balance into an Output controlled by the sending wallet. Understanding this will help later when we write software to create new transactions because we might refer to a UTXO quite a lot. The UTXO is where the money is.

After the transaction UTXO1 and UTXO2 become Spent Transactions and the balance in Alice's wallet (5 ADA) is represented by a new address, UTXO4.

Usually the source UTXOs for a transaction come from a single wallet, but it is easy to allow several different wallets to supply funds to a transaction, and output to several wallets. This is handy when creating a smart contract. For completeness, there will also be an output to pay the transaction fees, and inside the transaction will be witness signatures, which are approvals from the sending wallets, and perhaps Certificates and Scripts.

Plutus is an Extended UTXO model. There is extra data such as scripts, certificates, and perhaps source information from an Oracle in the transaction to allow rules to be built. Smart Contracts need this so that they can store and execute their rules. We will discuss this in a later section.

Smart Contracts

Smart Contracts allow people to be sure the rules are pre-defined, and can't be broken. As long as they have understood the contract, they are safe. When you design or review a contract you should ensure that the rules are complete and that there is a timeout which returns funds to the right places in the event of a failure to act. The contract will be comitted to the blockchain, and once that is done it cannot be changed. This means it needs to be checked and tested thoroughly first. It also means that when you put money into it you can be sure of what it will do.

I will use the same example here as in my Marlowe web page. It is a simple money transfer contract, allowing one person to put money in, then allowing the money to transfer if two out of three participants vote yes. Otherwise the money is returned. In this case the two things that are tested are the time and the votes, so gathering real-world data is via a trusted third party. The third vote is only used if the first two disagree. Of course trusted third parties are exactly what blockchains are meant to avoid, so other ways of knowing how to resolve a disagreement are often possible.

The alternative ways of collecting data are:

For a full definition of the different types of financial contract, ACTUS is a good place to look.

The commands in a Cardano smart contract use the Plutus environment, which runs on Haskell. Check out my Dev page for help and links to learn Haskell. To create a smart contract you don't need all of it, but learning about functional programming is worthwhile.

On-chain and off-chain

All your Plutus code will be in Haskell, so you can keep it all together and read it as one, but it will be divided into sections which run on-chain and off-chain. The Plutus toolset includes libraries which help with both, so you need to know where to put the two types of code. On-chain code interacts with the blockchain, what is called the Settlement Layer, so it checks balances and moves money. Off-chain code interacts with the user, so it renders the screen and tells people what is going on. The on-chain language is known as Plutus Core. Plutus Tx is the Haskell that compiles to Plutus Core to form the on-chain code. Use the Plutus Playground to create and test smart contracts. An important feature is ability to generate an on-chain script. In a live contract this would create the smart contract inside a transaction on the blockchain, which is why it is called Plutus Tx. The playground simulates this so that you can test it. You write the contract in Haskell in the Editor window, then use the Simulation and Transactions windows to run the code and see the results.

When you start in the playground you will see that there are examples, and the examples are in Haskell and start with a list of imports. The imports are the bits of the Plutus toolset you want to use. To get us started here are the imports you need to get a simple smart contract to compile:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Language.PlutusTx
import Language.PlutusTx.Prelude
import qualified Language.PlutusTx.Lift as Lift
import qualified Language.PlutusTx.Builtins as Builtins
import Playground.Contract
These go in at the top. Then to create a Plutus Tx script we will use a Template Haskell Quote. This is code surrounded by [|| and ||] which we can compile using the compile function. Template Haskell Quotes have a type of Q (TExp a) which has an abbreviation of TExpQ a. The compile function has a type of TExpQ a -> TExpQ (CompiledCode a). So we can include scripts to be turned into Plutus Core functions when our main program is compiled. This happens at compile time, not run time, but we will see that there are still some runtime values we can use in the scripts.

For your first program, lets say hello:

hello :: CompiledCode String
hello = $$(compile
    [||
        ("Hello World" :: String)
    ||])
$(mkFunctions ['hello])
Note the use of $$ (which is called a splice) to define your function as a compiled script. Also the call to $(mkFunctions which makes our function available to the outside world. If you are struggling with Haskell syntax, my Dev page offers several good resources for learning it.

In fact you would usually define your functions in the body of the program, then use them in the script:

{-# INLINABLE itsFour #-}
itsFour :: Integer
itsFour =
    let
        itfDouble :: Integer -> Integer
        itfDouble x = x `Builtins.addInteger` x
    in itfDouble 2

getFour :: CompiledCode Integer
getFour = $$(compile [|| itsFour ||])
$(mkFunctions ['getFour])
The INLINABLE marker specifies that the function needs to be kept available for re-use. It is not always needed, but it's good practice. The addInteger function comes from the import Language.PlutusTx.Builtins which I have imported as Builtins.

You can refer to functions in the main program from your script as well:

{-# INLINABLE double #-}
double :: Integer -> Integer
double x = x `Builtins.addInteger` x

{-# INLINABLE itsFour #-}
itsFour :: Integer
itsFour = double 2

getFour :: CompiledCode Integer
getFour = $$(compile [|| itsFour ||])
$(mkFunctions ['getFour])

Lifting values

The next thing to learn is how to get values into the contract so that they can be referred to in the code. For example, here we create a function called double which adds a number to itself, then a lambda function which calls it to double a number, then a function which can lift a parameter from the contract and apply the function to it:

{-# INLINABLE double #-}
double :: Integer -> Integer
double x = x `Builtins.addInteger` x

{-# INLINABLE doubleIt #-}
doubleIt :: CompiledCode (Integer -> Integer)
doubleIt = $$(compile [|| \(x:: Integer) -> double x ||])

{-# INLINABLE doubleLift #-}
doubleLift :: Integer -> CompiledCode Integer
doubleLift x = doubleIt `applyCode` Lift.liftCode x
$(mkFunctions ['doubleLift])
applyCode applies one CompiledCode to another. liftCode gets parameter x and "lifts" it into a CompiledCode Integer. A point to note is that the parameter type has to be an instance in the Lift class, which we can generate using the makeLift function. Here is an example for our end point function which defines a type which is either a Fixed Integer or Never:
data EndPoint = Fixed Integer | Never
Lift.makeLift ''EndPoint

pastEnd :: CompiledCode (EndPoint -> Integer -> Bool)
pastEnd = $$(compile [||
    \(end::EndPoint) (aTime::Integer) -> case end of
        Fixed x -> x `Builtins.lessThanEqInteger` aTime
        Never -> False
    ||])

pastEndAt :: EndPoint -> Integer -> CompiledCode Bool
pastEndAt end aTime =
    pastEnd `applyCode` Lift.liftCode end aTime

Handling money in scripts

Scripts have type Script and each has an address with type Addr_script. The address is the hash of the script, and this hash is the thing that gets stored on the blockchain in a UTXO. The program for the script is stored in a wallet, and the wallet witnesses the script by signing the transaction in the same way any other UTXO gets spent. The job of the script is not to create outputs, it is to validate that spends on UTXOs are valid. To enable this a list of UTXOs is associated with the script when it is created, giving it the ability to approve spends on them.


This website was created by Kevmate. Its all my own work. Contact me by emailing me at kev@kevmate.com