Skip to content

Commit

Permalink
Add a page comparing Plutus V1, V2 and V3 (#6400)
Browse files Browse the repository at this point in the history
  • Loading branch information
zliu41 authored Aug 12, 2024
1 parent 8366fcb commit dba8ca8
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@ sidebar_position: 15

# Exporting scripts, datums and redeemers

> :pushpin: **NOTE**
>
> This guide uses the scripts from the topic [Writing basic validator scripts](writing-basic-validator-scripts.md).
Since scripts must match their on-chain hashes exactly, it is important that the scripts which an application uses do not accidentally change.
For example, changing the source code or updating dependencies or tooling may lead to small changes in the script.
As a result, the hash will change.
For example, changing the source code or updating dependencies or tooling may lead to small changes in the script.
As a result, the hash will change.
In cases where the hashes must match exactly, even changes which do not alter the functionality of the script can be problematic.

For this reason, once you expect that you will not modify the on-chain part of your application more, it is sensible to *freeze* it by saving the final Plutus Core to a file.

Additionally, while most Plutus Applications use scripts by directly submitting them as part of transactions from the application itself, it can be useful to be able to export a serialized script.
Additionally, while most Plutus Applications use scripts by directly submitting them as part of transactions from the application itself, it can be useful to be able to export a serialized script.
For example, you might want to submit it as part of a manually created transaction with the Cardano node CLI, or send it to another party for them to use.

Fortunately, it is quite simple to do this.
Most of the types have typeclass instances for `Serialise` which allows translating directly into CBOR.
Fortunately, it is quite simple to do this.
Most of the types have typeclass instances for `Serialise` which allows translating directly into CBOR.
This applies to `Validator`, `Redeemer`, and `Datum` types.
If you want to create values that you can pass to the Cardano CLI, you will need to convert them to the appropriate types from `cardano-api` and use `serialiseToTextEnvelope`.

Expand All @@ -33,11 +29,10 @@ The serialized form of `CompiledCode` can also be dumped using a plugin option:
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:dump-uplc #-}
```

This will dump the output to a temporary file with a name based on the module name.
The filename will be printed to the console when compiling the source file.
This will dump the output to a temporary file with a name based on the module name.
The filename will be printed to the console when compiling the source file.
You can then move it to a more permanent location.

It can be read in conveniently with `loadFromFile` as an alternative to `compile`.

<LiteralInclude file="BasicValidators.hs" language="haskell" title="Block Title" start="-- BLOCK6" end="-- BLOCK7" />

132 changes: 132 additions & 0 deletions doc/docusaurus/docs/working-with-scripts/ledger-language-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
sidebar_position: 1
---

# Plutus Ledger Language Version (Plutus V1/V2/V3)

As explained in [Different Notions of Version](../essential-concepts/versions.md), Plutus V1, V2 and V3 are not distinct programming languages; the primary difference lies in the arguments the script receives from the ledger, and the value it returns.
Therefore, Plutus V1, V2 and V3 can be understood as type signatures, in the sense that they each represent a subset of Untyped Plutus Core (UPLC) programs with specific types.
Any UPLC program that matches the expected argument and return types can be considered and used as a Plutus V1, V2 or V3 script.

Next we'll start with a brief overview of the script context, followed by an in-depth explanation of these type signatures.

## ScriptContext

Every Plutus script receives an argument called script context.
It contains information about the transaction the script is validating, such as inputs, outputs, transaction fee, signatures and so on.
Additionally, since a transaction may have multiple things to validate (e.g., it may be spending multiple script UTXOs, or performing both spending and minting), each of which is validated by a separate script, the script context also has a script purpose field, telling the script what exactly it is supposed to validate.

Plutus V1, V2 and V3 scripts receive different script contexts even when all else is equal.
This is because different ledger language versions are introduced in different ledger eras; transactions in different ledger eras have different fields - a new era usually adds new fields and may also change existing fields.
As a result, The script contexts for Plutus V1, V2 and V3 also have different fields, leading to different Haskell types ([V1](https://plutus.cardano.intersectmbo.org/haddock/master/plutus-ledger-api/PlutusLedgerApi-V1-Contexts.html#t:ScriptContext), [V2](https://plutus.cardano.intersectmbo.org/haddock/master/plutus-ledger-api/PlutusLedgerApi-V2-Contexts.html#t:ScriptContext), [V3](https://plutus.cardano.intersectmbo.org/haddock/master/plutus-ledger-api/PlutusLedgerApi-V3-Contexts.html#t:ScriptContext)).
We cannot modify the script context fields of an existing ledger language version once it is published, since it would break existing scripts.

In general, a ledger language version cannot be used in a transaction, if the ledger language version was introduced in ledger era A, the transaction uses features in ledger era B, and A is earlier than B.
For instance, Plutus V1 (introduced in the Alonzo era) scripts cannot be used in a transaction which utilizes inline datum (a Babbage era feature); Plutus V2 (introduced in the Babbage era) scripts cannot be used in a transaction that registers a DRep (introduced in the Conway era)[^1].


## Plutus V1 and Plutus V2

Plutus V1 and Plutus V2 scripts have four [script purposes](https://plutus.cardano.intersectmbo.org/haddock/master/plutus-ledger-api/PlutusLedgerApi-V1-Contexts.html#t:ScriptPurpose): spending, minting, certifying, and rewarding[^2].
The arguments a Plutus V1 or V2 script receives depend on the script purpose.
There is no requirement on the return value of a Plutus V1 and V2 script: script evaluation succeeds as long as the evaluation terminates without error, and the execution budget is not exceeded.

### Spending Scripts

A Plutus V1/V2 spending script receives three arguments corresponding to datum, redeemer and script context.
All arguments are encoded as `BuiltinData`.
Thus in Plutus Tx, a spending script has the following type:

```haskell
BuiltinData -> BuiltinData -> BuiltinData -> any
```

To create a Plutus script using Plutus Tx, it is common to first write a function that takes the corresonding Haskell domain types and returns `Bool`.
For example, the following function can be used to implement the main business logic of a Plutus V1 spending script:

```haskell
myV1SpendingScriptTyped :: MyDatum -> MyRedeemer -> PlutusLedgerApi.V1.ScriptContext -> Bool
```

where `MyDatum` and `MyRedeemer` are your user-defined Haskell types specific to your contract.
If you are writing a Plutus V2 script, use `PlutusLedgerApi.V2.ScriptContext`.

From `myV1SpendingScriptTyped`, you can obtain `BuiltinData -> BuiltinData -> BuiltinData -> any`, and subsequently compile it to UPLC, via

```haskell
myV1SpendingScriptUntyped :: BuiltinData -> BuiltinData -> BuiltinData -> BuiltinUnit
myV1SpendingScriptUntyped myDatum myRedeemer scriptContext =
PlutusTx.Prelude.check
( myV1SpendingScriptTyped
(PlutusTx.unsafeFromBuiltinData myDatum)
(PlutusTx.unsafeFromBuiltinData myRedeemer)
(PlutusTx.unsafeFromBuiltinData scriptContext)
)

myV1SpendingScriptCompiled :: CompiledCode (BuiltinData -> BuiltinData -> BuiltinData -> BuiltinUnit)
myV1SpendingScriptCompiled = $$(PlutusTx.compile [||myV1SpendingScriptUntyped||])
```

`unsafeFromBuiltinData` is a method from the [`UnsafeFromData`](http://localhost:3000/docs/working-with-scripts/ledger-language-version) class.
Each call to `unsafeFromBuiltinData` decodes a `BuiltinData` into a value of a Haskell domain type, failing if the conversion fails.
The `check` function takes a `Bool` and returns a `BuiltinUnit`, throwing an error if the input is `False`.
It is needed because returning `False` does not cause the validation to fail; to fail the validation, an error needs to be thrown.

In this example the script happens to return `BuiltinUnit`, but this is not a requirement for Plutus V1 or V2.

### Minting, Certifying and Rewarding Scripts

Unlike spending scripts, Plutus V1 and V2 scripts for minting, certifying and rewarding purposes take one fewer argument: there is no datum argument.
Thus in Plutus Tx, a minting, certifying or rewarding script should have the following type:

```haskell
BuiltinData -> BuiltinData -> any
```

Since this type signature is shared by minting, certifying and rewarding scripts, the same script can be used for multiple of these three purposes, for example

```haskell
myV1MintingAndRewardingScriptTyped :: MyRedeemer -> PlutusLedgerApi.V1.ScriptContext -> Bool
myV1MintingAndRewardingScriptTyped myRedeemer scriptContext =
case scriptContextPurpose scriptContext of
Minting cs -> -- Perform minting validation
Rewarding cred -> -- Perform rewarding validation
```

Because spending scripts take one more argument, the same script cannot be used both for spending validation and for a different purpose (minting, certifying or rewarding).

### Script Evaluation and Unsaturated Scripts

As said before, evaluating a Plutus V1 and V2 script succeeds as long as the evaluation terminates without error, and the budget is not exceeded.

This means, crucially, that an unsaturated script (a script expecting more arguments than it receives) succeeds trivially, since the evaluation terminates almost immediately and returns a lambda.
Thus be careful: if, for example, you accidentally use a spending script (which expects three arguments) as a minting script (which will receive two arguments), it will always succeed, which is obviously not what you want.

## Plutus V3

Plutus V3 has two additional [script purposes](https://plutus.cardano.intersectmbo.org/haddock/master/plutus-ledger-api/PlutusLedgerApi-V3-Contexts.html#t:ScriptPurpose) for validating governance actions: voting and proposing.

Besides the usual differences between different Plutus ledger language versions, there are three additional key differences between Plutus V3 and V1/V2:

1. All Plutus V3 scripts, regardless of script purpose, take a single argument: the script context.
The datum (for spending scripts) and the redeemer are part of the Plutus V3 script context.
This means the same script can be used for spending validation and for different purposes.
2. The datum is now optional for spending scripts.
The script context may or may not contain a datum, depending on whether the UTXO being spent has a datum associated with it.
3. There is an additional condition for the evaluation of a Plutus V3 script to be considered successful: the return value must be a `BuiltinUnit`.

The first two points are attributed to [CIP-69](https://developers.cardano.org/docs/governance/cardano-improvement-proposals/cip-0069/), whereas the third point is attributed to [CIP-117](https://developers.cardano.org/docs/governance/cardano-improvement-proposals/cip-0117/).

In other words, all Plutus V3 scripts should have the following type in Plutus Tx:

```haskell
BuiltinData -> BuiltinUnit
```

Updating a Plutus V1/V2 script to turn it into a Plutus V3 script mostly involves straightforward refactoring, except that for a spending script, the case where the datum is absent will need to be handled.

---

[^1]: There is one exception to this: Plutus V1 can be used in transactions with reference inputs, even though reference inputs were introduced in the Babbage era.

[^2]: For more information on script purposes, refer to [Script Purposes](script-purposes.md).

0 comments on commit dba8ca8

Please sign in to comment.