diff --git a/doc/docs-revamp/010_introduction.md b/doc/docs-revamp/010_introduction.md new file mode 100644 index 00000000000..70e2e5e32cd --- /dev/null +++ b/doc/docs-revamp/010_introduction.md @@ -0,0 +1,46 @@ +--- +title: "Introduction" +description: "A brief introduction to Plutus and its significance in the Cardano ecosystem" +date: 2024-03-12 +--- + +# Section 1. Introduction + +- Welcome to Plutus: A brief introduction to Plutus and its significance in the Cardano ecosystem. +- Target audience: + - Description of assumed knowledge (basic Haskell) and what the documentation aims to cover. + - Identify the variety of primary roles we expect people to have who would be interested in the docs. +- Document overview: Briefly outline the key sections and what developers will learn. + +## Introductory resources +- Reference to the [Plutus Pioneer Program Gitbook](https://iog-academy.gitbook.io/plutus-pioneers-program-fourth-cohort/) (gitbook.io) +- Awareness of the Plutus Pioneer Program github page as a valuable resource: + - [https://github.com/input-output-hk/plutus-pioneer-program](https://github.com/input-output-hk/plutus-pioneer-program) +- Reference to the [IOG Academy Haskell Course](https://www.youtube.com/playlist?list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb) +- IOG's technical community on Discord for PPP + +> **Note** +> +> The draft outline above does not match the content below. At this stage, I've brought over the existing introduction so that everything is in one place. More editing is in the works. + +# Plutus Core + +The Plutus project consists of Plutus Core, the programming language used for scripts on Cardano; tooling and compilers for compiling various intermediate languages into Plutus Core; and Plutus Tx, the compiler that compiles the Haskell source code into Plutus Core to form the on-chain part of a contract application. +All of this is used in combination to write Plutus Core scripts that run on the Cardano blockchain. + +This documentation introduces the Plutus Core programming language and programming with Plutus Tx. +It includes explanations, tutorials, how-to instructions, troubleshooting, and reference information. + +The intended audience of this documentation includes people who want to implement smart contracts on the Cardano blockchain. +This involves using Plutus Tx to write scripts, requiring some knowledge of the Haskell programming language. + +This guide is also meant for certification companies, certification auditors, and people who need an accurate specification. +See, for example: + +- the [Cardano Ledger Specification](https://github.com/IntersectMBO/cardano-ledger#cardano-ledger) and +- the [Plutus Core Specification](https://github.com/IntersectMBO/plutus#specifications-and-design). + +# The Plutus repository + +The [Plutus repository](https://github.com/IntersectMBO/plutus) contains the implementation, specification, and mechanized metatheory of Plutus Core. +It also contains the Plutus Tx compiler and the libraries, such as `PlutusTx.List`, for writing Haskell code that can be compiled to Plutus Core. diff --git a/doc/docs-revamp/020_core-concepts.md b/doc/docs-revamp/020_core-concepts.md new file mode 100644 index 00000000000..0b42bbbd288 --- /dev/null +++ b/doc/docs-revamp/020_core-concepts.md @@ -0,0 +1,289 @@ +--- +title: "Core concepts" +description: "A set of definitions of core concepts" +date: 2024-04-11 +--- + +# Section 2. Core concepts + +## Resources to inform this section: +- Blog: [Plutus Tx: compiling Haskell into Plutus Core](https://iohk.io/en/blog/posts/2021/02/02/plutus-tx-compiling-haskell-into-plutus-core/), by Michael Peyton Jones, Feb. 2021 + - This is several years old, but the underlying essential definitions and concepts are still applicable. +- Draft document: [Smart contracts security and best practices](https://docs.google.com/document/d/1CrWYmG-I-Z2KeB06pPM9TqvjpSgpgt8e4ipLY9vJDKE/edit?usp=sharing) being prepared by Luka Kurnjek, Education team +- [An Introduction to Plutus Core](https://blog.hachi.one/post/an-introduction-to-plutus-core/) + +## Core concepts +- Names, terminology, ecosystem +- Ledger +- Scripts and the EUTXO model +- Different kinds of scripts +- Plutus Core +- Plutus Tx +- Plutus Haskell SDK +- Plutus language versions + +## Observation to consider +- The reference to the Plutus Application Framework is outdated. Many elements of it are no longer functioning or being supported. For example, the PAB is no longer supported. The Plutus Playground is no longer available and is not being supported. Plutus Apps are no longer supported. I suggest that it would be helpful to find a new way to talk about the Plutus development environment and ecosystem without referencing the “Plutus Application Framework.” + +## Understanding the Plutus platform +- What is Plutus? + - The relationship between Plutus, Haskell, and Cardano +- Anatomy of a smart contract +- A thumbnail sketch of Plutus smart contract developer workflow + - Most common tasks +- Plutus Components: + - Plutus Core (The foundational layer, low-level programming language for smart contracts on Cardano.) + - Plutus Tx (The library for writing high-level Haskell code that gets compiled down to Plutus Core) +- The Extended UTXO Model (EUTXO): + - How it differs from the account-based model + - Advantages for smart contracts + +> **Note** +> +> The draft outline above does not directly map to the way that the content below is organized. The content below has been brought over from the existing docs site and will be further edited and possibly reorganized. But it is helpful to get all of this content into one place as a next step. + +# The Plutus Platform + +The Plutus Platform is a platform for writing *applications* that interact with a *distributed ledger* featuring *scripting* capabilities, in particular the `Cardano`{.interpreted-text role="term"} blockchain. + + + +## Applications + +What sort of "applications" are we talking about here? +Let's think about a pair of users, Alice and Bob, who want to engage in an atomic swap of some assets stored on Cardano. + +::: {.uml caption="Alice and Bob doing an atomic swap"} +actor Alice +actor Bob +participant Application +database Cardano + +Alice -> Application: I want to do an escrowed swap with Bob, 50 Ada for my Special Token Application -> Ledger: I want to lock up Alice's Special Token so that it can only be unlocked if Bob completes the swap +Ledger -> Application: Ok, that change has settled +Application -> Bob: Hey, Alice wants to do a swap with you +Bob -> Application: I want to take up Alice's swap +Application -> Cardano: I want to spend that locked output with Alice's Special Token while sending 50 of Bob's Ada to Alice +Ledger -> Ledger: Does this transaction satisfy the conditions that were asked for? Yes it does! Ledger -> Application: Ok, that change has settled +Application -> Alice: The swap is completed! +Application -> Bob: The swap is completed! +::: + +Alice and Bob don't interact directly, nor do they directly interact with the ledger. +Very few "smart" blockchain systems encourage their users to interact directly with the chain themselves, since this is usually complex and error-prone. +Rather, the users interact with some *application* that presents the world in a form that they can understand and interact with. + +Of course, such an application must want to do something with the ledger, otherwise you wouldn't need anything new. +Simple applications might do nothing more than submit basic transactions that transfer assets - imagine a simple "regular payments" application. +However, our main focus is applications that *do* use smart features in order to have a kernel of trusted code that is validated as part of the ledger. + +This enables applications that are not possible otherwise. +Alice and Bob need trusted logic in order to perform their swap: a "dumb" application could submit the transactions transferring the assets, but would have no recourse against Bob defecting. +Using the smart features of the ledger ensures that Bob can't take Alice's token unless he *really does* send her the money, and it does this without involving a trusted third party. + +Creating and using the trusted kernel of code is the most technically difficult and security-sensitive part of the whole operation. +Nonetheless, writing the rest of the application contains plenty of complexity. +Amongst other things, an application needs to deal with the software around the ledger (wallets, nodes, etc.); distributed systems issues such as settlement delays, inconsistent state between parties, and rollbacks; and simple user-experience issues like upgrades, state management and synchronization. +Furthermore, while none of these are quite as security-critical as the trusted kernel, users certainly *can* be attacked through such applications, and even non-malicious bugs are likely to be quite upsetting when a user's money is at stake. + +Even simple applications must deal with this complexity, and for more advanced applications that deal with state across time, the difficulty is magnified. + +## The Plutus Platform + +This is why the Plutus Platform is a *platform*. +Rather than just providing a few tools to make the bare minimum possible, we aim to support application development in its entirety, all the way through from authoring to testing, runtime support, and (eventually) verification. +Ultimately, we wrote it because we needed it ourselves to do anything useful. + +Conceptually, the Platform breaks down based on which part of the system we're interested in: + +- `Plutus Foundation`{.interpreted-text role="ref"}: support for writing the trusted kernel of code, and executing it on the chain +- [The Plutus Application Framework](https://github.com/IntersectMBO/plutus-apps): support for writing applications ("Plutus Applications") in a particular style + +
+ +
A high-level architecture of the Plutus Platform, with an emphasis on applications.
+
+ +## Further reading + +The platform is introduced in :cite[plutus-platform-summit]{.title-ref}. + +The design of the platform is discussed in :cite[plutus-report]{.title-ref}. + +# Ledgers + +The Plutus Platform is designed to work with distributed ledgers (henceforth simply "ledgers"). +Ledgers are typically *implemented* with a blockchain, such as Cardano. +However, much of the time when we are talking about ledgers we don't care about the underlying +implementation, and so we will just talk about the ledger itself. + +> **Note** +> +> This is not always true: applications do need to care about details of how the underlying blockchain works, because that affects behaviour such as settlement time and rollback policies. As much as possible the Plutus Application Framework tries to shield developers from this complexity, but it is not always possible. + +In its simplest form, a ledger is a system that tracks who owns what. + +For example: + +| Owner | Balance | +|-------|---------| +| Alice | 43 USD | +| Bob | 12 USD | + +Ledgers are typically transformed by performing a *transaction* that transfers some assets from one party to another. +In order to be *valid* a transaction will have to pass some checks, such as demonstrating that the transfer is authorized by the owner of the funds. +After applying a transaction (say, Alice sends Bob 5 USD), we have a new state of the ledger. + +| Owner | Balance | +|-------|---------| +| Alice | 38 USD | +| Bob | 17 USD | + +## Account-based and UTXO-based ledgers + +There are two dominant paradigms for how to *represent* such a system. +The first, account-based ledgers, model the system exactly as in our example above. +They keep a list of accounts, and for each account, a balance. +A transaction simply decreases the balance of the sender, and increases the balance of the recipient. + +Account-based ledgers (such as Ethereum) are very simple to implement, but they have difficulties due to the fact that the state of an account is *global*: all transactions that do anything with an account must touch this one number. +This can lead to issues with throughput, as well as ordering issues (if Alice sends 5 USD to Bob, and Bob sends 5 USD to Carol, this may succeed or fail depending on the order in which the transactions are processed). + +The other paradigm is UTXO-based ledgers. +UTXO-based ledgers (such as Bitcoin) represent the state of the ledger as a set of "unspent +transaction outputs" (UTXOs). +A UTXO is like an envelope with some money in it: it is "addressed" to a particular party, and it contains some funds. +A transaction *spends* some number of UTXOs, and creates some more. + +So a transaction that sends 5 USD from Alice to Bob would do so by spending some number of already-existing UTXOs belonging to Alice, and creating a new UTXO with 5 USD belonging to Bob. + +UTXO-based ledgers are more complicated, but avoid some of the issues of account-based ledgers, since any transaction deals only with the inputs that it spends. +Cardano is a UTXO-based ledger, and we heavily rely on this. +For example, Hydra, Cardano's scalability solution, uses the fact that independent parts of the transaction graph can be processed in parallel to improve throughput. + +## Scripts and the Extended UTXO Model + +UTXO-based ledgers typically start out with a very simple model of "ownership" of UTXOs. +An output will have a public key (strictly, the hash of a public key) attached to it, and in order to spend this output the spending transaction must be signed by the corresponding private key. +We call this a "pay-to-pubkey" output. + +Cardano uses an extended model called the `Extended UTXO Model` (EUTXO). +In the EUTXO model, an output can be locked by (the hash of) a *script*. +We call this a "pay-to-script" output. +A script is a *program* that decides whether or not the transaction which spends the output is +authorized to do so. +Such a script is called a validator script, because it validates whether the spending is allowed. + +A simple validator script would be one that checked whether the spending transaction was signed by a particular key---this would exactly replicate the behaviour of simple pay-to-pubkey outputs. +However, with a bit of careful extension, we can use scripts to let us express a large amount of useful logic on the chain. + +With the EUTXO model, validator scripts are passed three arguments: + +- The *datum*: this is a piece of data attached to the *output* that the script is locking (strictly, again, just the hash is present). This is typically used to carry state. +- The *redeemer*: this is a piece of data attached to the *input* that is doing the spending. This is typically used to provide an input to the script from the spender. +- The *context*: this is a piece of data which represents information about the transaction doing the spending. This is used to make assertions about the way the output is being sent (such as "Bob signed it"). + +As an example, let's see how we could implement an atomic swap. + +- The datum contains the keys of the two parties in the swap, and a description of what they are swapping. +- The redeemer is unused. +- The context contains a representation of the transaction. + +The logic of the validator script is then: does the transaction make a payment from the second party to the first party, containing the value that they are supposed to send? +If so, then they may spend this output and send it where they want (or we could insist that they send it to their key, but we might as well let them do what they like with it). + +## Different kinds of scripts + +The Cardano ledger currently has a few different kinds of validator scripts: + +- The "simple" script language (introduced in the Allegra hard fork), which allows basic checks such as time locks +- Various Plutus language versions (see `What are Plutus language versions?`) + +## Further reading + +See [The EUTXO Handbook, A deep dive into Cardano's accounting model](https://www.essentialcardano.io/article/the-eutxo-handbook). + +The Extended UTXO Model is described in :cite[functional-smart-contracts-summit]{.title-ref}. +More formal detail can be found in in :cite[eutxo,utxoma,eutxoma]{.title-ref}. + +For more help on how to actually implement interesting logic using the EUTXO model and scripts, read some of our `tutorials`{.interpreted-text role="ref"}. + +# Plutus Foundation + +In order for an application to run its `trusted kernel` of logic as a script on a `ledger`, the ledger needs a way of specifying and executing scripts. +Scripts are simply programs, so this means we need a *programming language*. + +## Plutus Core + +In the Plutus Platform, this language is *Plutus Core*. +Plutus Core is a variant of the lambda calculus, a well-studied formalism for computing. + +> **Note** +> +> Plutus Core is our "assembly language". Trust me, you don't want to see any! Dealing with that is the compiler's job. + +Plutus Core is designed for simplicity, determinism, and to allow careful cost control of program execution. +Using the lambda calculus makes it an easy compilation target for functional programming languages, and allows us to have a simple, formally verified evaluator. + +## Plutus Tx + +Writing Plutus Core by hand is not a job for a human! +It is designed to be written by a compiler, and the Platform provides a compiler from a subset of Haskell to Plutus Core. +This allows you to seamlessly write applications in Haskell, while compiling part of the code to on-chain Plutus Core, and part into an off-chain application. + +Supporting "mixed" code in this way enables libraries written with the Plutus Haskell SDK to share logic and datatypes across both parts of the application, reducing the risk of errors significantly. + +## Further reading + +The formal details of Plutus Core are in its specification :cite`plutus-core-spec`{.interpreted-text role="p"}. +The design is discussed in :cite[plutus-report]{.title-ref}. + +For more about Plutus Tx, see the `tutorial`{.interpreted-text role="ref"}. + +# Plutus language versions + +The Cardano ledger tags scripts with a *language*. +This determines what the ledger will do with the script. + +For example, the "simple" script language introduced in the Allegra era allows for a few basic kinds of checks to be made, such as time locks. +In order to interpret simple scripts, the ledger must (among other things) extract the validation interval information from the transaction in order to check the conditions imposed by the script. + +Plutus scripts, introduced in the Alonzo era, have a more complex interface than simple scripts. +Plutus scripts are programs written in the Plutus Core programming language that receive three arguments: + +> 1. the datum, +> 2. the redeemer, and +> 3. the context. + +The *context* contains all the information about the transaction which is currently being validated. (See `Scripts and the Extended UTXO model` for more details). + +Languages must continue to behave the same forever; otherwise, we could change the behaviour of existing scripts, potentially making outputs un-spendable and breaking users' assumptions. +That means that many kinds of changes to the behaviour of the language instead require a "new" language. +This includes changes to the interface of the language. + +For example, if we want to put more information in the *context* (e.g., in order to convey information about new fields that have been added to transactions), then we need a new language, because old scripts would not be able to understand the new information. + +> **Note** +> +> For more details about what kinds of changes require a new language, see the Cardano Improvement Proposal, [CIP 35\--Plutus Core Evolution](https://cips.cardano.org/cips/cip35/). + +Hence, in order to change Plutus, we need to create a new language in the ledger. +Since in most cases this language will be very similar to the ones that came before, we refer to these as "Plutus language versions." +However, from the ledger's perspective, they are entirely unrelated and there is generally no requirement that they be similar or compatible in any way. + +There are two different uses of "language" here that are important to keep distinct: + +> - Plutus Core is a *programming* language in which Plutus scripts are written; +> - Plutus (the Plutus Core programming language and a particular interface) is a "language" in the terminology of the ledger. + +In particular, a specific version of the Plutus Core programming language may be used in multiple versions of the Plutus ledger language, if, for example, the only difference is to the interface. +To date, all versions of Plutus use the same version of the Plutus Core. +That means that, in practice, the process for creating scripts of different Plutus language versions tends to be similar. +The main difference is that you will likely need a different `ScriptContext` type, and different +built-in functions may be available. + +*See also:* + +- `Plutus language changes` for a description of what has changed between versions. +- `Upgrading to Vasil and Plutus script addresses`. diff --git a/doc/docs-revamp/030_dev-onboarding-quick-setup.md b/doc/docs-revamp/030_dev-onboarding-quick-setup.md new file mode 100644 index 00000000000..1c0c7f9974d --- /dev/null +++ b/doc/docs-revamp/030_dev-onboarding-quick-setup.md @@ -0,0 +1,375 @@ +--- +title: "Developer onboarding and quick setup guide" +description: "setting up and testing developer environment, writing first smart contract" +date: 2024-03-26 +--- + +# Section 3. Developer onboarding and quick setup guide + +This guide's objective is to help you set up your development environment, test it, and write your first smart contract within an hour. This will be a simple vesting contract in which someone sends a gift of ada to a beneficiary. The beneficiary can access their gift only after a specific deadline has passed and the beneficiary has signed the contract. + +## Setting up and testing your development environment + +### Prerequisites + +#### Hardware and OS requirements + +**Processing power** +- Minimum: Intel Core i5 or AMD equivalent +- Recommended: Intel Core i7 or AMD Ryzen 7 + +**RAM** +- Minimum: 8GB RAM +- Recommended: 16GB RAM or more + +**Operating system** +- Linux: A recent distribution of Linux such as Ubuntu 20.04 LTS or later is preferred due to its widespread use in the Haskell community and its compatibility with Plutus tooling. +- macOS: macOS Catalina (10.15) or later +- Windows: While Windows can be used for Haskell development, it is less common and might require additional setup such as using Windows Subsystem for Linux (WSL2) to provide a Linux-compatible environment. + +### Tools + +To start developing with Haskell and Plutus, you'll need to set up your development environment with the essential tools. This section will guide you through installing and configuring the necessary components. + +#### Nix + +Nix is a package manager that provides reproducible and reliable software environments. It is particularly useful in Plutus development for managing dependencies and ensuring consistent development environments across different machines. + +While not strictly required, Nix is highly recommended because of its effective management of complex library dependencies. + +To install Nix, follow the instructions on the [official Nix website](https://nixos.org/download.html). +After installing, configure Nix to use IOHK’s binary caches to speed up the build process. For important details and context, see these two documents: +- [Contributing to Plutus > Installing and setting up Nix](https://github.com/IntersectMBO/plutus/blob/master/CONTRIBUTING.adoc), +- [Nix setup guide](https://github.com/input-output-hk/iogx/blob/main/doc/nix-setup-guide.md): instructions for installing and configuring Nix to work with projects at IOG. + +#### Haskell tool stack ("Stack") + +Stack is a cross-platform program for developing Haskell projects. It handles dependency management, building, and testing your Haskell code. + +To install the Stack: +1. Visit the [official Haskell Stack website](https://docs.haskellstack.org/). +2. Follow the installation instructions for your operating system (Linux, macOS, or Windows). +3. Verify your installation by running `stack --version` in your terminal. + +#### GHC (Glasgow Haskell Compiler) + +- GHC is the main compiler for Haskell with many extensions and optimizations. +- To install GHC, run `stack setup`. Stack will automatically install the appropriate version of GHC for your project. + +#### Cabal + +Cabal is a system for building and packaging Haskell libraries and programs. It works well with Stack and is used to manage project dependencies and build configurations. Cabal is installed automatically when you install Stack. + +To verify your Cabal installation, run `cabal –version`. + +### Git + +Make sure you have Git installed and configured. See [https://git-scm.com/](https://git-scm.com/). + +### IDE + +An IDE provides a comprehensive environment for writing, testing, and debugging your Haskell code. Visual Studio Code (VS Code) is a widely used code editor that supports the Haskell extension. See [https://code.visualstudio.com/](https://code.visualstudio.com/). + +## Testing your environment setup + +To verify that your environment is set up correctly and functioning, create and run a simple Haskell program. + +The following simple program fetches and displays the current system time and date. This example involves importing and using libraries to handle dates and times. + +### To create your test program + +1. Create a new file named `Main.hs`. +2. Add the following code to the file: + +```haskell +import Data.Time +main :: IO () +main = do + currentTime <- getCurrentTime + putStrLn $ "Current system time is: " ++ show currentTime +``` + +3. Open a terminal in VS Code. +4. Navigate to the directory containing `Main.hs`. +5. Run the command `stack runghc Main.hs`. +6. Verify that the output is correct. + +## Core concepts of Plutus smart contracts +Before we start building our vesting smart contract, let's go over the following core concepts that underpin Plutus smart contracts. + +### Datum +In a vesting contract, the datum represents the state and conditions of the vesting agreement. It includes information such as the beneficiary's public key hash and the deadline for the vesting period. The datum is attached to the UTxO that holds the vested funds and is used by the contract's validator script to enforce the vesting conditions. It ensures that the funds can only be released to the beneficiary after the specified deadline has passed. + +### Redeemer +A redeemer is a piece of data that is provided by the spending transaction. It is used by the validator script to make decisions during the validation process. The redeemer is used to pass dynamic information to the validator script that may not be known at the time the UTXO is created but is only known at the time the UTXO is being spent. In the context of a vesting contract, the redeemer is typically not used, since the contract's logic does not need any additional input from the transaction. Instead, the validation of the transaction is based solely on the datum (vesting conditions) and the script context, such as checking if the deadline has passed and if the transaction is signed by the beneficiary. However, if required, a redeemer could be used to provide additional arguments to the validator script. + +### Script context +The script context is crucial for a vesting contract as it provides information about the current state of the blockchain and the transaction being validated. The contract's validator script uses the script context to check if the current time has passed the vesting deadline and if the transaction is signed by the beneficiary. This information is necessary to determine whether the conditions for releasing the vested funds have been met. + +### Validator scripts +In a vesting contract, the validator script is responsible for enforcing the vesting conditions and ensuring that the funds are only released to the beneficiary after the specified deadline. The validator script takes the datum (vesting conditions) and the script context as inputs and uses this information to validate the transaction. If the current time is beyond the deadline and the transaction is signed by the beneficiary, the validator script allows the transaction to consume the vested funds. Otherwise, the transaction is deemed invalid, and the funds remain locked in the contract's UTxO. + +## Writing your first Plutus smart contract + +Now that you have a basic understanding of the core concepts, let's create a simple vesting contract. + +### Setting up a Plutus project +To set up a Plutus project for our vesting contract, follow these steps: + +1. Create a new directory for your project and navigate to it in your terminal. + +2. If you are using Nix, enter a Nix shell that provides the necessary development environment by running `nix develop` in your project directory. If you are not using Nix, make sure that all required C libraries are installed since PlutusTx depends on `cardano-base`, which in turn depends on cryptographic C libraries like `libblst`, `libsecp256k1`, and `libsodium`. + +3. Create a new directory named `src` and a new file named `VestingContract.hs` inside it. This is where we'll write our smart contract code using Plutus Tx. + +## First smart contract example + +### Writing a simple Vesting contract + +For this example, we will walk through a smart contract that is designed to use only very limited conditions. The contract provides for someone to send a gift of ada to a beneficiary. The beneficiary can access their gift only after a specific deadline has been passed and the beneficiary has signed the contract. + +### Overview + +#### High-level conceptual stages of the vesting contract + +| Stage | Description | +|-------|-------------| +| A. Initialization | Define the data structure and data types for the vesting contract, including the beneficiary's public key hash and the deadline for the vesting period. | +| B. Validation Logic | Create the logic that will validate transactions attempting to withdraw funds from the contract. | +| C. Validation Conditions | Establish the conditions under which the funds can be released, specifically that the current time is beyond the deadline and that the transaction is signed by the beneficiary. | +| D. Compilation | Wrap and compile the validation logic into a Plutus Validator type. | +| E. Helper Functions | Provide additional functions to save the compiled validator to a file and to print the vesting datum in JSON format for easy inspection. | + +#### Summarized list of the vesting contract's functionality section by section + +1. Importing necessary modules and functions +2. On-chain / Validator code + - Define the `VestingDatum` data type that holds the beneficiary's public key hash and the deadline for fund release. + - Implement the core `mkVestingValidator` function that checks if the transaction is signed by the beneficiary and if the deadline has passed. + - Wrap the validator function and compile it to a Validator type. `mkWrappedVestingValidator` is a wrapper for the validator function to conform to the expected type signature for Plutus validators. The compiled validator script ready for deployment on the blockchain. +3. Helper functions + - `saveVal` is a helper function that saves and writes the validator script to a file. + - `printVestingDatumJSON` is a helper function to print the vesting datum in JSON format, given a public key hash and a deadline in ISO 8601 format. + +### Section 1: Importing the required modules and functions + +```haskell +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + +module Vesting where + +import Data.Maybe (fromJust) +import Plutus.V1.Ledger.Interval (contains) +import Plutus.V2.Ledger.Api (BuiltinData, POSIXTime, + PubKeyHash, + ScriptContext + (scriptContextTxInfo), + TxInfo (txInfoValidRange), + Validator, from, + mkValidatorScript) +import Plutus.V2.Ledger.Contexts (txSignedBy) +import PlutusTx (compile, unstableMakeIsData) +import PlutusTx.Prelude (Bool, traceIfFalse, ($), (&&)) +import Prelude (IO, String) +import Utilities (Network, posixTimeFromIso8601, + printDataToJSON, + validatorAddressBech32, wrap, + writeValidatorToFile) +``` + +When importing modules and functions in Plutus, import only what you need to keep the namespace clean, and comment on why certain imports are used for clarity. + +See the [Plutus Haddock documentation](https://intersectmbo.github.io/plutus/master/) for definitions, short descriptions and source code for Plutus functions, types and type classes. + +#### TODO: + +- These points reflect feedback from Marshall: +- Add more descriptions and explanations about what is being imported in the above code. For example, is it a standard library or a different kind of library that refers to functions to manupulate data? +- Introduce developers new to Plutus to anything that is being brought in from a standard library. +- Explain each of the imports. Each description could have a link to that specific library component. Developers want to have a sense of why they are doing these imports, and if they are standard imports. +- Include a brief explanation of `PlutusTx.Prelude`. + +### Section 2.1: Defining the data type, public key hash, and deadline + +```haskell +data VestingDatum = VestingDatum + { beneficiary :: PubKeyHash + , deadline :: POSIXTime + } + +unstableMakeIsData ''VestingDatum +``` + +This next section defines the `VestingDatum` data type that holds the beneficiary's public key hash, `PubKeyHash`, and the deadline, `POSIXTime`, for releasing the funds. It generates the required instance for `VestingDatum`, allowing the `VestingDatum` type to be used in the validator script. + +### Section 2.2: Implementing the validator function + +```haskell +------------------------------------------------------------------------------ +------------------------------ ON-CHAIN / VALIDATOR -------------------------- +{-# INLINABLE mkVestingValidator #-} +mkVestingValidator :: VestingDatum -> () -> ScriptContext -> Bool +mkVestingValidator dat () ctx = traceIfFalse "beneficiary's signature missing" signedByBeneficiary && + traceIfFalse "deadline not reached" deadlineReached + where + info :: TxInfo + info = scriptContextTxInfo ctx + + signedByBeneficiary :: Bool + signedByBeneficiary = txSignedBy info $ beneficiary dat + + deadlineReached :: Bool + deadlineReached = contains (from $ deadline dat) $ txInfoValidRange info +``` + +This section implements the core validator function, `mkVestingValidator`. It takes the `VestingDatum`, a unit value for the redeemer, and the `ScriptContext`. It returns a `Bool` that indicates whether the transaction is valid or not. + +The validation logic says that the funds can be unlocked only when the deadline has been reached and the transaction has been signed by the beneficiary. + +It extracts the transaction info from the script context and checks if the transaction is signed by the beneficiary, making sure that only the beneficiary can unlock the funds. Following that, it checks if the deadline has been reached, ensuring that the funds can be unlocked only after the specified deadline. + +In the validation code, we have the helper variables `signedByBeneficiary` and `deadlineReached` which are of type `Bool`. + +In the first variable, we use the helper function `txSignedBy` that takes in transaction info and a public key hash and checks whether this transaction has been signed with this public key hash. + +In the second variable, we access the validity range of the transaction and check that it is contained inside the interval starting with the deadline and going to infinity. + +### Section 2.3: Wrapping the validator function and compiling it to a Validator type + +```haskell +{-# INLINABLE mkWrappedVestingValidator #-} +mkWrappedVestingValidator :: BuiltinData -> BuiltinData -> BuiltinData -> () +mkWrappedVestingValidator = wrap mkVestingValidator + +validator :: Validator +validator = mkValidatorScript $$(compile [|| mkWrappedVestingValidator ||]) +``` + +In this section, the code wraps the validator function, which is required by the Plutus compiler. + +Next, the wrapped validator function is compiled to a Validator type, creating a compiled version of the validator script that can be used in transactions. `mkWrappedVestingValidator` is a wrapper for the validator function to conform to the expected type signature for Plutus validators. + +The compiled validator script is ready for deployment on the blockchain. + +### Section 3: Saving the compiled validator script and printing VestingDatum in JSON format + +```haskell +------------------------------------------------------------------------------ +-------------------------------- HELPER FUNCTIONS ---------------------------- + +saveVal :: IO () +saveVal = writeValidatorToFile "./assets/vesting.plutus" validator + +printVestingDatumJSON :: PubKeyHash -> String -> IO () +printVestingDatumJSON pkh time = printDataToJSON $ VestingDatum + { beneficiary = pkh + , deadline = fromJust $ posixTimeFromIso8601 time + } +``` + +These helper functions provide convenient ways to save the validator script to a file and print the `VestingDatum` in JSON format, which can be useful for testing and debugging purposes. + +`saveVal` is a helper function that saves the compiled validator script to a file. It uses the `writeValidatorToFile` function from the `Utilities` module to write the compiled validator script to a file named "vesting.plutus" in the "./assets" directory. + +The `printVestingDatumJSON` function is another helper function that takes the beneficiary's public key hash and the deadline as a string in ISO 8601 format. It constructs the `VestingDatum` using the provided values. The `fromJust` function is used to extract the `POSIXTime` value from the result of `posixTimeFromIso8601`. The `posixTimeFromIso8601` function converts the deadline string to a POSIXTime value. The constructed `VestingDatum` is then printed as JSON to the console using the `printDataToJSON` function. + +If we run this function and input a date and time together with a public key hash, we get the following example output: + +``` +Prelude> import Vesting +Prelude Vesting> :set -XOverloadedStrings +Prelude Vesting> import Plutus.V2.Ledger.Api +Prelude Vesting Plutus.V1.Ledger.Api> +pkh1 = "cff6e39ec5b3cf84b1078976c98706b73774d2c5523af4daaf7c5109" +Prelude Vesting Plutus.V1.Ledger.Api> +printVestingDatumJSON pkh1 "2023-03-11T13:12:11.123Z" +{ + "constructor": 0, + "fields": [ + { + "bytes": "cff6e39ec5b3cf84b1078976c98706b73774d2c5523af4daaf7c5109" + }, + { + "int": 1678540331123 + } + ] +} +``` + +This datum can then be stored in a JSON file and later used in off-chain code. In the examples we have seen so far, we had one specific validator that was a Haskell value of type Validator. If there was any variability in the contract, we model that by using the datum as in the vesting example where the datum contained the beneficiary and the deadline. + +## The larger context and next steps + +To put some context around this first contract example, let's talk about typical next steps that we would need to take to actually use this smart contract in the real world. + +Conceptually, we need to understand the following: +1. The vesting contract is designed to lock funds until a specified deadline has passed and only the designated beneficiary can unlock the funds. +2. The contract requires the beneficiary's public key hash and the deadline to be provided when creating the vesting datum. +3. The contract needs to be compiled and deployed on the Cardano blockchain. +4. The developer needs to interact with the deployed contract by sending transactions to lock funds and later unlock them. + +On a high level and as general guidelines, the steps required to use this smart contract are as follows: + +1. Cardano node: + - Set up a Cardano node and configure it to connect to the desired network (eg, testnet or mainnet). + - The Plutus Pioneer Program describes how to interact with a Plutus smart contract using [Blockfrost](https://blockfrost.io/), a Cardano provider for indexing and querying the blockchain. If using Blockfrost, a developer does not need to run a Cardano node for themselves. + +2. Compile the vesting contract: + - Save the vesting contract code in a Haskell file (e.g., `Vesting.hs`). + - Open a terminal and navigate to the directory containing the `Vesting.hs` file. + - Run the `saveVal` function to compile the contract and save it as a `.plutus` file. + +3. Deploy the vesting contract: + - Use a Cardano wallet (eg, Lace, Nami, Eternl or Daedalus) to create a new wallet and obtain the wallet address. + - Fund the wallet with some ada to cover transaction fees. + - Use the Cardano CLI to create a transaction that deploys the compiled vesting contract to the blockchain. + - Sign and submit the deployment transaction. + +4. Interact with the vesting contract: + - To lock funds in the vesting contract, create a transaction that sends ada to the contract address along with the vesting datum containing the beneficiary's public key hash and the deadline. + - Use the `printVestingDatumJSON` function to generate the vesting datum in JSON format. + - Sign and submit the transaction to lock the funds. + - After the deadline has passed, the beneficiary can create a transaction to unlock the funds. + - The transaction should reference the vesting contract address and provide the necessary input (the vesting datum) and the beneficiary's signature. + - Sign and submit the transaction to unlock the funds and transfer them to the beneficiary's wallet. + +5. Monitor and test: + - Use Cardano explorer tools to monitor the transactions and the contract's state on the blockchain. + - Test the contract thoroughly by locking funds, waiting for the deadline to pass, and unlocking the funds to ensure it behaves as expected. + +6. Integration and deployment: + - Integrate the vesting contract into a larger application or system if required. + - Deploy the contract to the Cardano mainnet when ready for production use. + +Remember to handle security aspects carefully, such as protecting private keys, properly validating inputs, and considering edge cases and potential vulnerabilities. + +It's important to note that this is a simplified overview, and in practice, there may be additional steps and considerations depending on the specific requirements and tools used. + +# Questions to address relating to testing and deploying smart contracts + +- *Feedback from Marshall: This onboarding quick setup document does NOT need to address testing.* + +*Questions for clarification:* + +Luka Kurnjek is working on a book called _Mastering Cardano_. The chapter that focuses on Plutus security mentions the resources below. Are these resources still accurate and usable? Some of it relates to testing, some of it relates to basic definitions of concepts about what is Plutus, etc. + +- Testing is a complex topic that is probably beyond the scope of this onboarding and quick setup guide. Instead, I suggest we write a few sentences referring people to other resources. Luka has about 20 pages written up about testing Plutus scripts. + +- [Atlas](https://atlas-app.io/) is an application backend developed by GeniusYield and other companies. Lars is CTO of GeniusYield. Is it appropriate to refer readers to this resource instead of the PAB? + +- Does it make sense to refer developers to the Plutus application backend (PAB) tool? Is it still usable? + +- Is the MLabs [`plutus-simple-model`](https://github.com/mlabs-haskell/plutus-simple-model/tree/main) relevant for testing in the context of this first smart contract example? It talks about property tests. + +- Similar question for [`cooked-validators`](https://github.com/tweag/cooked-validators) by Tweag. + - "With `cooked-validators` you can test Cardano smart contracts (including Plutus v2 features) by writing potentially malicious offchain code. You can also use the library to write "normal" offchain code in a comfortable and flexible way." + +- Is this video still accurate? [The Plutus platform](https://youtu.be/usMPt8KpBeI?si=X9uQsQQI4hjVv4KX) + - It was recorded in July, 2020. I want to clarify which parts, if any, may no longer be true today. For example, does it depend on Plutus Tools? + +- Is this blog article still accurate? [Plutus Application Backend (PAB): supporting DApp development on Cardano](https://iohk.io/en/blog/posts/2021/10/28/plutus-application-backend-pab-supporting-dapp-development-on-cardano/) + - Dated October, 2021 + diff --git a/doc/docs-revamp/040_simple-example.md b/doc/docs-revamp/040_simple-example.md new file mode 100644 index 00000000000..7bedb729a7f --- /dev/null +++ b/doc/docs-revamp/040_simple-example.md @@ -0,0 +1,334 @@ +--- +title: "Simple example" +description: "A Plutus script for an auction smart contract" +date: 2024-04-08 +--- + +# Section 4. Simple example + +> **Caution** +> +> This conceptual guide to an auction smart contract in Plutus introduces fundamentals for educational use. +> However, it is not optimized for security or efficiency and should not be deployed in production environments. +> This example simplifies some security aspects, leading to potential vulnerabilities. +> For detailed insights on developing secure smart contracts, please refer to the [Cardano Plutus Script Vulnerability Guide](https://library.mlabs.city/common-plutus-security-vulnerabilities) by MLabs. + +## Overview + +This example presents Plutus Tx code for a smart contract that controls the auction of an asset, which can be executed on the Cardano blockchain. +In a sense, the smart contract is acting as the auctioneer in that it enforces certain rules and requirements in order for the auction to occur successfully. + + + +Plutus Tx is a high-level language for writing the validation logic of the contract, the logic that determines whether a transaction is allowed to spend a UTXO. +Plutus Tx is not a new language, but rather a subset of Haskell, and it is compiled into Plutus Core, a low-level language based on higher-order polymorphic lambda calculus. +Plutus Core is the code that runs on-chain, i.e., by every node validating the transaction, using an interpreter known as the CEK machine. +A Plutus Core program included in a Cardano transaction is often referred to as Plutus script or Plutus validator. + + + +To develop and deploy a smart contract, you would also need off-chain code for building transactions, submitting transactions, deploying smart contracts, querying for available UTXOs on the chain and so on. +You may also want a front-end interface for your smart contract for better user experiences. +In this example, we are not covering these aspects. + + + + +Before we get to the Plutus Tx code, let's briefly go over some basic concepts, including UTXO, EUTXO, datum, redeemer and script context. + +## The EUTXO model, datum, redeemer and script context + +On the Cardano blockchain, a transaction contains an arbitrary number of inputs and an arbitrary number of outputs. +The effect of a transaction is to consume inputs and produce new outputs. + + + +UTXO (unspent transaction output) is the ledger model used by some blockchains, including bitcoin. +A UTXO is produced by a transaction, is immutable, and can only be spent once by another transaction. +In the original UTXO model, a UTXO contains a wallet address and a value (e.g., some amount of one or more currencies/tokens). +Inside a transaction, a UTXO is uniquely identified by the wallet address. +It can be spent by a transaction if the transaction is signed by the private key of the wallet address. + + + +The Extended UTXO model (EUTXO) extends the original model with a new kind of UTXO: script UTXO. +A script UTXO contains a value, a script (usually a Plutus script), a piece of data called *datum*, and is identified by the hash of the script. +For a transaction to spend it, the transaction must provide a piece of input data to the script, referred to as the *redeemer*. +The script is then run, and it must succeed in order for the transaction to be allowed to spend the UTXO. +In addition to the redeemer, the script also has access to the datum contained in the UTXO, as well as the details of the transaction trying to spend it. +This is referred to as *script context*. + + + +Note that the only thing a Plutus script does is to determine whether a transaction can spend the script UTXO that contains the script. +It is *not* responsible for such things as deciding whether it can spend a different UTXO, checking that the input value in a transaction equals the output value, or updating the state of the smart contract. +Consider it a pure function that returns `Bool`. +Checking transaction validity is done by the ledger rules, and updating the state of a smart contract is done by constructing the transaction to produce a new script UTXO with an updated datum. + + + +The immutability of UTXOs leads to the extremely useful property of completely predictable transaction fees. +The Plutus script in a transaction can be run off-chain to determine the fee before submitting the transaction onto the blockchain. +When the transaction is submitted, if some UTXOs it tries to spend have already been spent, the transaction is immediately rejected without penalty. +If all input UTXOs still exist, and the Plutus script is invoked, the on-chain behavior would be exactly identical to the off-chain behavior. +This could not be achieved if transaction inputs were mutable, such as is the case in Ethereum's account-based model. + +See also: + +- [Plutus scripts](https://docs.cardano.org/smart-contracts/plutus/plutus-scripts/) for further reading about scripts, and +- [Understanding the Extended UTXO model](https://docs.cardano.org/learn/eutxo-explainer) + +## Auction properties + +In this example, Alice wants to auction some asset she owns, represented as a non-fungible token (NFT) on Cardano. +She would like to create and deploy an auction smart contract with the following properties: + +- there is a minimum bid amount +- each bid must be higher than the previous highest bid (if any) +- once a new bid is made, the previous highest bid (if it exists) is immediately refunded +- there is a deadline for placing bids; once the deadline has passed, new bids are no longer accepted, the asset can be transferred to the highest bidder (or to the seller if there are no bids), and the highest bid (if one exists) can be transferred to the seller. + +Next, let's go through and discuss the Plutus Tx code we're using, shown below, for this specific example of an auction smart contract. + + + +## Plutus Tx code + +Recall that Plutus Tx is a subset of Haskell. +It is the source language one uses to write Plutus validators. +A Plutus Tx program is compiled into Plutus Core, which is interpreted on-chain. +The full Plutus Tx code for the auction smart contract can be found at [AuctionValidator.hs](https://github.com/IntersectMBO/plutus/blob/master/doc/read-the-docs-site/tutorials/AuctionValidator.hs). + + + +### Data types + +First, let's define the following data types and instances for the validator: + + + +The purpose of `makeLift` and `unstableMakeIsData` will be explained later. + +Typically, writing a Plutus Tx validator script for a smart contract involves four data types: + +#### Contract parameters + +These are fixed properties of the contract. +In our example, it is the `AuctionParams` type, containing properties like seller and minimum bid. + +#### Datum + +This is part of a script UTXO. +It should be thought of as the state of the contract. +Our example requires only one piece of state: the current highest bid. +We use the `AuctionDatum` type to represent this. + +#### Redeemer + +This is an input to the Plutus script provided by the transaction that is trying to spend a script UTXO. +If a smart contract is regarded as a state machine, the redeemer would be the input that ticks the state machine. +In our example, it is the `AuctionRedeemer` type: one may either submit a new bid, or request to close the auction and pay out the winner and the seller, both of which lead to a new state of the auction. + +#### Script context + +This type contains the information of the transaction that the validator can inspect. +In our example, our validator verifies several conditions of the transaction; e.g., if it is a new bid, then it must be submitted before the auction's end time; the previous highest bid must be refunded to the previous bidder, etc. + +The script context type is fixed for each Plutus language version. +For Plutus V2, for example, it is `PlutusLedgerApi.V2.Contexts.ScriptContext`. + +> **Note** +> +> When writing a Plutus validator using Plutus Tx, it is advisable to turn off Haskell's `Prelude`. +> Usage of most functions and methods in `Prelude` should be replaced by their counterparts in the `plutus-tx` library, e.g., `PlutusTx.Eq.==`. + +### Main validator function + +Now we are ready to introduce our main validator function. +The beginning of the function looks like the following: + + + +Depending on whether this transaction is attempting to submit a new bid or to request payout, the validator validates the corresponding set of conditions. + +The `sufficientBid` condition verifies that the bid amount is sufficient: + + + +The `validBidTime` condition verifies that the bid is submitted before the auction's deadline: + + + +Here, `to x` is the time interval ending at `x`, i.e., `(-∞, x]`. +`txInfoValidRange` is a transaction property. +It is the time interval in which the transaction is allowed to go through phase-1 validation. +`contains` takes two time intervals, and checks that the first interval completely includes the second. +Since the transaction may be validated at any point in the `txInfoValidRange` interval, we need to check that the entire interval lies within `(-∞, apEndTime params]`. + +The reason we need the `txInfoValidRange` interval instead of using the exact time the transaction is validated is due to [determinism](https://iohk.io/en/blog/posts/2021/09/06/no-surprises-transaction-validation-on-cardano/). +Using the exact time would be like calling a `getCurrentTime` function and branching based on the current time. +On the other hand, by using the `txInfoValidRange` interval, the same interval is always used by the same transaction. + +The `refundsPreviousHighestBid` condition checks that the transaction pays the previous highest bid to the previous bidder: + + + +It uses `PlutusTx.find` to find the transaction output (a UTXO) that pays to the previous bidder the amount equivalent to the previous highest bid, and verifies that there is at least one such output. + +`lovelaceValue amt` constructs a `Value` with `amt` Lovelaces (the subunit of the Ada currency). +`Value` is a multi-asset type that represents a collection of assets, including Ada. +An asset is identified by a (symbol, token) pair, where the symbol represents the policy that controls the minting and burning of tokens, and the token represents a particular kind of token manipulated by the policy. +`(adaSymbol, adaToken)` is the special identifier for Ada/Lovelace. + +The `correctNewDatum` condition verifies that the transaction produces a *continuing output* containing the correct datum (the new highest bid): + + + +A "continuing output" is a transaction output that pays to the same script address from which we are currently spending. +Exactly one continuing output must be present in this example so that the next bidder can place a new bid. +The new bid, in turn, will need to spend the continuing output and get validated by the same validator script. + +If the transaction is requesting a payout, the validator will then verify the other three conditions: `validPayoutTime`,`sellerGetsHighestBid` and `highestBidderGetsAsset`. +These conditions are similar to the ones already explained, so their details are omitted. + +Finally, we need to compile the validator written in Plutus Tx into Plutus Core, using the Plutus Tx compiler: + + + +The type of the compiled validator is `CompiledCode (BuiltinData -> BuiltinData -> BuiltinData -> ())`, where type `BuiltinData -> BuiltinData -> BuiltinData -> ()` is also known as the *untyped validator*. +An untyped validator takes three `BuiltinData` arguments, representing the serialized datum, redeemer, and script context. +The call to `PlutusTx.unsafeFromBuiltinData` is the reason we need the `PlutusTx.unstableMakeIsData` shown before, which derives `UnsafeFromData` instances. +And instead of returning a `Bool`, it simply returns `()`, and the validation succeeds if the script evaluates without error. + +Note that `AuctionParams` is an argument of neither the untyped validator nor the final UPLC program. +`AuctionParams` contains contract properties that don't change, so it is simply built into the validator. + +Since the Plutus Tx compiler compiles `a` into `CompiledCode a`, we first use `auctionUntypedValidator` to obtain an untyped validator. +It takes `AuctionParams`, and returns an untyped validator. +We then define the `auctionValidatorScript` function, which takes `AuctionParams` and returns the compiled Plutus Core program. + +To create the Plutus validator script for a particular auction, we call `auctionValidatorScript` with the appropriate `AuctionParams`. +We will then be able to launch the auction on-chain by submitting a transaction that outputs a script UTXO with `Nothing` as the datum. + +> **Note** +> +> It is worth noting that we must call `PlutusTx.compile` on the entire `auctionUntypedValidator`, rather than applying it to `params` before compiling, as in `$$(PlutusTx.compile [||auctionUntypedValidator params||])`. +> The latter won't work, because everything being compiled (inside `[||...||]`) must be known at compile time, but `params` is not: it can differ at runtime depending on what kind of auction we want to run. +> Instead, we compile the entire `auctionUntypedValidator` into Plutus Core, then use `liftCode` to lift `params` into a Plutus Core term, and apply the compiled `auctionUntypedValidator` to it at the Plutus Core level. +> To do so, we need the `Lift` instance for `AuctionParams`, derived via `PlutusTx.makeLift`. + +## Life cycle of the auction smart contract + +With the Plutus script written, Alice is now ready to start the auction smart contract. +At the outset, Alice creates a script UTXO whose address is the hash of the Plutus script, whose value is the token to be auctioned, and whose datum is `Nothing`. +Recall that the datum represents the highest bid, and there's no bid yet. +This script UTXO also contains the script itself, so that nodes validating transactions that try to spend this script UTXO have access to the script. + +### Initial UTXO + +Alice needs to create the initial UTXO transaction with the desired UTXO as an output. +The token being auctioned can either be minted by this transaction, or if it already exists in another UTXO on the ledger, the transaction should consume that UTXO as an input. +We will not go into the details here of how minting tokens works. + +### The first bid + +Suppose Bob, the first bidder, wants to bid 100 Ada for Alice's NFT. +In order to do this, Bob creates a transaction that has at least two inputs and at least one output. + +The required inputs are (1) the script UTXO Alice created; (2) Bob's bid of 100 Ada. +The 100 Ada can come in one or multiple UTXOs. +Note that the input UTXOs must have a total value of more than 100 Ada, because in addition to the bid amount, they also need to cover the transaction fee. + +The required output is a script UTXO with the same address as the initial UTXO (since the Plutus script itself remains the same), which is known as a *continuing output*. +This continuing output UTXO should contain: + +- a datum that contains Bob's wallet address and Bob's bid amount (100 Ada). + - Bob's wallet address is used to claim the token (if Bob ends up winning the auction) or receive the refund (if a higher bid is placed later). +- a value: the token being auctioned plus the 100 Ada from Bob's bid. + +If the input UTXOs contain more Ada than 100 plus the transaction fee, then there should be additional output UTXOs that return the extra Ada. +Again, verifying that the input value of a transaction minus the transaction fee equals the output value (unless the transaction is burning tokens) is the responsibility of the ledger, not the Plutus script. + +In order for Bob's transaction to be able to spend the initial script UTXO Alice created, Bob's transaction must also contain a redeemer. +As shown in the code above, there are two kinds of redeemers in our example: `NewBid Bid` and `Payout`. +The redeemer in Bob's transaction is a `NewBid Bid` where the `Bid` contains Bob's wallet address and bid amount. + +![First bid diagram](first-bid-simple-auction-v3.png){width="700px"} + +Once Bob's transaction is submitted, the node validating this transaction will run the Plutus script, which checks a number of conditions like whether the bid happens before the deadline, and whether the bid is high enough. +If the checks pass and everything else about the transaction is valid, the transaction will go through and be included in a block. +At this point, the initial UTXO created by Alice no longer exists on the ledger, since it has been spent by Bob's transaction. + +### The second bid + +Next, suppose a second bidder, Charlie, wants to outbid Bob. +Charlie wants to bid 200 Ada. + +Charlie will create another transaction. +This transaction should have an additional output compared to Bob's transaction: a UTXO that returns Bob's bid of 100 Ada. +Recall that this is one of the conditions checked by the Plutus script; the transaction is rejected if the refund output is missing. + +![Second bid diagram](second-bid-simple-auction-v3.png){width="700px"} + +Charlie's transaction needs to spend the script UTXO produced by Bob's transaction, so it also needs a redeemer. +The redeemer is a `NewBid Bid` where `Bid` contains Charlie's wallet address and bid amount. +Charlie's transaction cannot spend the initial UTXO produced by Alice, since it has already been spent by Bob's transaction. + +### Closing the auction + +Let's assume that there won't be another bid. +Once the deadline has passed, the auction can be closed. + +In order to do that, somebody has to create another transaction. +That could be Alice, who wants to collect the bid, or it could be Charlie, who wants to collect the NFT. +It can be anybody, but Alice and Charlie have an incentive to create it. + +This transaction has one required input: the script UTXO produced by Charlie's transaction, and two required outputs: (1) the payment of the auctioned token to Charlie; (2) the payment of 200 Ada to Alice. + +![Closing transaction diagram](../static/img/closing-tx-simple-auction-v3.png){width="700px"} + +## Libraries for writing Plutus Tx scripts + +This auction example shows a relatively low-level way of writing scripts using Plutus Tx. +In practice, you may consider using a higher-level library that abstracts away some of the details. +For example, [plutus-apps](https://github.com/IntersectMBO/plutus-apps) provides a constraint library for writing Plutus Tx. +Using these libraries, writing a validator in Plutus Tx becomes a matter of defining state transactions and the corresponding constraints, e.g., the condition `refundsPreviousHighestBid` can simply be written as `Constraints.mustPayToPubKey bidder (lovelaceValue amt)`. + +## Alternatives to Plutus Tx + +There are languages other than Plutus Tx that can be compiled into Plutus Core. +We list some of them here for reference. +However, we are not endorsing them; we are not representing their qualities nor their state of development regarding their production-readiness. + +- [Aiken](https://github.com/txpipe/aiken/) +- [Hebi](https://github.com/OpShin/hebi) +- [Helios](https://github.com/hyperion-bt/helios) +- [OpShin](https://github.com/OpShin/opshin) +- [plu-ts](https://github.com/HarmonicLabs/plu-ts) +- [Plutarch](https://github.com/Plutonomicon/plutarch-core) +- [Pluto](https://github.com/Plutonomicon/pluto) + +## Off-chain code + +Since the main purpose of this example is to introduce Plutus Tx and Plutus Core, we walked through only the on-chain code, which is responsible for validating transactions (in the sense of determining whether a transaction is allowed to spend a UTXO). + +In addition to the on-chain code, one typically needs the accompanying off-chain code and services to perform tasks like building transactions, submitting transactions, deploying smart contracts, querying for available UTXOs on the chain, etc. + + + +A full suite of solutions is [in development](https://plutus-apps.readthedocs.io/en/latest/plutus/explanations/plutus-tools-component-descriptions.html). +See the [plutus-apps](https://github.com/IntersectMBO/plutus-apps) repo and its accompanying [Plutus tools SDK user guide](https://plutus-apps.readthedocs.io/en/latest/) for more details. + +Some other alternatives include [cardano-transaction-lib](https://github.com/Plutonomicon/cardano-transaction-lib) and [lucid](https://github.com/spacebudz/lucid). +All these are based on the [Cardano API](https://github.com/IntersectMBO/cardano-node/tree/master/cardano-api), a low-level API that provides the capability to do the off-chain work with a local running node. + +See also: [Plutus application development](https://docs.cardano.org/smart-contracts/plutus/dapp-development/), a high-level overview of resources for building DApps using Plutus. +A DApp is basically a smart contract plus a front end. + +## Further reading + +### The EUTXO model + +- [The Extended UTXO Model](https://iohk.io/en/research/library/papers/the-extended-utxo-model/) (Paper) +- [The EUTXO Handbook](https://www.essentialcardano.io/article/the-eutxo-handbook) +- Blog Post: Cardano's Extended UTXO accounting model---built to support multi-assets and smart contracts ([part 1](https://iohk.io/en/blog/posts/2021/03/11/cardanos-extended-utxo-accounting-model/), [part 2](https://iohk.io/en/blog/posts/2021/03/12/cardanos-extended-utxo-accounting-model-part-2/)) diff --git a/doc/docs-revamp/050.1_proposed-edits_using-plutus-tx.md b/doc/docs-revamp/050.1_proposed-edits_using-plutus-tx.md new file mode 100644 index 00000000000..d83027fb86e --- /dev/null +++ b/doc/docs-revamp/050.1_proposed-edits_using-plutus-tx.md @@ -0,0 +1,355 @@ +--- +title: "Proposed Edits -- Using Plutus Tx" +description: "Draft in progress for review and discussion" +date: 2024-04-26 +--- + +# Section 5. Using Plutus Tx + +> NOTE: Paragraph numbering is being used temporarily to help with organizing and editing content. + +1. High-level overview of how Plutus Tx works + 1.1 Key technique for implementing Plutus tx: staged metaprogramming + +2. Basic syntax and structure of a Plutus Tx program + 2.1 Plutus-Tx-Template repo + 2.2 Template Haskell preliminaries + 2.3 Simple pattern + 2.4 Quotes + 2.5 Splicing quotes + +3. Writing Plutus Tx Programs + 3.1 Plutus Tx standard usage pattern (how all of our Plutus Tx programs are written) + 3.2 Functions and datatypes + 3.3 Typeclasses + 3.4 The Plutus Tx Prelude + 3.5 Plutus Tx Prelude has redefined versions of many standard typeclasses + 3.6 Lifting values for generating code dynamically + +4. Compiling Plutus Tx, describing the Plutus Tx compilation process + + 4.1 GHC Extensions, Flags and Pragmas + 4.1.1 Extensions + 4.1.2 Flags + 4.1.3 Pragmas + 4.2 Reference: Plutus Tx Compiler Options + +5. Troubleshooting and Debugging + 5.1 Common errors and how to fix them + 5.2 Debugging techniques for Plutus Tx programs + +6. Real-world Examples and Use Cases + 6.1 Practical applications of Plutus Tx + 6.2 Case studies and code examples + +## 1. High-level overview of how Plutus Tx works + +A Plutus application, or `Plutus Tx` program, where 'Tx' indicates that the component usually goes into a transaction, is written as a single Haskell program. +The Plutus Tx program describes both the code that runs off the chain (for example, on a user's computer, or in their wallet), and on the chain as part of transaction validation. +The parts of the program that describe the on-chain code are compiled into `Plutus Core`, rather than into the normal compilation target language (the languages or machine code that Haskell programs are usually compiled to in non-blockchain contexts, such as native machine code or intermediate languages). + +### 1.1 Key technique for implementing Plutus tx: staged metaprogramming + +The key technique that we use to implement Plutus Tx is called *staged metaprogramming*, which means that the main Haskell program generates *another* program (in this case, the Plutus Core program that will run on the blockchain). +Plutus Tx is the mechanism we use to write those programs, but since Plutus Tx is just part of the main Haskell program, we can share types and definitions between the two. + +## 2. Getting started with Plutus Tx + +### 2.1 Plutus-Tx-Template repo + + + +### 2.2 Template Haskell preliminaries + +Plutus Tx uses Haskell's metaprogramming support, Template Haskell, for two main reasons: + +- Template Haskell enables us to work at compile time, which is when we do Plutus Tx compilation. +- It allows us to wire up the machinery that invokes the Plutus Tx compiler. + +### 2.3 Simple pattern + +Template Haskell is very versatile, but we only use a few features. +Essentially, we often use the same simple pattern: + +- make a quote, +- immediately call `PlutusTx.TH.compile`{.interpreted-text role="hsobj"}, and then +- splice the result back in. + +### 2.4 Quotes + +Template Haskell begins with *quotes*. A Template Haskell quote is a Haskell expression `e` inside special brackets `[|| e ||]`. +It has type `Q (TExp a)` where `e` has type `a`. +`TExp a` is a *representation* of an expression of type `a`; in other words, the syntax of the actual Haskell expression that was quoted. +The quote lives in the type `Q` of quotes, which isn't very interesting for us. + +> **NOTE:** +> +> There is also an abbreviation `TExpQ a` for `Q (TExp a)`, which avoids some parentheses. + +### 2.5 Splicing quotes + +You can *splice* a quote into your program using the `$$` operator. +This inserts the syntax represented by the quote into the program at the point where the splice is written. + +Simply put, a quote allows us to talk about Haskell programs as *values*. + +The Plutus Tx compiler compiles Haskell *expressions* (not values), so naturally it takes a quote (representing an expression) as an argument. +The result is a new quote, this time for a Haskell program that represents the *compiled* program. +In Haskell, the type of `PlutusTx.TH.compile`{.interpreted-text role="hsobj"} is `TExpQ a → TExpQ (CompiledCode a)`. +This is just what we already said: + +- `TExpQ a` is a quote representing a program of type `a`. +- `TExpQ (CompiledCode a)` is a quote representing a compiled Plutus Core program. + +> **NOTE:** +> +> `PlutusTx.CompiledCode`{.interpreted-text role="hsobj"} also has a type parameter `a`, which corresponds to the type of the original expression. +> +> This lets us "remember" the type of the original Haskell program we compiled. + +Since `PlutusTx.TH.compile`{.interpreted-text role="hsobj"} produces a quote, to use the result we need to splice it back into our program. +The Plutus Tx compiler runs when compiling the main program, and the compiled program will be inserted into the main program. + +## 3. Writing Plutus Tx programs + +::: {.literalinclude start-after="BLOCK1" end-before="BLOCK2"} +BasicPlutusTx.hs +::: + +This simple program just evaluates to the integer `1`. + +> **NOTE:** +> +> The examples that show the Plutus Core generated from compilation include doctests. +> The syntax of Plutus Core might look unfamiliar, since this syntax is used for the 'assembly language', which means you don't need to inspect the compiler's output. + +::: {.literalinclude start-after="BLOCK2" end-before="BLOCK3"} +BasicPlutusTx.hs +::: + +We can see how the metaprogramming works: the Haskell program `1` was turned into a `CompiledCode Integer` at compile time, which we spliced into our Haskell program. +We can inspect the generated program at runtime to see the generated Plutus Core (or to put it on the blockchain). + +### 3.1 Plutus Tx standard usage pattern (how all of our Plutus Tx programs are written) + +We also see the standard usage pattern: a TH quote, wrapped in a call to `PlutusTx.TH.compile`{.interpreted-text role="hsobj"}, wrapped in a `$$` splice. +This is how all our Plutus Tx programs are written. + +The following is a slightly more complex program. +It includes the identity function on integers. + +::: {.literalinclude start-after="BLOCK3" end-before="BLOCK4"} +BasicPlutusTx.hs +::: + +### 3.2 Functions and datatypes + +You can use functions inside your expression. +In practice, you will usually want to define the entirety of your Plutus Tx program as a definition outside the quote, and then simply call it inside the quote. + +::: {.literalinclude start-after="BLOCK4" end-before="BLOCK5"} +BasicPlutusTx.hs +::: + +We can use normal Haskell datatypes and pattern matching freely: + +::: {.literalinclude start-after="BLOCK5" end-before="BLOCK6"} +BasicPlutusTx.hs +::: + +Unlike functions, datatypes do not need any kind of special annotation to be used inside a quote, hence we can use types like `Maybe` from the Haskell `Prelude`. +This works for your own datatypes too. + +#### Example + +Here's a small example with a datatype representing a potentially open-ended end date. + +::: {.literalinclude start-after="BLOCK6" end-before="BLOCK7"} +BasicPlutusTx.hs +::: + +We could also have defined the `pastEnd` function as a separate `INLINABLE` binding and just referred to it in the quote, but in this case, it's small enough to just write in place. + +### 3.3 Typeclasses + +So far we have used functions like `lessThanEqInteger` for comparing `Integer`s, which is much less convenient than `<` from the standard Haskell `Ord` typeclass. + +While Plutus Tx does support typeclasses, we cannot use many of the standard typeclasses, since we require their class methods to be `INLINABLE`, and the implementations for types such as `Integer` use the Plutus Tx built-ins. + +### 3.4 The Plutus Tx Prelude + +The `PlutusTx.Prelude`{.interpreted-text role="hsmod"} module is a drop-in replacement for the normal Haskell Prelude, with some redefined functions and typeclasses that make it easier for the Plutus Tx compiler to handle (such as `INLINABLE`). + +Use the Plutus Tx Prelude for code that you expect to compile with the Plutus Tx compiler. +All the definitions in the Plutus Tx Prelude include working Haskell definitions, which means that you can use them in normal Haskell code too, although for normal Haskell code, the Haskell Prelude versions will probably perform better. + +To use the Plutus Tx Prelude, use the `NoImplicitPrelude` language pragma and import `PlutusTx.Prelude`{.interpreted-text role="hsmod"}. + +Plutus Tx includes some built-in types and functions for working with primitive data (integers and bytestrings), as well as a few special functions. +These types are also exported from the Plutus Tx Prelude. + +### 3.5 Plutus Tx Prelude has redefined versions of many standard typeclasses + +Redefined versions of many standard typeclasses are available in the Plutus Tx Prelude. +As such, you should be able to use most typeclass functions in your Plutus Tx programs. + +For example, the following is a version of the `pastEnd` function using `<`. +This will be compiled to exactly the same code as the previous definition. + +::: {.literalinclude start-after="BLOCK7" end-before="BLOCK8"} +BasicPlutusTx.hs +::: + +### 3.6 Lifting values for generating code dynamically + +So far we've seen how to define pieces of code *statically* (when you *compile* your main Haskell program), but you might want to generate code *dynamically* (that is, when you *run* your main Haskell program). +For example, you might be writing the body of a transaction to initiate a crowdfunding smart contract, which would need to be parameterized by data determining the size of the goal, the campaign start and end times, and any additional data that may be required. + +We can do this in the same way that we parameterize code in functional programming: write the static code as a *function* and provide the argument later to configure it. + +In our case, there is a slight complication: we want to make the argument and apply the function to it at *runtime*. +Plutus Tx addresses this through *lifting*. +Lifting enables the use of the same types, both inside your Plutus Tx program *and* in the external code that uses it. + +> **NOTE:** +> +> In this context, *runtime* means the runtime of the main Haskell program, **not** when the Plutus Core runs on the chain. +> We want to configure our code when the main Haskell program runs, as that is when we will be getting user input. + +In this example, we add an add-one function. + +::: {.literalinclude start-after="BLOCK8" end-before="BLOCK9"} +BasicPlutusTx.hs +::: + +Now, suppose we want to apply this to `4` at runtime, giving us a program that computes to `5`. +We need to *lift* the argument (`4`) from Haskell to Plutus Core, and then we need to apply the function to it. + +::: {.literalinclude start-after="BLOCK9" end-before="BLOCK10"} +BasicPlutusTx.hs +::: + +We lifted the argument using the `PlutusTx.liftCode`{.interpreted-text role="hsobj"} function. +To use this, a type must have an instance of the `PlutusTx.Lift`{.interpreted-text role="hsobj"} class. +For your own datatypes you should generate these with the `PlutusTx.makeLift`{.interpreted-text role="hsobj"} TH function from `PlutusTx.Lift`{.interpreted-text role="hsmod"}. + +> **NOTE:** +> +> `PlutusTx.liftCode`{.interpreted-text role="hsobj"} is relatively unsafe because it ignores any errors that might occur from lifting something that might not be supported. +> There is a `PlutusTx.safeLiftCode`{.interpreted-text role="hsobj"} if you want to explicitly handle these occurrences. + +The combined program applies the original compiled lambda to the lifted value (notice that the lambda is a bit complicated now, since we have compiled the addition into a built-in). + +Here's an example with our custom datatype. The output is the encoded version of `False`. + +::: {.literalinclude start-after="BLOCK10" end-before="BLOCK11"} +BasicPlutusTx.hs +::: + + + +## 4. Compiling Plutus Tx + +> **Warning:** +> +> Strictly speaking, while the majority of simple Haskell will work, only a subset of Haskell is supported by the Plutus Tx compiler. +> The Plutus Tx compiler will tell you if you are attempting to use an unsupported component. + +### 4.1 GHC Extensions, Flags and Pragmas + +Plutus Tx is a subset of Haskell and is compiled to Untyped Plutus Core by the Plutus Tx compiler, a GHC (Glasgow Haskell Compiler) plugin. + +In order to ensure the success and correct compilation of Plutus Tx programs, all Plutus Tx modules (that is, Haskell modules that contain code to be compiled by the Plutus Tx compiler) should use the following GHC extensions, flags and pragmas. + +#### 4.1.1 Extensions + +Plutus Tx modules should use the `Strict` extension: : + + {-# LANGUAGE Strict #-} + +Unlike in Haskell, function applications in Plutus Tx are strict. +In other words, when evaluating `(\x -> 42) (3 + 4)` the expression `3 + 4` is evaluated first, before evaluating the function body (`42`), even though `x` is not used in the function body. +The `Strict` extension ensures that let bindings and patterns are also (by default) strict, for instance, evaluating `let x = 3 + 4 in 42` evaluates `3 + 4` first, even though `x` is not used. + +Bang patterns and lazy patterns can be used to explicitly specify whether a let binding is strict or non-strict, as in `let !x = 3 + 4 in 42` (strict) and `let ~x = 3 + 4 in 42` (non-strict). +At this time, it is not possible to make function applications non-strict: `(\(~x) -> 42) (3 + 4)` still evaluates `3 + 4` strictly. + +Making let bindings strict by default has the following advantages: + +- It makes let bindings and function applications semantically equivalent. For example, `let x = 3 + 4 in 42` has the same semantics as `(\x -> 42) (3 + 4)`. +This is what one would come to expect, as it is the case in most other programming languages, regardless of whether the language is strict or non-strict. +- Untyped Plutus Core programs, which are compiled from Plutus Tx, are not evaluated lazily (unlike Haskell), that is, there is no memoization of the results of evaluated expressions. +Thus using non-strict bindings can cause an expression to be inadvertently evaluated for an unbounded number of times. +Consider `let x = in \y -> x + y`. +If `x` is non-strict, `` will be evalutated every time `\y -> x + y` is applied to an argument, which means it can be evaluated 0 times, 1 time, 2 times, or any number of times (this is not the case if lazy evaluation was employed). +On the other hand, if `x` is strict, it is always evaluated once, which is at most one more time than what is necessary. + +#### 4.1.2 Flags + +GHC has a variety of optimization flags, many of which are on by default. +Although Plutus Tx is, syntactically, a subset of Haskell, it has different semantics and a different evaluation strategy (Haskell: non-strict semantics, call by need; Plutus Tx: strict semantics, call by value). As a result, some GHC optimizations are not helpful for Plutus Tx programs, and can even be harmful, in the sense that it can make Plutus Tx programs less efficient, or fail to be compiled. +An example is the full laziness optimization, controlled by GHC flag `-ffull-laziness`, which floats let bindings out of lambdas whenever possible. +Since Untyped Plutus Core does not employ lazy evaluation, the full laziness optimization is usually not beneficial, and can sometimes make a Plutus Tx program more expensive. +Conversely, some GHC features must be turned on in order to ensure Plutus Tx programs are compiled successfully. + +All Plutus Tx modules should use the following GHC flags: + + -fno-ignore-interface-pragmas + -fno-omit-interface-pragmas + -fno-full-laziness + -fno-spec-constr + -fno-specialise + -fno-strictness + -fno-unbox-strict-fields + -fno-unbox-small-strict-fields + +`-fno-ignore-interface-pragmas` and `-fno-omit-interface-pragmas` ensure unfoldings of Plutus Tx functions are available. +The rest are GHC optimizations that are generally bad for Plutus Tx, and should thus be turned off. + +These flags can be specified either in a Haskell module, for example: + + {-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} + +or in a build file. +For example, if your project is built using Cabal, you can add the flags to the `.cabal` files, like so: + +> ghc-options: +> +> : -fno-ignore-interface-pragmas + +Note that this section only covers GHC flags, not Plutus Tx compiler flags. +Information about the latter can be found in Reference > Writing scripts > Plutus Tx Compiler Options: `plutus_tx_options`. + +#### 4.1.3 Pragmas + +All functions and methods should have the `INLINEABLE` pragma, so that their unfoldings are made available to the Plutus Tx compiler. + +The `-fexpose-all-unfoldings` flag also makes GHC expose all unfoldings, but unfoldings exposed this way can be more optimized than unfoldings exposed via `INLINEABLE`. +In general, we do not want GHC to perform optimizations, since GHC optimizes a program based on the assumption that it has non-strict semantics and is evaluated lazily (call by need), which is not true for Plutus Tx programs. +Therefore, `INLINEABLE` is preferred over `-fexpose-all-unfoldings`, even though the latter is simpler. + +`-fexpose-all-unfoldings` can be useful for functions that are generated by GHC and do not have the `INLINEABLE` pragma. +`-fspecialise` and `-fspec-constr` are two examples of optimizations that can generate such functions. +The most reliable solution, however, is to simply turn these optimizations off. +Another option is to bump `-funfolding-creation-threshold` to make it more likely for GHC to retain unfoldings for functions without the `INLINEABLE` pragma. +`-fexpose-all-unfoldings` should be used as a last resort. + +### 4.2 Reference: Plutus Tx Compiler Options + +A number of options can be passed to the Plutus Tx compiler. +See `plutus_tx_options`{.interpreted-text role="ref"} for details. + +## 5. Advanced Plutus Tx concepts + +### 5.a Method for triggering a validation failure + +The `PlutusTx.Builtins.error`{.interpreted-text role="hsobj"} built-in deserves a special mention. +`PlutusTx.Builtins.error`{.interpreted-text role="hsobj"} causes the transaction to abort when it is evaluated, which is one way to trigger a validation failure. + diff --git a/doc/docs-revamp/050_using-plutus-tx.md b/doc/docs-revamp/050_using-plutus-tx.md new file mode 100644 index 00000000000..e57e37ca208 --- /dev/null +++ b/doc/docs-revamp/050_using-plutus-tx.md @@ -0,0 +1,326 @@ +--- +title: "Using Plutus Tx" +description: "" +date: 2024-04-02 +--- + + + +# Section 5. Using Plutus Tx + +- Template Haskell preliminaries +- Writing basic Plutus Tx programs +- Functions and datatypes +- Typeclasses +- The Plutus Tx Prelude +- Lifting values +- Plutus Tx Compiler Options +- GHC Extensions, Flags and Pragmas + - Extensions + - Flags + - Pragmas +- Alternatives to Plutus Tx + +Plutus applications are written as a single Haskell program, which describes both the code that runs off the chain (on a user's computer, or in their wallet, for example), and on the chain as part of transaction validation. + +The parts of the program that describe the on-chain code are still just Haskell, but they are compiled into `Plutus Core`{.interpreted-text role="term"}, rather than into the normal compilation target language. +We refer to them as `Plutus Tx`{.interpreted-text role="term"} programs (where 'Tx' indicates that these components usually go into transactions). + +> **Warning:** +> +> Strictly speaking, while the majority of simple Haskell will work, only a subset of Haskell is supported by the Plutus Tx compiler. +> The Plutus Tx compiler will tell you if you are attempting to use an unsupported component. + +The key technique that we use to implement Plutus Tx is called *staged metaprogramming*, which means that the main Haskell program generates *another* program (in this case, the Plutus Core program that will run on the blockchain). +Plutus Tx is the mechanism we use to write those programs, but since Plutus Tx is just part of the main Haskell program, we can share types and definitions between the two. + +## Template Haskell preliminaries + +Plutus Tx uses Haskell's metaprogramming support, Template Haskell, for two main reasons: + +- Template Haskell enables us to work at compile time, which is when we do Plutus Tx compilation. +- It allows us to wire up the machinery that invokes the Plutus Tx compiler. + +### Simple pattern + +Template Haskell is very versatile, but we only use a few features. +Essentially, we often use the same simple pattern: + +- make a quote, +- immediately call `PlutusTx.TH.compile`{.interpreted-text role="hsobj"}, and then +- splice the result back in. + +### Quotes + +Template Haskell begins with *quotes*. A Template Haskell quote is a Haskell expression `e` inside special brackets `[|| e ||]`. +It has type `Q (TExp a)` where `e` has type `a`. +`TExp a` is a *representation* of an expression of type `a`; in other words, the syntax of the actual Haskell expression that was quoted. +The quote lives in the type `Q` of quotes, which isn't very interesting for us. + +> **NOTE:** +> +> There is also an abbreviation `TExpQ a` for `Q (TExp a)`, which avoids some parentheses. + +### Splicing quotes + +You can *splice* a quote into your program using the `$$` operator. +This inserts the syntax represented by the quote into the program at the point where the splice is written. + +Simply put, a quote allows us to talk about Haskell programs as *values*. + +The Plutus Tx compiler compiles Haskell *expressions* (not values), so naturally it takes a quote (representing an expression) as an argument. +The result is a new quote, this time for a Haskell program that represents the *compiled* program. +In Haskell, the type of `PlutusTx.TH.compile`{.interpreted-text role="hsobj"} is `TExpQ a → TExpQ (CompiledCode a)`. +This is just what we already said: + +- `TExpQ a` is a quote representing a program of type `a`. +- `TExpQ (CompiledCode a)` is a quote representing a compiled Plutus Core program. + +> **NOTE:** +> +> `PlutusTx.CompiledCode`{.interpreted-text role="hsobj"} also has a type parameter `a`, which corresponds to the type of the original expression. +> +> This lets us "remember" the type of the original Haskell program we compiled. + +Since `PlutusTx.TH.compile`{.interpreted-text role="hsobj"} produces a quote, to use the result we need to splice it back into our program. +The Plutus Tx compiler runs when compiling the main program, and the compiled program will be inserted into the main program. + +## Writing basic Plutus Tx programs {#writing_basic_plutustx_programs} + +::: {.literalinclude start-after="BLOCK1" end-before="BLOCK2"} +BasicPlutusTx.hs +::: + +This simple program just evaluates to the integer `1`. + +> **NOTE:** +> +> The examples that show the Plutus Core generated from compilation include doctests. +> The syntax of Plutus Core might look unfamiliar, since this syntax is used for the 'assembly language', which means you don't need to inspect the compiler's output. +> But for the purpose of this tutorial, it is useful to understand what is happening. + +::: {.literalinclude start-after="BLOCK2" end-before="BLOCK3"} +BasicPlutusTx.hs +::: + +We can see how the metaprogramming works: the Haskell program `1` was turned into a `CompiledCode Integer` at compile time, which we spliced into our Haskell program. +We can inspect the generated program at runtime to see the generated Plutus Core (or to put it on the blockchain). + +### Plutus Tx standard usage pattern (how all of our Plutus Tx programs are written) + +We also see the standard usage pattern: a TH quote, wrapped in a call to `PlutusTx.TH.compile`{.interpreted-text role="hsobj"}, wrapped in a `$$` splice. +This is how all our Plutus Tx programs are written. + +The following is a slightly more complex program. +It includes the identity function on integers. + +::: {.literalinclude start-after="BLOCK3" end-before="BLOCK4"} +BasicPlutusTx.hs +::: + +## Functions and datatypes {#functions_and_datatypes} + +You can use functions inside your expression. +In practice, you will usually want to define the entirety of your Plutus Tx program as a definition outside the quote, and then simply call it inside the quote. + +::: {.literalinclude start-after="BLOCK4" end-before="BLOCK5"} +BasicPlutusTx.hs +::: + +We can use normal Haskell datatypes and pattern matching freely: + +::: {.literalinclude start-after="BLOCK5" end-before="BLOCK6"} +BasicPlutusTx.hs +::: + +Unlike functions, datatypes do not need any kind of special annotation to be used inside a quote, hence we can use types like `Maybe` from the Haskell `Prelude`. +This works for your own datatypes too. + +### Example + +Here's a small example with a datatype representing a potentially open-ended end date. + +::: {.literalinclude start-after="BLOCK6" end-before="BLOCK7"} +BasicPlutusTx.hs +::: + +We could also have defined the `pastEnd` function as a separate `INLINABLE` binding and just referred to it in the quote, but in this case, it's small enough to just write in place. + +## Typeclasses + +So far we have used functions like `lessThanEqInteger` for comparing `Integer`s, which is much less convenient than `<` from the standard Haskell `Ord` typeclass. + +While Plutus Tx does support typeclasses, we cannot use many of the standard typeclasses, since we require their class methods to be `INLINABLE`, and the implementations for types such as `Integer` use the Plutus Tx built-ins. + +### Plutus Tx Prelude has redefined versions of many standard typeclasses + +Redefined versions of many standard typeclasses are available in the Plutus Tx Prelude. +As such, you should be able to use most typeclass functions in your Plutus Tx programs. + +For example, the following is a version of the `pastEnd` function using `<`. +This will be compiled to exactly the same code as the previous definition. + +::: {.literalinclude start-after="BLOCK7" end-before="BLOCK8"} +BasicPlutusTx.hs +::: + +## The Plutus Tx Prelude {#the_plutus_tx_prelude} + +The `PlutusTx.Prelude`{.interpreted-text role="hsmod"} module is a drop-in replacement for the normal Haskell Prelude, with some redefined functions and typeclasses that make it easier for the Plutus Tx compiler to handle (such as `INLINABLE`). + +Use the Plutus Tx Prelude for code that you expect to compile with the Plutus Tx compiler. +All the definitions in the Plutus Tx Prelude include working Haskell definitions, which means that you can use them in normal Haskell code too, although for normal Haskell code, the Haskell Prelude versions will probably perform better. + +To use the Plutus Tx Prelude, use the `NoImplicitPrelude` language pragma and import `PlutusTx.Prelude`{.interpreted-text role="hsmod"}. + +Plutus Tx includes some built-in types and functions for working with primitive data (integers and bytestrings), as well as a few special functions. +These types are also exported from the Plutus Tx Prelude. + +### Method for triggering a validation failure + +The `PlutusTx.Builtins.error`{.interpreted-text role="hsobj"} built-in deserves a special mention. +`PlutusTx.Builtins.error`{.interpreted-text role="hsobj"} causes the transaction to abort when it is evaluated, which is one way to trigger a validation failure. + +## Lifting values for generating code dynamically {#lifting_values} + +So far we've seen how to define pieces of code *statically* (when you *compile* your main Haskell program), but you might want to generate code *dynamically* (that is, when you *run* your main Haskell program). +For example, you might be writing the body of a transaction to initiate a crowdfunding smart contract, which would need to be parameterized by data determining the size of the goal, the campaign start and end times, and any additional data that may be required. + +We can do this in the same way that we parameterize code in functional programming: write the static code as a *function* and provide the argument later to configure it. + +In our case, there is a slight complication: we want to make the argument and apply the function to it at *runtime*. +Plutus Tx addresses this through *lifting*. +Lifting enables the use of the same types, both inside your Plutus Tx program *and* in the external code that uses it. + +> **NOTE:** +> +> In this context, *runtime* means the runtime of the main Haskell program, **not** when the Plutus Core runs on the chain. +> We want to configure our code when the main Haskell program runs, as that is when we will be getting user input. + +In this example, we add an add-one function. + +::: {.literalinclude start-after="BLOCK8" end-before="BLOCK9"} +BasicPlutusTx.hs +::: + +Now, suppose we want to apply this to `4` at runtime, giving us a program that computes to `5`. +We need to *lift* the argument (`4`) from Haskell to Plutus Core, and then we need to apply the function to it. + +::: {.literalinclude start-after="BLOCK9" end-before="BLOCK10"} +BasicPlutusTx.hs +::: + +We lifted the argument using the `PlutusTx.liftCode`{.interpreted-text role="hsobj"} function. +To use this, a type must have an instance of the `PlutusTx.Lift`{.interpreted-text role="hsobj"} class. +For your own datatypes you should generate these with the `PlutusTx.makeLift`{.interpreted-text role="hsobj"} TH function from `PlutusTx.Lift`{.interpreted-text role="hsmod"}. + +> **NOTE:** +> +> `PlutusTx.liftCode`{.interpreted-text role="hsobj"} is relatively unsafe because it ignores any errors that might occur from lifting something that might not be supported. +> There is a `PlutusTx.safeLiftCode`{.interpreted-text role="hsobj"} if you want to explicitly handle these occurrences. + +The combined program applies the original compiled lambda to the lifted value (notice that the lambda is a bit complicated now, since we have compiled the addition into a built-in). + +Here's an example with our custom datatype. The output is the encoded version of `False`. + +::: {.literalinclude start-after="BLOCK10" end-before="BLOCK11"} +BasicPlutusTx.hs +::: + +## Reference: Plutus Tx Compiler Options + +A number of options can be passed to the Plutus Tx compiler. +See `plutus_tx_options`{.interpreted-text role="ref"} for details. + +## GHC Extensions, Flags and Pragmas + +Plutus Tx is a subset of Haskell and is compiled to Untyped Plutus Core by the Plutus Tx compiler, a GHC (Glasgow Haskell Compiler) plugin. + +In order to ensure the success and correct compilation of Plutus Tx programs, all Plutus Tx modules (that is, Haskell modules that contain code to be compiled by the Plutus Tx compiler) should use the following GHC extensions, flags and pragmas. + +### Extensions + +Plutus Tx modules should use the `Strict` extension: : + + {-# LANGUAGE Strict #-} + +Unlike in Haskell, function applications in Plutus Tx are strict. +In other words, when evaluating `(\x -> 42) (3 + 4)` the expression `3 + 4` is evaluated first, before evaluating the function body (`42`), even though `x` is not used in the function body. +The `Strict` extension ensures that let bindings and patterns are also (by default) strict, for instance, evaluating `let x = 3 + 4 in 42` evaluates `3 + 4` first, even though `x` is not used. + +Bang patterns and lazy patterns can be used to explicitly specify whether a let binding is strict or non-strict, as in `let !x = 3 + 4 in 42` (strict) and `let ~x = 3 + 4 in 42` (non-strict). +At this time, it is not possible to make function applications non-strict: `(\(~x) -> 42) (3 + 4)` still evaluates `3 + 4` strictly. + +Making let bindings strict by default has the following advantages: + +- It makes let bindings and function applications semantically equivalent. For example, `let x = 3 + 4 in 42` has the same semantics as `(\x -> 42) (3 + 4)`. +This is what one would come to expect, as it is the case in most other programming languages, regardless of whether the language is strict or non-strict. +- Untyped Plutus Core programs, which are compiled from Plutus Tx, are not evaluated lazily (unlike Haskell), that is, there is no memoization of the results of evaluated expressions. +Thus using non-strict bindings can cause an expression to be inadvertently evaluated for an unbounded number of times. +Consider `let x = in \y -> x + y`. +If `x` is non-strict, `` will be evalutated every time `\y -> x + y` is applied to an argument, which means it can be evaluated 0 times, 1 time, 2 times, or any number of times (this is not the case if lazy evaluation was employed). +On the other hand, if `x` is strict, it is always evaluated once, which is at most one more time than what is necessary. + +### Flags + +GHC has a variety of optimization flags, many of which are on by default. +Although Plutus Tx is, syntactically, a subset of Haskell, it has different semantics and a different evaluation strategy (Haskell: non-strict semantics, call by need; Plutus Tx: strict semantics, call by value). As a result, some GHC optimizations are not helpful for Plutus Tx programs, and can even be harmful, in the sense that it can make Plutus Tx programs less efficient, or fail to be compiled. +An example is the full laziness optimization, controlled by GHC flag `-ffull-laziness`, which floats let bindings out of lambdas whenever possible. +Since Untyped Plutus Core does not employ lazy evaluation, the full laziness optimization is usually not beneficial, and can sometimes make a Plutus Tx program more expensive. +Conversely, some GHC features must be turned on in order to ensure Plutus Tx programs are compiled successfully. + +All Plutus Tx modules should use the following GHC flags: + + -fno-ignore-interface-pragmas + -fno-omit-interface-pragmas + -fno-full-laziness + -fno-spec-constr + -fno-specialise + -fno-strictness + -fno-unbox-strict-fields + -fno-unbox-small-strict-fields + +`-fno-ignore-interface-pragmas` and `-fno-omit-interface-pragmas` ensure unfoldings of Plutus Tx functions are available. +The rest are GHC optimizations that are generally bad for Plutus Tx, and should thus be turned off. + +These flags can be specified either in a Haskell module, for example: + + {-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} + +or in a build file. +For example, if your project is built using Cabal, you can add the flags to the `.cabal` files, like so: + +> ghc-options: +> +> : -fno-ignore-interface-pragmas + +Note that this section only covers GHC flags, not Plutus Tx compiler flags. +Information about the latter can be found in Reference > Writing scripts > Plutus Tx Compiler Options: `plutus_tx_options`{.interpreted-text role="ref"}. + +### Pragmas + +All functions and methods should have the `INLINEABLE` pragma, so that their unfoldings are made available to the Plutus Tx compiler. + +The `-fexpose-all-unfoldings` flag also makes GHC expose all unfoldings, but unfoldings exposed this way can be more optimized than unfoldings exposed via `INLINEABLE`. +In general, we do not want GHC to perform optimizations, since GHC optimizes a program based on the assumption that it has non-strict semantics and is evaluated lazily (call by need), which is not true for Plutus Tx programs. +Therefore, `INLINEABLE` is preferred over `-fexpose-all-unfoldings`, even though the latter is simpler. + +`-fexpose-all-unfoldings` can be useful for functions that are generated by GHC and do not have the `INLINEABLE` pragma. +`-fspecialise` and `-fspec-constr` are two examples of optimizations that can generate such functions. +The most reliable solution, however, is to simply turn these optimizations off. +Another option is to bump `-funfolding-creation-threshold` to make it more likely for GHC to retain unfoldings for functions without the `INLINEABLE` pragma. +`-fexpose-all-unfoldings` should be used as a last resort. + +## Alternatives to Plutus Tx + +There are languages other than Plutus Tx that can be compiled into Plutus Core. +We list some of them here for reference. +However, we are not endorsing them; we are not representing their qualities nor their state of development regarding their production-readiness. + +- [Aiken](https://github.com/txpipe/aiken/) +- [Hebi](https://github.com/OpShin/hebi) +- [Helios](https://github.com/hyperion-bt/helios) +- [OpShin](https://github.com/OpShin/opshin) +- [plu-ts](https://github.com/HarmonicLabs/plu-ts) +- [Plutarch](https://github.com/Plutonomicon/plutarch-core) +- [Pluto](https://github.com/Plutonomicon/pluto) diff --git a/doc/docs-revamp/060_working-with-scripts.md b/doc/docs-revamp/060_working-with-scripts.md new file mode 100644 index 00000000000..4b56255602c --- /dev/null +++ b/doc/docs-revamp/060_working-with-scripts.md @@ -0,0 +1,293 @@ +--- +title: "Working with scripts" +description: "writing validator scripts, writing minting policies, creating and submitting transactions using an off-chain framework, libraries for writing Plutux Tx scripts, exporting scripts datums and redeemers, profiling the budget usage of Plutus scripts" +date: 2024-04-03 +--- + +# Section 6. Working with scripts + +- Writing basic validator scripts + - Validator arguments + - The Data type + - Signaling failure + - Validator functions + - Plutus script context versions +- Writing basic minting policies + - Minting policy arguments + - Plutus script context versions + - Writing minting policies + - Other policy examples +- Creating and submitting transactions using an off-chain framework +- Libraries for writing Plutus Tx scripts +- Exporting scripts, datums and redeemers +- Profiling the budget usage of Plutus scripts + - Compiling a script for profiling + - Acquiring an executable script + - Running the script + - Analyzing the results + +# Writing basic validator scripts + +`Validator scripts`{.interpreted-text role="term"} are the programs that can be used to lock transaction outputs on the chain. +Validator scripts are `Plutus Core`{.interpreted-text role="term"} programs, but we can use `Plutus Tx`{.interpreted-text role="term"} to write them easily in Haskell. +Check out the `Plutus Tx tutorial`{.interpreted-text role="ref"} before reading this one. + +## Validator arguments + +Validators receive some information from the validating node: + +- The `redeemer`{.interpreted-text role="term"}, which is some script-specific data specified by the party spending the output. +- The `datum`{.interpreted-text role="term"}, which is some script-specific data specified by the party who created the output. +- The `script context`{.interpreted-text role="term"}, which contains a representation of the spending transaction, as well as the index of the input whose validator is currently being run. + +The validator is a function which receives these three inputs as *arguments*. The validating node is responsible for passing them in and running the validator. + +## The `Data` type + +But how are the validator's arguments passed? +Different scripts are going to expect different sorts of values in their datums and redeemers. + +The answer is that we pass the arguments as a *generic* structured data type `PlutusCore.Data.Data`{.interpreted-text role="hsobj"}. +`Data` is designed to make it easy to encode structured data into it, and is essentially a subset of CBOR. + +Validator scripts take three arguments of type `Data`. +Since `Data` is represented as a builtin type in Plutus Core, we use a special Haskell type `BuiltinData` rather than the underlying `Data` type. + +However, you will typically not want to use `BuiltinData` directly in your program, rather you will want to use your own datatypes. +We can easily convert to and from `BuiltinData` with the `PlutusTx.IsData.Class.ToData`{.interpreted-text role="hsobj"}, +`PlutusTx.IsData.Class.FromData`{.interpreted-text role="hsobj"}, and `PlutusTx.IsData.Class.UnsafeFromData`{.interpreted-text role="hsobj"} typeclasses. +You usually don't need to write your own instances of these classes. +Instead, you can use the `unstableMakeIsData` or `makeIsDataIndexed` Template Haskell functions to generate one. + +> **NOTE** +> +> The `PlutusTx.IsData.Class.UnsafeFromData`{.interpreted-text role="hsobj"} class provides `unsafeFromBuiltinData`, which is the same as `fromBuiltinData`, but is faster and fails with `error` rather than returning a `Maybe`. +> We'll use `unsafeFromBuiltinData` in this tutorial, but sometimes the other version is useful. + +::: {.literalinclude start-after="BLOCK1" end-before="BLOCK2"} +BasicValidators.hs +::: + +## Signaling failure + +The most important thing that a validator can do is *fail*. +This indicates that the attempt to spend the output is invalid and that transaction validation should fail. +A validator succeeds if it does not explicitly fail. +The actual value returned by the validator is irrelevant. + +How does a validator fail? +It does so by using the `PlutusTx.Builtins.error`{.interpreted-text role="hsobj"} builtin. +Some other builtins may also trigger failure if they are used incorrectly (for example, `1/0`). + +## Validator functions + +We write validator scripts as Haskell functions, which we compile with Plutus Tx into Plutus Core. +The type of a validator function is `BuiltinData -> BuiltinData -> BuiltinData -> ()`, that is, a function which takes three arguments of type `BuiltinData`, and returns a value of type `()` ("unit" or "the empty tuple" -- since the return type doesn't matter we just pick something trivial). + +Here are two examples of simple validators that always succeed and always fail, respectively: + +::: {.literalinclude start-after="BLOCK2" end-before="BLOCK3"} +BasicValidators.hs +::: + +If we want to write a validator that uses types other than `BuiltinData`, we'll need to use the functions from `PlutusTx.IsData.Class.FromData`{.interpreted-text role="hsobj"} to decode them. +Importantly, `unsafeFromBuiltinData` can fail: in our example, if the `BuiltinData` in the second argument is *not* a correctly encoded `Date`, then it will fail the whole validation with `error`, which is usually what we want if we have bad arguments. + +> **Important** +> +> Unfortunately, there's no way to provide failure diagnostics when a validator fails on chain -- it just fails. +> However, since transaction validation is entirely deterministic, you'll always be informed of this before you submit the transaction to the chain, so you can debug it locally using `traceError`. + +Here's an example that uses our date types to check whether the date which was provided is less than the stored limit in the datum. + +::: {.literalinclude start-after="BLOCK3" end-before="BLOCK4"} +BasicValidators.hs +::: + +## Plutus script context versions + +Validators have access to the `script context`{.interpreted-text role="term"} as their third argument. +Each version of Plutus validators are differentiated only by their `ScriptContext` argument. + +> See this example from the file `MustSpendScriptOutput.hs` (lines 340 to 422) showing code addressing [Versioned Policies for both Plutus V1 and Plutus V2](https://github.com/IntersectMBO/plutus-apps/blob/05e394fb6188abbbe827ff8a51a24541a6386422/plutus-contract/test/Spec/TxConstraints/MustSpendScriptOutput.hs#L340-L422). + +The script context gives validators a great deal of power, because it allows them to inspect other inputs and outputs of the current transaction. +For example, here is a validator that will only accept the transaction if a particular payment is made as part of it. + +::: {.literalinclude start-after="BLOCK4" end-before="BLOCK5"} +BasicValidators.hs +::: + +This makes use of some useful functions for working with script contexts. + +# Writing basic minting policies + +`Minting policy scripts`{.interpreted-text role="term"} are the programs that can be used to control the minting of new assets on the chain. +Minting policy scripts are much like `validator scripts`{.interpreted-text role="term"}, and they are written similarly, so check out the `basic validators tutorial`{.interpreted-text role="ref"} before reading this one. + +## Minting policy arguments + +Minting policies, like validators, receive some information from the validating node: + +- The `redeemer`{.interpreted-text role="term"}, which is some script-specific data specified by the party performing the minting. +- The `script context`{.interpreted-text role="term"}, which contains a representation of the spending transaction, as well as the hash of the minting policy which is currently being run. + +The minting policy is a function which receives these two inputs as *arguments*. The validating node is responsible for passing them in and running the minting policy. +As with validator scripts, the arguments are passed encoded as `PlutusCore.Data.Data`{.interpreted-text role="hsobj"}. + +## Plutus script context versions + +Minting policies have access to the `script context`{.interpreted-text role="term"} as their second argument. +Each version of Plutus minting policy scripts are differentiated only by their `ScriptContext` argument. + +> See this example from the file `MustSpendScriptOutput.hs` (lines 340 to 422) showing code addressing [Versioned Policies for both Plutus V1 and Plutus V2](https://github.com/IntersectMBO/plutus-apps/blob/05e394fb6188abbbe827ff8a51a24541a6386422/plutus-contract/test/Spec/TxConstraints/MustSpendScriptOutput.hs#L340-L422). + +Minting policies tend to be particularly interested in the `mint` field, since the point of a minting policy is to control which tokens are minted. + +It is also important for a minting policy to look at the tokens in the `mint` field that use its own currency symbol i.e. policy hash. +Note that checking only a specific token name is usually not correct. +The minting policy must check for correct minting (or lack there of) of all token names under its currency symbol. +This requires the policy to refer to its own hash --- fortunately this is provided for us in the script context of a minting policy. + +## Writing minting policies + +Here is an example that puts this together to make a simple policy that allows anyone to mint the token so long as they do it one token at a time. +To begin with, we'll write a version that works with structured types. + +::: {.literalinclude start-after="BLOCK1" end-before="BLOCK2"} +BasicPolicies.hs +::: + +However, scripts are actually given their arguments as type `Data`, and must signal failure with `error`, so we need to wrap up our typed version to use it on-chain. + +::: {.literalinclude start-after="BLOCK2" end-before="BLOCK3"} +BasicPolicies.hs +::: + +## Other policy examples + +Probably the simplest useful policy is one that requires a specific key to have signed the transaction in order to do any minting. +This gives the key holder total control over the supply, but this is often sufficient for asset types where there is a centralized authority. + +::: {.literalinclude start-after="BLOCK3" end-before="BLOCK4"} +BasicPolicies.hs +::: + +> **Note** +> +> We don't need to check that this transaction actually mints any of our asset type: the ledger rules ensure that the minting policy will only be run if some of that asset is being minted. + +# Creating and submitting transactions using an off-chain framework + + + +# Libraries for writing Plutus Tx scripts + +This auction example shows a relatively low-level way of writing scripts using Plutus Tx. +In practice, you may consider using a higher-level library that abstracts away some of the details. +For example, [`plutus-apps`](https://github.com/IntersectMBO/plutus-apps) provides a constraint library for writing Plutus Tx. +Using these libraries, writing a validator in Plutus Tx becomes a matter of defining state transactions and the corresponding constraints. For example, the condition ``refundsPreviousHighestBid`` can simply be written as ``Constraints.mustPayToPubKey bidder (lovelaceValue amt)``. + +# Exporting scripts, datums and redeemers + +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. +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. +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. +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`. + +::: {.literalinclude start-after="BLOCK5" end-before="BLOCK6"} +BasicValidators.hs +::: + +`CompiledCode` has a different serialization method, `Flat`, but the principle is the same. + +The serialized form of `CompiledCode` can also be dumped using a plugin option: + +``` haskell +{-# 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. +You can then move it to a more permanent location. + +It can be read in conveniently with `loadFromFile` as an alternative to `compile`. + +::: {.literalinclude start-after="BLOCK6" end-before="BLOCK7"} +BasicValidators.hs +::: + +# Profiling the budget usage of Plutus scripts + +Figuring out why your script takes more CPU or memory units than you expect can be tricky. +You can find out more detail about how these resources are being used in your script by *profiling* it, and viewing the results in a flamegraph. + +## Compiling a script for profiling + +Profiling requires compiling your script differently so that it will emit information that we can use to analyse its performance. + +> **Note** +> +> As with profiling in other languages, this additional instrumentation can affect how your program is optimized, so its behaviour may not be identical to the non-profiled version. + +To do this, you need to give a couple of options to the Plutus Tx plugin in the source file where your script is compiled. + +``` haskell +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:profile-all #-} +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:conservative-optimisation #-} +``` + +This instructs the plugin to insert profiling instrumentation for all functions. +In the future there may be the option to profile a more targeted set of functions. +It also makes sure that any inserted profiling instrumentation code would not be optimized away during PlutusTx compilation. + +## Acquiring an executable script + +Profiling works by seeing how the budget is used as the script runs. +It therefore requires an executable script, which means that you need not only the validator script but all the arguments it receives. +You can get this fully-applied script from the emulator or from the Cardano node. + +## Running the script + +You can run the script with the `uplc` executable. + +> **Note** +> +> All the executables referred to here can be built from the `plutus` repository using `cabal build`. + +``` bash +uplc evaluate -i myscript.flat --if flat --trace-mode LogsWithBudgets -o logs +``` + +This runs the script using the trace mode that emits budget information, and puts the resulting logs in a file. +This will be a CSV file with three columns: a message indicating which function we are entering or exiting; the cumulative CPU used at that time; and the cumulative memory used at that time. + +## Analyzing the results + +We can then convert the logs into a flamegraph using the standard `flamegraph.pl` tool. +The `traceToStacks` executable turns the logs into a format that `flamegraph.pl` understands + +``` bash +cat logs | traceToStacks | flamegraph.pl > cpu.svg +cat logs | traceToStacks --column 2 | flamegraph.pl > mem.svg +``` + +Since `flamegraph.pl` can only handle one metric at a time, `traceToStacks` has a `--column` argument to select the other column if you want to get a memory flamegraph. + +You can then view the resulting SVGs in a viewer of your choice, such as a web browser. + +Alternatively, there are other, more powerful, tools that understand the format produced by `traceToStacks`, such as [speedscope](https://www.speedscope.app/). diff --git a/doc/docs-revamp/070_working-with-plutus-core.md b/doc/docs-revamp/070_working-with-plutus-core.md new file mode 100644 index 00000000000..4e815bf6088 --- /dev/null +++ b/doc/docs-revamp/070_working-with-plutus-core.md @@ -0,0 +1,55 @@ +--- +title: "Working with Plutus Core" +description: "Draft outline for review and comments" +date: 2024-04-04 +--- + +# Section 7. Working with Plutus Core + +## Introduction to Plutus Core + +Plutus Core is the low-level language that runs on the Cardano blockchain. +It is the language that the Cardano blockchain's ledger understands and executes. + +Plutus Core is designed for simplicity, determinism, and to allow careful cost control of program execution. +Using the lambda calculus makes it an easy compilation target for functional programming languages, and allows us to have a simple, formally verified evaluator. + +Plutus Core is: +- a minimal, typed, and functional programming language +- the programming language in which scripts on the Cardano blockchain are written +- not read or written by humans +- a variant of the lambda calculus, a well-studied formalism for computing +- a compilation target for higher-level languages, such as Plutus Tx, [Aiken](https://aiken-lang.org/), [OpShin](https://github.com/OpShin/opshin) and others + +## Plutus Core Syntax and Semantics +- The basic constructs of Plutus Core, such as variables, functions, and applications, as well as the type system that includes types and kinds +- Syntax and semantics of Plutus Core +- Understanding data types used in Plutus Core and how they relate to the types in Haskell + +## Compilation Process +- Understanding how high-level Plutus Tx code is compiled down to Plutus Core, including the role of the Plutus compiler and the abstract syntax tree (AST) +- Understanding the compilation target and execution environment of your Plutus Tx code + +## Execution Model +- Understanding how your contracts will execute on-chain +- The low-level execution model of Plutus Core +- The cost model for computing resource usage + +## Built-in Functions +- Exploring the built-in functions and types provided by Plutus Core that are essential for contract execution. + +## Formal Specification of Plutus Core +- The formal [Plutus Core Specification](https://ci.iog.io/job/input-output-hk-plutus/master/x86_64-linux.packages.plutus-core-spec/latest/download/1) for understanding the precise behavior of the Plutus Core language. + +## Security Considerations +- The security aspects of smart contract development, including auditing Plutus Core code for safety and correctness. + +## Interacting with the Extended UTXO Model (EUTXO) +- Understanding how Plutus Core interacts with the ledger and the EUTXO model specific to Cardano. + +## Advanced Topics +- Optimizations, bytecode generation, and other advanced features of Plutus Core for those who want to understand the language at a deeper level. + +## Tools and Resources +- The tools available for Plutus Core development, such as decompilers, pretty-printers, and debuggers. +- Troubleshooting diff --git a/doc/docs-revamp/080_adr-index.md b/doc/docs-revamp/080_adr-index.md new file mode 100644 index 00000000000..616d3865ca7 --- /dev/null +++ b/doc/docs-revamp/080_adr-index.md @@ -0,0 +1,27 @@ +--- +title: "Architectural decision records" +description: "Draft outline for review and comments" +date: 2024-04-11 +--- + +# Architectural decision records + +We document our architectural and design decisions for all of our components. +In order to do that, there is practice called architectural decision records ("ADR"), that we can integrate into our workflow. +An ADR is a document that captures an important architectural decision made along with its context and consequences. + +The goals are: + +- Making decisions transparent to internal/external stakeholders and contributors. +- Getting feedback on decisions that we're about to make or have made. +- Providing external contributors a framework to propose architectural changes. +- Providing a big picture of all major decisions that were made. + +The general process for creating an ADR is: + +1. Cloning the repository. +2. Creating a new file with the format: + [-.md]{.title-ref} in the directory [doc/adr]{.title-ref} +3. Adding the ADR in the table of content tree of the documentation. +4. Committing and pushing to the repository. + diff --git a/doc/docs-revamp/081_adr1.md b/doc/docs-revamp/081_adr1.md new file mode 100644 index 00000000000..ce40443bb54 --- /dev/null +++ b/doc/docs-revamp/081_adr1.md @@ -0,0 +1,172 @@ +--- +title: "ADR 1: Record architectural decisions" +description: "Draft outline for review and comments" +date: 2024-04-11 +--- + +# ADR 1: Record architectural decisions + +Date: 2022-06-08 + +## Authors + +koslambrou <<konstantinos.lambrou@iohk.io>> + +## Status + +Accepted + +## Context + +We are in search for a means to document our architectural and design decisions for all of our components. +In order to do that, there is a practice called architectural decision records ("ADR"), that we can +integrate into our workflow. + +This does not replace actual architecture documentation, but provides people who are contributing: + +- the means to understand architectural and design decisions that were made +- a framework for proposing changes to the current architecture + +For each decision, it is important to consider the following factors: + +- what we have decided to do +- why we have made this decision +- what we expect the impact of this decision to be +- what we have learned in the process + +## Decision + +- We will use ADRs to document, propose and discuss any important or significant architectural and design decisions. +- The ADR format will follow the format described in [Implications](#implications) section. +- We will follow the convention of storing those ADRs as rST or Markdown formatted documents stored under the [docs/adr]{.title-ref} directory, as exemplified in Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools). +This does not imply that we will be using [adr-tools]{.title-ref} itself, as we might diverge from the proposed structure. +- We will keep rejected ADRs +- We will strive, if possible, to create an ADR as early as possible in relation to the actual implementation. + +## Implications + +ADRs should be written using the template described in the [ADR template](#adr-template) which comes from Chapter 6.5.2 (*A Template for Documenting Architectural Decisions*) of *Documenting Software Architectures: Views and Beyond (2nd Edition)*. + +However, the mandatory sections are *Title*, *Status*, *Issue/Context*, *Decision*, *Implications/Consequences*. The rest are optional. + +Another good reference is the article [Architecture Decision Records](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) by Michael Nygard (Nov. 15, 2011). + +### ADR template + +What follows is the ADR format (adapted from the book). + ++---------------+------------------------------------------------------+ +| Section | Description | ++===============+======================================================+ +| Title | These documents have names that are short noun | +| | phrases. | +| | | +| | For example, "ADR 1: Deployment on Ruby on Rails | +| | 3.0.10" or "ADR 9: LDAP for Multitenant | +| | Integration" | ++---------------+------------------------------------------------------+ +| Authors | List each author's name and email. | ++---------------+------------------------------------------------------+ +| Status | State the status of the decision, such as "draft" | +| | if the decision is still being written, as | +| | "proposed" if the project stakeholders haven't | +| | agreed with it yet, "accepted" once it is agreed. | +| | If a later ADR changes or reverses a decision, it | +| | may be marked as "deprecated" or "superseded" | +| | with a reference to its replacement. (This is not | +| | the status of implementing the decision.) | ++---------------+------------------------------------------------------+ +| Issue (or | This section describes the architectural design | +| context) | issue being addressed. This description should leave | +| | no questions as to why this issue needs to be | +| | addressed now. The language in this section is | +| | value-neutral. It is simply describing facts. | ++---------------+------------------------------------------------------+ +| Decision | Clearly state the solution chosen. It is the | +| | selection of one of the positions that the architect | +| | could have taken. It is stated in full sentences, | +| | with active voice. "We will ..." | ++---------------+------------------------------------------------------+ +| Tags | Add one or more tags to the decision. Useful for | +| | organizing the set of decision. | ++---------------+------------------------------------------------------+ +| Assumptions | Clearly describe the underlying assumptions in the | +| | environment in which a decision is being made. These | +| | could be cost, schedule, technology, and so on. Note | +| | that constraints in the environment (such as a list | +| | of accepted technology standards, an enterprise | +| | architecture, or commonly employed patterns) may | +| | limit the set of alternatives considered. | ++---------------+------------------------------------------------------+ +| Argument | Outline why a position was selected. This is | +| | probably as important as the decision itself. The | +| | argument for a decision can include items such as | +| | implementation cost, total cost of ownership, time | +| | to market, and availability of required development | +| | resources. | ++---------------+------------------------------------------------------+ +| Alternatives | List alternatives (that is, options or positions) | +| | considered. | +| | | +| | Explain alternatives with sufficient detail to judge | +| | their suitability; refer to external documentation | +| | to do so if necessary. Only viable positions should | +| | be described here. While you don't need an | +| | exhaustive list, you also don't want to hear the | +| | question "Did you think about... ?" during a final | +| | review, which might lead to a loss of credibility | +| | and a questioning of other architectural decisions. | +| | Listing alternatives espoused by others also helps | +| | them know that their opinions were heard. Finally, | +| | listing alternatives helps the architect make the | +| | right decision, because listing alternatives cannot | +| | be done unless those alternatives were given due | +| | consideration. | ++---------------+------------------------------------------------------+ +| Implications | Describe the decision's implications. For example, | +| (or | it may | +| consequences) | | +| | > - Introduce a need to make other decisions | +| | > - Create new requirements | +| | > - Modify existing requirements | +| | > - Pose additional constraints to the environment | +| | > - Require renegotiation of scope | +| | > - Require renegotiation of the schedule with the | +| | > customers | +| | > - Require additional training for the staff | +| | | +| | Clearly understanding and stating the implications | +| | of the decisions has been a very effective tool in | +| | gaining buy-in. All consequences should be listed | +| | here, not just the "positive" ones. A particular | +| | decision may have positive, negative, and neutral | +| | consequences, but all of them affect the team and | +| | project in the future. | ++---------------+------------------------------------------------------+ +| Related | List decisions related to this one. Useful relations | +| Decisions | among decisions include causality (which decisions | +| | caused other ones), structure (showing decisions' | +| | parents or children, corresponding to architecture | +| | elements at higher or lower levels), or temporality | +| | (which decisions came before or after others). | ++---------------+------------------------------------------------------+ +| Related | Map decisions to objectives or requirements, to show | +| Requirements | accountability. Each architecture decision is | +| | assessed as to its contribution to each major | +| | objective. We can then assess how well the objective | +| | is met across all decisions, as part of an overall | +| | architecture evaluation. | ++---------------+------------------------------------------------------+ +| Affected | List the architecture elements and/or relations | +| Artifacts | affected by this decision. You might also list the | +| | effects on other design or scope decisions, pointing | +| | to the documents where those decisions are | +| | described. You might also include external artifacts | +| | upstream and downstream of the architecture, as well | +| | as management artifacts such as budgets and | +| | schedules. | ++---------------+------------------------------------------------------+ +| Notes | Capture notes and issues that are discussed during | +| | the decision process. They can be links to a | +| | external document, a PR, a Github issue, etc. | ++---------------+------------------------------------------------------+ diff --git a/doc/docs-revamp/082_adr2.md b/doc/docs-revamp/082_adr2.md new file mode 100644 index 00000000000..bd73625a330 --- /dev/null +++ b/doc/docs-revamp/082_adr2.md @@ -0,0 +1,216 @@ +--- +title: "ADR 2: Steppable CEK machine" +description: "Draft outline for review and comments" +date: 2024-04-11 +--- + +# ADR 2: Steppable CEK machine + +Date: 2022-10 + +## Authors + +Marty Stumpf <marty.stumpf@iohk.io> +Ziyang Liu <ziyang.liu@iohk.io> + +## Status + +Proposed + +## Context + +In order to have a minimal viable product of a debugger for Plutus, we need a CEK machine that will give us more information for debugging than our current one. + +In order to provide debugging information for each evaluation step, we need a steppable CEK machine. +Implementing the steppable CEK machine is a non-trivial task and involves some design decisions. +One decision to make is about whether we can share the code between the production and the debugging machine. +That is not the scope of this ADR. +See the next ADR for that. + +This ADR proposes a design for an implementation of a steppable CEK machine. +Of course, this doesn't mean that this is the final decision. +This means that the next step for us is to prototype the machine in this way - which we have reasons to believe will go well. +We may adjust our proposed approach depending on how the prototyping goes. + +## Decision + +This section describes the proposed implementation of the debugging machine. + +We first **abstract out the computation to "steps"** on our current machine. +We then **implement a coroutine system** to add the debugging functionalities. + +### Abstracting out the computation to "steps" + +This abstraction has been implemented in [PR#4909](https://github.com/IntersectMBO/plutus/pull/4909/). + +The current machine inlined the steps. +We separate each steps into separate functions. +They all return a `CekState`: + +```haskell +data CekState uni fun = + -- the next state is computing + Computing WordArray (Context uni fun) (Closure uni fun) + -- the next state is returning + | Returning WordArray (Context uni fun) (CekValue uni fun) + -- evaluation finished + | Terminating (Term NamedDeBruijn uni fun ()) + +data Closure uni fun = + Closure (Term NamedDeBruijn uni fun ()) (CekValEnv uni fun) +``` + +The computing step is `computeCekStep` with the following signature: + +```haskell +computeCekStep + :: forall uni fun s + . (Ix fun, PrettyUni uni fun, GivenCekReqs uni fun s) + => WordArray + -> Context uni fun + -> Closure uni fun + -> CekM uni fun s (CekState uni fun) +``` + +Similarly for the returning step (`returnCekStep`). +Then we link up all the steps with `continue`, and the machine behaves very similar to our current one: + +```haskell +continue :: forall uni fun s + . (Ix fun, PrettyUni uni fun, GivenCekReqs uni fun s) + => CekState uni fun + -> CekM uni fun s (Term NamedDeBruijn uni fun ()) +continue (Computing !unbudgetedSteps ctx (Closure term env)) = do + state <- computeCekStep unbudgetedSteps ctx env term + continue state +continue (Returning !unbudgetedSteps ctx val) = do + state <- returnCekStep unbudgetedSteps ctx val + continue state +continue (Terminating term) = pure term +``` + +### Coroutines in Haskell + +The next step is to add debugging capabilities between each step. +To do so, we implement it as a *coroutine system*. +A detailed introduction to coroutines in Haskell can be found in [Coroutine Pipelines](https://themonadreader.files.wordpress.com/2011/10/issue19.pdf). +This section gives a brief summary. + +A coroutine system is composed of multiple computations cooperatively passing data and control to one another. +In this instance, one computation is the user issuing commands like "step forward", and the other is the CEK machine processing the commands and performing actions like interpreting the script being debugged. +We'll refer to them as the "user computation" and the "machine computation" respectively. + +Coroutines in Haskell can be implemented using the free monad transformer, [FreeT](https://hackage.haskell.org/package/free/docs/Control-Monad-Trans-Free.html#t:FreeT). +The `Coroutine` type used in the above article is isomorphic to `FreeT`. + +To use `FreeT f m`, we need two things: a suspension functor `f`, and a base monad `m`. + +The suspension functor is a pattern functor that describes the ways the user computation can suspend and pass control to the machine computation. +Each constructor of the suspension functor should thus represent a user request, such as "step forward". +Constructors generally follow a `RequestType request (response -> a)` pattern. + +As an example, consider the following suspension functor (the `uni` and `fun` parameters are omitted for readability): + +```haskell +data RequestF a + = StepF CekState (CekState -> a) + | LogF Text a + | InputF (Command -> a) + deriving Functor +``` + +`StepF` passes a `CekState` to the machine computation and suspends, requesting the machine computation to progress one step, and send a `CekState` back. +`LogF` sends a `Text` to the machine computation (its response type is `()` and is omitted). +`InputF` requests a `Command` from the user. + +Note that this pattern is not limited to a single suspension functor and two computations. +Multiple suspension functors and computations can be composed using [coproducts](https://www.cambridge.org/core/services/aop-cambridge-core/content/view/14416CB20C4637164EA9F77097909409/S0956796808006758a.pdf/data-types-a-la-carte.pdf). + +The base monad `m` is the monad the machine computation runs in. +The machine computation interprets each request into an `m` action. +It is essentially a natural transformation from the suspension functor to `m`. +This `m` will replace our current monad `CekM`. +Although we can actually just use `CekM` in the steppable CEK machine when we add `IO` capabilities for debugging. +This is because we can convert it to/from `IO` via `unsafeSTToIO` and `unsafeIOToST`. + +Suppose we define a type `SteppableCekM a` as our base monad `m`. +Then the machine computation can be implemented as the following request handler function: + +```haskell +handle :: RequestF a -> SteppableCekM a +handle = \case + StepF state k -> step state >>= pure . k + LogF text k -> log text >> pure k + InputF k -> input >>= pure . k +``` + +where `step state`, `log text` and `input` return `SteppableCekM` actions. +`step` will likely correspond to `computeCekStep` and `returnCekStep` depending on the states. + +We can then use `handle` to construct a monad morphism, interpreting the user computation (a `FreeT` structure) into a `SteppableCekM` action: + +```haskell +runSteppableCek :: FreeT RequestF SteppableCekM a -> SteppableCekM a +runSteppableCek userAction = do + runFreeT userAction >>= \case + Pure res -> pure res + Free req -> handle req >>= runSteppableCek +``` + +To construct the user computation, `FreeT RequestF SteppableCekM`, we first provide helper functions for constructing `RequestF`s and lifting them into the `FreeT`: + +```haskell +stepF :: Monad m => CekState -> FreeT RequestF m CekState +stepF state = liftF (StepF state id) + +logF :: Monad m => Text -> FreeT RequestF m () +logF text = liftF (LogF text ()) + +inputF :: Monad m => FreeT RequestF m Command +inputF = liftF (InputF id) +``` + +Then we can implement the user computation like this: + +```haskell +userComputation :: CekState -> FreeT RequestF SteppableCekM () + userComputation currentState = do + cmd <- inputF + case cmd of + Step -> do + logF "Received Step command" + mState <- stepF currentState + userComputation mState + ... +``` + +We enter the debugging mode with the input UPLC program or term to debug with `enterDebug`: + +```haskell +enterDebug :: UPLCTerm -> FreeT RequestF SteppableCekM () +enterDebug termToDebug = do + state <- stepF (Computing (toWordArray 0) NoFrame (Closure term Env.empty)) + userComputation state + ... +``` + +### Argument: coroutine system + +Why a coroutine system? +In short, structuring the code this way will ease our future work. +Some of the advantages are mentioned above already. +Here is a summary: + +- The debugger is naturally a coroutine, where one routine is the user and the other is the CEK machine, and they take turns to suspend and pass data and control to each other in a debugging session. The literature has contributed a good way to design/implement a coroutine. It makes sense to implement a well studied design. +- We can probably reuse the same monad (`CekM`) in the steppable CEK machine, because we can convert it to/from `IO` via `unsafeSTToIO` and `unsafeIOToST`. +- It should be easier when we add more functionalities because multiple suspension functors and computations can be composed using [coproducts](https://www.cambridge.org/core/services/aop-cambridge-core/content/view/14416CB20C4637164EA9F77097909409/S0956796808006758a.pdf/data-types-a-la-carte.pdf). +- This should also play nicely when we implement Debug Adapter Protocol for the debugger later on. + +## Implications + +In summary, we proposed to implement the debugging machine as a coroutine system with "steps". +This implies that: + +- We have to maintain the CEK machine. E.g., we need to check its conformance. +- We will add a debugger for our users. We can give users more information at each evaluation step. +- We will need to write some tests to ensure that the debugging machine continuously output reasonable information. diff --git a/doc/docs-revamp/083_adr3.md b/doc/docs-revamp/083_adr3.md new file mode 100644 index 00000000000..99ce9d6484f --- /dev/null +++ b/doc/docs-revamp/083_adr3.md @@ -0,0 +1,140 @@ +--- +title: "ADR 3: Sharing code between the production and debugging CEK machine" +description: "Draft outline for review and comments" +date: 2024-04-11 +--- + +# ADR 3: Sharing code between the production and debugging CEK machine + +Date: 2022-10 + +## Authors + +Marty Stumpf <marty.stumpf@iohk.io> + +## Status + +Draft + +## Context + +In order to have a minimal viable product of a debugger for Plutus, we need a CEK machine that will give us more information for debugging than our current one. + +One of the first decision we need to make is: should the debugging machine be a separate one? +The debugging machine needs to satisfy these requirements: + +- We must not compromise the performance of the production machine, and +- The debugging machine must behave the same as the production machine. + +There are tradeoffs between these two requirements. +If we have a separate machine, the performance of the production machine will be untouched. +But there is more scope for us to make mistakes with the new machine. + +However, if we share code between the two machines, the performance of the production machine may be compromised. + +This ADR proposes an approach for the two machines to share code while not compromising performance. + +## Decision: Polymorphic compute/return steps + +As long as the debugging machine has the same type, we can alter `computeCek`/`returnCek` to be polymorphic over a type-level `Bool` specifying if we’re in debug mode or not. +Then we demote it to the term level in the definition of `computeCek`/`returnCek` and branch on the `Bool` thus implementing different logic depending on whether we're in debug mode or not. +This promotion to the type level allows us to statically instantiate the `Bool` in an instance and thus make GHC compile the whole worker of the CEK machine twice: once in debug mode and once in production mode. +Theoretically, GHC will take care to remove all the dead debug code when in production mode. + +### Detailed description with code snippets + +Whether we are debugging or not, we will be returning a `CekState`: + +```haskell +data CekState uni fun = + -- the next state is computing + Computing WordArray (Context uni fun) (Closure uni fun) + -- the next state is returning + | Returning WordArray (Context uni fun) (CekValue uni fun) + -- evaluation finished + | Terminating (Term NamedDeBruijn uni fun ()) + +data Closure uni fun = + Closure (Term NamedDeBruijn uni fun ()) (CekValEnv uni fun) +``` + +We enter either modes via `enterComputeCek`, which takes an extra `Bool` than our current implementation, to indicate whether we are in debugging mode or not: + +```haskell +enterComputeCek + :: forall uni fun s + . (Ix fun, PrettyUni uni fun, GivenCekReqs uni fun s) + => Bool + -> Context uni fun + -> Closure uni fun + -> CekM uni fun s (CekState uni fun) +enterComputeCek debug ctx (Closure term env) = + -- The initial step is always computing with zero budget, empty context and environment. + -- `computeCekStep` matches on the `term` and calls `computeCek` or `returnCek` depending on the clause. + computeCekStep (toWordArray 0) NoFrame (Closure term Env.empty) where + + computeCek + :: WordArray -- for costing + -> Context uni fun + -> Closure uni fun + -> CekM uni fun s (CekState uni fun) + computeCek = if debug then computeCekDebug else computeCekStep + {-# NOINLINE computeCek #-} -- Making sure the `if` is only evaluated once. + + -- in debugging mode, immediately returns the current `CekState` and halts execution. Debugging mode details to be worked out. + computeCekDebug + :: WordArray + -> Context uni fun + -> Closure uni fun + -> CekM uni fun s (CekState uni fun) + computeCekDebug budget ctx (Closure term env) = + pure $ Computing budget ctx (Closure term env) + + -- In production mode, `computeCekStep` matches on the term and calls `computeCek` or `returnCek` on a subterm. + -- In production mode, `computeCek` calls the original `computeCekStep`, i.e. in production mode `computeCekStep` calls itself through the thin `computeCek` wrapper thus achieving recursion and replicating the old behavior of the CEK machine. + computeCekStep + :: WordArray + -> Context uni fun + -> Closure uni fun + -> CekM uni fun s (CekState uni fun) -- the return type is `CekState` instead of a term. + computeCekStep unbudgetedSteps ctx (Closure (Force _ body) env) = do -- exactly like in current prod + !unbudgetedSteps' <- stepAndMaybeSpend BForce unbudgetedSteps -- update costs + computeCek unbudgetedSteps' (FrameForce ctx) (Closure body env) -- compute again with updated costs and ctx + <other_clauses> -- there's a lot of code in here! Some clauses call `returnCek`, some `computeCek`, achieving recursive calling similar to our current implementation. + + -- details of `forceEvaluate`, `applyEvaluate` etc to be worked out. + + -- similarly for the returning step + + returnCek = if debug then returnCekDebug else returnCekStep + {-# NOINLINE returnCek #-} + + returnCekDebug = ... + + + returnCekStep + :: forall uni fun s + . (PrettyUni uni fun, GivenCekReqs uni fun s) + => WordArray + -> Context uni fun + -> CekValue uni fun + -> CekM uni fun s (CekState uni fun) -- return a state instead of a term + returnCekStep !unbudgetedSteps NoFrame val = do + spendAccumulatedBudget unbudgetedSteps + pure $ Terminating $ dischargeCekValue val --wrap the term in the `Terminating` constructor when returning the term. + <other_clauses> +``` + +This trick lets us inline the "step" functions and call them recursively like before. +Because when we are not debugging, we are still using basically the same code as our current implementation, the performance should not be affected by much. +(Given that the machine is tail-recursive, the additional wrapping of the returned term in the `Terminating` constructor will affect performance in a negligible way.) + +## Implications + +This is a draft of an idea. +There are further details to be worked out in a prototype. +The implementor should use their own judgement. + +Whether we proceed with this approach or not depends on how the prototyping goes, and its benchmarking results. +If we find that the slow down is negligible enough, then we may proceed with this. +Otherwise, we proceed with a separate implementation for the debugging machine. diff --git a/doc/docs-revamp/084_adr4.md b/doc/docs-revamp/084_adr4.md new file mode 100644 index 00000000000..f5129667954 --- /dev/null +++ b/doc/docs-revamp/084_adr4.md @@ -0,0 +1,90 @@ +--- +title: "ADR 4: Deferred unlifting in Plutus Core" +description: "Draft outline for review and comments" +date: 2024-04-11 +--- + +# ADR 4: Deferred unlifting in Plutus Core + +Date: 2022-11 + +## Authors + +Michael Peyton Jones <michael.peyton-jones@iohk.io> + +## Status + +Accepted + +## Context + +A key part of the evaluation of builtin applications in Plutus Core is "unlifting". +Unlifting is the process of taking a Plutus Core term and turning it into a Haskell value of a known type. +For example, we can unlift an integer constant term into the actual Haskell integer it contains. +This is necessary in order to apply the denotation of the builtin being applied, since that is a Haskell function that operates on Haskell types (e.g. integer addition). + +However, unlifting can fail: we cannot unlift a _string_ constant into a Haskell integer. +This failure is visible in program execution, since it terminates the program with an error. + +The original design of the builtin application machinery performed unlifting of an argument as soon as it was received. +This meant that unlifting failures would surface at that point, whereas most of the errors that relate to builtin evaluation can only occur once the builtin has all its arguments, since that's when we run the actual function. + +For example: +``` +[(builtin addInteger) (con string "hello")] +``` +would fail (due to the unlifting failure), even though the builtin _never_ receives all its arguments and is never fully evaluated. + +The fact that unlifting errors occur early on makes the specification of the behaviour of builtins significantly more complex. +It would be simpler if unlifting errors occurred when the builtin has all its arguments. +We refer to these two alternatives as "immediate" unlifting (the status quo) and "deferred" unlifting. + +Deferred unlifting only makes evaluation slightly more lenient: some terms (such as the above example) do not give an error where they would do with immediate unlifting. + +## Decision + +We decided: +- To switch to deferred unlifting by default in protocol version 7 (Vasil). +- Having observed (after the hard fork) that no script evaluation in the history of the chain relied on immediate unlifting, to remove all support for immediate unlifting from the evaluator. + +## Argument + +The difference between immediate and deferred unlifting is only visible in quite specific circumstances. +Since builtins are _usually_ fully applied (otherwise they don't do anything), an unlifting error will usually be forced right away, regardless of whether we use immediate or deferred unlifting. +The only case where this is not true is where the builtin _never_ receives all its arguments, such as the example given above. +More generally, the only case where behaviour differs is _partially applied_ builtins which are applied to _ill-typed arguments_. +This is quite unusual, since users typically write programs that a) do something and b) are well-typed. + +Consequently we felt that it was safe to change the default unlifting behaviour. + +However, in order to gain the full benefit of simplification, we would like to remove the existence of immediate unlifting entirely. +If historical script evaluations on the chain still rely on immediate unlifting, then we must support it (and specify it) forever. +However, once the default has changed, if the history of the chain still validates with _deferred_ unlifting, then we know that no historical script evaluations relied on that behaviour. +At that point we can _unconditionally_ enable deferred unlifting without worrying about not being able to validate the chain. + +In theory, there could be outputs locked with script hashes whose behaviour would (if they are ever spent) rely on inmmediate unlifting. +We cannot rule this out, but given that it has never been relevant in the entire history of the chain, we considered this to be extremely unlikely. + +## Alternatives + +### 1. Status quo + +Undesirable, we face the complexity forever. + +### 2. Support both versions forever + +Arguably even worse than 1, in that we have to maintain and specify both versions forever, so our complexity burden is even greater. + +## Implications + +This has already been implemented, and the specification has been updated. +It has no further implications for other decisions that we know of. + +## Notes + +Relevant PRs: +- [Support for both versions of unlifting](https://github.com/IntersectMBO/plutus/pull/4516) +- [Choose the unlifting mode based on protocol version](https://github.com/IntersectMBO/plutus/pull/4522) +- [Remove immediate unlifting](https://github.com/IntersectMBO/plutus/pull/4879) +- [Mainnet script dump test](https://github.com/IntersectMBO/plutus/pull/4726) +- [Update PLC specification for deferred unlifting](https://github.com/IntersectMBO/plutus/pull/4960) diff --git a/doc/docs-revamp/090_reference.md b/doc/docs-revamp/090_reference.md new file mode 100644 index 00000000000..0e2d43b7f7e --- /dev/null +++ b/doc/docs-revamp/090_reference.md @@ -0,0 +1,553 @@ +--- +title: "Reference" +description: "TBD" +date: 2024-04-10 +--- + +# Section 8. Reference + +- Writing scripts + - Plutus Tx compiler options + - Optimization techniques for Plutus scripts + - Identifying problem areas + - Using strict let-bindings to avoid recomputation + - Specializing higher-order functions + - Common sub-expression elimination + - Using `error` for faster failure + - Common weaknesses + - Double satisfaction + - Hard limits +- Plutus on Cardano + - Plutus language changes + - Upgrading to Vasil and Plutus script addresses + - Cost model parameters +- Glossary +- Bibliography + +------- + +# Writing scripts + +## Plutus Tx Compiler Options + +These options can be passed to the compiler via the `OPTIONS_GHC` pragma, for instance + +``` haskell +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:dump-uplc #-} +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:max-simplifier-iterations=3 #-} +``` + +For each boolean option, you can add a `no-` prefix to switch it off, such as `no-typecheck`, `no-simplifier-beta`. + +| Option | Value Type | Default | Description | +|----------------------------------|---------------|---------|-------------| +| `conservative-optimisation` | Bool | False | When conservative optimisation is used, only the optimisations that never make the program worse (in terms of cost or size) are employed. Implies `no-relaxed-float-in`. | +| `context-level` | Int | 1 | Set context level for error messages. | +| `coverage-all` | Bool | False | Add all available coverage annotations in the trace output | +| `coverage-boolean` | Bool | False | Add boolean coverage annotations in the trace output | +| `coverage-location` | Bool | False | Add location coverage annotations in the trace output | +| `defer-errors` | Bool | False | If a compilation error happens and this option is turned on, the compilation error is suppressed and the original Haskell expression is replaced with a runtime-error expression. | +| `dump-compilation-trace` | Bool | False | Dump compilation trace for debugging | +| `dump-pir` | Bool | False | Dump Plutus IR | +| `dump-plc` | Bool | False | Dump Typed Plutus Core | +| `dump-uplc` | Bool | False | Dump Untyped Plutus Core | +| `max-cse-iterations` | Int | 4 | Set the max iterations for CSE | +| `max-simplifier-iterations-pir` | Int | 12 | Set the max iterations for the PIR simplifier | +| `max-simplifier-iterations-uplc` | Int | 12 | Set the max iterations for the UPLC simplifier | +| `optimize` | Bool | True | Run optimization passes such as simplification and floating let-bindings. | +| `pedantic` | Bool | False | Run type checker after each compilation pass | +| `profile-all` | ProfileOpts | None | Set profiling options to All, which adds tracing when entering and exiting a term. | +| `relaxed-float-in` | Bool | True | Use a more aggressive float-in pass, which often leads to reduced costs but may occasionally lead to slightly increased costs. | +| `remove-trace` | Bool | False | Eliminate calls to `trace` from Plutus Core | +| `simplifier-beta` | Bool | True | Run a simplification pass that performs beta transformations | +| `simplifier-inline` | Bool | True | Run a simplification pass that performs inlining | +| `simplifier-remove-dead-bindings`| Bool | True | Run a simplification pass that removes dead bindings | +| `simplifier-unwrap-cancel` | Bool | True | Run a simplification pass that cancels unwrap/wrap pairs | +| `strictify-bindings` | Bool | True | Run a simplification pass that makes bindings stricter | +| `target-version` | Version | 1.1.0 | The target Plutus Core language version | +| `typecheck` | Bool | True | Perform type checking during compilation. | +| `verbosity` | Verbosity | Quiet | Set logging verbosity level (0=Quiet, 1=Verbose, 2=Debug) | + + +## Optimization techniques for Plutus scripts + +### Identifying problem areas + +<!-- The link in this paragraph needs to become a cross-reference --> + +In order to identify which parts of the script are responsible for significant resource consumption, you can use the `profiling support <profiling_scripts>`{.interpreted-text role="ref"}. + +### Using strict let-bindings to avoid recomputation + +Let-bindings in Haskell are translated to strict let-bindings in Plutus IR, unless they look like they might do computation, in which case they are translated to non-strict let-bindings. +This is to avoid triggering effects (e.g. errors) at unexpected times. + +However, non-strict let-bindings are less efficient. +They do not evaluate their right-hand side immediately, instead they do so where the variable is used. +But they are not *lazy* (evaluating the right-hand side at most once), instead it may be evaluated once each time it is used. +You may wish to explicitly mark let-bindings as strict in Haskell to avoid this. + +``` haskell +-- This may be compiled non-strictly, which could result +-- in it being evaluated multiple times. However, it will +-- not be evaluated if we take the branch where it is not used. +let x = y + z +in if b then x + x else 1 + +-- This will be compiled strictly, but this will mean it +-- is evaluated even if we take the branch where it is not used. +let !x = y + z +in if b then x + x else 1 +``` + +### Specializing higher-order functions + +The use of higher-order functions is a common technique to facilitate code reuse. +Higher-order functions are widely used in the Plutus libraries but can be less efficient than specialized versions. + +For instance, the Plutus function `findOwnInput` makes use of the higher-order function `find` to search for the current script input. + +``` haskell +findOwnInput :: ScriptContext -> Maybe TxInInfo +findOwnInput ScriptContext{scriptContextTxInfo=TxInfo{txInfoInputs}, + scriptContextPurpose=Spending txOutRef} = + find (\TxInInfo{txInInfoOutRef} -> txInInfoOutRef == txOutRef) txInfoInputs +findOwnInput _ = Nothing +``` + +This can be rewritten with a recursive function specialized to the specific check in question. + +``` haskell +findOwnInput :: ScriptContext -> Maybe TxInInfo +findOwnInput ScriptContext{scriptContextTxInfo=TxInfo{txInfoInputs}, + scriptContextPurpose=Spending txOutRef} = go txInfoInputs + where + go [] = Nothing + go (i@TxInInfo{txInInfoOutRef} : rest) = if txInInfoOutRef == txOutRef + then Just i + else go rest +findOwnInput _ = Nothing +``` + +### Common sub-expression elimination + +When several instances of identical expressions exist within a function's body, it's worth replacing them with a single (strict) let-bound variable to hold the computed value. + +In this example, the cost of storing and retrieving `n * c` in a single variable is significantly less than recomputing it several times. + +``` haskell +let a' = a `divide` n * c + -- occurrence 1 + b' = b * (n * c) + -- occurrence 2 + C' = c + (n * c) +in + foo a' b' c' n + +-- Only one occurrence +let !t_mul = n * c + a' = a `divide` t_mul + b' = b * t_mul + c' = c + t_mul +in + foo a' b' c' n +``` + +### Using `error` for faster failure + +Plutus scripts have access to one impure effect, `error`, which immediately terminates the script evaluation and will fail validation. +This failure is very fast, but it is also unrecoverable, so only use it in cases where you want to fail the entire validation if there is a failure. + +The Plutus libraries have some functions that fail with `error`. +Usually these are given an `unsafe` prefix to their name. +For example, `PlutusTx.IsData.Class.FromData`{.interpreted-text role="hsobj"} parses a value of type `Data`, returning the result in a `Maybe` value to indicate whether it succeeded or failed; whereas `PlutusTx.IsData.Class.UnsafeFromData`{.interpreted-text role="hsobj"} does the same but fails with `error`. + +## Examples + +Full examples of Plutus Applications can be found in the `plutus-apps` [repository](https://github.com/IntersectMBO/plutus-apps/tree/master/plutus-use-cases). +The source code can be found in the `src` and the tests in the `test` folder. + +The examples are a mixture of simple examples and more complex ones, including: + +- A crowdfunding application +- A futures application +- A stablecoin +- A uniswap clone + +> **Important** +> +> Make sure to look at the same version of the [plutus-apps]{.title-ref} repository as you are using, to ensure that the examples work. + +## Common weaknesses + +This section provides a listing of common *weaknesses* in Plutus applications. "Weakness" is used in the sense of the [Common Weakness Enumeration](https://cwe.mitre.org/), as a potential source of vulnerabilities in applications. + +### Double satisfaction + +Suppose we have a validator V that implements a typical "atomic swap" or "escrowed swap" between A and B where A goes first, i.e. V says: + +> This output can only be spent if, in the same transaction, there is an output sending the agreed-upon payment (encoded in the output's datum) to A. + +Now suppose that A and B have two swaps in progress, one for a token T1 at the price of 10 Ada, and one for a token T2 at the same price. +That means that there will exist two outputs, both locked by V. + +Now B constructs a transaction which spends both outputs, and creates one output addressed to A with 10 Ada (taking T1 and T2 for himself). + +<figure> +<img src="double-satisfaction.png" alt="double-satisfaction.png" /> +<figcaption>A diagram showing the transaction setup for the double satisfaction of two swaps.</figcaption> +</figure> + +A naive implementation of V will just check that the transaction has *an* output to A with 10 Ada in it, and then be satisfied. +But this allows B to "double satisfy" the two validators, because they will both see the same output and be satisfied. +The end result is that B can get away with paying only 10 Ada to A, even though B's true liability to A is 20 Ada. + +**What is going wrong here?** + +It is difficult to say exactly what is going wrong here. +Neither validator's expectations are explicitly being violated. + +One way of looking at it is that this is a consequence of the fact that validators only *validate*, rather than *doing* things. +In a model like Ethereum's, where smart contracts *make transfers*, then two smart contracts would simply make two transfers, and there would be no problem. +But in the EUTXO model all a validator can do is try to ascertain whether its wishes have been carried out, which in this case is ambiguous. + +Following this metaphor, we can see how the same problem could arise in the real world. +Suppose that two tax auditors from two different departments come to visit you in turn to see if you've paid your taxes. +You come up with a clever scheme to confuse them. +Your tax liability to both departments is $10, so you make a single payment to the tax office's bank account for $10. +When the auditors arrive, you show them your books, containing the payment to the tax office. +They both leave satisfied. + +How do we solve this problem in the real world? +Well, the two tax offices might have different bank accounts, but more likely they would simply require you to use two different payment references. +That way, the payment that each auditor expect to see is unique, so they know it's for them. +We can do something similar in the EUTXO model. +See the section on [Unique outputs](#unique-outputs) below. + +**Risks** + +This is a serious problem for many kinds of application. +Any application that makes payments to specific parties needs to ensure that those payments are correctly identified and don't overlap with other payments. + +**Solutions** + +It's possible that a solution will be developed that makes this weakness easier to avoid. +In the mean time, there are workarounds that developers can use. + +- **Unique outputs** + +The simplest workaround is to ensure that the outputs which your scripts care about are unique. +This prevents them being confused with other outputs. + +In the swap example, if A had used a different key hashes as their payment addresses in each, then one output could not have satisfied both validators, since each one would want an output addressed to a different key hash. + +It is not too difficult to use unique outputs. +For payments to users, wallets typically already generate unique key hashes for every payment received. +For payments to script addresses it is a bit more complicated, and applications may wish to include the equivalent of a "payment reference" in the datum to keep things unique. + +- **Ban other scripts** + +A more draconian workaround is for your script to insist that it runs in a transaction which is running no other scripts, so there is no risk of confusion. +Note that it is not enough to consider just validator scripts, minting and reward scripts must also be banned. + +However, this prevents even benign usage of multiple scripts in one transaction, which stops people from designing interesting interactions, and may force users to break up transactions unnecessarily. + +### Hard limits + +Many resources on Cardano are limited in some fashion. +At a high level, limits can be enforced in two ways: + +- *Hard limits*: these are limits which cannot be breached. Typically, these are implemented with specific thresholds, where exceeding the threshold causes a hard failure. +- *Soft limits*: these are limits which *can* be breached, but where there is a significant disincentive to do so. One way of implementing a soft limit is to have sharply increasing costs to using the resource beyond the soft limit. + +Hard limits are clear, easy to specify, and provide hard guarantees for the protocol, but they have the disadvantage that there is no way to evade the limit. +This means that there is a discontinuity at the limit: beforehand you can always do more by paying more, but after the limit there is nothing you can do. + +Currently, these resources on Cardano have hard limits: + +- Transaction size +- Block size +- UTXO size +- Script execution units + +If an application *requires* a transaction that exceeds one of these limits, then the application will be stuck unless the limit is increased or removed. +This is most common when scripts are involved, since a script can require a very particular shape of transaction, regardless of whether this exceeds limits. + +Examples: + +- A script requires providing a datum which is extremely large and exceeds the transaction size limit. +- A script which locks an output needs more execution units than the limit. +- A script requires creating a single output containing a very large amount of tokens, which exceeds the output size limit. + +**Risks** + +This is typically an issue for applications that work with user-supplied data, or data that can grow in an unbounded way over time. +This can result in data which itself becomes large, or which requires a large amount of resources to process. + +For example: + +- Managing an arbitrary collection of assets (unbounded over time). +- Allowing user-specified payloads in datums (user-supplied unbounded data). + +Script size should not itself be a risk (since scripts and their sizes should generally be known ahead of time), but large scripts can reduce the amount of space available for other uses, heightening the risk of hitting a limit. + +**Solutions** + +In the long run, hard limits may be increased, removed, or turned into soft limits. + +In the mean time, there are some approaches that developers can use to reduce the risk. + +- **Careful testing** + +It is important to test as many of the execution paths of your application as possible. +This is important for correctness, but also to ensure that there are not unexpected cases where script resource usage spikes. + +- **Bounding data usage** + +Carefully consider whether your application may rely on unbounded data, and try to avoid that. +For example, if your application needs to manage a large quantity of assets, try to split them across multiple UTXOs instead of relying on a single UTXO to hold them all. + +- **Providing datums when creating outputs** + +Datum size issues are most likely to be discovered when an output is spent, because the datum is provided only as a hash on the output. +Insisting that the datum is provided in the transaction that creates the output can reveal that it is too big earlier in the process, allowing another path to be taken. +Depending on the application, this may still prevent it from progressing, if there is only one way to move forwards. + +If [CIP-32](https://cips.cardano.org/cips/cip32/) is implemented, this can be done conveniently by using inline datums, although that also risks hitting the output size limit. + +- **Reducing script size costs through reference inputs** + +If [CIP-33](https://cips.cardano.org/cips/cip33/) is implemented, then the contribution of scripts to transaction size can be massively reduced by using a reference script instead of including the entire script. + +<!-- Verify that CIP-32 and CIP-33 have already been implemented, and update statements above as appropriate. --> + +# Plutus on Cardano + +## Plutus language changes + +### Language versions + +See the documentation on `language versions <what_are_plutus_language_versions>`{.interpreted-text role="ref"} for an explanation of what they are. + +#### Plutus V1 + +`PlutusV1` was the initial version of Plutus, introduced in the Alonzo hard fork. + +#### Plutus V2 + +`PlutusV2` was introduced in the Vasil hard fork. + +The main changes in `PlutusV2` were to the interface to scripts. +The `ScriptContext` was extended to include the following information: + +- The full "redeemers" structure, which contains all the redeemers used in the transaction +- Reference inputs in the transaction (proposed in [CIP-31](https://cips.cardano.org/cips/cip31/)) +- Inline datums in the transaction (proposed in [CIP-32](https://cips.cardano.org/cips/cip32/)) +- Reference scripts in the transaction (proposed in [CIP-33](https://cips.cardano.org/cips/cip33/)) + +### Examples + +- [Plutus V2 functionalities](https://github.com/IntersectMBO/cardano-node/blob/master/doc/reference/plutus/babbage-script-example.md) +- [How to use reference inputs](https://github.com/perturbing/vasil-tests/blob/main/reference-inputs-cip-31.md) +- [How to use inline datums](https://github.com/perturbing/vasil-tests/blob/main/inline-datums-cip-32.md) +- [How to reference scripts](https://github.com/perturbing/vasil-tests/blob/main/referencing-scripts-cip-33.md) +- [How to use collateral outputs](https://github.com/perturbing/vasil-tests/blob/main/collateral-output-cip-40.md) + +### Built-in functions and types + +Built-in functions and types can be introduced with just a hard fork. +In some cases they are also available only in particular language versions. +This section indicates in which hard fork particular built-ins were introduced, and any language version constraints. + +#### Alonzo + +This is when the majority of the built-in types and functions were added to `PlutusV1`. +You can find an enumeration of them in :cite[plutus-core-spec]{.title-ref}. + +#### Vasil + +All of the built-in types and functions from `PlutusV1` were added to `PlutusV2`. + +The following built-in function was added to `PlutusV2` only (i.e., it is not available in `PlutusV1`). + +- `serializeData` (proposed in [CIP-42](https://cips.cardano.org/cips/cip42/)) + +#### PlutusV3 + +Plutus and cryptography teams at IOG, in collaboration with [MLabs](https://mlabs.city/), continue to develop Plutus capabilities. +Starting with the release of [Cardano node v.8.8.0-pre](https://github.com/IntersectMBO/cardano-node/releases/tag/8.8.0-pre), `PlutusV3` is available on [SanchoNet](https://sancho.network/), introducing the Cardano community to governance features from [CIP-1694](https://cips.cardano.org/cip/CIP-1694#goal) in a controlled testnet environment. + +`PlutusV3` is the new ledger language that enhances Plutus Core's cryptographic capabilities, offering the following benefits for the smart contract developer community: + +- Providing an updated script context that will let users see [CIP-1694](https://cips.cardano.org/cip/CIP-1694#goal) governance-related entities and voting features +- Interoperability between blockchains +- Advanced Plutus primitives +- Well-known and optimal cryptographic algorithms +- Support for porting of smart contracts from Ethereum +- Creating sidechain bridges +- Improving performance by adding a sums of products (SOPs) feature to support the direct encoding of differrent data types. + +#### Sums of products + +`PlutusV3` introduces sums of products - a way of encoding data types that leads to smaller and cheaper scripts compared with [Scott encoding](https://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding), a common way of encoding data types in Plutus Core. + +The sums of products approach aims to boost script efficiency and improve code generation for Plutus Core compilers. +The changes involve new term constructors for packing fields into constructor values and efficient tag inspection for case branches, potentially running programs 30% faster. +For an in-depth discussion, see [CIP-85](https://cips.cardano.org/cip/CIP-0085). + +#### New cryptographic primitives + +`PlutusV3` provides new built-in primitives that expand the language's capabilities. + +- **BLS12-381**: A curve pairing that includes 17 primitives that support cryptographic curves. This is a benefit to sidechain specification implementation and [Mithril](https://iohk.io/en/blog/posts/2023/07/20/mithril-nears-mainnet-release/) integration. +- **Blake2b-224**: A cryptographic hash function for on-chain computation of public-key hashes for the validation of transaction signatures. Supports community projects and contributes to Cardano's versatility. +- **Keccak-256**: A cryptographic hash function that produces a 256-bit (32-byte) hash value, commonly used for secure data verification. Supports Ethereum signature verification within scripts and cross-chain solutions. + +#### Bitwise primitives + +PlutusV3 initially brings several new bitwise primitives (with more to come at later stages). +The introduction of [CIP-58](https://cips.cardano.org/cip/CIP-0058) bitwise primitives will enable the following features: + +- Very low-level bit manipulations within Plutus, supporting the ability to execute high-performance data manipulation operations. +- Supporting the implementation of secure and robust cryptographic algorithms within Plutus. +- Facilitating standard, high-performance implementations for conversions between integers and bytestrings. + +`PlutusV3` adds two bitwise primitives: `integerToByteString` and `byteStringToInteger`. +The remaining primitives will be added to `PlutusV3` gradually and will not require a new ledger language. + +## Upgrading to Vasil and Plutus script addresses + +### A Plutus V2 script will not have the same hash value as a Plutus V1 script + +DApp developers might expect that when doing a migration from `PlutusV1` scripts to `PlutusV2` scripts, the same source code, when recompiled, will generate the same hash value of that script address. +However, it is impossible for a compiled `PlutusV2` script to have the same script hash and address as a compiled `PlutusV1` script. + +Using the exact same script with different language versions will result in different hashes. +The exact same script (as in `UPLC.Program`) can be used as a `PlutusV1` script or a `PlutusV2` script, and since the language version is part of the hash, the two hashes will be different. + +### A Plutus V1 script will not necessarily have the same hash value when recompiled with a later version of the Plutus Compiler + +Suppose you write your Haskell source code (Plutus Tx), compile it into Plutus Core code (PLC), generate its hash value, then use it in a transaction. +If you don't save your compiled code, and then decide to use the same script in the future, you would have to recompile it. +This could result in a different hash value of the script address even without upgrading from `PlutusV1` to `PlutusV2` scripts. +This is because the hash is computed based on the output of the compiled code. + +Given Plutus compiler version changes, changes in the dependencies, and multiple other improvements, it is expected that the hash value of the script address will change after the source code is recompiled. + +### When to export and save the output of a compiled script + +Once you expect that you will not modify the on-chain part of your application and you don't want the hash value of your script address to change, the best way to keep it the same is to save the output of your final compiled Plutus Core code (PLC) to a file. + +For details on how to export scripts, please see `How to export scripts, datums and +redeemers </howtos/exporting-a-script>`{.interpreted-text role="doc"}. + +## Cost model parameters + +> **Note** +> +> The cost model parameters has been automatically generated in the Read-the-docs platform. +> Will need to investigate what approach will make sense for Docusaurus and/or markdown files. + +The cost model for Plutus Core scripts has a number of parameters. +These are listed and briefly described below. +All of these parameters are listed in the Cardano protocol parameters and can be individually adjusted. + +``` +.. csv-table:: Machine parameters + :file: ./machine-parameters.csv + :widths: 20, 30, 40 + :header-rows: 1 + +.. csv-table:: Builtin parameters + :file: ./builtin-parameters.csv + :widths: 20, 30, 40 + :header-rows: 1 +``` + +# Glossary + +**address** +The address of an UTXO says where the output is "going". The address stipulates the conditions for unlocking the output. This can be a public key hash, or (in the Extended UTXO model) a script hash. + +**Cardano** +The blockchain system upon which the Plutus Platform is built. + +**currency** +A class of token whose minting is controlled by a particular monetary policy script. On the Cardano ledger, there is a special currency called Ada which can never be minted and which is controlled separately. + +**datum** +The data field on script outputs in the Extended UTXO model. + +**Extended UTXO Model** +The ledger model which the Plutus Platform relies on. This is implemented in the Alonzo hard fork of the Cardano blockchain. +See [what_is_a_ledger](#). + +**minting** +A transaction which mints tokens creates new tokens, providing that the corresponding minting policy script is satisfied. The amount minted can be negative, in which case the tokens will be destroyed instead of created. + +**minting policy script** +A script which must be satisfied in order for a transaction to mint tokens of the corresponding currency. + +**Hydra** +A Layer 2 scalability solution for Cardano. See [chakravarty2020hydra](#). + +**distributed ledger** +See [what_is_a_ledger](#). + +**Marlowe** +A domain-specific language for writing financial contract applications. + +**multi-asset** +A generic term for a ledger which supports multiple different asset types natively. + +**off-chain code** +The part of a contract application's code which runs off the chain, usually as a contract application. + +**on-chain code** +The part of a contract application's code which runs on the chain (i.e., as scripts). + +**Plutus Core** +The programming language in which scripts on the Cardano blockchain are written. Plutus Core is a small functional programming language --— a formal specification is available with further details. Plutus Core is not read or written by humans, it is a compilation target for other languages. +See [what_is_plutus_foundation](#). + +**Plutus IR** +An intermediate language that compiles to Plutus Core. Plutus IR is not used by users, but rather as a compilation target on the way to Plutus Core. However, it is significantly more human-readable than Plutus Core, so should be preferred in cases where humans may want to inspect the program. + +**Plutus Platform** +The combined software support for writing contract applications, including: +1. Plutus Foundation, and +2. The Plutus Application Framework +See [what_is_the_plutus_platform](#). + +**Plutus Tx** +The libraries and compiler for compiling Haskell into Plutus Core to form the on-chain part of a contract application. + +**redeemer** +The argument to the validator script which is provided by the transaction which spends a script output. + +**rollback** +The result of the local node switching to the consensus chain. + +**script** +A generic term for an executable program used in the ledger. In the Cardano blockchain, these are written in Plutus Core. + +**script context** +A data structure containing a summary of the transaction being validated, as well as a way of identifying the current script being run. + +**script output** +A UTXO locked by a script. + +**token** +A generic term for a native tradeable asset in the ledger. + +**transaction output** +Outputs produced by transactions. They are consumed when they are spent by another transaction. Typically, some kind of evidence is required to be able to spend a UTXO, such as a signature from a public key, or (in the Extended UTXO Model) satisfying a script. + +**UTXO** +An unspent transaction output. + +**utxo congestion** +The effect of multiple transactions attempting to spend the same transaction output. + +**validator script** +The script attached to a script output in the Extended UTXO model. Must be run and return positively in order for the output to be spent. Determines the address of the output. diff --git a/doc/docs-revamp/100_troubleshooting.md b/doc/docs-revamp/100_troubleshooting.md new file mode 100644 index 00000000000..b088dd89a6c --- /dev/null +++ b/doc/docs-revamp/100_troubleshooting.md @@ -0,0 +1,122 @@ +--- +title: "Troubleshooting" +description: "Troubleshooting" +date: 2024-04-08 +--- + +# Section 9. Troubleshooting + +# Plugin errors + +Errors that start with `GHC Core to PLC plugin` are errors from `plutus-tx-plugin`. + +> **Note** +> +> Often these errors arise due to GHC doing something to the code before the plugin gets to see it. +> So the solution is often to prevent GHC from doing this, which is why we often recommend trying various GHC compiler flags. + +## Haddock + +The plugin will typically fail when producing Haddock documentation. +However, in this instance you can simply tell it to defer any errors to runtime (which will never happen since you're building documentation). + +A easy way to do this is to add the following lines for your `package-name` to `cabal.project`: + +``` + package package-name + haddock-options: "--optghc=-fplugin-opt PlutusTx.Plugin:defer-errors" +``` + +## Non-`INLINABLE` functions + +A common error is: + +`Error: Reference to a name which is not a local, a builtin, or an external INLINABLE function` + +This means the plugin doesn't have access to implementation of the function, which it needs to be able to compile the function to Plutus Core. +Some things you can do to fix it: + +- Make sure to add `{-# INLINABLE functionname #-}` to your function. +- If there's an extra `$c` in front of the function name in the error, GHC has generated a specialised version of your function, which prevents the plugin from accessing it. You can turn off specialization with `{-# OPTIONS_GHC -fno-specialise #-}` +- Other compiler options that can help: + - `{-# OPTIONS_GHC -fno-strictness #-}` + - `{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-}` + - `{-# OPTIONS_GHC -fno-omit-interface-pragmas #-}` + - `{-# OPTIONS_GHC -fobject-code #-}` + +Some more details are in [the plutus-tx readme](https://github.com/IntersectMBO/plutus/tree/master/plutus-tx#building-projects-with-plutus-tx). + +# Haskell Language Server issues + +For more advice on using Haskell Language Server (HLS), consult the [CONTRIBUTING guide](https://github.com/IntersectMBO/plutus/blob/master/CONTRIBUTING.adoc) in the `plutus` repository. + +## Wrong version + +`ghcide compiled against GHC 8.10.3 but currently using 8.10.2.20201118` + +Your editor is not picking up the right version of the Haskell Language Server (HLS). +Plutus needs a custom version of HLS which is provided by Nix. +To get this working in your editor, make sure to do these two things: + +- Start your editor from `nix develop` (or use `direnv`) +- Most editors are configured to use `haskell-language-server-wrapper`, which is a wrapper which picks the right HLS version. Change this to just `haskell-language-server`. + +If this doesn't work, run `which haskell-language-server` in [nix develop]{.title-ref}, and use this absolute path in the configuration of your editor. + +# Error codes + +To reduce code size, on-chain errors only output codes. +Here's what they mean: + +## Ledger errors +- `L0: Input constraint` +- `L1: Output constraint` +- `L2: Missing datum` +- `L3: Wrong validation interval` +- `L4: Missing signature` +- `L5: Spent value not OK` +- `L6: Produced value not OK` +- `L7: Public key output not spent` +- `L8: Script output not spent` +- `L9: Value minted not OK` +- `La: MustPayToPubKey` +- `Lb: MustPayToOtherScript` +- `Lc: MustHashDatum` +- `Ld: checkScriptContext failed` +- `Le: Can't find any continuing outputs` +- `Lf: Can't get any continuing outputs` +- `Lg: Can't get validator and datum hashes` +- `Lh: Can't get currency symbol of the current validator script` +- `Li: DecodingError` +## Prelude errors +- `PT1: TH Generation of Indexed Data Error` +- `PT2: Void is not supported` +- `PT3: Ratio number can't have a zero denominator` +- `PT4: 'round' got an incorrect input` +- `PT5: 'check' input is 'False'` +- `PT6: PlutusTx.List.!!: negative index` +- `PT7: PlutusTx.List.!!: index too large` +- `PT8: PlutusTx.List.head: empty list` +- `PT9: PlutusTx.List.tail: empty list` +- `PT10: PlutusTx.Enum.().succ: bad argument` +- `PT11: PlutusTx.Enum.().pred: bad argument` +- `PT12: PlutusTx.Enum.().toEnum: bad argument` +- `PT13: PlutusTx.Enum.Bool.succ: bad argument` +- `PT14: PlutusTx.Enum.Bool.pred: bad argument` +- `PT15: PlutusTx.Enum.Bool.toEnum: bad argument` +- `PT16: PlutusTx.Enum.Ordering.succ: bad argument` + `PT17: PlutusTx.Enum.Ordering.pred: bad argument` +- `PT18: PlutusTx.Enum.Ordering.toEnum: bad argument` +## State machine errors +- `S0: Can't find validation input` +- `S1: State transition invalid - checks failed` +- `S2: Thread token not found` +- `S3: Non-zero value allocated in final state` +- `S4: State transition invalid - constraints not satisfied by ScriptContext` +- `S5: State transition invalid - constraints not satisfied by ScriptContext` +- `S6: State transition invalid - input is not a valid transition at the current state` +- `S7: Value minted different from expected` +- `S8: Pending transaction does not spend the designated transaction output` +## Currency errors +- `C0: Value minted different from expected` +- `C1: Pending transaction does not spend the designated transaction output` diff --git a/doc/docs-revamp/110_faq.md b/doc/docs-revamp/110_faq.md new file mode 100644 index 00000000000..8d05335ebcc --- /dev/null +++ b/doc/docs-revamp/110_faq.md @@ -0,0 +1,42 @@ +--- +title: "FAQ" +description: "Frequently asked questions" +date: 2024-03-12 +--- + +# Section 10. FAQ + +Draft FAQ for Plutus Core Plutus Tx + +_NOTE: Some comments made in the Google doc are not reflected here yet._ + +Stage 1: Identify and refine the questions + +QUESTION TO ALL REVIEWERS: Can you think of questions that haven’t been included here yet that are likely to be on the minds of people who are coming to the documentation for the very first time? + +Stage 2: Develop answers to the questions + +Below is a draft list of questions for consideration and review. Please add to the list if you have more suggestions, and edit/comment if a question should be clarified or modified to make more sense. + +Once we have reviewed and more or less settled on the questions, I’ll schedule a meeting inviting anyone who is available and interested to collect your thoughts to help inform the answers. Then I’ll circulate another draft that includes answers for review. + +- What are the prerequisites for developing Plutus smart contracts on the Cardano blockchain? +- What are the benefits of using Haskell and Plutus for smart contract development on Cardano? +- Can you explain the relationship between Haskell, Plutus, Plutus Core, and Plutus Tx in the context of Cardano smart contracts? +- How do I set up my development environment for Plutus and Haskell? +- What tools and resources are essential for a beginner to start developing with Plutus? +- Can you dissect the anatomy of a Plutus smart contract to highlight its core elements? +- How do you write basic Plutus Tx programs, and what are the foundational principles? +- What libraries are most beneficial for writing Plutus Tx scripts, and how do they enhance development? +- How do I create a new Cabal package for my Plutus project? +- How can developers declare and manage Plutus Tx dependencies within their projects? +- What are the key components of an on-chain validator in Plutus? +- Could you explain the process of writing an on-chain validator script and its role in a Plutus smart contract? +- What are the basics of Template Haskell relevant to Plutus developers, and how does it facilitate smart contract development? +- What are the best methods you recommend for Plutus smart contract developers to use to test and/or simulate their smart contracts? +- What are some common errors beginners might encounter when starting with Plutus, and how can they be resolved? +- What are the best practices for creating and submitting transactions using an off-chain framework in Plutus? +- How does interfacing between Plutus Tx and off-chain code work, and what are the common patterns? +- What guidelines should be followed when writing validator scripts in Plutus? +- What guidelines should be followed when writing minting policies in Plutus? + diff --git a/doc/docs-revamp/AuctionValidator.hs b/doc/docs-revamp/AuctionValidator.hs new file mode 100644 index 00000000000..60e94db0b00 --- /dev/null +++ b/doc/docs-revamp/AuctionValidator.hs @@ -0,0 +1,208 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ImportQualifiedPost #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE Strict #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE ViewPatterns #-} + +{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-} +{-# OPTIONS_GHC -fno-omit-interface-pragmas #-} +{-# OPTIONS_GHC -fno-full-laziness #-} +{-# OPTIONS_GHC -fno-spec-constr #-} +{-# OPTIONS_GHC -fno-specialise #-} +{-# OPTIONS_GHC -fno-strictness #-} +{-# OPTIONS_GHC -fno-unbox-strict-fields #-} +{-# OPTIONS_GHC -fno-unbox-small-strict-fields #-} +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.0.0 #-} + +module AuctionValidator where + +import PlutusCore.Version (plcVersion100) +import PlutusLedgerApi.V1 (Lovelace, POSIXTime, PubKeyHash, Value) +import PlutusLedgerApi.V1.Address (pubKeyHashAddress) +import PlutusLedgerApi.V1.Interval (contains) +import PlutusLedgerApi.V1.Value (lovelaceValue) +import PlutusLedgerApi.V2 (Datum (..), OutputDatum (..), ScriptContext (..), TxInfo (..), + TxOut (..), from, to) +import PlutusLedgerApi.V2.Contexts (getContinuingOutputs) +import PlutusTx +import PlutusTx.Prelude qualified as PlutusTx +import PlutusTx.Show qualified as PlutusTx + +-- BLOCK1 +data AuctionParams = AuctionParams + { apSeller :: PubKeyHash, + -- ^ Seller's wallet address. The highest bid (if exists) will be sent to the seller. + -- If there is no bid, the asset auctioned will be sent to the seller. + apAsset :: Value, + -- ^ The asset being auctioned. It can be a single token, multiple tokens of the same + -- kind, or tokens of different kinds, and the token(s) can be fungible or non-fungible. + -- These can all be encoded as a `Value`. + apMinBid :: Lovelace, + -- ^ The minimum bid in Lovelace. + apEndTime :: POSIXTime + -- ^ The deadline for placing a bid. This is the earliest time the auction can be closed. + } + +PlutusTx.makeLift ''AuctionParams + +data Bid = Bid + { bBidder :: PubKeyHash, + -- ^ Bidder's wallet address. + bAmount :: Lovelace + -- ^ Bid amount in Lovelace. + } + +PlutusTx.deriveShow ''Bid +PlutusTx.unstableMakeIsData ''Bid + +instance PlutusTx.Eq Bid where + {-# INLINEABLE (==) #-} + bid == bid' = + bBidder bid PlutusTx.== bBidder bid' + PlutusTx.&& bAmount bid PlutusTx.== bAmount bid' + +-- | Datum represents the state of a smart contract. In this case +-- it contains the highest bid so far (if exists). +newtype AuctionDatum = AuctionDatum { adHighestBid :: Maybe Bid } + +PlutusTx.unstableMakeIsData ''AuctionDatum + +-- | Redeemer is the input that changes the state of a smart contract. +-- In this case it is either a new bid, or a request to close the auction +-- and pay out the seller and the highest bidder. +data AuctionRedeemer = NewBid Bid | Payout + +PlutusTx.unstableMakeIsData ''AuctionRedeemer +-- BLOCK2 + + +{-# INLINEABLE auctionTypedValidator #-} +-- | Given the auction parameters, determines whether the transaction is allowed to +-- spend the UTXO. +auctionTypedValidator :: + AuctionParams -> + AuctionDatum -> + AuctionRedeemer -> + ScriptContext -> + Bool +auctionTypedValidator params (AuctionDatum highestBid) redeemer ctx@(ScriptContext txInfo _) = + PlutusTx.and conditions + where + conditions :: [Bool] + conditions = case redeemer of + NewBid bid -> + [ -- The new bid must be higher than the highest bid. + -- If this is the first bid, it must be at least as high as the minimum bid. + sufficientBid bid, + -- The bid is not too late. + validBidTime, + -- The previous highest bid should be refunded. + refundsPreviousHighestBid, + -- A correct new datum is produced, containing the new highest bid. + correctNewDatum bid + ] + Payout -> + [ -- The payout is not too early. + validPayoutTime, + -- The seller gets the highest bid. + sellerGetsHighestBid, + -- The highest bidder gets the asset. + highestBidderGetsAsset + ] +-- BLOCK3 + sufficientBid :: Bid -> Bool + sufficientBid (Bid _ amt) = case highestBid of + Just (Bid _ amt') -> amt PlutusTx.> amt' + Nothing -> amt PlutusTx.>= apMinBid params +-- BLOCK4 + validBidTime :: Bool + validBidTime = to (apEndTime params) `contains` txInfoValidRange txInfo +-- BLOCK5 + refundsPreviousHighestBid :: Bool + refundsPreviousHighestBid = case highestBid of + Nothing -> True + Just (Bid bidder amt) -> + case PlutusTx.find + (\o -> txOutAddress o PlutusTx.== pubKeyHashAddress bidder + PlutusTx.&& txOutValue o PlutusTx.== lovelaceValue amt) + (txInfoOutputs txInfo) of + Just _ -> True + Nothing -> PlutusTx.traceError ("Not found: refund output") +-- BLOCK6 + correctNewDatum :: Bid -> Bool + correctNewDatum bid = case getContinuingOutputs ctx of + [o] -> case txOutDatum o of + OutputDatum (Datum newDatum) -> case PlutusTx.fromBuiltinData newDatum of + Just bid' -> + PlutusTx.traceIfFalse + ( "Invalid output datum: expected " + PlutusTx.<> PlutusTx.show bid + PlutusTx.<> ", but got " + PlutusTx.<> PlutusTx.show bid' + ) + (bid PlutusTx.== bid') + Nothing -> + PlutusTx.traceError + ( "Failed to decode output datum: " + PlutusTx.<> PlutusTx.show newDatum + ) + OutputDatumHash _ -> + PlutusTx.traceError "Expected OutputDatum, got OutputDatumHash" + NoOutputDatum -> + PlutusTx.traceError "Expected OutputDatum, got NoOutputDatum" + os -> + PlutusTx.traceError + ( "Expected exactly one continuing output, got " + PlutusTx.<> PlutusTx.show (PlutusTx.length os) + ) +-- BLOCK7 + validPayoutTime :: Bool + validPayoutTime = from (apEndTime params) `contains` txInfoValidRange txInfo + + sellerGetsHighestBid :: Bool + sellerGetsHighestBid = case highestBid of + Nothing -> True + Just (Bid _ amt) -> + case PlutusTx.find + ( \o -> + txOutAddress o PlutusTx.== pubKeyHashAddress (apSeller params) + PlutusTx.&& txOutValue o PlutusTx.== lovelaceValue amt + ) + (txInfoOutputs txInfo) of + Just _ -> True + Nothing -> PlutusTx.traceError ("Not found: Output paid to seller") + + highestBidderGetsAsset :: Bool + highestBidderGetsAsset = case highestBid of + Nothing -> True + Just (Bid bidder _) -> + case PlutusTx.find + ( \o -> + txOutAddress o PlutusTx.== pubKeyHashAddress bidder + PlutusTx.&& txOutValue o PlutusTx.== apAsset params + ) + (txInfoOutputs txInfo) of + Just _ -> True + Nothing -> PlutusTx.traceError ("Not found: Output paid to highest bidder") +-- BLOCK8 +{-# INLINEABLE auctionUntypedValidator #-} +auctionUntypedValidator :: AuctionParams -> BuiltinData -> BuiltinData -> BuiltinData -> () +auctionUntypedValidator params datum redeemer ctx = + PlutusTx.check + ( auctionTypedValidator + params + (PlutusTx.unsafeFromBuiltinData datum) + (PlutusTx.unsafeFromBuiltinData redeemer) + (PlutusTx.unsafeFromBuiltinData ctx) + ) + +auctionValidatorScript :: + AuctionParams -> + CompiledCode (BuiltinData -> BuiltinData -> BuiltinData -> ()) +auctionValidatorScript params = + $$(PlutusTx.compile [||auctionUntypedValidator||]) + `PlutusTx.unsafeApplyCode` PlutusTx.liftCode plcVersion100 params +-- BLOCK9 diff --git a/doc/docs-revamp/BasicPlutusTx.hs b/doc/docs-revamp/BasicPlutusTx.hs new file mode 100644 index 00000000000..f7ce8375007 --- /dev/null +++ b/doc/docs-revamp/BasicPlutusTx.hs @@ -0,0 +1,188 @@ +-- BLOCK1 +-- Necessary language extensions for the Plutus Tx compiler to work. +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} + +{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.0.0 #-} + +module BasicPlutusTx where + +import PlutusCore.Default qualified as PLC +import PlutusCore.Version (plcVersion100) +-- Main Plutus Tx module. +import PlutusTx +-- Additional support for lifting. +import PlutusTx.Lift +-- Builtin functions. +import PlutusTx.Builtins +-- The Plutus Tx Prelude, discussed further below. +import PlutusTx.Prelude + +-- Setup for doctest examples. + +-- $setup +-- >>> import Tutorial.PlutusTx +-- >>> import PlutusTx +-- >>> import PlutusCore +-- >>> import PlutusCore.Evaluation.Machine.Ck +-- >>> import Data.Text.Prettyprint.Doc + +-- BLOCK2 +integerOne :: CompiledCode Integer +{- 'compile' turns the 'TExpQ Integer' into a + 'TExpQ (CompiledCode Integer)' and the splice + inserts it into the program. -} +integerOne = $$(compile + {- The quote has type 'TExpQ Integer'. + We always use unbounded integers in Plutus Core, so we have to pin + down this numeric literal to an ``Integer`` rather than an ``Int``. -} + [|| (1 :: Integer) ||]) + +{- | +>>> pretty $ getPlc integerOne +(program 1.0.0 + (con 1) +) +-} +-- BLOCK3 +integerIdentity :: CompiledCode (Integer -> Integer) +integerIdentity = $$(compile [|| \(x:: Integer) -> x ||]) + +{- | +>>> pretty $ getPlc integerIdentity +(program 1.0.0 + (lam ds (con integer) ds) +) +-} +-- BLOCK4 +{- Functions which will be used in Plutus Tx programs should be marked + with GHC’s 'INLINABLE' pragma. This is usually necessary for + non-local functions to be usable in Plutus Tx blocks, as it instructs + GHC to keep the information that the Plutus Tx compiler needs. While + you may be able to get away with omitting it, it is good practice to + always include it. -} +{-# INLINABLE plusOne #-} +plusOne :: Integer -> Integer +{- 'addInteger' comes from 'PlutusTx.Builtins', and is + mapped to the builtin integer addition function in Plutus Core. -} +plusOne x = x `addInteger` 1 + +{-# INLINABLE myProgram #-} +myProgram :: Integer +myProgram = + let + -- Local functions do not need to be marked as 'INLINABLE'. + plusOneLocal :: Integer -> Integer + plusOneLocal x = x `addInteger` 1 + + localTwo = plusOneLocal 1 + externalTwo = plusOne 1 + in localTwo `addInteger` externalTwo + +functions :: CompiledCode Integer +functions = $$(compile [|| myProgram ||]) + +{- We’ve used the CK evaluator for Plutus Core to evaluate the program + and check that the result was what we expected. -} +{- | +>>> pretty $ unsafeEvaluateCk $ toTerm $ getPlc functions +(con 4) +-} +-- BLOCK5 +matchMaybe :: CompiledCode (Maybe Integer -> Integer) +matchMaybe = $$(compile [|| \(x:: Maybe Integer) -> case x of + Just n -> n + Nothing -> 0 + ||]) +-- BLOCK6 +-- | Either a specific end date, or "never". +data EndDate = Fixed Integer | Never + +-- | Check whether a given time is past the end date. +pastEnd :: CompiledCode (EndDate -> Integer -> Bool) +pastEnd = $$(compile [|| \(end::EndDate) (current::Integer) -> case end of + Fixed n -> n `lessThanEqualsInteger` current + Never -> False + ||]) +-- BLOCK7 +-- | Check whether a given time is past the end date. +pastEnd' :: CompiledCode (EndDate -> Integer -> Bool) +pastEnd' = $$(compile [|| \(end::EndDate) (current::Integer) -> case end of + Fixed n -> n < current + Never -> False + ||]) +-- BLOCK8 +addOne :: CompiledCode (Integer -> Integer) +addOne = $$(compile [|| \(x:: Integer) -> x `addInteger` 1 ||]) +-- BLOCK9 +addOneToN :: Integer -> CompiledCode Integer +addOneToN n = + addOne + -- 'unsafeApplyCode' applies one 'CompiledCode' to another. + `unsafeApplyCode` + -- 'liftCode' lifts the argument 'n' into a + -- 'CompiledCode Integer'. It needs a version to tell it what + -- Plutus Core language version to target, if you don't care you + -- can use 'liftCodeDef' + liftCode plcVersion100 n + +{- | +>>> pretty $ getPlc addOne +(program 1.0.0 + [ + (lam + addInteger + (fun (con integer) (fun (con integer) (con integer))) + (lam ds (con integer) [ [ addInteger ds ] (con 1) ]) + ) + (lam + arg + (con integer) + (lam arg (con integer) [ [ (builtin addInteger) arg ] arg ]) + ) + ] +) +>>> let program = getPlc $ addOneToN 4 +>>> pretty program +(program 1.0.0 + [ + [ + (lam + addInteger + (fun (con integer) (fun (con integer) (con integer))) + (lam ds (con integer) [ [ addInteger ds ] (con 1) ]) + ) + (lam + arg + (con integer) + (lam arg (con integer) [ [ (builtin addInteger) arg ] arg ]) + ) + ] + (con 4) + ] +) +>>> pretty $ unsafeEvaluateCk $ toTerm program +(con 5) +-} +-- BLOCK10 +-- 'makeLift' generates instances of 'Lift' automatically. +makeLift ''EndDate + +pastEndAt :: EndDate -> Integer -> CompiledCode Bool +pastEndAt end current = + pastEnd + `unsafeApplyCode` + liftCode plcVersion100 end + `unsafeApplyCode` + liftCode plcVersion100 current + +{- | +>>> let program = getPlc $ pastEndAt Never 5 +>>> pretty $ unsafeEvaluateCk $ toTerm program +(abs + out_Bool (type) (lam case_True out_Bool (lam case_False out_Bool case_False)) +) +-} +-- BLOCK11 diff --git a/doc/docs-revamp/BasicPolicies.hs b/doc/docs-revamp/BasicPolicies.hs new file mode 100644 index 00000000000..4bf2565a008 --- /dev/null +++ b/doc/docs-revamp/BasicPolicies.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +module BasicPolicies where + +import PlutusCore.Default qualified as PLC +import PlutusTx +import PlutusTx.Lift +import PlutusTx.Prelude + +import PlutusLedgerApi.V1.Contexts +import PlutusLedgerApi.V1.Crypto +import PlutusLedgerApi.V1.Scripts +import PlutusLedgerApi.V1.Value +import PlutusTx.AssocMap qualified as Map + +tname :: TokenName +tname = error () + +key :: PubKeyHash +key = error () + +-- BLOCK1 +oneAtATimePolicy :: () -> ScriptContext -> Bool +oneAtATimePolicy _ ctx = + -- 'ownCurrencySymbol' lets us get our own hash (= currency symbol) + -- from the context + let ownSymbol = ownCurrencySymbol ctx + txinfo = scriptContextTxInfo ctx + minted = txInfoMint txinfo + -- Here we're looking at some specific token name, which we + -- will assume we've got from elsewhere for now. + in currencyValueOf minted ownSymbol == singleton ownSymbol tname 1 + +{-# INLINABLE currencyValueOf #-} +-- | Get the quantities of just the given 'CurrencySymbol' in the 'Value'. +currencyValueOf :: Value -> CurrencySymbol -> Value +currencyValueOf (Value m) c = case Map.lookup c m of + Nothing -> mempty + Just t -> Value (Map.singleton c t) +-- BLOCK2 +-- The 'plutus-ledger' package from 'plutus-apps' provides helper functions to automate +-- some of this boilerplate. +oneAtATimePolicyUntyped :: BuiltinData -> BuiltinData -> () +-- 'check' fails with 'error' if the argument is not 'True'. +oneAtATimePolicyUntyped r c = + check $ oneAtATimePolicy (unsafeFromBuiltinData r) (unsafeFromBuiltinData c) + +-- We can use 'compile' to turn a minting policy into a compiled Plutus Core program, +-- just as for validator scripts. +oneAtATimeCompiled :: CompiledCode (BuiltinData -> BuiltinData -> ()) +oneAtATimeCompiled = $$(compile [|| oneAtATimePolicyUntyped ||]) +-- BLOCK3 +singleSignerPolicy :: () -> ScriptContext -> Bool +singleSignerPolicy _ ctx = txSignedBy (scriptContextTxInfo ctx) key +-- BLOCK4 diff --git a/doc/docs-revamp/BasicValidators.hs b/doc/docs-revamp/BasicValidators.hs new file mode 100644 index 00000000000..5dde554e8c2 --- /dev/null +++ b/doc/docs-revamp/BasicValidators.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} +module BasicValidators where + +import PlutusCore.Default qualified as PLC +import PlutusTx +import PlutusTx.Lift +import PlutusTx.Prelude + +import PlutusLedgerApi.Common +import PlutusLedgerApi.V1.Contexts +import PlutusLedgerApi.V1.Crypto +import PlutusLedgerApi.V1.Scripts +import PlutusLedgerApi.V1.Value + +import Data.ByteString qualified as BS +import Data.ByteString.Lazy qualified as BSL + +import Codec.Serialise +import Flat qualified + +import Prelude (IO, print, show) +import Prelude qualified as Haskell + +myKeyHash :: PubKeyHash +myKeyHash = Haskell.undefined + +-- BLOCK1 +-- | A specific date. +newtype Date = Date Integer +-- | Either a specific end date, or "never". +data EndDate = Fixed Integer | Never + +-- 'unstableMakeIsData' is a TemplateHaskell function that takes a type name and +-- generates an 'IsData' instance definition for it. It should work for most +-- types, including newtypes and sum types. For production usage use 'makeIsDataIndexed' +-- which ensures that the output is stable across time. +unstableMakeIsData ''Date +unstableMakeIsData ''EndDate +-- BLOCK2 +alwaysSucceeds :: BuiltinData -> BuiltinData -> BuiltinData -> () +alwaysSucceeds _ _ _ = () + +alwaysFails :: BuiltinData -> BuiltinData -> BuiltinData -> () +alwaysFails _ _ _ = error () + +-- We can use 'compile' to turn a validator function into a compiled Plutus Core program. +-- Here's a reminder of how to do it. +alwaysSucceedsCompiled :: CompiledCode (BuiltinData -> BuiltinData -> BuiltinData -> ()) +alwaysSucceedsCompiled = $$(compile [|| alwaysSucceeds ||]) +-- BLOCK3 +-- | Checks if a date is before the given end date. +beforeEnd :: Date -> EndDate -> Bool +beforeEnd (Date d) (Fixed e) = d <= e +beforeEnd (Date _) Never = True + +-- | Check that the date in the redeemer is before the limit in the datum. +validateDate :: BuiltinData -> BuiltinData -> BuiltinData -> () +-- The 'check' function takes a 'Bool' and fails if it is false. +-- This is handy since it's more natural to talk about booleans. +validateDate datum redeemer _ = + check $ beforeEnd (unsafeFromBuiltinData datum) (unsafeFromBuiltinData redeemer) + +dateValidator :: CompiledCode (BuiltinData -> BuiltinData -> BuiltinData -> ()) +dateValidator = $$(compile [|| validateDate ||]) +-- BLOCK4 +validatePayment :: BuiltinData -> BuiltinData -> BuiltinData -> () +validatePayment _ _ ctx = + let valCtx = unsafeFromBuiltinData ctx + -- The 'TxInfo' in the validation context is the representation of the + -- transaction being validated + txinfo = scriptContextTxInfo valCtx + -- 'pubKeyOutputsAt' collects the 'Value' at all outputs which pay to + -- the given public key hash + values = pubKeyOutputsAt myKeyHash txinfo + -- 'fold' sums up all the values, we assert that there must be more + -- than 1 Ada (more stuff is fine!) + in check $ lovelaceValueOf (fold values) >= 1_000_000 +--- BLOCK5 +-- We can serialize a 'Validator' directly to CBOR +serialisedDateValidator :: SerialisedScript +serialisedDateValidator = serialiseCompiledCode dateValidator + +-- The serialized forms can be written or read using normal Haskell IO functionality. +showSerialised :: IO () +showSerialised = print serialisedDateValidator +-- BLOCK6 +-- The 'loadFromFile' function is a drop-in replacement for 'compile', but +-- takes the file path instead of the code to compile. +validatorCodeFromFile :: CompiledCode (() -> () -> ScriptContext -> Bool) +validatorCodeFromFile = $$(loadFromFile "howtos/myscript.uplc") +-- BLOCK7 diff --git a/doc/docs-revamp/README.md b/doc/docs-revamp/README.md new file mode 100644 index 00000000000..622c1ab0f2c --- /dev/null +++ b/doc/docs-revamp/README.md @@ -0,0 +1,279 @@ +# Plutus docs revamp project + +This outline shows the proposed reorganized structure for the Plutus Core Plutus Tx documentation revamp. + +# Reorganized top levels + +## 1. [Introduction](010_introduction.md) +> **Status** +> +> - Ported over existing content. +> - Planning further reorganization and editing. +> - Not ready for review. + +## 2. [Core concepts](020_core-concepts.md) +> **Status** +> +> - Ported over existing content. +> - Planning further reorganization and editing. +> - Not ready for review. +> - Observation: The reference to the Plutus Application Framework is outdated. Many elements of it are no longer functioning or being supported. For example, the PAB is no longer supported. The Plutus Playground is no longer available and is not being supported. Plutus Apps are no longer supported. I suggest that it would be helpful to find a new way to talk about the Plutus development environment and ecosystem without referencing the “Plutus Application Framework.” + +- The Plutus Platform + - Applications + - The Plutus Platform + - Further reading +- Ledgers + - Account-based and UTXO-based ledgers + - Scripts and the Extended UTXO Model + - Different kinds of scripts + - Further reading +- Plutus Foundation + - Plutus Core + - Plutus Tx + - Further reading +- Plutus language versions + +## 3. [Developer onboarding and quick setup](030_dev-onboarding-quick-setup.md) + +> **Notes** +> - Review comments incorporated as of March 26. +> - Ziyang's comments in [Draft PR](https://github.com/IntersectMBO/plutus/pull/5866/commits) speak to intended audience, scope, and reconsidering our example contract selection. +> - Take into account the [Plutus-tx-template repo](https://github.com/IntersectMBO/plutus-tx-template) and how to use it. + +- Objective +- Setting up and testing your development environment +- Testing your environment setup +- Core concepts of Plutus smart contracts +- Writing your first Plutus smart contract +- First smart contract example + - Code example with explanations +- The larger context and next steps + +## 4. [Simple example](040_simple-example.md) +> **Status** +> +> - Ported over content. +> - Intending to prune down and edit. +> - Some content will probably be moved to other sections. + +- Section 4. Simple example + - Overview +- The EUTXO model, datum, redeemer and script context +- Auction properties +- Plutus Tx code + - Data types + - Main validator function +- Life cycle of the auction smart contract + - Initial UTXO + - The first bid + - The second bid + - Closing the auction +- Libraries for writing Plutus Tx scripts +- Alternatives to Plutus Tx +- Off-chain code +- Further reading + - The EUTXO model + +## 5. [Using Plutus Tx](050.1_proposed-edits_using-plutus-tx.md) +> **Status** +> +> - Code block includes are not yet functional. Working with web dev team to figure out how we will handle this if we discontinue using .rst files and migrate to Docusaurus and .md files. +> - Proposed new organizational structure for this section is reflected below. The file [050.1_proposed-edits_using-plutus-tx.md](050.1_proposed-edits_using-plutus-tx.md) is a draft in progress as a basis for discussion and for gathering more information from the Plutus team. + +> NOTE: Paragraph numbering is being used temporarily to help with organizing and editing content. + +1. High-level overview of how Plutus Tx works + + 1.1 Key technique for implementing Plutus tx: staged metaprogramming + +2. Basic syntax and structure of a Plutus Tx program + + 2.1 Plutus-Tx-Template repo + + 2.2 Template Haskell preliminaries + + 2.3 Simple pattern + + 2.4 Quotes + + 2.5 Splicing quotes + +3. Writing Plutus Tx Programs + + 3.1 Plutus Tx standard usage pattern (how all of our Plutus Tx programs are written) + + 3.2 Functions and datatypes + + 3.3 Typeclasses + + 3.4 The Plutus Tx Prelude + + 3.5 Plutus Tx Prelude has redefined versions of many standard typeclasses + + 3.6 Lifting values for generating code dynamically + +4. Compiling Plutus Tx, describing the Plutus Tx compilation process + + 4.1 GHC Extensions, Flags and Pragmas + + 4.1.1 Extensions + + 4.1.2 Flags + + 4.1.3 Pragmas + + 4.2 Reference: Plutus Tx Compiler Options + +5. Troubleshooting and Debugging + + 5.1 Common errors and how to fix them + + 5.2 Debugging techniques for Plutus Tx programs + +6. Real-world Examples and Use Cases + + 6.1 Practical applications of Plutus Tx + + 6.2 Case studies and code examples + +## 6. [Working with scripts](060_working-with-scripts.md) +> **Status** +> +> - Ported over existing content into new org structure. +> - Code block includes are not yet functional. Working with web dev team to figure out how we will handle this if we discontinue using .rst files and migrate to Docusaurus and .md files. +> - Not ready for review -- more editing planned. + +- Writing basic validator scripts + - Validator arguments + - The Data type + - Signaling failure + - Validator functions + - Plutus script context versions +- Writing basic minting policies + - Minting policy arguments + - Plutus script context versions + - Writing minting policies + - Other policy examples +- Creating and submitting transactions using an off-chain framework +- Libraries for writing Plutus Tx scripts +- **Using AsData to optimize scripts** (advanced topic) +- Exporting scripts, datums and redeemers +- Profiling the budget usage of Plutus scripts + - Compiling a script for profiling + - Acquiring an executable script + - Running the script + - Analyzing the results + +## 7. [Working with Plutus Core](070_working-with-plutus-core.md) + +> **Status** +> +> First draft outline for review and feedback. +> The scope of this draft may encompass more than is needed for our docs. + +- Introduction to Plutus Core +- Plutus Core Syntax and Semantics + - The basic constructs of Plutus Core, such as variables, functions, and applications, as well as the type system that includes types and kinds + - Syntax and semantics of Plutus Core + - Understanding data types used in Plutus Core and how they relate to the types in Haskell +- Compilation Process + - Understanding how high-level Plutus Tx code is compiled down to Plutus Core, including the role of the Plutus compiler and the abstract syntax tree (AST) + - Understanding the compilation target and execution environment of your Plutus Tx code +- Execution Model + - Understanding how your contracts will execute on-chain + - The low-level execution model of Plutus Core + - The cost model for computing resource usage +- Built-in Functions + - Exploring the built-in functions and types provided by Plutus Core that are essential for contract execution. +- Formal Specification of Plutus Core + - The formal [Plutus Core Specification](https://ci.iog.io/job/input-output-hk-plutus/master/x86_64-linux.packages.plutus-core-spec/latest/download/1) for understanding the precise behavior of the Plutus Core language. +- Security Considerations + - The security aspects of smart contract development, including auditing Plutus Core code for safety and correctness. +- Interacting with the Extended UTXO Model (EUTXO) + - Understanding how Plutus Core interacts with the ledger and the EUTXO model specific to Cardano. +- Advanced Topics + - Optimizations, bytecode generation, and other advanced features of Plutus Core for those who want to understand the language at a deeper level. +- Tools and Resources + - The tools available for Plutus Core development, such as decompilers, pretty-printers, and debuggers. + - Troubleshooting + +## 8. [Architectural decision records](080_adr-index.md) + +## 8a. [ADR 1](081_adr1.md) + +## 8b. [ADR 2](082_adr2.md) + +## 8c. [ADR 3](083_adr3.md) + +## 8d. [ADR 4](084_adr4.md) + +## 9. [Reference](090_reference.md) +> **Status** +> +> - Ported over content. +> - Not ready for review. +> - I expect to break this section into multiple pages once we have our docs platform available to use. It is admittedly pretty long right now, but useful to see it all in one file for this stage. + +- Writing scripts + - Plutus Tx Compiler Options + - Optimization techniques for Plutus scripts + - Identifying problem areas + - Using strict let-bindings to avoid recomputation + - Specializing higher-order functions + - Common sub-expression elimination + - Using `error` for faster failure + - Examples + - Common weaknesses + - Double satisfaction + - What is going wrong here? + - Risks + - Solutions + - Unique outputs + - Ban other scripts + - Hard limits + - Risks + - Solutions + - Careful testing + - Bounding data usage + - Providing datums when creating outputs + - Reducing script size costs through reference inputs +- Plutus on Cardano + - Plutus language changes + - Language versions + - Plutus V1 + - Plutus V2 + - Examples + - Built-in functions and types + - Alonzo + - Vasil + - PlutusV3 + - Sums of products + - New cryptographic primitives + - Bitwise primitives + - Upgrading to Vasil and Plutus script addresses + - A Plutus V2 script will not have the same hash value as a Plutus V1 script + - A Plutus V1 script will not necessarily have the same hash value when recompiled with a later version of the Plutus Compiler + - When to export and save the output of a compiled script + - Cost model parameters (Need to investigate how to migrate this content) +- Glossary + +## 10. [Troubleshooting](100_troubleshooting.md) +> **Status** +> +> - Ported over content. +> - Not ready for review. + +- Plugin errors + - Haddock + - Non-`INLINABLE` functions +- Haskell Language Server issues + - Wrong version +- Error codes + - Ledger errors + - Prelude errors + - State machine errors + - Currency errors + +## 11. [FAQ](110_faq.md) +- Draft list of questions in place. Changes to the questions are pending. diff --git a/doc/docs-revamp/adr2.md b/doc/docs-revamp/adr2.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/docs-revamp/closing-tx-simple-auction-v3.png b/doc/docs-revamp/closing-tx-simple-auction-v3.png new file mode 100644 index 00000000000..54d4a0befbb Binary files /dev/null and b/doc/docs-revamp/closing-tx-simple-auction-v3.png differ diff --git a/doc/docs-revamp/double-satisfaction.png b/doc/docs-revamp/double-satisfaction.png new file mode 100644 index 00000000000..a75a0ba20fa Binary files /dev/null and b/doc/docs-revamp/double-satisfaction.png differ diff --git a/doc/docs-revamp/first-bid-simple-auction-v3.png b/doc/docs-revamp/first-bid-simple-auction-v3.png new file mode 100644 index 00000000000..090947d2917 Binary files /dev/null and b/doc/docs-revamp/first-bid-simple-auction-v3.png differ diff --git a/doc/docs-revamp/platform-architecture.png b/doc/docs-revamp/platform-architecture.png new file mode 100644 index 00000000000..4da5fbf4457 Binary files /dev/null and b/doc/docs-revamp/platform-architecture.png differ diff --git a/doc/docs-revamp/second-bid-simple-auction-v3.png b/doc/docs-revamp/second-bid-simple-auction-v3.png new file mode 100644 index 00000000000..8d5969dd57c Binary files /dev/null and b/doc/docs-revamp/second-bid-simple-auction-v3.png differ