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.
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:
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.
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 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:
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.
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
For your first program, lets say hello:
hello :: CompiledCode String hello = $$(compile [|| ("Hello World" :: String) ||]) $(mkFunctions ['hello])
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])
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])
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])
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
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