Share
Plutus and Extended UTxO
VIDEO: https://www.youtube.com/watch?v=TFezjgwuw8Y
This presentation was part of PlutusFest 2018 and introduces the design and architecture of the Plutus Platform, a Haskell-based environment and toolchain for developing contracts for the Cardano blockchain. The key features are its use of a standard purely functional programming language (instead of inventing a new language, which appears to be the popular approach), being based on a ledger-architecture that resembles a functional data flow graph (UTxO ledger, rather than an account-based ledger), and its tight integration and co-development of on-chain code (executed by block producing nodes) and off-chain code (executed on client machines).
See https://testnet.iohkdev.io/plutus/ for an online environment to write and test Plutus code and https://github.com/input-output-hk/plutus/ for the code.
Manuel Chakravarty PRO
Transcript
109 slides
-
PLUTUS Plutus and Extended UTxO Manuel M T Chakravarty, Language
Architect @ IOHK -
PLUTUS What is Plutus? Innovative development and execution platform for
distributed contract applications on Cardano SL & CL. -
functional programming modern type systems PLUTUS input output typed function
safe usage :: ⍺ :: β -
PLUTUS input output typed function safe usage :: ⍺ ::
β local reasoning -
PLUTUS input output typed function safe usage :: ⍺ ::
β local reasoning type-directed design -
PLUTUS input output typed function safe usage :: ⍺ ::
β local reasoning type-directed design advanced testing & verification -
PLUTUS local reasoning type-directed design advanced testing & verification }
understand code behaviour -
PLUTUS local reasoning type-directed design advanced testing & verification }
understand code behaviour find exploits -
PLUTUS core Plutus Core not a bytecode not a classic
virtual machine typed FP calculus abstract machine interpreter -
PLUTUS core Plutus Core small, powerful & based on peer-reviewed
science not a bytecode not a classic virtual machine typed FP calculus abstract machine interpreter -
PLUTUS Account-based Ledger UTxO-based Ledger functional imperative mutable shared state
dataflow compositional entangled -
PLUTUS What about the expressiveness of smart contracts? Bitcoin Ethereum
minimal enough rope -
PLUTUS Input Output ν: Validator x: Value ρ: Redeemer ν(ρ)
≟ True core core -
PLUTUS Input Output ν: Validator x: Value ρ: Redeemer ν(ρ)
≟ True ν: Validator x: Value ρ: Redeemer core core core core -
PLUTUS Input Output ν: Validator x: Value ρ: Redeemer ν(ρ)
≟ True ν: Validator x: Value ρ: Redeemer δ: Data core core core core core -
PLUTUS Input Output ν: Validator x: Value ρ: Redeemer ν(ρ)
≟ True ν: Validator x: Value ρ: Redeemer δ: Data σ: State core core core core core -
PLUTUS Input Output ν: Validator x: Value ρ: Redeemer ν(ρ)
≟ True ν: Validator x: Value ρ: Redeemer ν(ρ, δ, σ, x) ≟ True δ: Data σ: State core core core core core -
PLUTUS ν: Validator x: Value ρ: Redeemer ν(ρ, δ, σ,
x) ≟ True δ: Data σ: State Extended UTxO core core core -
PLUTUS ν: Validator x: Value ρ: Redeemer ν(ρ, δ, σ,
x) ≟ True δ: Data σ: State Extended UTxO Preserves the superior structure of UTxO ledgers core core core -
PLUTUS ν: Validator x: Value ρ: Redeemer ν(ρ, δ, σ,
x) ≟ True δ: Data σ: State Extended UTxO Preserves the superior structure of UTxO ledgers Greatly increases the expressive power of scripts core core core -
PLUTUS Extended UTxO Preserves the superior structure of UTxO ledgers
Greatly increases the expressive power of scripts -
PLUTUS Extended UTxO Preserves the superior structure of UTxO ledgers
Greatly increases the expressive power of scripts data flows with value -
PLUTUS Extended UTxO Preserves the superior structure of UTxO ledgers
Greatly increases the expressive power of scripts data flows with value scripts have an identity -
PLUTUS Extended UTxO Preserves the superior structure of UTxO ledgers
Greatly increases the expressive power of scripts data flows with value scripts have an identity invariants across transaction chains -
PLUTUS What does it take to write a smart contract
application? -
PLUTUS Simple crowdfunding 1. Payment into campaign until a payment
deadline 2. If funding goal reached, campaign owner can collect funds -
PLUTUS Simple crowdfunding 1. Payment into campaign until a payment
deadline 2. If funding goal reached, campaign owner can collect funds 3. If funding goal not reached, refunds can be obtained by contributors -
PLUTUS Simple crowdfunding 1. Payment into campaign until a payment
deadline 2. If funding goal reached, campaign owner can collect funds 3. If funding goal not reached, refunds can be obtained by contributors 4. If campaign owner does not collect by collection deadline, contributors can also obtain refunds -
PLUTUS 1. Payment into campaign until a payment deadline 2.
If funding goal reached, campaign owner can collect funds 3. If funding goal not reached, refunds can be obtained by contributors 4. If campaign owner does not collect by collection deadline, contributors can also obtain refunds -
PLUTUS 1. Payment into campaign until a payment deadline 2.
If funding goal reached, campaign owner can collect funds 3. If funding goal not reached, refunds can be obtained by contributors 4. If campaign owner does not collect by collection deadline, contributors can also obtain refunds pragma solidity ^0.4.6; contract WinnerTakesAll { uint minimumEntryFee; uint public deadlineProjects; uint public deadlineCampaign; uint public winningFunds; address public winningAddress; struct Project { address addr; string name; string url; uint funds; bool initialized; } … Solidity (on-chain) -
PLUTUS pragma solidity ^0.4.6; contract WinnerTakesAll { uint minimumEntryFee; uint
public deadlineProjects; uint public deadlineCampaign; uint public winningFunds; address public winningAddress; struct Project { address addr; string name; string url; uint funds; bool initialized; } … Solidity (on-chain) var Web3 = require('web3'); var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider(”http:… var accounts = web3.eth.accounts; accounts.forEach(function(v) { $(”#supportFrom”).append(”<opt… $(”#projectAddr”).append(”<opt… }); var compiled = web3.eth.compile.so… var code = compiled.code; var abi = compiled.info.abiDefinit… var contract = web3.eth.contract(a… … JavaScript (off-chain) + -
PLUTUS pragma solidity ^0.4.6; contract WinnerTakesAll { uint minimumEntryFee; uint
public deadlineProjects; uint public deadlineCampaign; uint public winningFunds; address public winningAddress; struct Project { address addr; string name; string url; uint funds; bool initialized; } … Solidity var Web3 = require('web3'); var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider(”http:… var accounts = web3.eth.accounts; accounts.forEach(function(v) { $(”#supportFrom”).append(”<opt… $(”#projectAddr”).append(”<opt… }); var compiled = web3.eth.compile.so… var code = compiled.code; var abi = compiled.info.abiDefinit… var contract = web3.eth.contract(a… … JavaScript -
PLUTUS pragma solidity ^0.4.6; contract WinnerTakesAll { uint minimumEntryFee; uint
public deadlineProjects; uint public deadlineCampaign; uint public winningFunds; address public winningAddress; struct Project { address addr; string name; string url; uint funds; bool initialized; } … Solidity var Web3 = require('web3'); var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider(”http:… var accounts = web3.eth.accounts; accounts.forEach(function(v) { $(”#supportFrom”).append(”<opt… $(”#projectAddr”).append(”<opt… }); var compiled = web3.eth.compile.so… var code = compiled.code; var abi = compiled.info.abiDefinit… var contract = web3.eth.contract(a… … JavaScript off-chain (wallet) on-chain (transaction) -
PLUTUS pragma solidity ^0.4.6; contract WinnerTakesAll { uint minimumEntryFee; uint
public deadlineProjects; uint public deadlineCampaign; uint public winningFunds; address public winningAddress; struct Project { address addr; string name; string url; uint funds; bool initialized; } … Solidity var Web3 = require('web3'); var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider(”http:… var accounts = web3.eth.accounts; accounts.forEach(function(v) { $(”#supportFrom”).append(”<opt… $(”#projectAddr”).append(”<opt… }); var compiled = web3.eth.compile.so… var code = compiled.code; var abi = compiled.info.abiDefinit… var contract = web3.eth.contract(a… … JavaScript off-chain (wallet) on-chain (transaction) two-level (staged) programming model JavaScript Solidity -
PLUTUS Solidity JavaScript off-chain (wallet) on-chain (transaction) two-level (staged) programming
model ad hoc inconvenient -
PLUTUS Solidity JavaScript off-chain (wallet) on-chain (transaction) two-level (staged) programming
model ad hoc inconvenient complex -
PLUTUS Solidity JavaScript off-chain (wallet) on-chain (transaction) two-level (staged) programming
model ad hoc inconvenient complex fragile -
PLUTUS Haskell (Plutus Tx) Haskell off-chain (wallet) on-chain (transaction) two-level
(staged) programming model -
PLUTUS Haskell (Plutus Tx) Haskell off-chain (wallet) on-chain (transaction) two-level
(staged) programming model meta programming -
PLUTUS Haskell (Plutus Tx) Haskell off-chain (wallet) on-chain (transaction) two-level
(staged) programming model meta programming integrated -
PLUTUS Haskell (Plutus Tx) Haskell off-chain (wallet) on-chain (transaction) two-level
(staged) programming model meta programming integrated compact -
PLUTUS Haskell (Plutus Tx) Haskell off-chain (wallet) on-chain (transaction) two-level
(staged) programming model meta programming integrated compact robust -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) register (refundTrigger campaign) (refundHandler (Ledger.hashTx tx) campaign) contributionScript :: Campaign -> ValidatorScript Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner Plutus Tx Haskell -
PLUTUS contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript
(validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner in $$(P.errorIfNot) isValid ||]) Plutus Tx Haskell -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) register (refundTrigger campaign) (refundHandler (Ledger.hashTx tx) campaign) contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript (validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner in $$(P.errorIfNot) isValid ||]) off-chain on-chain -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) register (refundTrigger campaign) (refundHandler (Ledger.hashTx tx) campaign) contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript (validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner in $$(P.errorIfNot) isValid ||]) core off-chain on-chain -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) register (refundTrigger campaign) (refundHandler (Ledger.hashTx tx) campaign) contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript (validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner in $$(P.errorIfNot) isValid ||]) core core off-chain on-chain -
PLUTUS contribute :: Campaign -> Value -> MockWallet () contribute
campaign value = do when (value <= 0) $ throwOtherError "Must contribute a positive value" ownPK <- ownPubKey tx <- payToScript (Ledger.scriptAddress (contributionScript campaign)) value DataScript (Ledger.lifted ownPK) register (refundTrigger campaign) (refundHandler (Ledger.hashTx tx) campaign) contributionScript :: Campaign -> ValidatorScript contributionScript campaign = ValidatorScript (validator `apply` campaign) where validator = Ledger.fromCompiledCode $$(PlutusTx.compile [|| (\Campaign{..} action contrib tx -> let PendingTx ps outs _ _ (Height h) _ _ = tx isValid = case action of Refund -> h > collectionDeadline && contributorOnly outs && $$(txSignedBy) tx contrib Collect -> h > deadline && h <= collectionDeadline && totalInputs >= target && $$(txSignedBy) p campaignOwner in $$(P.errorIfNot) isValid ||]) core core off-chain on-chain