A software engineer website

Plutus: Pioneers Program 4th cohort kick start

Gautier DI FOLCO March 05, 2023 [HaskellBlockchain] #haskell #cardano #smart contracts

For a long time I have been interested in blockchain ecosystem for multiple reasons:

Which leds me to take Plutus Pioneers Program: 1st Cohort two years ago.

My experience with the course was deceiving because:

Since I plan to be involved in blockchains in a near future, it was a good opportunity to register for the 4th Cohort just starting.

Note that it will be a new logs series in which I'll try to document the evolutions of my understanding, I don't intend to provide an uptodate reference of Cardano or Plutus.

Addionally to my regular look for contributions, I'm willing to accept Plutus smart contracts suggestions.

Importants links:

Main differences compare to the 1st cohort:

Let's start with some definitions:

A transaction is composed of:

A Datum looks like aeson's Value:

data Data
  = Constr Integer [Data]
  | Map [(Data, Data)]
  | List [Data]
  | I Integer
  | B ByteString

But, if we have a look at a simple validator script:

-- This validator succeeds only if the redeemer is 42
--                  Datum         Redeemer     ScriptContext
mk42Validator :: BuiltinData -> BuiltinData -> BuiltinData -> ()
mk42Validator _ r _
    | r == Builtins.mkI 42 = ()
    | otherwise            = traceError "expected 42"
{-# INLINABLE mk42Validator #-}

validator :: PlutusV2.Validator
validator = PlutusV2.mkValidatorScript $$(PlutusTx.compile [|| mk42Validator ||])

We can notice that:

Actually BuiltinData is the Plutus Core equivalent of Data, going back and forth via:

class ToData (a :: Type) where
    toBuiltinData :: a -> BuiltinData

class FromData (a :: Type) where
    fromBuiltinData :: BuiltinData -> Maybe a

class UnsafeFromData (a :: Type) where
    unsafeFromBuiltinData :: BuiltinData -> a

We can strengthen the types using wrap (which relies on UnsafeFromData):

mk42Validator :: () -> Integer -> PlutusV2.ScriptContext -> Bool
mk42Validator _ r _ = traceIfFalse "expected 42" $ r == 42
{-# INLINABLE mk42Validator #-}

validator :: PlutusV2.Validator
validator = PlutusV2.mkValidatorScript $$(PlutusTx.compile [|| wrap mk42Validator ||])

You can also create specific types thanks to unstableMakeIsData:

data MyRedeemer = MyRedeemer
    { flag1 :: Bool
    , flag2 :: Bool
    }

PlutusTx.unstableMakeIsData ''MyRedeemer

Let's try a simple validator checking the factorization of a number:

--              Datum  Redeemer        ScriptContext
mkValidator :: Integer -> (Integer, Integer) -> PlutusV2.ScriptContext -> Bool
mkValidator t (x, y) _ = shouldNotBeOne x && shouldNotBeOne y && shouldBeFactors
  where shouldNotBeOne :: Integer -> Bool
        shouldNotBeOne n = traceIfFalse "No factor should be 1" $ n /= 1
        shouldBeFactors  = traceIfFalse "Not factors" $ x * y == t
{-# INLINABLE mkValidator #-}

validator :: PlutusV2.Validator
validator = PlutusV2.mkValidatorScript $$(PlutusTx.compile [|| wrap mkValidator ||])

Compiling it to Plutus Core gives something like:

{
  "type": "PlutusScriptV2",
  "description": "",
  "cborHex": "5908..."
}

CBOR standing for Concise Binary Object Representation and is specified by RFC 7049.

Let's submit our contract with a random Datum:

{"int":42}

We can check it works via:

$ scripts/query-address.sh $(cat code/Week02/experiments/factoring.addr)
                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
2d1604d8ba3128cd1526b6c68f94f8eddcd83b530dda4560dd327f25ad39164d     0        3000000 lovelace + TxOutDatumInline ReferenceTxInsScriptsInlineDatumsInBabbageEra (ScriptDataNumber 42)

If we then try to collect (using bob's key address first transaction as collateral and above TxHash as txin) with an incorrect Datum:

$ code/Week02/experiments/collect.sh wrong
# Query the protocol parameters
# Build the transaction
Command failed: transaction build  Error: The following scripts have execution failures:
the script for transaction input 0 (in the order of the TxIds) failed with:
transaction input 0 (in the order of the TxIds) points to a script hash that is not known.

# Sign the transaction
# Submit the transaction
Command failed: transaction submit  Error: Error while submitting tx: ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (FromAlonzoUtxowFail (WrappedShelleyEraFailure (ExtraneousScriptWitnessesUTXOW (fromList [ScriptHash "c06d15e27842d0ccd6c825a13b84830a14215e01c98a93567f8cdbf8"])))),UtxowFailure (FromAlonzoUtxowFail (ExtraRedeemers [RdmrPtr Spend 0])),UtxowFailure (FromAlonzoUtxowFail (PPViewHashesDontMatch (SJust (SafeHash "abb568f2abaa14e3f59d0feaceb981033fdb16850af8339863a05343d8ac19f6")) (SJust (SafeHash "29f1fbb67e8a3f06db19ae5a5f8c6f51968e16920f3b486925beafd1c3e27391")))),UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (ValueNotConservedUTxO (Value 0 (fromList [])) (Value 3000000 (fromList []))))),UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (BadInputsUTxO (fromList [TxIn (TxId {_unTxId = SafeHash "2d1604d8ba3128cd1526b6c68f94f8eddcd83b530dda4560dd327f25ad39164d"}) (TxIx 0)]))))])
transaction id: ee269f32f5eb8abaf580f3684221c8c82066136e6e9ea7b8455fba61ee53f590
Cardanoscan: https://preview.cardanoscan.io/transaction/ee269f32f5eb8abaf580f3684221c8c82066136e6e9ea7b8455fba61ee53f590

if fails right away, without going to the blockchain, while a good Datum works:

$ code/Week02/experiments/collect.sh good
Transaction successfully submitted.
transaction id: ee269f32f5eb8abaf580f3684221c8c82066136e6e9ea7b8455fba61ee53f590
Cardanoscan: https://preview.cardanoscan.io/transaction/ee269f32f5eb8abaf580f3684221c8c82066136e6e9ea7b8455fba61ee53f590

Updating transactions:

$ scripts/query-address.sh $(cat keys/bob.addr)
TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
2e1fe0fe79677804a70bff96b249d9912143a3464f4788d7a42b1f772bfb2050     0        10000000000 lovelace + TxOutDatumNone
ee269f32f5eb8abaf580f3684221c8c82066136e6e9ea7b8455fba61ee53f590     0        2701365 lovelace + TxOutDatumNone
$ scripts/query-address.sh $(cat keys/alice.addr)
TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
2d1604d8ba3128cd1526b6c68f94f8eddcd83b530dda4560dd327f25ad39164d     1        9996833927 lovelace + TxOutDatumNone
$ scripts/query-address.sh $(cat code/Week02/experiments/factoring.addr)
TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------

Bonus: At some point I did, mistakenly change the validator, which triggered this error:

Command failed: transaction build  Error: The following scripts have execution failures:
the script for transaction input 0 (in the order of the TxIds) failed with: 
The redeemer pointer: RdmrPtr Spend 0 points to a Plutus script that does not exist.
The pointers that can be resolved are: fromList [(RdmrPtr Spend 0,(Spending (TxIn (TxId {_unTxId = SafeHash "2d1604d8ba3128cd1526b6c68f94f8eddcd83b530dda4560dd327f25ad39164d"}) (TxIx 0)),Nothing,ScriptHash "c06d15e27842d0ccd6c825a13b84830a14215e01c98a93567f8cdbf8"))]

In order to detect it, you can recompute it with:

$ cardano-cli address build-script --testnet-magic 2 --script-file code/Week02/experiments/factoring.plutus
addr_test1wr5wslr3utlm0vccvveg2z4p0p0jp02fcrwmyeaqqwmglsc6urkf0
$ cat code/Week02/experiments/factoring.addr
addr_test1wrqx690z0ppdpnxkeqj6zwuysv9pgg27q8yc4y6k07xdh7qyreaje

Back to top