# Table of Contents - [Aiken | Getting started](#aiken-getting-started) - [Aiken | Installation Instructions](#aiken-installation-instructions) - [Aiken | EUTxO crash course](#aiken-eutxo-crash-course) - [Aiken | About](#aiken-about) - [Aiken | Learn Aiken in 5 minutes!](#aiken-learn-aiken-in-5-minutes-) - [Aiken | Common Design Patterns](#aiken-common-design-patterns) - [Aiken | Primitive Types](#aiken-primitive-types) - [Aiken | Variables & Constants](#aiken-variables-constants) - [Aiken | Functions](#aiken-functions) - [Aiken | Modules](#aiken-modules) - [Aiken | Validators](#aiken-validators) - [Aiken | What I wish I knew when exploring Cardano](#aiken-what-i-wish-i-knew-when-exploring-cardano) - [Aiken | Control flow](#aiken-control-flow) - [Aiken | Benchmarks](#aiken-benchmarks) - [Aiken | The modern smart contract platform for Cardano](#aiken-the-modern-smart-contract-platform-for-cardano) - [Aiken | Troubleshooting](#aiken-troubleshooting) - [Aiken | Hello, World!](#aiken-hello-world-) - [Aiken | Hello, World! - with Mesh](#aiken-hello-world-with-mesh) - [Aiken | Custom types](#aiken-custom-types) - [Aiken | Hello, World! - with PyCardano](#aiken-hello-world-with-pycardano) - [Aiken | Hello, World! - with cardano-cli](#aiken-hello-world-with-cardano-cli) - [Aiken | Vesting](#aiken-vesting) - [Aiken | Untyped Plutus Core](#aiken-untyped-plutus-core) - [Aiken | Syntax](#aiken-syntax) - [Aiken | Tests](#aiken-tests) - [Aiken | Ecosystem Overview](#aiken-ecosystem-overview) - [Aiken | Command-line utilities](#aiken-command-line-utilities) - [Aiken | Builtins](#aiken-builtins) - [Aiken | Resources](#aiken-resources) - [Aiken | Gift Card](#aiken-gift-card) - [Unknown](#unknown) --- # Aiken | Getting started Fundamentals Getting Started Getting started =============== Checklist[](https://aiken-lang.org/fundamentals/getting-started#checklist) --------------------------------------------------------------------------- Here's a little checklist to get started with Aiken. Use it if you don't know where to start. * [ ] Install `aikup` and `aiken` following [the installation instructions](https://aiken-lang.org/installation-instructions#installation-instructions) ; * [ ] Create your first project as indicated below; * [ ] Have a look at [the standard library (opens in a new tab)](https://aiken-lang.github.io/stdlib/) ; * [ ] Complete the [first steps of the _"Hello, World!"_ tutorial](https://aiken-lang.org/example--hello-world/basics) ; * [ ] If you're not familiar with the eUTxO model (datums, redeemers, etc.), read [the eUTxO Crash Course](https://aiken-lang.org/fundamentals/eutxo) ; * [ ] Complete the [end-to-end part of the _"Hello, World!"_ tutorial](https://aiken-lang.org/example--hello-world/end-to-end/pycardano) using your favorite frontend library; * [ ] Have a look at [Aiken's Awesome List (opens in a new tab)](https://github.com/aiken-lang/awesome-aiken#readme) and explore tutorials, courses or projects that catch your attention; * [ ] Explore the [language-tour](https://aiken-lang.org/language-tour/primitive-types) ; * [ ] Join the [#aiken channel on Discord (opens in a new tab)](https://discord.gg/ub6atE94v4) and [follow us (opens in a new tab)](https://twitter.com/aiken_eng) on Twitter! Want more? * [ ] Try more complex examples like [The _Gift Card_](https://aiken-lang.org/example--gift-card) ; * [ ] Experiment with [property-based testing](https://aiken-lang.org/language-tour/tests#property-based-test) ; * [ ] Read about [what we wish we knew](https://aiken-lang.org/fundamentals/what-i-wish-i-knew) when we started with Cardano; * [ ] Learn about [design patterns (opens in a new tab)](https://github.com/Anastasia-Labs/design-patterns?tab=readme-ov-file#design-patterns) in the eUTxO model; Creating a new project[](https://aiken-lang.org/fundamentals/getting-started#creating-a-new-project) ----------------------------------------------------------------------------------------------------- Use `aiken new foo/bar` to create a scaffold a new project and follow instructions from the generated `README.md` at the root of your project. On success, your project should look roughly as follows: . ├── README.md ├── aiken.toml ├── lib │   └── bar └── validators Compiling a project[](https://aiken-lang.org/fundamentals/getting-started#compiling-a-project) ----------------------------------------------------------------------------------------------- Use `aiken build` to compile a project, and `aiken check` to only type-check a project and run tests. If you're writing a library `aiken docs` is a powerful utility to generate HTML documentation from types, type annotations and comments. Abuse it! Finally, once compiled, you may look at the `aiken blueprint` command group to generate addresses, apply parameters and convert the build output to various formats. Project structure[](https://aiken-lang.org/fundamentals/getting-started#project-structure) ------------------------------------------------------------------------------------------- ### Folder organisation[](https://aiken-lang.org/fundamentals/getting-started#folder-organisation) Aiken projects divide their source code in two categories: library code, and application code. Library code must be located in a `lib` folder, and application code (i.e. on-chain validators) located in a `validators` folder. After compilation, the compiler will generate a Plutus blueprint (`plutus.json`), which is an interoperable document that summarizes your project. The blueprint also contains compiled code for each validator of your project and their corresponding hash digests to be used in addresses. ### Configuration[](https://aiken-lang.org/fundamentals/getting-started#configuration) A project has a top-level `aiken.toml` file at its root containing metadata about the projects, as well as dependencies required by it. Here's a sample of (documented) project configuration: aiken.toml # [Optional - if 'members' is present] # # Project's name, as {organisation}/{repository} name = "foo/bar" # [Optional - if 'members' is present] # # Project's version, we recommend semantic versioning for libraries. However, # any UTF-8 string is accepted. version = "1.0.0" # [Optional] # # A license name. We recommend Apache-2.0 or MPL-2.0 for open source projects. licence = "Apache-2.0" # [Optional] # # An informative albeit short description of the project. description = "A next-level DeFi platform" # [Optional] # # A list of folders to look for Aiken projects. See Workspaces below. members = ["."] # [Optional] # # Structured information on a project to show in generated documentation. [repository] platform = "github" # Platform type, only `github` for now. user = "aiken-lang" # Username or organisation on that platform project = "stdlib" # Repository or project on that platform # [Optional] # # A list of dependencies. Avoid editing by hand, use `aiken packages` to manage # them. [[dependencies]] source = "github" # Platform type, only `github` for now. name = "aiken-lang/stdlib" # The github project, in the form of {organisation}/{repository}. version = "main" # Version, as a git tag, branch name or git commit hash ### Workspaces[](https://aiken-lang.org/fundamentals/getting-started#workspaces) ⚠️ This feature is still in early development but it is simple to incrementally improve so we are making it available early in a less useful state to get some feedback. Aiken has minimal support for workspaces (a.k.a. monorepos). This is enabled using the `members` configuration fields in an `aiken.toml` file at the root of a project. myproject/aiken.toml members = ["pkgs/my_library", "pkgs/my_validators"] Globs are supported so it is possible to do this: myproject/aiken.toml members = ["pkgs/*"] Note also that, other configuration fields are ignored when `members` is present. #### Caveats[](https://aiken-lang.org/fundamentals/getting-started#caveats) * This does not support referencing local dependencies by path (yet) * Dependencies are not intelligently resolved and cached once. Each sub-project gets it's own `build/` folder with dependencies redundantly fetched for now. * The LSP is not yet aware of this configuration option and may behave strangely. Well-known packages & modules[](https://aiken-lang.org/fundamentals/getting-started#well-known-packages--modules) ------------------------------------------------------------------------------------------------------------------ ### Aiken's prelude[](https://aiken-lang.org/fundamentals/getting-started#aikens-prelude) The `prelude` contains a minimum set of functions, constructors and modules available _by default_ to all Aiken projects. You can find it on [aiken-lang/prelude (opens in a new tab)](https://aiken-lang.github.io/prelude/) and at the bottom of this website. ### Aiken's standard library[](https://aiken-lang.org/fundamentals/getting-started#aikens-standard-library) The standard library (or `stdlib` in short) gathers useful functions and data-structures that might come in handy in your Aiken programming journey. It's also a good reference of well-written and well-tested Aiken code if you need some examples to get started with. You can find it on [aiken-lang/stdlib (opens in a new tab)](https://aiken-lang.github.io/stdlib/) and at the bottom of this website. [Installation](https://aiken-lang.org/installation-instructions "Installation") [EUTxO Crash Course](https://aiken-lang.org/fundamentals/eutxo "EUTxO Crash Course") --- # Aiken | Installation Instructions Installation Installation Instructions[](https://aiken-lang.org/installation-instructions#installation-instructions) -------------------------------------------------------------------------------------------------------- via aikupmanually Via `aikup` ----------- First, install **aikup**: a basic cross-platform utility tool to download and manage Aiken's across multiple versions and for seamless upgrades. Once installed, simply run: aikup `aikup` alone installs the latest version available. You can install specific versions by specifying a version number. For usage instructions, see `aikup --help`. ### From a package manager #### npm npm install -g @aiken-lang/aikup #### Homebrew brew install aiken-lang/tap/aikup #### From url (Linux & MacOS) curl --proto '=https' --tlsv1.2 -LsSf https://install.aiken-lang.org | sh #### From url (Windows) powershell -c "irm https://windows.aiken-lang.org | iex" > You can also find an MSI [here (opens in a new tab)](https://github.com/aiken-lang/aikup/releases/latest) > . Language Server[](https://aiken-lang.org/installation-instructions#language-server) ------------------------------------------------------------------------------------ The `aiken` command-line comes with a built-in [language server (opens in a new tab)](https://microsoft.github.io/language-server-protocol/) . If needed, configure your language client with the following configuration (refer to your language client's instructions): * command: `aiken lsp` (the command is hidden from the command-line help usage) * root pattern: `aiken.toml` * filetype: aiken (`.ak`) The language server supports a variety of capabilities (but not all). For more details, please refer to the [supported capabilities (opens in a new tab)](https://github.com/aiken-lang/aiken/tree/main/crates/aiken-lsp#supported-capabilities) on the main repository. Auto-completion[](https://aiken-lang.org/installation-instructions#auto-completion) ------------------------------------------------------------------------------------ The command-line comes with few auto-completion scripts for `bash`, `zsh` and `fish` users. The scripts can be obtained using the `aiken completion ` command. You can install completions on their standard/default locations as follows: bashzshfish aiken completion bash --install # or, manually aiken completion bash > /usr/local/share/bash-completion/completions/aiken source /usr/local/share/bash-completion/completions/aiken Editor integrations[](https://aiken-lang.org/installation-instructions#editor-integrations) -------------------------------------------------------------------------------------------- The following plugins provide syntax highlighting and indentation rules for Aiken. | Editor | Plugin | | --- | --- | | Zed | [aiken-lang/zed-aiken (opens in a new tab)](https://github.com/aiken-lang/zed-aiken) | | VSCode | [aiken-lang/vscode-aiken (opens in a new tab)](https://github.com/aiken-lang/vscode-aiken) | | Vim/Neovim | [aiken-lang/editor-integration-nvim (opens in a new tab)](https://github.com/aiken-lang/editor-integration-nvim) | | Emacs | [aiken-lang/aiken-mode (opens in a new tab)](https://github.com/aiken-lang/aiken-mode) | [Getting Started](https://aiken-lang.org/fundamentals/getting-started "Getting Started") --- # Aiken | EUTxO crash course Fundamentals EUTxO Crash Course EUTxO crash course ================== If you have absolutely no idea what developing on Cardano looks like, worry not. You just found the right piece to get started. Aiken is a language platform that makes on-chain programming easy. But what is _"on-chain programming"_ to begin with? While this succinct documentation piece has no ambition to be a complete course on blockchains, it should give you enough insights to build a basic understanding of the fundamentals. ⚠️ This course will reference cryptography concepts such as **hash digests** or **digital signatures**. We, therefore, expect readers to be either familiar with those concepts (at least a tiny bit) or to read up on them. There are plenty of resources available in the wild regarding cryptography and this crash course **isn't** one of them. As a suggestion, if you want to learn more on those fundamentals, we heartwarmingly recommend [Cardano Blockchain Certified Associate (CBCA)'s first module (opens in a new tab)](https://academy.cardanofoundation.org/) . Blocks & transactions[](https://aiken-lang.org/fundamentals/eutxo#blocks--transactions) ---------------------------------------------------------------------------------------- Blockchains are made of blocks. And blocks are made of transactions. Without going into the details, you can think of blocks as being objects divided into two parts: a header and a body. The header contains information about the blocks, such as who produced them and when they were made. The body is nothing more than an ordered sequence of transactions. Note that the _"chain"_ of blockchain comes from how blocks reference one another. Indeed, each block header includes at least two things: * A hash digest of the block body * A hash digest of the previous block header Block ┏━ Header ━━━━━━━━━━━━━━┳━ Body ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ ┃ ┃ ┃ Body hash ┃ ┌────────────────┬────────────────┬─────┐ ┃ ┃ Previous header hash ┃ │ Transaction #1 │ Transaction #2 │ ... │ ┃ ┃ Timestamp ┃ └────────────────┴────────────────┴─────┘ ┃ ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ A hash digest is a tamper-proof mechanism that maps an input value to a fixed-sized output. Think of it as a way to assign an identifier to a piece of content, such that the identifier depends on the content itself: change the content, change the identifier. A chain is formed by including in every block header: * a hash of the block body; and * a header hash of the previous block. Changing any transaction in a block will change the block body hash, thus changing the block header hash, the header hash of the next block, and so on, invalidating the entire chain that follows. ____ ____ ____ ____ ____ / /\ / /\ / /\ / /\ / /\ o ❮❮ /____/ \ ❮❮ /____/ \ ❮❮ /____/ \ ❮❮ /____/ \ ❮❮ /____/ \ ... \ \ / \ \ / \ \ / \ \ / \ \ / ╿ \____\/ \____\/ \____\/ \____\/ \____\/ │ │ ╿ │ │ └ Genesis configuration │ └ Block A transaction is, therefore, the most fundamental primitive on blockchains. They are the mechanism whereby users (a.k.a you) can take actions to change the state of the blockchain. A chain starts from an initial state typically referred to as _genesis configuration_. And from there, transactions map a previous state into a new state. Finally, blocks are merely there to batch transactions together. Unspent Transaction Outputs[](https://aiken-lang.org/fundamentals/eutxo#unspent-transaction-outputs) ----------------------------------------------------------------------------------------------------- In the traditional database world, a _transaction_ is a means to bundle together a series of atomic operations so that all are successful or none happen. In the financial world, it is a way to transfer assets from one location to another. In the blockchain world, it is a bit of both. A transaction is, first and foremost, an object with an input from where it takes assets and an output to where it sends them. Often, as is the case in Cardano, transactions have many inputs and many outputs. And, in addition to inputs and outputs, blockchain protocols often include other elements that modify different parts of the blockchain state (e.g. delegation certificates, governance votes, user-defined assets definitions...) More so, like in the database world, a transaction is an all-or-nothing atomic series of commands. Either it is valid, and all its changes are applied, or it isn't, and none are applied. We'll talk more about other capabilities later. For now, let's focus on inputs and outputs, starting with the outputs. ### Outputs[](https://aiken-lang.org/fundamentals/eutxo#outputs) In Cardano, an output is an object that describes at least two things: * a quantity of assets -- also known as, a _value_; * a condition for spending (and/or delegating) those assets -- also known as an _address_. In addition, a data payload can also be added to outputs but let's not bother with that just now. The role of the value is pretty transparent, and it indicates how many assets the output holds. Incidentally, Cardano supports two kinds of assets: the main protocol currency (a.k.a. Ada); and user-defined currencies. Both live side-by-side in values though slightly different rules apply to each. The address captures the logic that tells the protocol under what conditions one can utilize the assets at a particular output. It is what defines ownership of the assets. We'll explore this very soon. Bear with us a little more. Output ┏━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃ Value ┃ Address ┃ Data payload ┃ ┃ ┃ ┃ ┃ ┗━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┛ ### Inputs[](https://aiken-lang.org/fundamentals/eutxo#inputs) An input is a reference to a previous output. Think of outputs as post-it notes with a unique serial number and inputs as being this serial number. ![](https://aiken-lang.org/_next/static/media/post-it-notes.83493321.jpg) A transaction is a document indicating which post-it notes should be destroyed and which new ones should be pinned to the wall. Note that there are rules regarding the construction of transactions. For example, there must be as much value in as there's value out. Said differently, the total value should balance out but might be shuffled differently. An output that hasn't been spent yet (i.e. is still on the wall) is called -- you guessed it -- an _unspent transaction output_, or UTxO in short. The blockchain state results from looking at the entire wall of remaining post-it notes. In addition to all of the available UTxOs, the state also includes any additional data defined by the protocol. Okay, back to inputs. Technically speaking, an input's _"serial number"_ is the hash digest of the transaction that emitted the output it refers to and the position of the output within that transaction. These two elements make each input unique. And because outputs are removed from the available set (the post-it note is destroyed) when spent, they can only be spent once. This is ensured by the blockchain protocol. Input ┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ ┃ ┃ ┃ ┃ Transaction hash ┃ Output index ┃ ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┛ Where do the first outputs come from? If you've carefully followed the narrative we just went through, you might have realized that we have a chicken-and-egg situation. Inputs are references to outputs. And outputs are created by spending inputs. This is what the genesis configuration is for. It defines the starting point of the blockchain in the form of an agreed-upon initial list of outputs. Those outputs can be referred to using some special identifiers. For example, the genesis configuration hash digest and the output's position in the configuration. How the genesis configuration comes to be is also an interesting question but out of the scope of the current course. In the case of Cardano, the initial distribution resulted from an initial token vouchers sale where a portion of the total Ada supply was sold in the form of vouchers to stakeholders before the launch of the network. ### TL;DR[](https://aiken-lang.org/fundamentals/eutxo#tldr) Let's quickly recap what we've seen so far: * A blockchain has an initial state called a _genesis configuration_; * A _transaction_ captures instructions to modify that state (e.g. transfer of assets); * A _block_ batches transactions together and has a reference to a parent block; * Assets movement are expressed using _inputs_ and _outputs_ in transactions; * An _output_ is an object with at least an _address_ and a _value_; * An _address_ describes the conditions needed to use the _value_ associated with it; * An _input_ is a reference to a previous _output_. Addresses[](https://aiken-lang.org/fundamentals/eutxo#addresses) ----------------------------------------------------------------- ### Overview[](https://aiken-lang.org/fundamentals/eutxo#overview) It is now time to delve more into Cardano addresses. A typical address is made of 2 or 3 parts: Address ┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ ┃ ┃ ┃ ┃ Header ┃ Payment credentials ┃ Delegation credentials ┃ ┃ ┃ ┃ ┃ ┗━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━┛ We said 2 or 3 because the last part -- the delegation credentials -- is optional. The first part is called the `Header`, and it describes the type of address (i.e. what comes next) and the network within which this address can be used. We call that last bit a _network discriminant_ and it prevents silly mistakes like sending real Mainnet funds to a test address. An address is represented as a sequence of bytes, usually encoded using [bech32 (opens in a new tab)](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#user-content-Introduction) or simply [base16 (opens in a new tab)](https://en.wikipedia.org/wiki/Hexadecimal) text strings. For example: An address (bech32) addr1x8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gt7r0vd4msrxnuwnccdxlhdjar77j6lg0wypcc9uar5d2shskhj42g or alternatively The same address (base16) 31c37b1b5dc0669f1d3c61a6fddb2e8fde96be87b881c60bce8e8d542fc37b1b5dc0669f1d3c61a6fddb2e8fde96be87b881c60bce8e8d542f We can dissect the latter text string to make the three parts mentioned above more apparent: Type = 3 ┐┌ Network = 1 (1 = mainnet, 0 = testnet) ││ ╽╽ Header: 31 Payment credentials: c37b1b5dc0669f1d3c61a6fddb2e8fde96be87b881c60bce8e8d542f Delegation credentials: c37b1b5dc0669f1d3c61a6fddb2e8fde96be87b881c60bce8e8d542f As we can see, this address is a type 3, is for mainnet and uses the same credentials for both the payment and the delegation part. You will often need to convert back-and-forth between bech32-encoded strings and hex-encoded strings. A great command-line tool for working with bech32 can be found at [input-output-hk/bech32 (opens in a new tab)](https://github.com/input-output-hk/bech32#bech32-command-line) . Use it! ### Payment credentials[](https://aiken-lang.org/fundamentals/eutxo#payment-credentials) The next part is the _payment credentials_, also called the _payment part_. This is what describes the spending conditions for the address. Remember how UTxOs are like post-it notes on a wall? Well, you don't get to hang them or pick them up directly yourself. You have to hand over a _transaction_ to the network validators. Imagine an employee who's gatekeeping the wall of post-it notes and to whom you must give a form that describes what you want to do. Written on each post-it note are the conditions that must be met before it can be picked up and destroyed. That's what the payment credentials are for in the address. They come in one of two forms: * a verification key hash digest; or * a script hash digest. In the first form, the validator nodes -- or the employee -- will ask you to provide a digital signature from the signing key corresponding to the verification key. This approach relies on asymmetric cryptography, where one generates credentials as a public (verification) and private (signing) key pair. In the address, we store only a hash digest of the verification key for conciseness and to avoid revealing it too early (even though it is public material). When spending from such an address, one must reveal the public key and show a signature of the entire transaction as _witnesses_ (a.k.a proofs). This way of specifying spending conditions is relatively straightforward but also constrained because it doesn't allow for expressing any elaborate logic. This is where the second form gets more interesting. Cardano allows locking funds using a script representing the validation logic that must be satisfied to spend funds guarded by the address. We typically call such addresses "_script addresses_". Similarly to the first form, the entire script must be provided as a witness by any transaction spending from a script address, as well as any other elements required by the script. Scripts are like predicates. Said differently, they are functions that return a boolean value: `True` or `False`. To be considered valid, all scripts in a transaction must return `True`. We'll explore how this mechanism works in a short moment. ### Delegation credentials[](https://aiken-lang.org/fundamentals/eutxo#delegation-credentials) Addresses may also contain _delegation credentials_, also called a _delegation part_. We will only go a little into the details here, but think of the delegation credentials as a way to control what can be done with the _stake_ associated with the address. The _stake_ corresponds to the Ada quantity in the output's value that the consensus protocol counts to elect block producers. In Cardano, the stake can be delegated to registered entities called stake pools. By delegating, one indicates that the stake associated with an output should be counted as if it belonged to the delegatee, increasing their chance of producing a block. In return, the delegatee agrees to share a portion of their block-producing rewards with the delegator. While the payment credentials control how to **spend** an output, delegation credentials control two separate operations: * how to **publish** a delegation certificate (e.g. to delegate stake to a stake pool); * how to **withdraw** rewards associated with the stake credentials. Like payment credentials, delegation credentials comes in two forms: as verification key hash digest or as script hash digest. More information about addresses and how they work can be found in [CIP-0019 (opens in a new tab)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019/#readme) ### TL;DR[](https://aiken-lang.org/fundamentals/eutxo#tldr-1) Address ┌ For spending │ ╽ ┏━ Header ━━━━━━━━━━━━━┳━ Payment credentials ━━━━━━━┳━ Delegation credentials ━━━━┓ ┃ ┃ ┃ ┃ ┃ ┃ ┌───────────────────────┐ ┃ ┌───────────────────────┐ ┃ ┃ ┌──────┬─────────┐ ┃ │ Verification key hash │ ┃ │ Verification key hash │ ┃ ┃ │ Type │ Network │ ┃ ├────────── OR ─────────┤ ┃ ├────────── OR ─────────┤ ┃ ┃ └──────┴─────────┘ ┃ │ Script hash │ ┃ │ Script hash │ ┃ ┃ ┃ └───────────────────────┘ ┃ └───────────────────────┘ ┃ ┃ ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ╿ │ └ For: - publishing certificates - withdrawing rewards Before we move on, let's recap again: * An _address_ is made up of either 2 or 3 parts: it always contains a _header_ and _payment credentials_, and may optionally also contain _delegation credentials_; * The _header_ describes the _type_ of address and the _network_ it is for; * the _delegation credentials_ part, while optional, is still highly recommended; * Credentials (payment or delegation) take one of two forms: * a verification key hash; * a script hash; * Payment credentials control how to **spend** from an _address_; * Delegation credentials control how to **publish** certificates and how to **withdraw** rewards; * A script allows the definition of arbitrary validation logic. Scripts, Datums and Redeemers[](https://aiken-lang.org/fundamentals/eutxo#scripts-datums-and-redeemers) -------------------------------------------------------------------------------------------------------- ### Overview[](https://aiken-lang.org/fundamentals/eutxo#overview-1) Hang in there! We are almost at the end of this crash course. We've had a look at what UTxOs are and what addresses are made of, and we spoke a bit about scripts. In particular, we said that scripts are like predicates: that is, pure functions (in the mathematical sense) that take a transaction as an argument and return either `True` or `False`. Well, _not exactly_. We lied to you (but only a tiny bit). If we only had that, it would be hard to express more elaborate logic. In particular, capturing a state, which programs often require, would be infeasible. A state and transitions from that state. This is where the _extended UTxO_ model comes in. It adds two new components to what we've already seen: datums and redeemers. We mentioned the datum earlier without calling it a datum when we said that outputs contained a value, an address and a data payload. This is what the datum is, a free payload that developers can use to attach data to script execution. When a script is executed in a spending scenario, it receives not only the transaction as context but also the datum associated with the output being spent. The redeemer, on the other hand, is another piece of data that is also provided with the transaction for any script execution. Notice that the datum and redeemer intervene at two distinct moments. A datum is set when the output is created (i.e. when the post-it note is hung on the wall, it is part of the note). In contrast, the redeemer is provided only when spending the output (i.e. provided along with the form as it is handed over to the employee). ### Analogy[](https://aiken-lang.org/fundamentals/eutxo#analogy) Another way to look at scripts, datums and redeemers is to think of them together as forming parameterised mathematical functions. Script, datum and redeemer Script ╭─────────╮ f(x) = x * a + b = true | false ╿ ╿ ╿ Redeemer ┘ │ │ └─┬─┘ Datum The script defines the function as a whole. It indicates how the parameters and arguments are combined to produce a boolean outcome. The datum corresponds to the parameters of the function. It allows _configuring the function_ and re-using a similar validation logic with different settings. Both the function and the parameters are defined when assets are locked in an output. Which leaves only the function argument to be later provided. That argument is the redeemer (as well as the rest of the transaction). This is why scripts are often referred to as _validators_. Unlike some other blockchain systems, they are also, therefore, fully deterministic. Their execution only depends on the transaction they're involved with, and evaluating the transaction's outcome is possible before sending it to the network. Datums act as local states, and redeemers are user inputs provided in the transaction itself. If we take a step back and look at the typical public/private key procedure for spending funds, we can see how eUTxO is merely a generalization of that. Indeed, the public key (hash) can be seen as _the datum_, whereas the signature is the _redeemer_. The script is the digital signature verification algorithm that controls whether the signature is valid w.r.t. the provided key. ### Purposes[](https://aiken-lang.org/fundamentals/eutxo#purposes) So far, we've mostly talked about scripts in the context of validating whether an output can be spent. We've also briefly mentioned earlier how scripts can be used to control the publication of delegation certificates or how consensus rewards can be withdrawn. These different use cases are commonly referred to as script purposes. Until now, we've seen three purposes: `spend`, `publish` and `withdraw`. There are three more: `mint`, `vote` and `propose` Each purpose indicates _for what purpose_ a script is being executed. During validation, that information is passed to the script alongside the transaction and the redeemer. Note that only scripts executed with the `spend` purpose are given a datum. This is because they can leverage the data payload present in outputs, unlike the other purposes that do not get this opportunity. #### Mint[](https://aiken-lang.org/fundamentals/eutxo#mint) The mint purpose refers to scripts that are executed to validate whether user-defined assets can be minted (i.e. created) or burned (i.e. destroyed). Cardano indeed supports user-defined assets which are used to represent both fungible quantities (like a protocol currency) or non-fungible quantities (a.k.a NFTs). The rules that govern the creation or destruction of an asset are defined as a script. We often refer to such scripts as _minting policies_, which correspond to the `mint` purpose above. #### Vote[](https://aiken-lang.org/fundamentals/eutxo#vote) The vote purpose validates governance votes from script delegate representative. It is executed once per transaction per delegate credential, even if the transaction contains in fact multiple votes. This is handy to build more complex delegate representative than mere public keys; for example, to emulate a hot/cold key setup. #### Propose[](https://aiken-lang.org/fundamentals/eutxo#propose) Finally, the propose purpose is a very unique one since there can only be one script in the entire ledger that may execute that purpose: the constitution guardrails script. That script is (optionally) defined with the on-chain constitution, and is executed by ledger for every transaction that is proposing a governance action. Its goal is to ensure that some proposals are rejected from the get-go to prevent spam with ill-advised proposals on the network -- in particular with regards to protocol parameters. For example, a proposal that would lower the block size from its current value to zero would completely incapacitate the chain and has little chance of being ever accepted anyway. The guardrails script can prevent that to even be submitted. In principle, it is a programmatic enforcement of the constitution. ### TL;DR[](https://aiken-lang.org/fundamentals/eutxo#tldr-2) And we've reached the end of this crash course. Let's do a final recap regarding scripts, datums and redeemers. * _Scripts_ are akin to parameterized predicate functions, returning either true or false. * _Datums_ take the role of function parameters, whereas _redeemers_ the one of argument. * _Scripts_ are also called _validators_ and are completely deterministic. * _Scripts_ are used to validate specific operations in a transaction. * What a _script_ is used for is referred to as its _purpose_. There are 6 purposes: * `mint` -- controls how to mint or burn assets; * `spend` -- controls how to spend outputs; * `withdraw` -- controls how to withdraw consensus rewards; * `publish` -- controls how to publish delegation certificates; * `vote` -- controls how to vote on proposal procedures; * `propose` -- controls constitutionality of proposal procedures. * Only _spending scripts_ (i.e. purpose=spend) have access to a datum. [Getting Started](https://aiken-lang.org/fundamentals/getting-started "Getting Started") [Common Design Patterns](https://aiken-lang.org/fundamentals/common-design-patterns "Common Design Patterns") --- # Aiken | About About ===== Team[](https://aiken-lang.org/credits#team) -------------------------------------------- Aiken is currently maintained by [Lucas Rosa (opens in a new tab)](https://github.com/rvcas) , [Kasey White (opens in a new tab)](https://github.com/MicroProofs) , [Matthias Benkort (opens in a new tab)](https://github.com/KtorZ) , and [Riley Kilgore (opens in a new tab)](https://github.com/Riley-Kilgore) . You can check out the full list of contributors on [GitHub (opens in a new tab)](https://github.com/aiken-lang/aiken/graphs/contributors) . Credits[](https://aiken-lang.org/credits#credits) -------------------------------------------------- ### ![](https://avatars.githubusercontent.com/u/161820110?s=200&v=4) PRAGMA[](https://aiken-lang.org/credits#-pragma) [Pragma (opens in a new tab)](https://pragma.io/) holds the license for the project and handles conflict resolution. ### ![](https://avatars.githubusercontent.com/u/12909177?s=200&v=4) Input Output[](https://aiken-lang.org/credits#-input-output) [Input Output (opens in a new tab)](https://iohk.io/) supports the project by fueling its development with engineering resources. ### ![](https://raw.githubusercontent.com/aiken-lang/.github/main/.github/CF-Logo-Full-Blue_1500px.png) ![](https://raw.githubusercontent.com/aiken-lang/.github/main/.github/CF-Logo-Full-White_1500px.png)[](https://aiken-lang.org/credits#-) The [Cardano Foundation (opens in a new tab)](https://cardanofoundation.org/) supports the project by fueling its development with engineering resources. ### ![](https://avatars.githubusercontent.com/u/92830323?s=200&v=4) TxPipe[](https://aiken-lang.org/credits#-txpipe) [TxPipe (opens in a new tab)](https://txpipe.io/) has been Aiken's original home, and still provides great support to the project as well as a room for discussion on their Discord server. Besides, TxPipe developed [Pallas (opens in a new tab)](https://github.com/txpipe/pallas) -- Rust-native building blocks for the Cardano blockchain ecosystem -- which Aiken uses internally for some features. Through their platform [demeter.run (opens in a new tab)](https://demeter.run/) they also provide infrastructure and streamlined access to Aiken. ### Louis Pilfold[](https://aiken-lang.org/credits#louis-pilfold) A substantial part of Aiken was forked from [the Gleam language (opens in a new tab)](https://gleam.run/) made and maintained by Louis Pilfold. We are thankful for the love he's put into Gleam and are delighted to build on top of his excellent work. Media kit[](https://aiken-lang.org/credits#media-kit) ------------------------------------------------------ Need some media kit for a press release? Look no further, you can find Aiken's assets brand in [aiken-lang/branding (opens in a new tab)](https://github.com/aiken-lang/branding) . Or just, download [a zip archive (opens in a new tab)](https://github.com/aiken-lang/branding/archive/refs/heads/main.zip) with everything in there. DescriptionPreviewIcon![](https://raw.githubusercontent.com/aiken-lang/branding/main/assets/icon.png)Logo | Dark | Light | | --- | --- | | ![](https://raw.githubusercontent.com/aiken-lang/branding/main/assets/logo-dark.png) | ![](https://raw.githubusercontent.com/aiken-lang/branding/main/assets/logo-light.png) | Typography | Dark | Light | | --- | --- | | ![](https://raw.githubusercontent.com/aiken-lang/branding/main/assets/aiken-typo8k-dark.png) | ![](https://raw.githubusercontent.com/aiken-lang/branding/main/assets/aiken-typo8k.png) | Social Card![](https://aiken-lang.org/open-graph.png) Thanks to [@nkz (opens in a new tab)](https://twitter.com/nkzthecreator) for the work on the logo and [© Hurcastock (opens in a new tab)](https://www.hurcastock.com/) for the stunning illustrations. Press releases & interviews[](https://aiken-lang.org/credits#press-releases--interviews) ----------------------------------------------------------------------------------------- ### April 2024[](https://aiken-lang.org/credits#april-2024) * [Software Unscripted: Compiling Smart Contracts with Lucas Rosa (opens in a new tab)](https://shows.acast.com/software-unscripted/episodes/664fde448c77cc0013b33385) podcast by _[Software Unscripted (opens in a new tab)](https://x.com/sw_unscripted) _ ### March 2024[](https://aiken-lang.org/credits#march-2024) * [High-level overview of Aiken (opens in a new tab)](https://www.youtube.com/watch?v=6qZDldB3HLA) video by Whiteboard Cardano ### September 2023[](https://aiken-lang.org/credits#september-2023) * [Optimizing the Throughput of Cardano: The Evolution of Indigo Protocol’s Smart Contracts (opens in a new tab)](https://indigoprotocol1.medium.com/optimizing-the-throughput-of-cardano-the-evolution-of-indigo-protocols-smart-contracts-a498ecc42823) article by _[Indigo (opens in a new tab)](https://indigoprotocol.io/) _ * [Disrupting DeFi: How Aiken Changed the Cardano Landscape In Less Than A Year (opens in a new tab)](https://medium.com/@lenfi/disrupting-defi-how-aiken-changed-the-cardano-landscape-in-less-than-a-year-946c113d4636) article by _[LenFi (opens in a new tab)](https://lenfi.io/) _ * [Aiken to Help Scale Cardano DeFi (opens in a new tab)](https://medium.com/tap-in-with-taptools/aiken-to-help-scale-cardano-defi-371027f6c7ce) article by _[TapTools (opens in a new tab)](https://www.taptools.io/) _ * [Aiken Brings a Massive Cardano Smart Contract Performance Increase (opens in a new tab)](https://www.youtube.com/watch?v=4Y1UieAhszY) video by _[P₳UL (opens in a new tab)](https://twitter.com/cwpaulm) _ ### May 2023[](https://aiken-lang.org/credits#may-2023) * [Aiken: Revolutionizing Smart Contract Development on Cardano (opens in a new tab)](https://www.youtube.com/watch?v=SpNX3Hun7fM) video by _[AdaPulse (opens in a new tab)](https://adapulse.io/) _ ### April 2023[](https://aiken-lang.org/credits#april-2023) * [Aiken: The Future of Smart Contracts (opens in a new tab)](https://cardanofoundation.org/en/news/aiken-the-future-of-smart-contracts/) article by _[The Cardano Foundation (opens in a new tab)](https://twitter.com/Cardano_CF) _ * [Aiken: Revolutionizing Smart Contract Development on Cardano (opens in a new tab)](https://adapulse.io/aiken-revolutionizing-smart-contract-development-on-cardano/) article by _[Sooraj (opens in a new tab)](https://twitter.com/SoorajKSaju) _ ### December 2022[](https://aiken-lang.org/credits#december-2022) * [Writing Smart Contracts on Cardano with Aiken (opens in a new tab)](https://www.youtube.com/watch?v=JWmvVJz_X1M&pp=ygUNYWlrZW4gY2FyZGFubw%3D%3D) video by _[LearnCardano (opens in a new tab)](https://learncardano.io/) _ License[](https://aiken-lang.org/credits#license) -------------------------------------------------- Aiken is licensed under [Apache-2.0 (opens in a new tab)](https://github.com/aiken-lang/aiken/blob/main/LICENSE) . --- # Aiken | Learn Aiken in 5 minutes! Language Tour Learn Aiken in 5 minutes! ========================= The following excerpt gives you a quick tour of Aiken as a language. It's a good cheatsheet if you are already familiar with similar languages or if you need to remind yourself about Aiken. For details, do not hesitate to look at the other sections of the language tour which explain in more depth all the features of the language. // ## Variables & Constants // Constants are defined using 'const' // Constants may be used at the top level of a module const my_constant: Int = 42 // Values assigned to let-bindings are immutable // New bindings can shadow previous bindings let x = 1 let y = x let x = 2 // y + x == 3 // ## Functions // Functions are defined using 'fn' and can be named fn add(a: Int, b: Int) -> Int { a + b } // Annotations are often optional (albeit recommended), as they can be inferred. fn add_inferred(a, b) { a + b } // Functions are private by default. They can be exported with the 'pub' keyword. pub fn public_function(x: Int) -> Int { x * 2 } // Functions can accept functions as arguments fn apply_function_twice(x, f: fn(t) -> t) { f(f(x)) } // We can achieve the same outcome as above with pipelining using '|>' fn apply_function_with_pipe_twice(x, f: fn(t) -> t) { x |> f |> f } // Function can be partially applied to create new functions. This behavior is also referred to as 'function captures'. fn capture_demonstration() { let add_one = add(_, 1) apply_function_twice(2, add_one) // Evaluates to 4 } // ### Validators // Validators are special functions in Aiken for smart contracts validator my_validator { mint(redeemer: Data, policy_id: PolicyId, self: Transaction) { todo @"smart contract logic goes here" } spend(datum: Option, redeemer: Data, utxo: OutputReference, self: Transaction) { todo @"smart contract logic goes here" } } // ### Backpassing // Functions that take callbacks can leverage backpassing using `<-` fn cubed_backpassing(n) { let total <- apply_function_twice(n) total * n } // which is equivalent to writing: fn cubed_callback(n) { apply_function_twice(n, fn(total) { total * n }) } // ## Data Types // Aiken has several primitive types: const my_int: Int = 10 const my_bool: Bool = True const my_string: ByteArray = "Hello, Aiken!" const my_hex_string: ByteArray = #"666f6f" const my_utf8_string: String = @"Hello, Aiken!" // Lists const my_list: List = [1, 2, 3, 4, 5] // Tuples const my_tuple: (Int, ByteArray) = (1, "one") // ### Custom Types // Records can be defined using 'type', a constructor and typed fields. type RGB { red: Int, green: Int, blue: Int, } // Updating some of the fields of a custom type record. fn set_red_255(rgb: RGB) { RGB {..rgb, red: 255} } // Enums are also supported, as well as full algebraic data-types, with (or // without) generic arguments. pub type Option { None Some(a) } // Types can also be aliased type CartesianCoordinates = (Int, Int) // ### Data // Any serialisable type can be upcast to `Data` const anything_int: Data = 10 const anything_bytes: Data = "Hello, Aiken!" const anything_list: Data = [True, False, False] // Downcasting from `Data` is no guaranteed, but can be achieved with `expect` fn downcast(data: Data) -> RGB { expect d: RGB = data // fails if datum doesn't fit as an `RGB` d } // Serialisable values can be implicitly upcast to Data during calls fn serialise(data: Data) -> ByteArray {} let color: RGB = RGB { red: 255, green: 255, blue: 255 } serialise(color) // RGB is implicitly passed as `Data` // ## Control Flow // If expressions fn abs(x: Int) -> Int { if x < 0 { -x } else { x } } // When expressions (pattern matching) fn describe_list(list: List) -> String { when list is { [] -> @"The list is empty" [x] -> @"The list has one element" [x, y, ..] -> @"The list has multiple elements" } } // This syntax can be used instead of when to check a single case and fail otherwise. expect Some(foo) = my_optional_value // When the left-hand side pattern is `True`, it can be omitted for conciseness. fn positive_add(a, b) { // Error if the sum is negative, return it otherwise let sum = add(abs(a), abs(b)) expect sum >= 0 sum } // 'and' and 'or' boolean operators come with their own syntactic sugar fn my_validation_logic() -> Bool { (should_satisfy_condition_1 && should_satisfy_condition_2) || should_satisfy_condition_3 || should_satisfy_condition_4 } fn my_more_readable_validation_logic() -> Bool { or { and { should_satisfy_condition_1, should_satisfy_condition_2, }, should_satisfy_condition_3, should_satisfy_condition_4, } } // Sometimes it is useful to have an unfinished function which still allows compilation // The 'todo' key word will always fail, and provide a warning upon compilation. fn favourite_number() -> Int { // The type annotations says this returns an Int, but we don't need // to implement it yet. todo @"An optional message to the user" } // The fail keyword works similarly, but without any warning on compilation. fn expect_some_value(opt: Option) -> a { when opt is { Some(a) -> a None -> fail @"An optional message to the user" // We want this to fail when we encounter 'None'. } } // Trace for debugging fn debug_function(x: Int) -> Int { trace @"We can trace using this syntax." (x * 2 < 10)? // This will trace if false. } // Trace is variadic and can display any number of serialisable values let color = RGB(140, 49, 224) trace @"Color": color, True // ## Modules and Imports // Importing modules use aiken/collection/list // Using imported functions fn use_imports() { let numbers = [1, 2, 3, 4, 5] list.at(numbers, 2) // evaluates to 3 } // Importing values to use as unqualified identifiers use aiken/collection/list.{at} fn use_unqualified_imports() { let numbers = [1, 2, 3, 4, 5] at(numbers, 2) // still 3 } // ## Testing // Tests are defined using the 'test' keyword test simple_addition() { let result = add(2, 3) result == 5 } // Tests can also be properties! use aiken/fuzz test prop_commutative((a, b) via fuzz.both(fuzz.int(), fuzz.int())) { add(a, b) == add(b, a) } [What I Wish I Knew](https://aiken-lang.org/fundamentals/what-i-wish-i-knew "What I Wish I Knew") [Primitive Types](https://aiken-lang.org/language-tour/primitive-types "Primitive Types") --- # Aiken | Common Design Patterns Fundamentals Common Design Patterns Common Design Patterns ====================== There are number of design patterns that have been refined and enabled as Plutus has advanced from V1 to V2, and now to V3. This document seeks to be a non-exhaustive reference to these design patterns and practices. Enforcing Uniqueness[](https://aiken-lang.org/fundamentals/common-design-patterns#enforcing-uniqueness) -------------------------------------------------------------------------------------------------------- Enforcing the uniqueness of policies, asset names, or new outputs is useful in a number of contexts. ### "One-Shot" Minting Policies[](https://aiken-lang.org/fundamentals/common-design-patterns#one-shot-minting-policies) A validator is parameterized with an `OutputReference`, the minting validator enforces that the inputs to the transaction contain the corresponding `UTxO` as input. By doing this, the minting policy is ensured to only validate once and only once (since an unspent transaction output can only be spent once, by definition). In some designs, this logic is used for only a subset of redeemers to allow more flexible minting policies. Let's walk through an example. An NFT (Non-Fungible Token) can be created using a one-shot minting policy that ensures each minted value is validated by spending a specific UTxO provided through the transaction inputs. This minting policy uses a validator parameter of OutputReference to confirm that the transaction spends the UTxO. Additionally, the policy guarantees that only one token is minted, ensuring the NFT's uniqueness. First define OutputReference as parameter and set the action type to mint or burn the token based on the value provided in the redeemer. use cardano/transaction.{OutputReference, Transaction} use cardano/assets.{PolicyId} pub type Action { Minting Burning } validator one_shot(utxo_ref: OutputReference) { mint(redeemer: Action, policy_id: PolicyId, self: Transaction) { todo @"mint and burn" } } The validator must handle minting/burning operations and ensures that only one value is minted; it will fail otherwise. use aiken/collection/dict use cardano/transaction.{OutputReference, Transaction} use cardano/assets.{PolicyId} pub type Action { Minting Burning } validator one_shot(utxo_ref: OutputReference) { mint(redeemer: Action, policy_id: PolicyId, self: Transaction) { // It checks that only one minted asset exists and will fail otherwise expect [Pair(_asset_name, quantity)] = self.mint |> assets.tokens(policy_id) |> dict.to_pairs() todo @"Check if output is consumed" } } To enforce uniqueness, we need to ensure that the UTxO defined as OutputReference in the validator parameters is consumed. This is because every OutputReference is a unique combination of the Transaction ID and an Output Index Integer. It's important to remember that the Transaction ID is a `Hash`, which is also a unique identifier and will not be repeated. use aiken/collection/dict use aiken/collection/list use cardano/transaction.{OutputReference, Transaction} use cardano/assets.{PolicyId} pub type Action { Minting Burning } validator one_shot(utxo_ref: OutputReference) { mint(redeemer: Action, policy_id: PolicyId, self: Transaction) { // It checks that only one minted asset exists and will fail otherwise expect [Pair(_asset_name, quantity)] = self.mint |> assets.tokens(policy_id) |> dict.to_pairs() let Transaction{inputs, mint , ..} = self // Check if the specified UTxO reference (utxo_ref) is consumed by any input let is_output_consumed = list.any(inputs, fn(input) { input.output_reference == utxo_ref }) when redeemer is { Minting -> is_output_consumed? && (quantity == 1)? Burning -> (quantity == -1)? // No need to check if output is consumed for burning } } } ### Receipts[](https://aiken-lang.org/fundamentals/common-design-patterns#receipts) A validator can mint a unique receipt for a transaction by requiring that the name of the asset is any unique value specific to the transaction where validation is set to occur. In other words, if we enforce that only one receipt is to be minted per transaction, we can use `blake2b_256` and `cbor.serialise` to get a unique value that can be assigned to the `AssetName` expected for the receipt from the `OutputReference` from the first `Input` in our transactions inputs. use aiken/builtin.{blake2b_256} use aiken/cbor use aiken/collection/dict use aiken/collection/list use cardano/assets.{PolicyId} use cardano/transaction.{Transaction} validator receipt { // This validator expects a minting transaction mint(_r: Data, policy_id: PolicyId, self: Transaction) { let Transaction { inputs, mint, .. } = self // Select the first input and concatenate its output reference and index to // generate the expected token name expect Some(first_input) = list.at(inputs, 0) expect [Pair(asset_name, quantity)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() let expected_token_name = first_input.output_reference |> cbor.serialise |> blake2b_256 // Compare the asset name with the first utxo output reference asset_name == expected_token_name && quantity == 1 } // The validator will fail if the transaction is not for minting. else(_) { fail } } We could use this validator to mint a unique receipt for a transaction. It will get the first UTxO reference and will compare it with the asset name. ### Unique Outputs[](https://aiken-lang.org/fundamentals/common-design-patterns#unique-outputs) #### Problem: Double Satisfaction[](https://aiken-lang.org/fundamentals/common-design-patterns#problem-double-satisfaction) To prevent a vulnerability called _'Double Satisfaction'_ (see more below), one must ensure that outputs associated with a given input are only counted once across all possible validations occuring in a transaction. In the eUTxO model, a common anti-pattern is to predicate spending upon logic that is specific to a given input - without ensuring the uniqueness of the corresponding output. Let's walk through a short example: Bob wants to sell 20 SCOIN and wants at least 5 ADA in return; the contract would require that at least 5 ADA is paid to Bob. Step-by-step swap: 1. Bob sends 20 SCOIN to the validator with a datum containing his VerificationKeyHash and the price (5 ADA) required to get the 20 SCOIN. 2. Alice makes a new transaction getting the 20 SCOIN and paying 5 ADA to Bob. 3. Alice will get 20 SCOIN. 4. Bob will get 5 ADA. use aiken/collection/list use aiken/crypto.{Blake2b_224, Hash, VerificationKey} use cardano/address use cardano/assets.{lovelace_of, merge} use cardano/transaction.{Output, OutputReference, Transaction} type VerificationKeyHash = Hash pub type DatumSwap { beneficiary: VerificationKeyHash, price: Int, } validator exploitable_swap { spend( optional_datum: Option, _redeemer: Data, _own_ref: OutputReference, self: Transaction, ) { expect Some(datum) = optional_datum let beneficiary = address.from_verification_key(datum.beneficiary) let user_outputs = list.filter( self.outputs, fn(output) { output.address == beneficiary }, ) let value_paid = list.foldl( user_outputs, assets.zero, fn(output, total) { merge(output.value, total) }, ) (lovelace_of(value_paid) >= datum.price)? } } So far, everything is ok, but what if we have some UTxOs locked in the validator at similar prices? Bob wants to sell 20 XCOIN, and 20 SCOIN and wants at least 10 ADA in return for each UTxO; the contract would require that at least 10 ADA be paid to Bob. Now Alice comes and pays Bob 10 ADA, in the same transaction she takes both the 20 SCOIN and 20 XCOIN because the contract only ensures that at least 10 ADA is paid to Bob. So, this validator could potentially cause be satisfied twice with the same inputs, where anyone can pay once and get every UTxO unlocked at the same price or less. #### Solution: Tagged Outputs[](https://aiken-lang.org/fundamentals/common-design-patterns#solution-tagged-outputs) What can we do? We have to ensure that each input has a corresponding unique output to pay or predicate the logic of spending any input of the script on all of the inputs and outputs relevant to the business logic of the dApp. In addition, we have to remember that the code in the validator will be executed for every UTxO locked by the validator that we are trying to spend from. So we have to make sure that outputs aren't counted multiple times across multiple executions of the validator (for each input validation). This can be achieved by _tagging_ outputs with a value which is unique to the input. Enough information is present in the `OutputReference` of the input to create a unique tag that must then be found in outputs. use aiken/collection/list use aiken/crypto.{Blake2b_224, Hash, VerificationKey} use cardano/address use cardano/assets.{lovelace_of, merge} use cardano/transaction.{InlineDatum, Output, OutputReference, Transaction} type VerificationKeyHash = Hash pub type DatumSwap { beneficiary: VerificationKeyHash, price: Int, } validator swap { spend( optional_datum: Option, _redeemer: Data, own_ref: OutputReference, self: Transaction, ) { expect Some(datum) = optional_datum let beneficiary = address.from_verification_key(datum.beneficiary) // We will get all UTxO outputs with a datum equal to the UTxO input's reference // we are validating. We have to remember that this code will be executed for every // UTxO locked to the validator address that we are trying to unlock. let user_outputs_restricted = list.filter( self.outputs, fn(output) { when output.datum is { InlineDatum(output_datum) -> // Note that we use a soft-cast here and not an expect, because the transaction // might still contain other kind of outputs that we simply chose to ignore. // Using expect here would cause the entire transaction to be rejected for any // output that doesn't have a datum of that particular shape. if output_datum is OutputReference { and { output.address == beneficiary, own_ref == output_datum, } } else { False } _ -> False } }, ) // We sum all output values and check that the amount paid is greater than or equal to the price // asked by the seller. let value_paid = list.foldl( user_outputs_restricted, assets.zero, fn(n, acc) { merge(n.value, acc) }, ) (lovelace_of(value_paid) >= datum.price)? } } State Thread Tokens (a.k.a STT)[](https://aiken-lang.org/fundamentals/common-design-patterns#state-thread-tokens-aka-stt) -------------------------------------------------------------------------------------------------------------------------- It is often useful to have a mutable state which either changes with each transaction, or on a periodic basis. One way to ensure that a datum is not 'spoofed' is to ensure that the input or reference input with that datum contains an NFT which has been generated to be unique using one of the method described above. In this example, we will create an STT that tracks the sum of every transaction that uses the STT. And for this, we will create a multivalidator with two responsabilities: a minting and spending policy. The STT Minting Policy allows us to create new tokens with a counter datum initialized at 0. use aiken/collection/dict use aiken/collection/list use cardano/address.{Script} use cardano/assets.{PolicyId, policies} use cardano/transaction.{InlineDatum, OutputReference, Transaction} use config validator counter_stt(utxo_ref: OutputReference) { mint(_redeemer: Data, policy_id: PolicyId, self: Transaction) { let Transaction { inputs, outputs, mint, .. } = self expect [Pair(_asset_name, quantity)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() let is_output_consumed = list.any(inputs, fn(input) { input.output_reference == utxo_ref }) expect Some(nft_output) = list.find( outputs, fn(output) { list.has(policies(output.value), policy_id) }, ) expect InlineDatum(datum) = nft_output.datum expect counter: Int = datum is_output_consumed? && (1 == quantity)? && counter == 0 } // "Create the spending part to handle the STT" } Here is the part of the validator that ensures every transaction increments the value of the counter: * We check if the transaction is signed by the operator. * We obtain the `ScriptHash` to identify the NFT, which is the same as the `PolicyId`. * We check if an input NFT exists and has a datum with an integer. * We check if an output exists and has a datum with an integer. * We check if the output datum equals the input datum + 1. validator counter_stt(utxo_ref: OutputReference) { // Mint code... spend( _optional_datum: Option, _redeemer: Data, own_ref: OutputReference, self: Transaction, ) { let Transaction { inputs, outputs, .. } = self // Getting the script hash from this validator. Note that since the // `mint` handler is defined as part of the same validator, they share // the same hash digest. Thus, our `payment_credential` is ALSO our STT // minting policy. expect Some(own_input) = list.find(inputs, fn(input) { input.output_reference == own_ref }) expect Script(own_script_hash) = own_input.output.address.payment_credential // Checking if the transaction is signed by the operator. let is_signed_by_operator = list.has(self.extra_signatories, config.operator) // One input should hold the STT, with the expected format. expect Some(stt_input) = list.find( inputs, fn(input) { list.has(policies(input.output.value), own_script_hash) }, ) expect InlineDatum(input_datum) = stt_input.output.datum expect counter_input: Int = input_datum // The STT must be forwarded to an output expect Some(stt_output) = list.find( outputs, fn(output) { list.has(policies(output.value), own_script_hash) }, ) expect InlineDatum(output_datum) = stt_output.datum expect counter_output: Int = output_datum expect stt_input.output.address == stt_output.address // Final validations is_signed_by_operator? && (counter_output == counter_input + 1)? } } One important thing to point out is that we're pulling the operator value from the config, which comes from the `config` section of the aiken.toml file: [config.default.operator] encoding = "hex" bytes = "00000000000000000000000000000000000000000000000000000000" Forwarding Validation & Other Withdrawal Tricks[](https://aiken-lang.org/fundamentals/common-design-patterns#forwarding-validation--other-withdrawal-tricks) ------------------------------------------------------------------------------------------------------------------------------------------------------------- By enforcing withdrawals from a specific given script, we can effectively 'forward' the validation to this script being evaluated with the `withdraw` script purpose. This is possible in particular because it is always possible to withdraw an amount of 0 lovelace. We can leverage this to allow a script to be owner of one or multiple UTxOs themselves locked by a much simpler script. In stead of normally ensuring that the owner's PKH is present in the required signatories, we use a small script that forward the validation to another single script also present in the transaction. By using this trick in a spending validator, we can reduce the overhead that comes from authorizing multiple spending. Indeed, instead of running the same bunch of logic multiple times (one for each input), we only run it once for the withdrawal script. Since validators have access to the entire transaction as a context, regardless of their execution purpose, it is feasible most of the time. This is being used by a number of dApps now in production in order to optimize evaluation budgets and reach a higher efficiency. Going further[](https://aiken-lang.org/fundamentals/common-design-patterns#going-further) ------------------------------------------------------------------------------------------ ### Anastasia Labs' design patterns[](https://aiken-lang.org/fundamentals/common-design-patterns#anastasia-labs-design-patterns) > [https://github.com/Anastasia-Labs/design-patterns (opens in a new tab)](https://github.com/Anastasia-Labs/design-patterns) A library designed to abstract away some of the more unintuitive and lesser-known eUTxO design patterns, making them more accessible to developers. ### Plutonomicon[](https://aiken-lang.org/fundamentals/common-design-patterns#plutonomicon) > [https://github.com/Plutonomicon/plutonomicon (opens in a new tab)](https://github.com/Plutonomicon/plutonomicon) A developer-driven guide to the Plutus smart contract platform _in practice_. [EUTxO Crash Course](https://aiken-lang.org/fundamentals/eutxo "EUTxO Crash Course") [What I Wish I Knew](https://aiken-lang.org/fundamentals/what-i-wish-i-knew "What I Wish I Knew") --- # Aiken | Primitive Types [Language Tour](https://aiken-lang.org/language-tour) Primitive Types Primitive Types =============== Aiken has 6 primitive types that are built in the language and can be typed as literals: booleans, integers, strings, bytearrays, data and void. The language also includes few base building blocks for associating types together: lists, tuples, options, pairs... Worry not, we'll see later in this manual how to create your own [custom types](https://aiken-lang.org/language-tour/custom-types) . Inline comments are denoted via `//`. We'll use them to illustrate the value of some expressions in examples given all across this guide. Bool[](https://aiken-lang.org/language-tour/primitive-types#bool) ------------------------------------------------------------------ A `Bool` is a boolean value that can be either `True` or `False`. Aiken defines a handful of operators that work with booleans. No doubts that they'll look quite familiar. | Operator | Description | Precedence | | --- | --- | --- | | `==` | Equality | 4 | | `&&` | Logical conjunction (a.k.a 'AND') | 3 | | `\|` | Logical disjunction (a.k.a. 'OR') | 2 | | `!` | Logical negatation (a.k.a 'NOT') | 1 | | `?` | Trace if false (see also [Troubleshooting](https://aiken-lang.org/language-tour/troubleshooting#-operator)
) | 1 | Like most languages, `&&` and `||` are _right-associative_, which means they don't evaluate their second operand if the first one gives the answer (i.e. short-circuit). ### `and`/`or` keywords[](https://aiken-lang.org/language-tour/primitive-types#andor-keywords) You'll soon come to realize that long chains of logical operators are quite common when building validators. True && False && True || False In this example one could derive the final boolean after thinking for a little but it also requires that you carefully consider how precedence affects the expression grouping. Although this specific example may be simple, in practice those `True` and `False` literals would actually be expressions of type `Bool`. Considering that these kinds of boolean checks are a huge corner stone of validator logic, we've introduced keywords that increase the readability of these so called _logical operator chains_. and { True, False, or { True, False, } } With these keywords, the grouping becomes immediately obvious at a glance resulting in more readable and maintainable logical operator chains. This shines especially when composing large numbers of logical operators (i.e. four or more) but is less impactful when there are one or two logical operators. Int[](https://aiken-lang.org/language-tour/primitive-types#int) ---------------------------------------------------------------- Aiken's only number type is an arbitrary sized integer. This means there is no underflow or overflow. 42 14 1337 Literals can also be written with `_` as separators to enhance readability: 1_000_000 Aiken also supports writing integer literals in other bases than decimals. Binary, octal, and hexadecimal integers begin with `0b`, `0o`, and `0x` respectively. 0b00001111 == 15 0o17 == 15 0xF == 15 Aiken has several binary arithmetic operators that work with integers. | Operator | Description | Precedence | | --- | --- | --- | | `+` | Arithmetic sum | 6 | | `-` | Arithmetic difference | 6 | | `/` | Whole division | 7 | | `*` | Arithmetic multiplication | 7 | | `%` | Remainder by whole division | 7 | Integers are also of course comparable, so they work with a variety of binary logical operators too: | Operator | Description | Precedence | | --- | --- | --- | | `==` | Equality | 4 | | `>` | Greater than | 4 | | `<` | Smaller than | 4 | | `>=` | Greater or equal | 4 | | `<=` | Smaller or equal | 4 | Any _serialisable_ type is comparable through `==` in Aiken. A serialisable type represents any of the primitive type in this guide with the exception of `Fuzzer` and `MillerLoopResult`, as well as any user-defined custom data-type made of serialisable types. Said differently, you can compare bytestrings, booleans, integers, lists, tuples, etc. using the `==` operator. ByteArray[](https://aiken-lang.org/language-tour/primitive-types#bytearray) ---------------------------------------------------------------------------- A _ByteArray_ is exactly what it seems, an array of bytes. Aiken supports three notations to declare ByteArray literals. #### As an array of bytes[](https://aiken-lang.org/language-tour/primitive-types#as-an-array-of-bytes) First, as list of integers ranging from 0 to 255 (a.k.a. _bytes_): #[10, 255] #[1, 256] // results in a parse error because 256 is bigger than 1 byte Syntax rules for literal integers also apply to byte arrays. Thus, the following is a perfectly valid syntax: #[0xff, 0x42] #### As a byte string[](https://aiken-lang.org/language-tour/primitive-types#as-a-byte-string) Second, as a UTF-8 encoded byte string. This is generally how common text strings are represented under the hood. In Aiken, simply use double-quotes for that: "foo" == #[0x66, 0x6f, 0x6f] == #[102, 111, 111] #### As a hex-encoded byte string[](https://aiken-lang.org/language-tour/primitive-types#as-a-hex-encoded-byte-string) Because it is quite common to manipulate base16-encoded byte strings in a blockchain context (e.g. transaction id, policy id, etc..); Aiken also supports a shorthand syntax for declaring bytearrays as an hexadecimal string. Behind the scene, Aiken decodes the encoded string for you and stores only the raw bytes as a ByteArray. This is achieved by prefixing a double-quotes byte string with a `#`, like so: #"666f6f" == #[0x66, 0x6f, 0x6f] == #[102, 111, 111] == "foo" Note how this is different from: "666f6f" == #[0x36, 0x36, 0x36, 0x66, 0x36, 0x66] == #[54, 54, 54, 102, 54, 54] List[](https://aiken-lang.org/language-tour/primitive-types#list) ------------------------------------------------------------------ Lists are ordered collections of values. They're one of the most common data structures in Aiken. Unlike tuples, all the elements of a List must be of the same type. Attempting to make a list using multiple different types will result in a type error. [1, 2, 3, 4] // List ["text", 3, 4] // Type error! Inserting at the front of a list is very fast, and is the preferred way to add new values. [1, ..[2, 3]] // [1, 2, 3] Note that all data structures in Aiken are immutable so prepending to a list does not change the original list. Instead it efficiently creates a new list with the new additional element. let x = [2, 3] let y = [1, ..x] x // [2, 3] y // [1, 2, 3] Tuple(s)[](https://aiken-lang.org/language-tour/primitive-types#tuples) ------------------------------------------------------------------------ Aiken has tuples which can be useful for grouping values. Unlike lists, each element in a tuple can have a different type. (10, "hello") // Type is (Int, ByteArray) (1, 4, [0]) // Type is (Int, Int, List) Long tuples (i.e. more than 3 elements) are usually discouraged. Indeed, tuples are anonymous constructors, and while they are quick and easy to use, they often impede readability. When types become more complex, one should use records instead (as we'll see later). Elements of a tuple can be accessed using the dot, followed by the index of the element (ordinal). So for example: let point = (14, 42, 1337, 0) let a = point.1st let b = point.2nd let c = point.3rd let d = point.4th (c, d, b, a) // (1337, 0, 42, 14) ### Pair[](https://aiken-lang.org/language-tour/primitive-types#pair) Aiken has a specific type for representing a pair of values of two not-necessarily equal types. Thus a `Pair` is akin to a 2-tuple `(a, b)`. Like for tuples, you can access elements of a pair using the ordinal syntax: let foo = Pair(14, "aiken") foo.1st == 14 foo.2nd == "aiken" So why have another type specifically for pairs? The answer is two folds: 1. This is a specific kind of term that also exists on the underlying Plutus Virtual Machine. Hence, given Aiken's proximity to that machine's representation, it is useful, if not simply necessary, to have ways to represent those when needed. 2. There's an ambiguity at the contract's boundary regarding how to serialise lists of pairs in CBOR. One way is to use a CBOR array of arrays (of two elements). And the other is to use a CBOR map. While the former is more intuitive, it is in fact the latter that end up being used by the ledger for many internal types. In the first versions of Aiken, the compiler would implicitly treat list of pairs as CBOR map in serialisation functions to mimic the ledger. This has proven inconvenient and confusing in many scenarios involving user-defined data-types, hence the introduction of pairs. So as a rule of thumb, unless you specifically want to serialize external types (in datums and redeemers) as CBOR maps, you probably never want to use a `Pair` and can stick to a 2-tuple `(a, b)`. Option[](https://aiken-lang.org/language-tour/primitive-types#option) ---------------------------------------------------------------------- We define `Option` as a generic type with two constructors (see also [custom types](https://aiken-lang.org/language-tour/custom-types) for details about this syntax): type Option
{ Some(a) None } The type parameter `a` indicates that an `Option` may be inhabited by any other type. Functions may manipulate it without making assumptions on what this type is, or may require the type to be instantiated to a particular concrete type. Options are used in situation where function must return optional values. For example: /// Extract the first element of a list, if any. fn get_head(xs: List) -> Option { when xs is { [] -> None [head, ..] -> Some(head) } } /// Ensures a number is strictly positive. fn to_positive(n: Int) -> Option { if n <= 0 { None } else { Some(n) } } The `Option` type is readily available in Aiken; it is part of the types and constructors available by default. Don't hesitate to use it! ### Never[](https://aiken-lang.org/language-tour/primitive-types#never) In some rare cases, it is needed to refer to an `Option` that can only ever be `None`. This is the case in some parts of the standard library and mainly due to unforeseen bugs in the ledger and backward compatibility concerns. For these scenarios, we introduce a `Never` type, which has a single constructor of the same name. It is identical in all points to `None` and serialises down to the same binary structure. Said differently, we have: let some: Data = None let never: Data = Never some == never Ordering[](https://aiken-lang.org/language-tour/primitive-types#ordering) -------------------------------------------------------------------------- [`Ordering` (opens in a new tab)](https://aiken-lang.github.io/prelude/aiken.html#Ordering) type is helpful when comparing two values of same type. It is defined as: pub type Ordering { Less Equal Greater } And [standard library (opens in a new tab)](https://aiken-lang.github.io/stdlib/) often defines `compare` function corresponding to different types, for instance, to compare two `ByteArray`s one can use this [`bytearray.compare` (opens in a new tab)](https://aiken-lang.github.io/stdlib/aiken/primitive/bytearray.html#compare) function. Void[](https://aiken-lang.org/language-tour/primitive-types#void) ------------------------------------------------------------------ `Void` is a type representing the nullary constructor, or put simply, the absence of value. It is denoted `Void` as a type and constructor. Fundamentally, if you think in terms of tuples, `Void` is a tuple with no element in it. `Void` is useful in certain situations, but because in Aiken everything is a typed expression (there's no "statement"), you'll rarely end up in a situation where you need it. Data[](https://aiken-lang.org/language-tour/primitive-types#data) ------------------------------------------------------------------ A `Data` is an opaque compound type that can represent any possible user-defined type in Aiken. We'll see more about `Data` when we cover [custom types](https://aiken-lang.org/language-tour/custom-types) . In the meantime, think of _Data_ as a kind of wildcard that can possibly represent _any serialisable_ value. This is useful when you need to use values from different types in an homogeneous structure. Any user-defined type can be cast to a `Data`, and you can try converting from a _Data_ to any [custom type](https://aiken-lang.org/language-tour/custom-types) in a safe manner. Besides, several language builtins only work with `Data` as a way to deal with polymorphism. String[](https://aiken-lang.org/language-tour/primitive-types#string) ---------------------------------------------------------------------- In Aiken text strings can be written as text surrounded by double quotes, prefixed with `@`. @"Hello, Aiken!" They can span multiple lines. @"Hello Aiken!" Under the hood text strings are [UTF-8 (opens in a new tab)](https://en.wikipedia.org/wiki/UTF-8) encoded binaries and can contain any valid unicode. @"🌘 アルバイト Aiken 🌒" ⚠️ Beware the use case for strings is extremely narrow in Aiken and on-chain code. They are only used for _tracing_, a bit like labels attached to specific execution paths of your program. You can't find them in the interface exposed by your validator, for example. So most of the time, you probably want to use a `ByteArray` instead and only resort to `String` for debugging. Advanced[](https://aiken-lang.org/language-tour/primitive-types#advanced) -------------------------------------------------------------------------- 💡 If you're just **getting started** with Aiken, you can probably **skip** the following section as it describes some more advanced types and behaviors. Feel free to come back to it in due time. ### Pairs[](https://aiken-lang.org/language-tour/primitive-types#pairs) An associative list (a.k.a Pairs) is a mere type alias such that: `type Pairs = List>`. It exists as a convenience since pairs are mainly present in validator's script contexts under this form. In addition to functions for `List` which are also all usable on `Pairs`, the stdlib also provides a [dedicated module of helpers (opens in a new tab)](https://aiken-lang.github.io/stdlib/aiken/collection/pairs.html) specifically crafted for associative lists. ### PRNG & Fuzzer[](https://aiken-lang.org/language-tour/primitive-types#prng--fuzzer) Aiken has an integrated property-based testing framework. Yes, you read that right. Property-based test is a first-class citizen here. If you want to read more about this, we strongly recommend looking at [our guide about property-based test](https://aiken-lang.org/language-tour/tests#property-based-test) ; it should teach you everything you need to get started with the framework. To support this framework, the prelude comes with two pre-defined types. `PRNG` stands for Pseudo-Random Number Generator. Its definition is opaque and matters only to the internals of the test framework. If you're still interested about the details, head towards the [aiken-lang/fuzz (opens in a new tab)](https://github.com/aiken-lang/fuzz.git) ! Similarly, the `Fuzzer` is an interface for building random generators. It offers a base set of primitives for a variety of types, and compose nicely with one another. ### G1Element, G2Element & MillerLoopResult[](https://aiken-lang.org/language-tour/primitive-types#g1element-g2element--millerloopresult) Specific to the use of BLS12-381 cryptographic primitives, the `G1Element`, `G2Element` and `MillerLoopResult` capture the types of various operands and return values of some builtin functions. Their usage is agnostic to Aiken itself and described in more details in [CIP-0381: Plutus support for Pairings over BLS12-381 (opens in a new tab)](https://cips.cardano.org/cip/CIP-0381) . [Language Tour](https://aiken-lang.org/language-tour "Language Tour") [Variables & Constants](https://aiken-lang.org/language-tour/variables-and-constants "Variables & Constants") --- # Aiken | Variables & Constants [Language Tour](https://aiken-lang.org/language-tour) Variables & Constants Variables & Constants ===================== Variables / let-bindings[](https://aiken-lang.org/language-tour/variables-and-constants#variables--let-bindings) ----------------------------------------------------------------------------------------------------------------- Aiken has let-bindings for declaring variables. A value can be given a name using the keyword `let`. Names can be reused by later let-bindings. Values assigned to let-bindings are immutable, however new bindings can shadow previous bindings. let x = 1 let y = x let x = 2 y + x == 3 Module constants[](https://aiken-lang.org/language-tour/variables-and-constants#module-constants) -------------------------------------------------------------------------------------------------- Let-bindings aren't allowed in a top-level Aiken module. Yet, Aiken provides module constants as a way to use certain fixed values in multiple places of a Aiken project. const start_year = 2101 const end_year = 2111 Like all values in Aiken, module constants are immutable. They cannot be used as global mutable state. When a constant is referenced, its value is inlined by the compiler so they can be used in any place where you'd have written a literal to begin with (e.g. when-expression, if/else-expression ...). We'll see some example of that when dealing with control flows. Constants are quite powerful in Aiken. They can hold (almost) any Aiken expression and are fully **evaluated at compile-time**. Said differently, you can write something like: const summer = "Summer" const autumn = "Autumn" const winter = "Winter" const spring = "Spring" const seasons = [summer, autumn, winter, spring] Constants can refer to other identifiers such as functions and other constants with one restriction: constants cannot refer to other constants that are defined _after_ them in the file. As such, there's no recursive or mutually recursive constants. Type annotations[](https://aiken-lang.org/language-tour/variables-and-constants#type-annotations) -------------------------------------------------------------------------------------------------- Variables and constants can be given type annotations. These annotations serve as documentation or can be used to provide a more specific type than the compiler would otherwise infer. const name: ByteArray = "Aiken" const size: Int = 100 let result: Bool = 14 > 42 Documentation[](https://aiken-lang.org/language-tour/variables-and-constants#documentation) -------------------------------------------------------------------------------------------- You may add user facing documentation to any constant definition with a documentation comment starting with `///`. Markdown is supported and this text will be included with the module's entry in generated HTML documentation. /// Timeout, in number of **seconds**. const timeout: Int = 60 When exported as module documentation, constant definitions are alphabetically sorted (in ascending order). [Primitive Types](https://aiken-lang.org/language-tour/primitive-types "Primitive Types") [Functions](https://aiken-lang.org/language-tour/functions "Functions") --- # Aiken | Functions [Language Tour](https://aiken-lang.org/language-tour) Functions Functions ========= Defining functions[](https://aiken-lang.org/language-tour/functions#defining-functions) ---------------------------------------------------------------------------------------- ### Named functions[](https://aiken-lang.org/language-tour/functions#named-functions) Named functions in Aiken are defined using the `fn` keyword. Functions can have (typed) arguments, and always have a return type. Because in Aiken, pretty much everything is an expression, functions do not have an explicit _return_ keyword. Instead, they implicitly return whatever they evaluate to. fn add(x: Int, y: Int) -> Int { x + y } fn multiply(x: Int, y: Int) -> Int { x * y } Functions are first class values and so can be assigned to variables, passed to other functions, or anything else you might do with any other data type with the exception of: being part of a data-type definition. We'll see more on that in the next section about [Custom Types](https://aiken-lang.org/language-tour/custom-types) . /// This function takes a function as an argument fn twice(f: fn(t) -> t, x: t) -> t { f(f(x)) } fn add_one(x: Int) -> Int { x + 1 } fn add_two(x: Int) -> Int { twice(add_one, x) } ### Anonymous functions[](https://aiken-lang.org/language-tour/functions#anonymous-functions) Anonymous functions can be defined with a similar syntax and assigned to an identifier using a let-binding. The identifier then serves as a name to call and pass the function around. fn run() { let add = fn(x, y) { x + y } add(1, 2) } ⚠️ One cannot define a _recursive anonymous function_. If you need recursion, make it a top-level definition. Labeled arguments[](https://aiken-lang.org/language-tour/functions#labeled-arguments) -------------------------------------------------------------------------------------- When functions take several arguments it can be difficult for the user to remember what the arguments are, and what order they are expected in. To help with this Aiken supports _labeled arguments_, where function arguments are given by labels instead of position. Take this function that replaces sections of a string: fn replace(self: String, pattern: String, replacement: String) { // ... } When calling the function, it is possible to use the defined labels to pass the arguments: replace(self: @"A,B,C", pattern: @",", replacement: @" ") // Labeled arguments can be given in any order replace(pattern: @",", replacement: @" ", self: @"A,B,C") // Positional arguments and labels can be mixed replace(@"A,B,C", pattern: @",", replacement: @" ") The use of argument labels can allow a function to be called in an expressive, sentence-like manner, while still providing a function body that is readable and clear in intent. ### Overriding default labels[](https://aiken-lang.org/language-tour/functions#overriding-default-labels) Note that, when defining a function, it is possible to override the default label to use different names (e.g. a shorter name) in the function body. For example: fn insert(self: List<(String, Int)>, key k: String, value v: Int) { // ... do something with `k` and `v` } Externally, the function can still be called using `key` and `value` as labelled, but in the function body, they are named `k` and `v` for conciseness. Type annotations[](https://aiken-lang.org/language-tour/functions#type-annotations) ------------------------------------------------------------------------------------ Function arguments are normally annotated with their type, and the compiler will check these annotations and ensure they are correct. fn identity(x: some_type) -> some_type { x } fn inferred_identity(x) { x } The Aiken compiler can infer all the types of Aiken code without annotations and both annotated and unannotated code is equally safe. It's considered a best practice to always write type annotations for your functions as they provide useful documentation, and they encourage thinking about types as code is being written. Documentation[](https://aiken-lang.org/language-tour/functions#documentation) ------------------------------------------------------------------------------ You may add user facing documentation in front of function definitions with a documentation comment `///` per line. Markdown is supported and this text will be included with the module's entry in generated HTML documentation. /// Always **true**. fn always_true(_) -> Bool { True } Unlike constants and data-types definitions, functions are exported and appear in generated documentation in the same order they are defined. This enables one to group similar functions together to build a coherent module API. ### Section headings[](https://aiken-lang.org/language-tour/functions#section-headings) Library modules generally export several (many) functions which can rapidly becomes overwhelming. Hence, Aiken provides means of organizing functions by allowing and processing section headings. A section headings is a double-slash comment `//` whose payload starts with a markdown heading (`#`, `##`, `###`...). Such comment headings can be interspersed in the module to indicate the documentation generation tool to create a new heading in the sidebar as well as in the function main content. The size and margins surrounding the heading depends on its importance (`#` being the most important). So for example: // ## Constructing /// Construct a new `MyType` holding a value. fn new(value) -> MyType {} /// Construct an empty `MyType`. fn empty() -> MyType {} // ## Inspecting /// Obtain the value, if any, at the given key. fn get(self, key) -> Option {} Advanced[](https://aiken-lang.org/language-tour/functions#advanced) -------------------------------------------------------------------- 💡 If you're just **getting started** with Aiken, you can probably **skip** the following section as it describes some more function usages and behaviors. Feel free to come back to it in due time. ### Argument destructuring[](https://aiken-lang.org/language-tour/functions#argument-destructuring) It is possible to use [destructuring](https://aiken-lang.org/language-tour/custom-types#destructuring) directly on function arguments. It is particularly handy to pull fields out of records while keeping the code concise: use aiken/math pub type Point { x: Int, y: Int, } fn distance(Point { x, y }: Point, Point { x: x_r, y: y_r }: Point) -> Option { math.sqrt(math.pow2(x_r - x) + math.pow2(y_r - y)) } ### Function capturing[](https://aiken-lang.org/language-tour/functions#function-capturing) There is a shorthand syntax for creating anonymous functions that take one argument and call another function. The `_` is used to indicate where the argument should be passed. fn add(x, y) { x + y } fn run() { let add_one = add(1, _) add_one(2) } The function capture syntax is often used with the [pipe operator](https://aiken-lang.org/language-tour/control-flow#piping) to create a series of transformations on some data. fn add(x: Int , y: Int ) -> Int { x + y } fn run() { // This is the same as add(add(add(1, 3), 6), 9) 1 |> add(_, 3) |> add(_, 6) |> add(_, 9) } In fact, this usage is so common that there is a special shorthand for it. fn run() { // This is the same as the example above 1 |> add(3) |> add(6) |> add(9) } The pipe operator will first check to see if the left hand value could be used as the first argument to the call, e.g. `a |> b(1, 2)` would become `b(a, 1, 2)`. If not it falls back to calling the result of the right hand side as a function , e.g. `b(1, 2)(a)`. ### Generic functions[](https://aiken-lang.org/language-tour/functions#generic-functions) At times you may wish to write functions that are generic over multiple types. For example, consider a function that consumes any value and returns a list containing two of the value that was passed in. This can be expressed in Aiken like this: fn list_of_two(my_value: a) -> List { [my_value, my_value] } Here the type variable `a` is used to represent any possible type. You can use any number of different type variables in the same function. This function declares type variables `a` and `b`. fn multi_result(x: a, y: b, condition: Bool) -> Result { when condition is { True -> Ok(x) False -> Error(y) } } Type variables can be named anything and may contain underscores (\_), but the names must be lowercase. Like other type annotations, they are completely optional, but using them may make it easier to understand the code. ### Backpassing `←`[](https://aiken-lang.org/language-tour/functions#backpassing-) Functions that take a continuation (i.e. callback with a single argument) can be invoked using the backpassing syntax: let x <- foo() // which is equivalent to writing: foo(fn(x) { }) This is particularly well suited for functions that operate over abstractions such as `Option` or `Fuzzer` and that provide an API of primitives to manipulate them. For example, let's consider the following small API over a `Fuzzer`: fn constant(a) -> Fuzzer fn bool() -> Fuzzer fn map(Fuzzer, fn(a) -> b) -> Fuzzer fn and_then(Fuzzer, fn(a) -> Fuzzer) -> Fuzzer And let's look at an excerpt constructing a pipeline using those primitives _without backpassing_. Something along the lines of: pub fn option(fuzz_a: Fuzzer) -> Fuzzer> { bool() |> and_then( fn(predicate) { if predicate { fuzz_a |> map(Some) } else { constant(None) } }, ) } A bit involved. Here, we can see how `and_then` is used to step through the bool's `Fuzzer`. This can be re-written more simply with backpassing as: pub fn option(fuzz_a: Fuzzer) -> Fuzzer> { let predicate <- and_then(bool()) if predicate { fuzz_a |> map(Some) } else { constant(None) } } Which reads a lot better! In fact, this technique becomes even more helpful when there are a lot of nested callbacks as it _flattens_ them into a sequence of backpassed let-bindings. [Variables & Constants](https://aiken-lang.org/language-tour/variables-and-constants "Variables & Constants") [Custom Types](https://aiken-lang.org/language-tour/custom-types "Custom Types") --- # Aiken | Modules [Language Tour](https://aiken-lang.org/language-tour) Modules Modules ======= Aiken programs are made up of bundles of functions and types called modules. Each module has its own namespace and can export types and values to be used by other modules in the program. // inside module lib/straw_hats/sunny.ak fn count_down() { "3... 2... 1..." } fn blast_off() { "BOOM!" } pub fn set_sail() { [\ count_down(),\ blast_off(),\ ] } Here we can see a module named `straw_hats/sunny`, the name determined by the filename `lib/straw_hats/sunny.ak`. Typically all the modules for one project would live within a directory with the name of the project, such as `straw_hats` in this example. The `pub` keyword makes this type usable from other modules. For the functions `count_down` and `blast_off` we have omitted the `pub` keyword, so these functions are _private_ module functions. They can only be called by other functions within the same module. Functions, type-aliases and constants can all be exported from a module using the `pub` keyword. ### Qualified imports[](https://aiken-lang.org/language-tour/modules#qualified-imports) To use functions or types from another module we need to import them using the `use` keyword. // inside module src/straw_hats/laugh_tale.ak use straw_hats/sunny pub fn find_the_one_piece() { sunny.set_sail() } #### Custom names[](https://aiken-lang.org/language-tour/modules#custom-names) It is also possible to give a module a custom name when importing it using the `as` keyword. use unix/dog use animal/dog as kitty This may be useful to differentiate between multiple modules that would have the same default name when imported. The definition `use straw_hats/sunny` creates a new variable with the name `sunny` and the value of the `sunny` module. In the `find_the_one_piece` function we call the imported module's public `set_sail` function using the `.` operator. If we had attempted to call `count_down` it would result in a compile time error as this function is private to the `sunny` module. ### Unqualified import[](https://aiken-lang.org/language-tour/modules#unqualified-import) Values and types can also be imported in an unqualified fashion. use animal/dog.{Dog, stroke} pub fn foo() { let puppy = Dog { name: "Zeus" } stroke(puppy) } This may be useful for values that are used frequently in a module, but generally qualified imports are preferred as it makes it clearer where the value is defined. You may also combine unqualified imports with custom names as such: use animal/dog.{Dog, stroke} as kitty Opaque types[](https://aiken-lang.org/language-tour/modules#opaque-types) -------------------------------------------------------------------------- At times it may be useful to create a type and make the constructors and fields private so that users of this type can only use the type through publicly exported functions. For example we can create a `Counter` type which holds an int which can be incremented. We don't want the user to alter the `Int` value other than by incrementing it, so we can make the type opaque to prevent them from being able to do this. // The type is defined with the opaque keyword pub opaque type Counter { Counter(value: Int) } pub fn new() { Counter(0) } pub fn increment(counter: Counter) { Counter(counter.value + 1) } Because the `Counter` type has been marked as `opaque` it is not possible for code in other modules to construct or pattern match on counter values or access the `value` field. Instead other modules have to manipulate the opaque type using the exported functions from the module, in this case `new` and `increment`. ⚠️ It isn't possible to downcast from `Data` into an opaque type because it isn't generally possibly to know what restrictions applies to that type. For example, imagine a `Rational` opaque type holding a pair of integer values as _numerator_ and _denominator_. By using an opaque type, one can ensure that the _denominator_ remains always non-negative, and that both numbers remain coprime. Hence, it is in many cases unsafe to downcast any arbitrary pair of integer into a `Rational` since there's more than just a structural equivalence at play. For the same reasons, it isn't possible to use an opaque type (or any type holding an opaque type) as a validator's datum and/or redeemer. There's a _special treatment_ for an opaque type with only one constructor, and if that constructor has only 1 field. Under the hood, it behaves like a Haskell's `newtype`. This matters when we're dealing with CBOR. For example: pub opaque type NewType { field: a } pub fn new_type(a) -> NewType { NewType(a) } .. trace new_type(43) // 43 // Compare that to a non-opaque type: pub type Constr { field: a } pub fn new_constr(a) -> Constr { Constr(a) } .. trace new_constr(43) // 121([_ 43]) Well-known modules[](https://aiken-lang.org/language-tour/modules#well-known-modules) -------------------------------------------------------------------------------------- ### The prelude module[](https://aiken-lang.org/language-tour/modules#the-prelude-module) There are two modules that are built into the language, the first is the `aiken` prelude module. By default its types and values are automatically imported into every module you write, but you can still chose to import it the regular way. This may be useful if you have created a type or value with the same name as an item from the prelude. use aiken /// This definition locally overrides the `Option` type /// and the `Some` constructor. pub type Option { Some } /// The original `Option` and `Some` can still be used pub fn go() -> aiken.Option { aiken.Some(1) } The content of the Prelude module is documented in [aiken-lang/prelude (opens in a new tab)](https://aiken-lang.github.io/prelude/aiken.html) ### The builtin module[](https://aiken-lang.org/language-tour/modules#the-builtin-module) The second module that comes with the language is for exposing useful builtin functions from Plutus core. Most underlying platform functions are available here using a "snake\_case" name. Much of Aiken's syntax ends up compiling to combinations of certain bultins but many aren't "exposed" through the syntax and need to be used directly. The standard library wraps these in a more Aiken-friendly interface so you'll probably never need to use these directly unless you're making your own standard library. use aiken/builtin fn eq(a, b) { builtin.equals_integer(a, b) } // is implicitly used when doing: a == b The content of the builtin module is documented in [aiken-lang/prelude (opens in a new tab)](https://aiken-lang.github.io/prelude/aiken/builtin.html) . Conditional modules[](https://aiken-lang.org/language-tour/modules#conditional-modules) ---------------------------------------------------------------------------------------- ### Environments[](https://aiken-lang.org/language-tour/modules#environments) Since `v1.1.0`, Aiken supports conditional environment modules. Environment modules follow special rules: 1. They must be located in an `env` directory at the root of the project. 2. When used, at least one of them must be called `default.ak`; it is used by default when no explicit module is provided. 3. Only one of them is available at the time, in a given compilation pass. 4. From within other modules, they are always referred to as `env`, regardless of their _actual name_. So for example, imagine the following project structure: . ├── aiken.toml ├── env │   ├── default.ak │   ├── preprod.ak │   └── preview.ak └── validators    └── main.ak From within `main`, one can assume the existence of a module `env` and import it like any other module `use env`. The content of the module depends on the `--env` argument passed to the command used to `check` or `build` the project. When `--env` is omitted, it is assumed to be `default.ak`. Note that each of those modules must define a similar API. Usually, they will export constants that are used throughout the validator but that depend on the execution environment. For example: validators/main.ak use env validator main() { mint(redeemer, policy_id, self) { self.signatories |> list.has(env.administrator) } } defaultpreprodpreview env/default.ak pub const administrator = #"0000000000" The environment names are not restricted (other than the usual restriction for Aiken's module names). They can contain arbitrary logic and definitions and import other modules as well as other environment modules by explicitly importing them as `use {env}` (e.g. `use preview`). Rules regarding import cycles, however, still apply. ### Configurations[](https://aiken-lang.org/language-tour/modules#configurations) Environment modules are quite flexible and expressive. They are useful when needing to wield complex dynamic configurations together which may even leverage other functions. In many cases though, their full power isn't needed and one only needs to define a handful of static constants. For these scenarios, Aiken provides a similar features called conditional configuration values. Those configurations are defined directly in the `aiken.toml` and can represent either integers, booleans, bytearrays or lists / tuples of thoses. Like environment, they expect one configuration to be named `default`. All values under it are then injected into a special module `config` which can be imported in the usual ways. Here's a thorough example of a conditional configuration, with their Aiken's equivalent: aiken.toml name = "aiken-lang/scratchpad" version = "1.0.0" [config.default] price = 1000000 # pub const price: Int = 1_000_000 is_mainnet = true # pub const is_mainnet: Bool = True network = "mainnet" # pub const network: ByteArray = "mainnet" quotas = [1, 2, 3] # pub const ratio: List = [1, 2, 3] asset = ["HOSKY", 42] # pub const asset: (ByteArray, Int) = ("HOSKY", 42) [config.default.owner] # pub const owner: ByteArray = #"0000111122223333" bytes = "0000111122223333" encoding = "hex" Then, one can refer to any of the configuration value as they would from a standard module: use config fn main() { if config.is_mainnet { config.price * 2 } else { .. } } Like conditional environment modules, the `--env` option drives the selection of the right configuration values. Yet unlike modules, configuration values cannot refer to other configuration values, and are limited to what the TOML syntax supports. Documentation[](https://aiken-lang.org/language-tour/modules#documentation) ---------------------------------------------------------------------------- You may add user-facing documentation at the head of modules with a module documentation comment `////` (quadruple slash!) per line. Markdown is supported and this text block will be included with the module's entry in generated HTML documentation. ### Hiding module[](https://aiken-lang.org/language-tour/modules#hiding-module) At times, you may want to hide modules from the generated documentation because they may contains internal functions that are not relevant to expose to any users, but split out for various reasons. To hide a module, you can start the module documentation with the tag `@hidden`. //// @hidden //// Some more documentation [Validators](https://aiken-lang.org/language-tour/validators "Validators") [Tests](https://aiken-lang.org/language-tour/tests "Tests") --- # Aiken | Validators [Language Tour](https://aiken-lang.org/language-tour) Validators Validators ========== Handlers[](https://aiken-lang.org/language-tour/validators#handlers) --------------------------------------------------------------------- In Aiken, you can promote some functions to _validator handlers_ using the keyword `validator`. use cardano/assets.{PolicyId} use cardano/transaction.{Transaction} validator my_script { mint(redeemer: MyRedeemer, policy_id: PolicyId, self: Transaction) { todo @"validator logic goes here" } } As you can see, a validator is a named block that contains one or more handlers. The handler name must match Cardano's well-known purposes: * `mint`, `spend`, `withdraw`, `publish`, `vote` or `propose`. Every handler is a predicate function: they must return `True` or `False`. When `True`, they authorize the action they are validating. Alternatively, to return false, they can instead halt using the [`fail`](https://aiken-lang.org/language-tour/control-flow#fail--todo) keyword or via an invalid [`expect`](https://aiken-lang.org/language-tour/control-flow#non-exhaustive-pattern-matching) assignment. With the exception of the `spend` handler, each handler is a function with exactly three arguments: * A **redeemer**, which is a user-defined type and value. * A **target**, whose type depends on the purpose. * A **transaction**, which represents the script execution context. The `spend` handler takes an additional first argument which is an optional datum, which is also a user-defined type. More on that in the section below. Datum? Redeemer? If you aren't yet familiar with the eUTxO model yet, we recommend reading through the [eUTxO Crash Course](https://aiken-lang.org/fundamentals/eutxo) . In fact, handlers act in a similar manner to pattern-matching on a [`ScriptContext` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/script_context.html#ScriptContext) ; they pull out elements for you while also ensuring safety around your validator boundaries. For convenience, here's a table that summarizes the different target types with their corresponding definitions from the standard library: | Purpose | Target | What for | | --- | --- | --- | | `mint` | [`PolicyId` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/assets.html#PolicyId) | minting / burning of assets | | `spend` | [`OutputReference` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/transaction.html#OutputReference) | spending of transaction outputs | | `withdraw` | [`Credential` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/address.html#Credential) | withdrawing staking rewards | | `publish` | [`Certificate` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/certificate.html#Certificate) | publishing of delegation certificates | | `vote` | [`Voter` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/governance.html#Voter) | voting on governance proposals | | `propose` | [`ProposalProcedure` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/governance.html#ProposalProcedure) | Constitution guardrails, executed when submitting governance proposals | Or, seen in action: use cardano/address.{Credential} use cardano/assets.{PolicyId} use cardano/certificate.{Certificate} use cardano/governance.{ProposalProcedure, Voter} use cardano/transaction.{Transaction, OutputReference} validator my_script { mint(redeemer: MyMintRedeemer, policy_id: PolicyId, self: Transaction) { todo @"mint logic goes here" } spend(datum: Option, redeemer: MySpendRedeemer, utxo: OutputReference, self: Transaction) { todo @"spend logic goes here" } withdraw(redeemer: MyWithdrawRedeemer, account: Credential, self: Transaction) { todo @"withdraw logic goes here" } publish(redeemer: MyPublishRedeemer, certificate: Certificate, self: Transaction) { todo @"publish logic goes here" } vote(redeemer: MyVoteRedeemer, voter: Voter, self: Transaction) { todo @"vote logic goes here" } propose(redeemer: MyProposeRedeemer, proposal: ProposalProcedure, self: Transaction) { todo @"propose logic goes here" } } Notice how every handler can take a different redeemer type and all take a `Transaction` as last argument. ### Managing (Optional) Datum[](https://aiken-lang.org/language-tour/validators#managing-optional-datum) Spend handlers contain an extra argument: the (optional) datum which may be set with the output when assets get initially locked. Because there's no way to enforce that the datum is present (you cannot prevent anyone from sending/locking assets to/in your validator), it always produces an `Option` where `T` is a user-defined type that depends on the contract. Nevertheless, should your contract require a datum to be present, then it is straightforward to enforce this constraint using [`expect`](https://aiken-lang.org/language-tour/control-flow#non-exhaustive-pattern-matching) and halt the execution of the validator when the datum is missing. use cardano/transaction.{Transaction, OutputReference} validator my_script { spend(datum_opt: Option, redeemer: MyRedeemer, input: OutputReference, self: Transaction) { expect Some(datum) = datum_opt todo @"validator logic goes here" } } ### Fallback handler[](https://aiken-lang.org/language-tour/validators#fallback-handler) The keen reader would have noticed that the example validators above are _non-exhaustive_ and only cover one of the six purposes. It may be cumbersome to always define a handler for all purposes, especially if your application isn't expected to work in those contexts. A special handler can thus serve as a fallback / catch-all with one notable difference: the fallback handler takes a single argument of type [`ScriptContext` (opens in a new tab)](https://aiken-lang.github.io/stdlib/cardano/script_context.html#ScriptContext) . It is then your responsibility as a smart contract developer to assert the script purposes and recover your redeemer and/or datum. use cardano/assets.{PolicyId} use cardano/transaction.{Transaction, OutputReference} use cardano/script_context.{ScriptContext} validator my_multi_purpose_script { mint(redeemer: MyRedeemer, policy_id: PolicyId, self: Transaction) { todo @"validator logic goes here" } spend(datum_opt: Option, redeemer: MyRedeemer, input: OutputReference, self: Transaction) { expect Some(datum) = datum_opt todo @"validator logic goes here" } else(_ctx: ScriptContext) { fail @"unsupported purpose" } } There are also scenarios where you might not want the granularity and guardrails offered by Aiken. A typical use-case for example is writing a validator on a layer-2 system that uses the Plutus Virtual Machine (e.g. Hydra) but may have different purpose(s) and/or script context. The fallback handler comes in handy for those situations and allows you to define and use arbitrary script contexts to match any environment. In the long run, a dedicated syntax might be introduced to declare handlers based on some type definition. #### Default fallback[](https://aiken-lang.org/language-tour/validators#default-fallback) When no fallback is explicitly specified, Aiken defaults to a validator that is always rejecting. validator my_script { else(_) { fail } } Parameters[](https://aiken-lang.org/language-tour/validators#parameters) ------------------------------------------------------------------------- Validators themselves can take _parameters_, which represent configuration elements that must be provided to create an instance of the validator. Once provided, parameters are embedded within the compiled validator and part of the generated code. Hence they must be provided before any address can be calculated for the corresponding validator. Parameters are accessible to all handlers from within the validator and can be any serialisable (non-opaque) data-type. For example, it is common to parameterize the validator with a UTxO reference, in order to ensure the uniqueness of execution of a minting policy . In the mint handler, one can then check that the referenced UTxO is spent, which ensures the uniqueness of execution for the mint handler (since by definition UTxOs can only be spent once). Handy! validators/my\_script.ak use aiken/collection/list use cardano/assets.{PolicyId} use cardano/transaction.{Transaction, OutputReference} validator my_script(utxo_ref: OutputReference) { mint(redeemer: Data, policy_id: PolicyId, self: Transaction) { expect list.any( self.inputs, fn (input) { input.output_reference == utxo_ref } ) todo @"rest of the logic goes here" } } Calling handlers[](https://aiken-lang.org/language-tour/validators#calling-handlers) ------------------------------------------------------------------------------------- Handlers are your smart contract's interface with the rest of the world. However, there are situations where you may want to invoke a handler as a standalone function (e.g. for testing). Aiken provides a convenient syntax, akin to calling functions from a module: `{validator_name}.{handler_name}` The result is a function that takes the same arguments as the handler, prepended with any parameter from the validator. For example: test return_true_when_utxo_ref_match() { let utxo_ref = todo @"OutputReference" let redeemer = todo @"Redeemer" let policy_id = todo @"OutputReference" let transaction = todo @"Transaction" my_script.mint(utxo_ref, redeemer, policy_id, transaction) } ### Importing validators[](https://aiken-lang.org/language-tour/validators#importing-validators) Should you need to split tests into a different module, Aiken allows the import of _validators_ with a few restrictions: 1. Only _test modules_ can import validators. A module is considered a _test module_ if it doesn't export any public definitions. 2. Validators cannot be imported as unqualified objects. They must be used as qualified imports. So, assuming the validator above, we could imagine writing a test module like: use my_script test return_true_when_utxo_ref_match() { let utxo_ref = todo @"OutputReference" let redeemer = todo @"Redeemer" let policy_id = todo @"OutputReference" let transaction = todo @"Transaction" my_script.my_script.mint(utxo_ref, redeemer, policy_id, transaction) } [Control Flow](https://aiken-lang.org/language-tour/control-flow "Control Flow") [Modules](https://aiken-lang.org/language-tour/modules "Modules") --- # Aiken | What I wish I knew when exploring Cardano Fundamentals What I Wish I Knew What I wish I knew when exploring Cardano ========================================= This documentation is a collection of tips and secret knowledge that may be useful in your Cardano journey. Some may never be useful to you or only useful in the distant future. Yet, we hope that if we can save someone a few hours of debugging and hunting down rabbits in bottomless holes, we have accomplished our mission. CBOR / CDDL[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#cbor--cddl) --------------------------------------------------------------------------------- Pretty much everything in Cardano that requires serialization is done using a a structured binary format called [CBOR (opens in a new tab)](https://www.rfc-editor.org/rfc/rfc8949) . Think of it as JSON but for binary data. You don't have to be an expert in CBOR to work on Cardano, but being familiar with the notation can be useful especially for troubleshooting low-level stuff. There are two specific areas worth knowing: * CDDL, which is a specification meta-language for CBOR. It's used to describe how some data is encoded into bytes. In particular, the Cardano ledger maintains [a CDDL specification of all the Cardano objects (opens in a new tab)](https://github.com/input-output-hk/cardano-ledger/blob/81548171f2cd336714bb0425640a6553c46aa09e/eras/babbage/test-suite/cddl-files/babbage.cddl) that have to be serialized on-chain. Transaction serialization, for example, is entirely specified in this document. This specification is generally **the** source of truth for many other tools. * CBOR diagnostic provides a more human-readable, JSON-like syntax that is handy for debugging. We explain CBOR diagnostic in more detail in the [Troubleshooting](https://aiken-lang.org/language-tour/troubleshooting#cbor-diagnostic) section. A well-known tool for debugging CBOR is [cbor.me (opens in a new tab)](https://cbor.me/) ; there are also numerous command-line utilities in various languages. Byron Addresses[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#byron-addresses) ------------------------------------------------------------------------------------------ Cardano has two kinds of addresses, which are called by different names depending on who you ask. Prior to the introduction of the Shelley era, addresses looked vastly different than what they look like today. For example: `Ae2tdPwUPEYz6ExfbWubiXPB6daUuhJxikMEb4eXRp5oKZBKZwrbJ2k7EZe`. We call these addresses _"Bootstrap addresses"_ or simply _"Byron addresses"_; in contrast to other addresses that are either called _"addresses"_ or _"Shelley addresses"_ when there's a possible ambiguity. Byron addresses are considered deprecated today and are likely not something you want to use ever. But, you might come across them, for some legacy systems still use them. Outputs locked by a Byron address or inputs corresponding to such outputs are **forbidden** in transactions that have Plutus scripts! This means that on-chain validators should never encounter Byron addresses in a validation context. This used to be different in the early days of the Alonzo era but was later changed to avoid hazardous situations. Validity Intervals[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#validity-intervals) ------------------------------------------------------------------------------------------------ You probably know already that Cardano smart-contract execution is fully deterministic. This, however, raises an interesting question: **How to deal with time?** Asking for _the current time_ usually breaks determinism because asking the same question at different moments may lead to different answers and, thus, different execution paths. So how to introduce time in scripts validating transactions? Cardano decouples transaction validations in two phases, and we typically refer to them as "phase-1 validations" and "phase-2 validations". Phase-1 validations are structural checks on a transaction performed by the ledger. For example, this is when the ledger verifies that inputs referenced in a transaction are valid, that minimum amounts for fees and outputs are met, etc... Among the available features of a transaction are "validity intervals" that define a period after which and until the ledger can consider the transaction valid. The validity interval is made of a lower bound (optional) and an upper bound (optional), and it is verified during phase-1 validations. That is if a transaction is said to be valid only after the 5th January 2030, it can only be submitted and considered valid by the ledger after that date. Similarly, if the validity interval defines an upper bound, then the transaction will sit in mempools until then. It is pruned if it doesn't make it into a block by the specified date. Thereby, one can introduce a notion of time in validators through the means of validity intervals. Since they are checked during phase-1, a validator can assume that the transaction it validates is within the specified validity interval. Hence, should you ensure that an action happens only after a specific date, you can record that date as a datum and check that the transaction's lower bound is greater than the specified date. The validity interval can be as narrow as one second, allowing scripts to run at very thin precisions. However, a narrow interval means getting the transaction in a block may be more difficult. As a reminder, there's one block every 20 seconds on average minted on the Cardano blockchain. Blocks are usually propagated fast, but it can take a few minutes under moments of heavy load. Serialization strategies[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#serialization-strategies) ------------------------------------------------------------------------------------------------------------ There's no canonical serialization of objects on Cardano. While there is indeed a CDDL specification for core objects[see below](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#cddl-files) , as mentioned earlier, there are still multiple possible interpretations possible of the specification. For example, a CDDL specification is unable to express in what order should keys in a map be serialized, or whether optional fields with default values (e.g. an optional list of elements) should be omitted entirely or specified with an empty default value. While there are attempts, such as [CIP-0021 (opens in a new tab)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0021) to agree on a canonical serialization; the current Cardano ledger does not provide any such guarantee. Consequently, the recommended strategy when dealing with deserialized objects that need to be reserialized is to always preserve the original bytes and not attempt to reserialize anything. At the same time, parsers should not assume one way over another and be ready to deserialize any possible representation that is compliant with the CDDL specification. This means, amongst other things, that there are multiple possible serializations of a transaction, and it may lead to surprising situations when trying to recalculate the hash of an object. #### CDDL files[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#cddl-files) | Era | CDDL Specification | | --- | --- | | Byron | [byron.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/byron/cddl-spec/byron.cddl) | | Shelley | [shelley.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/shelley/impl/cddl-files/shelley.cddl) | | Allegra | [allegra.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/allegra/impl/cddl-files/allegra.cddl) | | Mary | [mary.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/mary/impl/cddl-files/mary.cddl) | | Alonzo | [alonzo.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/alonzo/impl/cddl-files/alonzo.cddl) | | Babbage | [babbage.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/babbage/impl/cddl-files/babbage.cddl) | | Conway | [conway.cddl (opens in a new tab)](https://github.com/IntersectMBO/cardano-ledger/blob/92fd6688b3cce015abc46c133c68e6ea7baa6503/eras/conway/impl/cddl-files/conway.cddl) | Hash digests[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#hash-digests) ------------------------------------------------------------------------------------ Cardano uses mostly only blake2b as a hashing algorithm throughout the chain. Saying "mostly" because we can find some examples of SHA-256 in some parts of the Byron era, but let's not dwell on that. Many things called `id` are hash digests of some serialized objects. For example, a stake pool id is a hash digest of the pool public cold key. A transaction id is a hash digest value of the serialized transaction body. And so forth. Hashes are generally 32-byte long on Cardano (or 256 bits), **except for credentials** (i.e. keys or scripts) which are 28-byte long (or 224 bits). This is why a policy id is only 28 bytes long: a policy id is the hash digest of a tagged script, and scripts can be used as credentials (i.e. part of an address). The same goes for any hash digest of a verification key. Policy Id and language tags[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#policy-id-and-language-tags) ------------------------------------------------------------------------------------------------------------------ A policy id is a hash digest of a tagged script. Tagged is the keyword here. Should you try to calculate the policy id by simply hashing a serialized script, you may find yourself with a wrong hash without knowing why. Raw scripts aren't exact pre-image of their hash digest. Before hashing, scripts are prefixed with a certain discriminator byte depending on the language. For instance, any native script is prefixed with a `0x00` (`0b0000_0000`) byte before hashing. Here's a table summarizing all discriminators: | Language | Discriminator Byte | | --- | --- | | `Native` | `0x00` | | `Plutus V1` | `0x01` | | `Plutus V2` | `0x02` | | `Plutus V3` | `0x03` | The subsequent versions of Plutus may likely follow the pattern. Rewards & Withdrawals[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#rewards--withdrawals) ----------------------------------------------------------------------------------------------------- Ouroboros, the consensus algorithm used by Cardano, defines an incentive mechanism for stakeholders to participate in the consensus through rewards. Of course, you probably know that by now. Rewards are paid every epoch to delegators that delegate their stake (i.e. ADA tokens) to a stake pool of the network producing blocks on their behalf. Those rewards are, however, not paid directly to stakeholders; this would cause the network at every epoch boundary to pay out all rewards. Instead, Cardano has introduced a restricted concept of account, similar to what exists on account-based ledgers. This account is, however, singular in many ways: * It is defined by some stake credentials and owned by them; * It can only receive rewards from the protocol but not from a user-defined transaction; * It is automatically delegated; It is possible to withdraw funds from the account by issuing a withdrawal (which takes the form of a specific field in a transaction). A withdrawal sets the balance of an account to 0 and provides a virtual pot of the account value to the transaction carrying it as if it was an input of that same value. That value can then be dispatched in one or more UTxO, as typically done with any input. The stake credentials associated with the account protect withdrawals as well. Who owns the stake credentials owns the right to withdraw from the account. This means that reward withdrawals can be commanded by a script (and thus an Aiken program). Native Scripts[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#native-scripts) ---------------------------------------------------------------------------------------- Before full-blown Plutus scripts, Cardano had a minimalistic scripting language usually referred to as _"Native Script"_ or _"Phase-1 scripts"_. It still exists and provides simple, albeit useful, programmability features to Cardano. Native scripts come in the form of a little domain-specific language with 6 constructors: `key`, `all-of`, `any-of`, `n-of-m`, `after` and `before`. In particular, they are quite handy in defining multisig addresses owned by multiple tenants. You can find more about native scripts in [CIP-1854 (opens in a new tab)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-1854#overview) and in the [Formal Ledger Specification, Figure 4: Multi-signature via Native Scripts (opens in a new tab)](https://github.com/input-output-hk/cardano-ledger/releases/latest/download/shelley-ledger.pdf) . The 'clean' trick: avoid replaying blocks[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#the-clean-trick-avoid-replaying-blocks) ------------------------------------------------------------------------------------------------------------------------------------------- Sometimes, the Cardano node will perform a complete re-validation of the chain as an integrity check. It does so by scanning through its local files, and _replaying_ blocks onto each other. On Mainnet, this procedure can take multiple hours and is caused, in principle, by mainly two things: 1. A restart after a non-clean shutdown of the node (e.g. a SIGKILL, or power outage); 2. A critical change in the ledger version. The detection of the first scenario is sometimes a bit clunky, and the unlucky ones amongst us have likely already experienced (sometimes several times) the infamous: Replayed block: slot 3844799 out of 112665654. Progress: 3.41% Replayed block: slot 3844783 out of 112665654. Progress: 3.42% ... It turns out that there exists a way to skip re-validations of the first kind. Indeed, on start-up, the node will look for an empty file named `clean` at the root of its database folder. node.db/ ├── clean <------------ this fellow ├── immutable ├── ledger ├── lock ├── protocolMagicId └── volatile When present, the node will not replay blocks, unless it detects a critical change in the ledger (second scenario) which happens fairly rarely. So it suffices to create such a file (e.g. `touch node.db/clean` to avoid replaying blocks unecessarily. This is a useful trick to use when developing with a local node, but **totally unsound in a production setting** (especially for SPO). Transaction latency vs finality[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#transaction-latency-vs-finality) -------------------------------------------------------------------------------------------------------------------------- In a distributed system like Cardano, the notion of _latency_ and _finality_ are often misunderstood (or swapped). Yet, it is crucial to get them right especially when it comes to transactions. Latency is the time it takes for a transaction to appear on the blockchain, in a block. Finality is the time it takes for a transaction to become immutable and permanent. Why is that different? Because the system is distributed! And that means the information is only _eventually_ true. How long is enough depends on the interested parties and the type of transaction. It is hard to give a definite answer because finality directly depends on the proportion of adversarial stake in the system. And unfortunately, adversaries don't walk the street waving their hands about the fact they are, in fact, adversaries. If we call `ε` the proportion of adversaries (`[0;0.5]`), and consider `g` their grinding power we find that under Ouroboros Praos (current Cardano consensus algorithm), the _probability of settlement errors_ in terms of the number of blocks (`x`) is given by the following equation: ![](https://aiken-lang.org/settlement_error_probability.light.svg) ![](https://aiken-lang.org/settlement_error_probability.dark.svg) Now, what are good values for `ε` and `g` is hard to say. Regarding the grinding power, values in the range of `10^9`..`10^10` are quite conservative values. The entire Bitcoin network has a grinding power of about `10^12`. So unless the entire Bitcoin network is attacking Cardano, `g` is likely smaller than `10^12`. For the adversary proportion, as a rule of thumb, it's good to look at the total stake of the largest stake pools to get an idea of how much power can a single entity gather. ![](https://aiken-lang.org/settlement_error_probability.png) A good recommendation for sensitive transactions is thus to wait around 100-150 blocks (30-50 min) whereas a few blocks is usually sufficient for small payments. If you want to play with the equation, feel free to look at [this interactive calculator (opens in a new tab)](https://www.geogebra.org/calculator/rjttcxk8) . Developer Portal[](https://aiken-lang.org/fundamentals/what-i-wish-i-knew#developer-portal) -------------------------------------------------------------------------------------------- There exists plenty of resources available on Cardano. Though there are a bit hard to find sometimes. As a rule of thumb (and unfortunately): avoid [http://docs.cardano.org (opens in a new tab)](http://docs.cardano.org/) as it is often inaccurate, plain wrong or missing crucial elements. On the other hand, the [Cardano Developer Portal (opens in a new tab)](https://developers.cardano.org/) is a good entry point to many of the ecosystem tooling and resources. The "Builder Tools" section is particularly furnished. It is community-maintained, curated and under constant evolution. Feel free to contribute and ask questions there as well! [Common Design Patterns](https://aiken-lang.org/fundamentals/common-design-patterns "Common Design Patterns") [Language Tour](https://aiken-lang.org/language-tour "Language Tour") --- # Aiken | Control flow [Language Tour](https://aiken-lang.org/language-tour) Control Flow Control flow ============ Blocks[](https://aiken-lang.org/language-tour/control-flow#blocks) ------------------------------------------------------------------- Every block in Aiken is an expression. All expressions in the block are executed, and the result of the last expression is returned. let value: Bool = { "Hello" 42 + 12 False } value == False Expression blocks can be used instead of parenthesis to change the precedence of operations. let celsius = { fahrenheit - 32 } * 5 / 9 Piping[](https://aiken-lang.org/language-tour/control-flow#piping) ------------------------------------------------------------------- | Operator | Precedence | | --- | --- | | `\|>` | 0 | Aiken provides syntax for passing the result of one function to the arguments of another function: the pipe operator (`|>`). This is similar in functionality to the same operator in Elixir, Elm or F#. The pipe operator allows you to chain function calls without using a lot of parenthesis and nesting. For a simple example, consider the following implementation of an imaginary `string.reverse` in Aiken: string_builder.to_string(string_builder.reverse(string_builder.from_string(string))) This can be expressed more naturally using the pipe operator, eliminating the need to track parenthesis closure. string |> string_builder.from_string |> string_builder.reverse |> string_builder.to_string Each line of this expression applies the function to the result of the previous line. This works easily because each of these functions takes only one argument. Syntax is available to substitute specific arguments of functions that take more than one argument; for more, look below in the section "Function capturing". Looping through recursion[](https://aiken-lang.org/language-tour/control-flow#looping-through-recursion) --------------------------------------------------------------------------------------------------------- Aiken is a functional programming language, and as such, doesn't provide any control flow for loops. Instead, Aiken embraces recursion. A recursive function is a function that calls itself in its own definition. And similarly, a recursive type is a type that is defined in terms of itself. Let's see an example for both. type MyList { Empty Prepend(a, MyList) } fn length(xs: MyList) -> Int { when xs is { Empty -> 0 Prepend(_head, tail) -> 1 + length(tail) } } length(Prepend(1, Prepend(2, Prepend(3, Empty)))) // == 3 Here we artificially define a custom `MyList` type which represents a linked list. And we define a function over it that computes its length. In both cases, you can see how it is crucial that there exist a terminal case. Recursion is extremely powerful, in particular on an execution environment like Plutus on Cardano. Abuse it. If-Else[](https://aiken-lang.org/language-tour/control-flow#if-else) --------------------------------------------------------------------- Pattern matching on a `Bool` value is discouraged and `if *condition* else` expressions should be use instead. let some_bool = True if some_bool { "It's true!" } else { "It's not true." } Note that, while it may look like an imperative instruction: if this then do that or else do that, it is in fact one single expression. This means, in particular, that the return types of both branches have to match. Incidentally, you can have as many conditional `else/if` branches as you need: fn fibonacci(n: Int) -> Int { if n == 0 { 0 } else if n == 1 { 1 } else { fibonacci(n-2) + fibonacci(n-1) } } Fail & Todo[](https://aiken-lang.org/language-tour/control-flow#fail--todo) ---------------------------------------------------------------------------- Sometimes, you need to halt the evaluation of your program because you've reached a case that is considered invalid or simply because you haven't yet finished developing some logic. Aiken provides two convenient keywords for that: `fail` and `todo`. When encountered, both will halt the evaluation of your program which will be considered failed. They differ in their semantic i.e. how the compiler behaves towards them. In fact, `todo` will trigger warnings at compilation to remind you of those unfinished parts. `fail` will not, as it is assumed to be a desired break point. Note that the warning also includes the expected type of the expression that needs to replace the `todo`. This can be a useful way of asking the compiler what type is needed if you are ever unsure. Let's see an example for both to grasp the difference: fn favourite_number() -> Int { // The type annotations says this returns an Int, but we don't need // to implement it yet. todo } fn expect_some_value(opt: Option) -> a { when opt is { Some(a) -> a None -> fail // We want this to fail when we encounter 'None'. } } When this code is built Aiken will type check and compile the code to ensure it is valid, and the `todo` or `fail` will be replaced with code that crashes the program if that function is run. ### Giving a reason[](https://aiken-lang.org/language-tour/control-flow#giving-a-reason) A `String` message can be given as a form of documentation. The message will be traced when the `todo` or `fail` code is evaluated. Note that this is likely the only place where you will encounter the type `String` and this is because the message needs to be printable -- unlike most `ByteArray` which are often plain gibberish. fn favourite_number() -> Int { todo @"Believe in the you that believes in yourself!" } fn expect_some_value(opt: Option) -> a { when opt is { Some(a) -> a None -> fail @"Option has no value" } } Expect[](https://aiken-lang.org/language-tour/control-flow#expect) ------------------------------------------------------------------- `expect` is a special assignment keyword which works like `let`, but allows to perform some potentially unsafe conversions. There are two main use cases for it: ### Non-exhaustive pattern-matching[](https://aiken-lang.org/language-tour/control-flow#non-exhaustive-pattern-matching) In cases where you have a value of a type that has multiple constructor variants, but are only truly interested in one of the possible variants as outcomes (i.e. any other outcomes invalidate the program), then `expect` is the perfect tool. Consider the `Option` type in the following example: let x = Some(42) // As a pattern-match let y = when x is { None -> fail Some(y) -> y } // Using expect expect Some(y) = x As you can see, `expect` works like a non-exhaustive pattern-match. The difference being that it is instructed to crash the entire program in case where the right-hand side wouldn't have the expected shape. It signals to the compiler that in this particular place, it is acceptable to not be exhaustive. This may seem like a bad practice from the traditional world, but remember that Aiken is used in a smart contract context where there's often no room for error handling. Either the data has the expected form, or the entire contract fails. ### Casting from `Data` into a custom type[](https://aiken-lang.org/language-tour/control-flow#casting-from-data-into-a-custom-type) Another crucial use of `expect` is to turn some opaque `Data` into a concrete representation. In the context of a Cardano smart contract, we can encounter `Data` in various places although in general it's most likely when dealing with the datums attached to outputs. Often, we do expect a specific structure for some given datum, and so being able to safely relay those assumptions in the validators comes in handy. The syntax is identical to the other use case, but it requires an explicit type annotation. type MyDatum { foo: Int, bar: ByteArray, } fn to_my_datum(data: Data) -> MyDatum { expect my_datum: MyDatum = data my_datum } Note that this conversion will fail if the given `Data` isn't actually a valid representation of the target type `MyDatum`. The primary use-case here is for instantiating script contexts, datums and redeemers provided to scripts in a serialized form. ### Custom traces[](https://aiken-lang.org/language-tour/control-flow#custom-traces) When compiling in `verbose` mode (`--trace-level verbose`), expect expressions will generate a corresponding runtime trace. This allows for troubleshooting code more easily. By default, the trace message is auto-generated from the source code but it is possible to replace it with a custom one using doc-comments: /// life, universe and everything. expect answer == 42 Notice the 3 (not 2!) leading slashes. If the assertion fails, then a trace `" life, universe and everything"` will be yielded. In `compact` mode, the trace will be truncated to its _label_, if it contains any. A _label_ is defined as a portion of text before a colon `:`. For example: /// assertion 001: redefining mathematics expect 1 + 1 == 3 will yield `" assertion 001: redefining mathematics"` in `verbose` mode, but only `" assertion 001"` in `compact` mode. And of course, nothing at all in `silent` mode. Soft casting with `if/is`[](https://aiken-lang.org/language-tour/control-flow#soft-casting-with-ifis) ------------------------------------------------------------------------------------------------------ The `expect` keyword works well in cases where failing is an option. But there are situations, like for example when recursively traversing elements of a list, where it isn't acceptable. The `if *expr* is *pattern*` therefore comes into play when you need to fallback in case a right-hand side value doesn't have the right shape instead of failing loudly. It can also be particularly handy when you simply don't know the expected type of an opaque `Data` and need to try several alternatives. `if *expr* is *pattern*` There is an extension to if expressions to be able to attempt casting but without returning an error by using the provided else instead. type Foo { foo: Int } type Bar { Buzz(Int) Bazz(Int) } fn soft_casting(d: Data, x: Data) -> Bool { // with no pattern provided if d is Foo { d.foo == 1 // with a pattern } else if d is Bazz(y): Bar { y == 1 // with a pattern } else if x is Buzz(y): Bar { y == 2 } else { False } } Like `expect`, patterns can be used for either type-casting or pattern-matching on a single pattern. And like `expect`, it can also introduce new identifiers in scope. As shown, in the second and third branch, we introduce a `y` variable that is only available within those branches. Note that the use of `if/is` is discourage in situation where no type-casting is necessary: just use `when/is`. A final `else` is always required and acts like a wildcard / catch-all case to handle any remaining pattern or casting not explicitly covered by any of the previous branch. [Custom Types](https://aiken-lang.org/language-tour/custom-types "Custom Types") [Validators](https://aiken-lang.org/language-tour/validators "Validators") --- # Aiken | Benchmarks [Language Tour](https://aiken-lang.org/language-tour) Benchmarks Benchmarks ========== Aiken has built-in support for benchmarking through a syntax similar to [property-based tests](https://aiken-lang.org/language-tour/tests#property-based-test) . Benchmarks allow you to measure execution costs (memory and CPU) across increasing input complexity. Writing benchmarks[](https://aiken-lang.org/language-tour/bench#writing-benchmarks) ------------------------------------------------------------------------------------ To write a benchmark, use the `bench` keyword along with a `Sampler`. A `Sampler` takes a size parameter and generates increasingly larger inputs based on a specified growth pattern (typically constant or linear). In fact, a `Sampler` is defined as such: type Sampler = fn(Int) -> Fuzzer Hence, samplers can be constructed from fuzzers quite easily using the [`aiken-lang/fuzz` (opens in a new tab)](https://github.com/aiken-lang/fuzz) package and used in a similar fashion. A `Sampler` is introduced as a special annotation for the argument using the `via` keyword. use aiken/fuzz use aiken/primitive/bytearray use aiken/primitive/int fn sample_bytearray(size: Int) -> Fuzzer { fuzz.bytearray_between(size * 128, size * 128) } bench bytearray_length(bytes: ByteArray via sample_bytearray) { bytearray.length(bytes) } // Note, you can also omit the `: ByteArray` type annotation bench bytearray_to_integer(bytes via sample_bytearray) { int.from_bytearray_big_endian(bytes) } ✍️ The size will grow linearly from `0` to `--max-size` (`30` by _default_). It is then up to the sampler to define how the generated value should grow. For example, it's totally possible to generate values that grow quadratically in terms of the size: fn sample_bytearray_quadratic(size: Int) -> Fuzzer { fuzz.bytearray_between(size * size, size * size) } It's also possible, like in `sample_bytearray` above to scale the size further to ensure benchmarks capture interesting behaviours. Running benchmarks[](https://aiken-lang.org/language-tour/bench#running-benchmarks) ------------------------------------------------------------------------------------ Benchmarks are collected using the `aiken bench` command. They provide a report showing execution costs (in abstract memory and CPU units) in terms of a monotically increasing size. ![](https://aiken-lang.org/benchmarks_bytearray_length.png) ![](https://aiken-lang.org/benchmarks_bytearray_to_integer.png) ### JSON Output[](https://aiken-lang.org/language-tour/bench#json-output) By default, the `bench` command will generate plot charts in your terminal, since they are immediately visual and can help spot behaviours in a glance. You can also obtain the complete dataset of points used for the plot by redirecting the standard output to a file. aiken bench > benchmarks.json The output is a structured JSON file containing all results for your benchmarks. Handy if you want to conduct your own analysis! ### Filtering benchmarks[](https://aiken-lang.org/language-tour/bench#filtering-benchmarks) Like tests, you can run specific benchmarks using the usual command-line options: # Run benchmarks in specific module aiken bench -m "my_module" # Run specific benchmark aiken bench -e -m "my_bench" Benchmarks are particularly useful when optimizing validator scripts since they allow you to measure execution costs across different input sizes and complexity levels. For more information about the testing functionality that benchmarking builds upon, see the [testing documentation](https://aiken-lang.org/language-tour/tests) . [Tests](https://aiken-lang.org/language-tour/tests "Tests") [Troubleshooting](https://aiken-lang.org/language-tour/troubleshooting "Troubleshooting") --- # Aiken | The modern smart contract platform for Cardano A pure functional programming language -------------------------------------- * Small and easy to learn * Strong static typing with inference * First-class functions and expressions, referential transparency * Custom types, recursion, modules, generics... * Modules, imports and package manager validator hello\_world { spend(datum, redeemer, input, self) { expect Some(Datum { owner }) \= datum let must\_say\_hello \= redeemer \== "Hello, World!" let must\_be\_signed \= list.has(self.extra\_signatories, owner) must\_say\_hello? && must\_be\_signed? } } A modern development environment -------------------------------- * Zero configuration, one single capable tool * Quick and friendly feedback with helpful error diagnotics * Language-Server Protocol (LSP) with auto-formatting * Easy-to-produce and beautiful libraries documentation * Editors integration (VSCode, NeoVim, Emacs, Zed...) * Tree-sitter support **Error:** err::aiken::check::unknown\_record\_field × While trying to make sense of your code... ╰─▶ I looked for the field 'extra\_signatures' in a record of type 'Transaction' but didn't find it. ╭─\[examples/hello\_world/validators/hello\_world.ak:18:1\] 18 │ let must\_be\_signed = 19 │ list.has( 20 │ context.transaction.extra\_signatures, · ──────────────────────────────────── 21 │ datum.owner 22 │ ) ╰───────────────────────────────────────────────────── help: _Did you mean 'extra\_signatories'?_ A toolkit for working with Plutus --------------------------------- * Baked-in unit test framework * Property test framework with integrated shrinking * UPLC type reification into Aiken types * Full-blown Plutus interpreter & disassembler * Execution cost evaluation with trace reporting * Low-level scripts arguments injection **Compiling** aiken-lang/stdlib 2.0.0 **Testing** ... ┍━ **aiken/option** ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ PASS \[mem: 5099, cpu: 1884852\] or\_else\_1 │ PASS \[mem: 5963, cpu: 3091855\] map\_1 │ PASS \[mem: 11985, cpu: 5076974\] map\_2 │ PASS \[mem: 6863, cpu: 3298855\] and\_then\_1 │ PASS \[mem: 14187, cpu: 5872937\] and\_then\_2 │ PASS \[mem: 11657, cpu: 4918544\] and\_then\_3 ┕━━━━━━━━━━━━━━ **13 tests** | **13 passed** | **0 failed** **Summary** 0 error, 0 warning(s) A truly open source ecosystem ----------------------------- * Written in Rust, licensed under _Apache-2.0_ * 200+ contributors from the Cardano community * [Open contribution process](https://github.com/aiken-lang/aiken/issues/947) with clear guidelines * Fully documented with end-to-end examples * Initiated by [TxPipe](https://txpipe.io/) , nurtured by [Cardano Foundation](https://cardanofoundation.org/) , elevated by [PRAGMA](https://pragma.io/) [Join the Discord](https://discord.gg/ub6atE94v4) Trusted by builders at ---------------------- ![MinSwap](https://aiken-lang.org/companies/minswap.svg)![jpg.store](https://aiken-lang.org/companies/jpg-store.svg)![Sundae Labs](https://aiken-lang.org/companies/sundae-labs.svg)![Cardano Foundation](https://aiken-lang.org/companies/cardano-foundation.svg)![TxPipe](https://aiken-lang.org/companies/txpipe.svg)![Input Output HK](https://aiken-lang.org/companies/iohk.svg)![Lenfi](https://aiken-lang.org/companies/lenfi.svg)![Butane](https://aiken-lang.org/companies/butane.webp)![Book.io](https://aiken-lang.org/companies/book.png) Roadmap ------- ### Recently delivered ― see the [full CHANGELOG](https://github.com/aiken-lang/aiken/blob/main/CHANGELOG.md#changelog) * Plutus V3 support (language & virtual machine), and Plutus V3 transaction evaluation * New standard library v2 ― see [aiken-lang/stdlib](https://aiken-lang.github.io/stdlib/) * Conditional environment and configuration modules * Softcasting (a.k.a `if/is` syntax) * Supercharged constants * Variadic traces with baked-in CBOR diagnostics ### Currently working on ― see [project tracking](https://github.com/orgs/aiken-lang/projects/2) * Additional LSP quickfixes * Support for mono-repo ― see [aiken-lang/aiken#951](https://github.com/aiken-lang/aiken/issues/951) * Ecosystem packages registry & search tool ### Future steps & ideas ― see [ongoing discussions](https://github.com/aiken-lang/aiken/discussions) * Code coverage tooling ― see [aiken-lang/aiken#933](https://github.com/aiken-lang/aiken/discussions/933) * More aggressive UPLC optimizations * First-class zero-knowledge support * Layer-2 builtin support ― see [aiken-lang/aiken#976](https://github.com/aiken-lang/aiken/discussions/976) FAQ --- ### What is Aiken? Aiken is a modern **programming language and toolkit for developing smart contracts** on the [Cardano](https://cardano.org/) blockchain. It is geared towards robustness and developer experience. Aiken takes inspiration from many modern languages such as Gleam, Rust, and Elm, which are known for friendly error messages and an overall excellent developer experience. We believe Cardano deserves a dedicated language with these kinds of features, developed in the open with the community. ### Why build another smart contract platform? We love functional programming. Yet, we couldn't help to think that the current Haskell framework wasn't _quite right_ in terms of developer experience and felt we could do something about it. So we came up with a set of shared goals we kept in mind while building Aiken: * Writing smart contracts should be **easy and safe**. You should be able to get started in minutes, not days, and rapidly build confidence that your on-chain code is doing what is intended. * We want a complete and delightful experience. A modern blockchain deserves a modern smart contract toolkit. **You should be and feel productive** when writing contracts. This includes editor integrations such as LSP and tree-sitter, beautiful error messages, rapid and intuitive test feedback loop and easy-to-produce documentation. * We want there to be **as little configuration as possible**. It should work out of the box and establish reasonable opinionated conventions with the community. * We want to have a **modular design** such that components can be picked and chosen as needed. Like the Unix philosophy. Aiken is only one part of a much bigger picture; While we firmly believe it is a great tool, we want to encourage other approaches and favour interoperability as much as possible. ### I thought Cardano smart contracts had to be written in Haskell? This is a common misconception. The current Cardano node implementation does indeed happen to be written in Haskell. The virtual machine for executing smart contracts that comes baked into the node is also implemented in Haskell. But that does not mean that it is Haskell which is executed by the smart contract virtual machine. The virtual machine is a language interpreter which executes a smart contract language called _'Untyped Plutus Core'_ (abbrev. `UPLC`) often referred to simply as _'Plutus'_. Yet UPLC isn't something developers are expected to write by hand. Instead, it is a compilation target (like WebAssembly for the world wide web). Oddly enough, until recently, the only established framework that produced UPLC from a high-level syntax was called _'Plutus-Tx'_ and happened to be a Haskell framework. Aiken changes the game by providing a modern framework that compiles straight to UPLC. ### Can I write off-chain/backend code with Aiken? Our main goal is to improve the smart contract development experience **for the Cardano blockchain**. By developing Aiken as a bespoke language, we can better align its features with what is truly needed for on-chain development. We want to keep the language simple and manageable. In a decentralized architecture, smart contracts present a significant challenge where a single flaw can escalate into a multi-million financial exploit. More so, on-chain code typically represents a small fraction of an entire DApp source code. While writing contracts should be as straightforward as possible, smart contracts ought to be optimized for review, audit, and static analysis. Hence, Aiken is **not** intended as a general-purpose language. Rather, it focuses on Cardano and aims for a high-quality toolkit for developing reliable smart contracts with confidence. ### Are there any projects using Aiken? Where can I find them? We keep an [Awesome List](https://github.com/aiken-lang/awesome-aiken#readme) of projects and resources built with and on Aiken. The list contains resources that **we are aware of**. If you have a cool project not listed but want to see it up there, do not hesitate to make a pull request and introduce yourself! --- # Aiken | Troubleshooting [Language Tour](https://aiken-lang.org/language-tour) Troubleshooting Troubleshooting =============== On-chain programming can be a bit tedious and shares a lot of commonalities with embedded programming. Because the execution environment is so constrained, programs have to be optimized and usually leave little room for troubleshooting errors. Aiken tries its best to provide developers with extra tools and debugging capabilities. Let's explore them. Traces[](https://aiken-lang.org/language-tour/troubleshooting#traces) ---------------------------------------------------------------------- ### `trace` keyword[](https://aiken-lang.org/language-tour/troubleshooting#trace-keyword) Your first ally in this journey are traces. Think of a trace as a log message, that is captured by the virtual machine at specific moment. You can add traces to top-level expressions in Aiken using the `trace` keyword. For example: fn is_even(n: Int) -> Bool { trace @"is_even" n % 2 == 0 } fn is_odd(n: Int) -> Bool { trace @"is_odd" n % 2 != 0 } Traces can be a little hard to grasp initially since Plutus -- and therefore Aiken -- is a purely functional execution engine. There's no _statements_ in a compiled program. There only are _expressions_. A trace will be collected if it is evaluated by the virtual machine. There are two common ways to capture traces in Aiken: when running tests via `aiken check` or when simulating a transaction using `aiken tx simulate`. In both cases, traces captured during evaluation will be printed on screen. For example, in the following program: let n = 10 is_even(n) || is_odd(n) Only the trace `is_even` will be captured, because `is_odd` is in fact never evaluated (there's no need because the left-hand side already returns `True`). There's more! Traces are quite powerful in Aiken. The `trace` keyword is actually variadic: it accepts any number of arguments between 1 and ... please don't go too high. The syntax for it might look a bit surprising, unless you think of the first argument as _a label_. fn foo() { trace @"A": @"foo", @"bar" // outputs "A: foo, bar" Void } In fact, since `v1.1.0` traces are no longer limited to strings. You can trace any _serialisable_ value auto-magically. The values will be traced using the [diagnostic notation described below](https://aiken-lang.org/language-tour/troubleshooting#cbor-diagnostic) . fn foo(my_list: List>) -> Bool { trace @"foo": my_list list.is_empty(my_list) } ⚠️ Note that traces are: * **Removed by default** when building your project with `aiken build`. They can be preserved using `--trace-level verbose`. You can also only preserve traces' labels (i.e. first arguments) by using `--trace-level compact`. * **Kept by default** when checking your project with `aiken check`. They can be left out using `--trace-level silent`. This is because tracing makes compiled code bigger and can add an extra overhead which is often undesired for final production-ready validators. Yet, they are useful for development and when testing. The command-line is thus geared towards those use-cases. Beware that while enabling or disabling traces doesn't change the semantic of your program, it effectively changes its hash value, and thus its associated addresses. ### `expect` traces[](https://aiken-lang.org/language-tour/troubleshooting#expect-traces) When compiling in `verbose` mode (`--trace-level verbose`), expect expressions will generate a corresponding runtime trace. This allows for troubleshooting code more easily. By default, the trace message is auto-generated from the source code but it is possible to replace it with a custom one using doc-comments: /// life, universe and everything. expect answer == 42 Notice the 3 (not 2!) leading slashes. If the assertion fails, then a trace `" life, universe and everything"` will be yielded. In `compact` mode, the trace will be truncated to its _label_, if it contains any. A _label_ is defined as a portion of text before a colon `:`. For example: /// assertion 001: redefining mathematics expect 1 + 1 == 3 will yield `" assertion 001: redefining mathematics"` in `verbose` mode, but only `" assertion 001"` in `compact` mode. And of course, nothing at all in `silent` mode. ### `?` operator[](https://aiken-lang.org/language-tour/troubleshooting#-operator) On-chain programs are fundamentally nothing more than predicates. Said differently, they are functions that return `True` or `False`. Hence, it is common practice to structure on-chain programs as conjunctions and disjunctions of booleans expressions. This can be a little hard to reason about however because booleans are "blind". That is, you lose information about the original context as you evaluate complex boolean expressions. Take for example the following simple expression: let must_be_after = True let must_spend_token = False must_be_after && must_spend_token It evaluates to `False`. From just `False`, you can't really tell which branch was actually `False` in the original expression. Yet it is often useful to reason about even larger expressions to troubleshoot an issue. This is why Aiken provides the `?` operator (reads as _"trace-if-false operator"_). This postfix operator can be appended to any boolean expression and will trace the expression only if it evaluates to `False`. This is useful to trace an entire evaluation path that led to a final expression being `False`. In the example above, we could have written: must_be_after? && must_spend_token? Which would have generated the trace `"must_spend_token ? False"`. Handy, right? ⚠️ Both the `?` operator and `trace` are affected by the `aiken build` `--trace-level` options. The available trace level options are `silent`, `compact`, and `verbose`. The behaviors are as follows: | Trace Level | `?` operator | `trace` | | --- | --- | --- | | `silent` | No trace will be kept. | No trace will be kept. | | `compact` | No trace will be kept. | Preserve trace labels. For example: `trace @"Label": 1, 2` will print `Label` | | `verbose` | A full trace will be printed. For example: `(a == 1)?` will print `a == 1 ? False` | A full trace will be printed. For example: `trace @"Label": 1, 2` will print `Label: 1, 2` | There is an alias for `aiken build`, which is `aiken b`. Without any trace level option, it defaults to `silent` build. CBOR diagnostic[](https://aiken-lang.org/language-tour/troubleshooting#cbor-diagnostic) ---------------------------------------------------------------------------------------- This is all great but sometimes, you need more. Sometimes, you need to inspect the value of some specific object _at runtime_. This is harder than it seems because a compiled Aiken program has erased any context and any notion of types. Even functions and variable names are replaced by compact indices which makes it relatively hard to inspect programs and values at runtime. For example, this is what a compiled function may look like in UPLC: (lam i_31 (lam i_32 (lam i_33 (force [ [ [ i_2 i_32 ] (delay (con unit ())) ]\ (delay\ [ [ i_4 [ i_33 [ i_1 i_32 ] ] ]\ [ [ [ i_31 i_31 ] [ i_0 i_32 ] ] i_33\ ]\ ]\ )\ ] ) ) ) ) Not quite easy to read, huh? But there's hope! [Aiken's standard library (opens in a new tab)](https://aiken-lang.github.io/stdlib/) provides a convenient method to inspect **any value** at runtime and obtain a `String` representation of them. The syntax used for this representation is called a [CBOR diagnostic (opens in a new tab)](https://www.rfc-editor.org/rfc/rfc8949#name-diagnostic-notation) . Think of it as a high-level syntax that resembles JSON and that can represent binary data. aiken/cbor pub fn diagnostic(data: Data) -> String Why use CBOR diagnostics you may ask? Well, because it is what most faithfully captures the representation of objects present at runtime and in the interface of on-chain validators. Getting familiar with CBOR diagnostics requires a bit of practice but can be a useful skill to master when working with Cardano in general. CBOR is everywhere in Cardano, including in on-chain validators. Datum and redeemers are, for example, provided as CBOR objects to the validator by the ledger. Transactions are also themselves encoded as CBOR when serialized and propagated to the network. A CBOR diagnostic is merely a slightly more human-friendly way to visualize a binary object. For example, a serialized list of integers such as `83010203` is represented as `[1, 2, 3]` in diagnostic notation. In addition, most tools and libraries that deal with CBOR make it easy to go back-and-forth between the raw encoding and the diagnostic notation. This is the case of [cbor.me (opens in a new tab)](https://cbor.me/) or [cbor-diag (opens in a new tab)](https://github.com/cabo/cbor-diag) for instance. Here's a little cheatsheet to help you decipher CBOR diagnostics: | Type | Examples | | --- | --- | | `Int` | `1`, `-14`, `42` | | `ByteArray` | `h'FF00'`, `h'666f6f'` | | `List` | `[]`, `[1, 2, 3]`, `[_ 1, 2, 3]` | | `Map` | `{}`, `{ 1: h'FF', 2: 14 }`, `{_ 1: "AA" }` | | `Tag` | `42(1)`, `10(h'ABCD')`, `1280([1, 2])` | While most are pretty transparent, the use-case for `Tag` may not strike many as obvious. In fact, `Tag` is used to encode custom types on-chain, starting from tag `121` for the first constructor of a data-type, `122` for the next, and so forth. What is tagged corresponds to the fields of the constructors, as a list of objects. Let's see some more examples of diagnostics from real Aiken values. use aiken/cbor // An Int becomes a CBOR int cbor.diagnostic(42) == @"42" // A ByteArray becomes a CBOR bytestring cbor.diagnostic("foo") == @"h'666F6F'" // A List becomes a CBOR array cbor.diagnostic([1, 2, 3]) == @"[_ 1, 2, 3]" // A Tuple becomes a CBOR array cbor.diagnostic((1, 2)) == @"[_ 1, 2]" // A List of 2-tuples becomes a CBOR map cbor.diagnostic([(1, #"ff")]) == @"{ 1: h'FF' }" // 'Some' is the first constructor of Option → tagged as 121 cbor.diagnostic(Some(42)) == @"121([_ 42])" // 'None' is the second constructor of Option → tagged as 122 cbor.diagnostic(None) == @"122([])" Diagnostics are meant to be used only in development or for testing; in combination with `trace` for example. Incidentally, they also make for a convenient way to double-check the binary representation of some instance of your datum or redeemer through tests. Imagine the following type: type MyDatum { foo: Int, bar: ByteArray } Eventually, you will need to construct compatible values for building an associated transaction. Aiken provides blueprints as build outputs to help with that. Yet you may also control some chosen values directly using `cbor.diagnostic` and a test: use aiken/cbor test my_datum_1() { let datum = MyDatum { foo: 42, bar: "Hello, World!" } cbor.diagnostic(datum) == @"121([42, h'48656c6c6f2c20576f726c6421'])" } You can turn this diagnostic into raw CBOR using tools such as [cbor.me (opens in a new tab)](https://cbor.me/?diag=121(%5B42,%20h%2748656c6c6f2c20576f726c6421%27%5D)) . [Benchmarks](https://aiken-lang.org/language-tour/bench "Benchmarks") [First steps](https://aiken-lang.org/example--hello-world/basics "First steps") --- # Aiken | Hello, World! Hello, World! First steps Hello, World! ============= Let's write and execute a smart contract on Cardano in 10 minutes. Yes, you read that right. You can find code supporting this tutorial on [Aiken's main repository (opens in a new tab)](https://github.com/aiken-lang/aiken/tree/main/examples/hello_world) . Covered in this tutorial[](https://aiken-lang.org/example--hello-world/basics#covered-in-this-tutorial) -------------------------------------------------------------------------------------------------------- * [x] Writing a basic Aiken validator; * [x] Writing & running tests with Aiken; * [x] Troubleshooting smart contracts. 📘 When encountering an unfamiliar syntax or concept, do not hesitate to refer to the [language-tour](https://aiken-lang.org/language-tour/primitive-types) for details and extra examples. Pre-requisites[](https://aiken-lang.org/example--hello-world/basics#pre-requisites) ------------------------------------------------------------------------------------ We'll use Aiken to write the script so make the command-line installed already or else, look at the [installation instructions](https://aiken-lang.org/installation-instructions) . Scaffolding[](https://aiken-lang.org/example--hello-world/basics#scaffolding) ------------------------------------------------------------------------------ First, let's create a new Aiken project: aiken new aiken-lang/hello-world cd hello-world This command scaffolds an Aiken project. In particular, it creates a `lib` and `validators` folders in which you can put Aiken source files. ./hello-world │ ├── README.md ├── aiken.toml ├── lib └── validators Using the standard library[](https://aiken-lang.org/example--hello-world/basics#using-the-standard-library) ------------------------------------------------------------------------------------------------------------ We'll use the [standard library (opens in a new tab)](https://aiken-lang.github.io/stdlib) for writing our validator. Fortunately, `aiken new` did automatically add the standard library to our `aiken.toml` for us. It should look roughly like that: aiken.toml name = "aiken-lang/hello-world" version = "0.0.0" license = "Apache-2.0" description = "Aiken contracts for project 'aiken-lang/hello-world'" [repository] user = 'aiken-lang' project = 'hello-world' platform = 'github' [[dependencies]] name = "aiken-lang/stdlib" version = "v2" source = "github" Now, running `aiken check`, we should see dependencies being downloaded. That shouldn't take long. ❯ aiken check Compiling aiken-lang/hello-world 1.0.0 (examples/hello-world/) Resolving aiken-lang/hello-world Fetched 1 package in 0.01s from cache Compiling aiken-lang/stdlib v2 (/Users/aiken/Documents/aiken-lang/hello-world/build/packages/aiken-lang-stdlib) Summary 0 errors, 0 warnings Our first validator[](https://aiken-lang.org/example--hello-world/basics#our-first-validator) ---------------------------------------------------------------------------------------------- Let's write our first validator as `validators/hello_world.ak`: validators/hello\_world.ak use aiken/collection/list use aiken/crypto.{VerificationKeyHash} use cardano/transaction.{OutputReference, Transaction} pub type Datum { owner: VerificationKeyHash, } pub type Redeemer { msg: ByteArray, } validator hello_world { spend( datum: Option, redeemer: Redeemer, _own_ref: OutputReference, self: Transaction, ) { expect Some(Datum { owner }) = datum let must_say_hello = redeemer.msg == "Hello, World!" let must_be_signed = list.has(self.extra_signatories, owner) must_say_hello && must_be_signed } } Our first validator is rudimentary, yet there's already a lot to say about it. 1. It looks for a verification key hash (`owner`) in the datum and a message (`msg`) in the redeemer. Remember that, in the eUTxO model, the **datum** is set **when locking funds** in the contract and can be therefore seen as configuration. Here, we'll indicate the owner of contract and require a signature from them to unlock funds—very much like it already works on a typical non-script address. 2. Moreover, because there's no "Hello, World!" without a proper "Hello, World!" our little contract also demands this very message, as a UTF-8-encoded byte array, to be passed as redeemer (i.e. when spending from the contract). It's now time to build our first contract! aiken build This command generates a [CIP-0057 Plutus blueprint (opens in a new tab)](https://github.com/cardano-foundation/CIPs/pull/258) as `plutus.json` at the root of your project. This blueprint describes your on-chain contract and its binary interface. In particular, it contains the generated on-chain code that will be executed by the ledger, and a hash of your validator(s) that can be used to construct addresses. This format is framework-agnostic and is meant to facilitate interoperability between tools. The blueprint is fully integrated into Aiken, which can automatically generate it based on your type definitions and comments. Let's see the validator in action! Adding traces[](https://aiken-lang.org/example--hello-world/basics#adding-traces) ---------------------------------------------------------------------------------- In a way, validators are nothing more than _predicates_. A predicate is a function that returns a boolean. It indicates whether the operation is permitted or not. Here, we are writing a `spend` validator which controls who is allowed to spend funds locked by it. Troubleshooting validators can rapidly become difficult as the only real output they give is _yes_ or _no_. To cope with that, you can add _traces_ to a validator. Traces are special commands which tells the ledger—or whomever is executing the validator—to collect messages when encountered. On failure, it spits out the messages encountered, thus giving a trace of the program execution. So let's add a few traces. validators/hello-world.ak use aiken/collection/list use aiken/crypto.{VerificationKeyHash} use aiken/primitive/string use cardano/transaction.{OutputReference, Transaction} pub type Datum { owner: VerificationKeyHash, } pub type Redeemer { msg: ByteArray, } validator hello_world { spend( datum: Option, redeemer: Redeemer, _own_ref: OutputReference, self: Transaction, ) { trace @"redeemer": string.from_bytearray(redeemer.msg) expect Some(Datum { owner }) = datum let must_say_hello = redeemer.msg == "Hello, World!" let must_be_signed = list.has(self.extra_signatories, owner) must_say_hello? && must_be_signed? } } Here we have done two changes: 1. We've added a manual message using the `trace` keyword. The message is the one passed as redeemer. With this, we can check that the value seen by the validator is the expected one. 2. Notice how we also added a question mark `?` at the end of each expression `must_say_hello` and `must_be_signed`. This is what we call the `trace-if-false` operator, and is pretty handy to debug things. This operator will trace the expression it is attached to only if it evaluates to `False`. This encourages an approach where validators are built as a conjunction or disjunction of requirements. On unsuccessful executions, all the invalidated requirements will leave a trace! In order to see those traces, we'll need to write a short test. Writing a test[](https://aiken-lang.org/example--hello-world/basics#writing-a-test) ------------------------------------------------------------------------------------ Aiken has support for tests built-in! As you'll see shortly, tests can also serve as benchmarks since they display the exact memory and steps execution units required to run them. They also collect traces for us. Let's write a simple test which runs our validator. Tests are functions without arguments which return boolean. Yet unlike functions, they are denoted with the keyword `test`. We will need a datum, a redeemer and a script context as well as a few more imports: validators/hello-world.ak // ... rest of the code is unchanged test hello_world_example() { let datum = Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } let redeemer = Redeemer { msg: "Aiken Rocks!" } let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } hello_world.spend( Some(datum), redeemer, placeholder_utxo, transaction.placeholder, ) } Here, we have a test! A failing test, but we'll get it to pass, no worries. But first, let's execute it. Simply run `aiken check`: ❯ aiken check          ┍━ hello_world ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ FAIL [mem: 11387, cpu: 4370671] hello_world_example │ · with traces │ | redeemer: Aiken Rocks! │ | must_say_hello ? False ┕━━━━━━━━━━━━━━━━━━━━━━ 1 tests | 0 passed | 1 failed This output is already pretty useful. We can see the `trace` that we added in our validator which spits back the `msg` in the redeemer. Then, we see the `?` operator at play. It shows a trace since the predicate `must_say_hello` returned `False`. Note that the other predicate `must_be_signed` isn't shown here because Aiken ensures that the conditions are checked one after the other. Since the first one already failed, the entire expression shortcircuits to `False`. Let's fix this and ensure that we say `Hello, World!` instead. validators/hello-world.ak test hello_world_example() { let datum = Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } let redeemer = Redeemer { msg: "Hello, World!" } let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } hello_world.spend( Some(datum), redeemer, placeholder_utxo, transaction.placeholder, ) } Now, we can run `aiken check` again: ❯ aiken check          ┍━ hello_world ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ FAIL [mem: 18729, cpu: 7160240] hello_world_example │ · with traces │ | redeemer: Hello, World! │ | must_be_signed ? False ┕━━━━━━━━━━━━━━━━━━━━━━ 1 tests | 0 passed | 1 failed It fails again, as expected, but we got further. Notice how the `mem` and `cpu` execution units are slightly higher than on the first execution. Now, we have moved to evaluating the second part of the validator requirements: `must_be_signed`. To satisfy this second requirement, we'll need to add our test owner to the transaction's extra signatories. As such: validators/hello-world.ak // ...rest of the code is unchanged test hello_world_example() { let datum = Datum { owner: #"00000000000000000000000000000000000000000000000000000000" } let redeemer = Redeemer { msg: "Hello, World!" } let placeholder_utxo = OutputReference { transaction_id: "", output_index: 0 } hello_world.spend( Some(datum), redeemer, placeholder_utxo, Transaction { ..transaction.placeholder, extra_signatories: [datum.owner] }, ) } This should do the trick. Note that, at this point, we do not provide any signature of any kind. This is because we are not performing any of the ledger phase-1 validations. Yet, prior to executing smart contracts, the ledger will verify that the content of the transaction is valid. In particular, it will verify that any `extra_signatories` has a corresponding valid signature in the transaction. Here, we can just go with our placeholder verification key! ❯ aiken check          ┍━ hello_world ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ PASS [mem: 32451, cpu: 11921833] hello_world_example │ · with traces │ | redeemer: Hello, World! ┕━━━━━━━━━━━━━━━━━━━━━━━ 1 tests | 1 passed | 0 failed And, it works! We are left with our `Hello, World!` trace and no failure 🎉! Congratulations, you've made it. Of course, this particular test isn't really interesting. Yet, in practice, validators are more complex and layered. We encourage you to split validators into smaller functions that do one thing at a time, and test those functions independently. ⚠️ Traces can add some overhead to a validator's execution. This is why Aiken _erases_ all traces by default when you build validators. To keep them in the final validators, use the `--trace-level verbose` option when building. Conversely, the `check` command _preserves_ traces by default since most of the time, this is what you want. If you need to benchmark an execution without traces, you can always pass the `--trace-level silent` flag when running tests to remove all traces. You are now ready to move on to the next steps and look into performing this end-to-end, with a real transaction! Exciting, isn't it? [Troubleshooting](https://aiken-lang.org/language-tour/troubleshooting "Troubleshooting") [with Mesh (TypeScript)](https://aiken-lang.org/example--hello-world/end-to-end/mesh "with Mesh (TypeScript)") --- # Aiken | Hello, World! - with Mesh Hello, World! End-to-End with Mesh (TypeScript) Hello, World! - with Mesh ========================= Covered in this tutorial[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#covered-in-this-tutorial) ----------------------------------------------------------------------------------------------------------------- * [x] Interact with a validator on the `Preview` network; * [x] Using [Mesh (opens in a new tab)](https://meshjs.dev/) through [Blockfrost (opens in a new tab)](https://blockfrost.io/) ; * [x] Getting test funds from the [Cardano Faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) ; * [x] Using web explorers such as [CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/) . Pre-requisites[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#pre-requisites) --------------------------------------------------------------------------------------------- We assume that you have followed the _Hello, World!_'s [First steps](https://aiken-lang.org/example--hello-world/basics) and thus, have Aiken installed an ready-to-use. We will also use [Mesh (opens in a new tab)](https://meshjs.dev/) , so make sure you have your dev environment ready for some JavaScript!. You can install [Mesh (opens in a new tab)](https://meshjs.dev/) and setup the project as follows: npm init -y npm install @meshsdk/core@1.9.0-beta.2 tsx Getting funds[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#getting-funds) ------------------------------------------------------------------------------------------- For this tutorial, we will use the validator we built in [First steps](https://aiken-lang.org/example--hello-world/basics) . Yet, before moving on, we'll need some funds, and a public/private key pair to hold them. We can generate a private key and an address using [MeshWallet (opens in a new tab)](https://meshjs.dev/apis/wallets/meshwallet) . Let's write our first script as `generate-credentials.ts`: generate-credentials.ts import { MeshWallet } from "@meshsdk/core"; import fs from "node:fs"; async function main() { const secret_key = MeshWallet.brew(true) as string; fs.writeFileSync("me.sk", secret_key); const wallet = new MeshWallet({ networkId: 0, key: { type: "root", bech32: secret_key, }, }); fs.writeFileSync("me.addr", (await wallet.getUnusedAddresses())[0]); } main(); You can run the instructions above via: npx tsx generate-credentials.ts Now, we can head to [the Cardano faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) to get some funds on the preview network to our newly created address (inside `me.addr`). ![](https://aiken-lang.org/faucet_preview.webp) 👉 Make sure to select _"Preview Testnet"_ as network. Using [CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/) we can watch for the faucet sending some ADA our way. This should be pretty fast (a couple of seconds). Using the contract[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#using-the-contract) ----------------------------------------------------------------------------------------------------- Now that we have some funds, we can lock them in our newly created contract. We'll use [Blockfrost Provider (opens in a new tab)](https://meshjs.dev/providers/blockfrost) to construct and submit our transaction through [Blockfrost (opens in a new tab)](https://blockfrost.io/) . This is only one example of possible setup using tools we love. For more tools, make sure to check out the [Cardano Developer Portal (opens in a new tab)](https://developers.cardano.org/tools) ! ### Setup[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#setup) First, we setup Mesh with Blockfrost as a provider. This will allow us to let Mesh handle transaction building for us, which includes managing changes. It also gives us a direct way to submit the transaction later on. Create a file named `common.ts` in the root of your project and add the following code: common.ts import fs from "node:fs"; import { BlockfrostProvider, MeshTxBuilder, MeshWallet, serializePlutusScript, UTxO, } from "@meshsdk/core"; import { applyParamsToScript } from "@meshsdk/core-csl"; const blockchainProvider = new BlockfrostProvider(process.env.BLOCKFROST_PROJECT_ID); // wallet for signing transactions export const wallet = new MeshWallet({ networkId: 0, fetcher: blockchainProvider, submitter: blockchainProvider, key: { type: "root", bech32: fs.readFileSync("me.sk").toString(), }, }); ⚠️ Note that the highlighted line above looks for an environment variable named `BLOCKFROST_PROJECT_ID` which its value must be set to your Blockfrost project id. You can define a new environment variable in your terminal by running (in the same session you're also executing the script!): export BLOCKFROST_PROJECT_ID=preview... Replace `preview...` with your actual project id. Next, we'll need to read the validator from the blueprint (`plutus.json`) we generated earlier. We'll also need to convert it to a format that Mesh understands. This is done by serializing the validator and then converting it to a hexadecimal text string, we can do this by using the `applyParamsToScript` function. common.ts import fs from "node:fs"; import { BlockfrostProvider, MeshTxBuilder, MeshWallet, serializePlutusScript, UTxO, } from "@meshsdk/core"; import { applyParamsToScript } from "@meshsdk/core-csl"; import blueprint from "./plutus.json"; const blockchainProvider = new BlockfrostProvider(process.env.BLOCKFROST_PROJECT_ID); // wallet for signing transactions export const wallet = new MeshWallet({ networkId: 0, fetcher: blockchainProvider, submitter: blockchainProvider, key: { type: "root", bech32: fs.readFileSync("me.sk").toString(), }, }); export function getScript() { const scriptCbor = applyParamsToScript( blueprint.validators[0].compiledCode, [] ); const scriptAddr = serializePlutusScript( { code: scriptCbor, version: "V3" }, ).address; return { scriptCbor, scriptAddr }; } Lastly, let's add 2 more useful functions to our `common.ts` file. One to get a transaction builder and another function to fetch a UTxO by transaction hash. common.ts import fs from "node:fs"; import { BlockfrostProvider, MeshTxBuilder, MeshWallet, serializePlutusScript, UTxO, } from "@meshsdk/core"; import { applyParamsToScript } from "@meshsdk/core-csl"; import blueprint from "./plutus.json"; const blockchainProvider = new BlockfrostProvider(process.env.BLOCKFROST_PROJECT_ID); // wallet for signing transactions export const wallet = new MeshWallet({ networkId: 0, fetcher: blockchainProvider, submitter: blockchainProvider, key: { type: "root", bech32: fs.readFileSync("me.sk").toString(), }, }); export function getScript() { const scriptCbor = applyParamsToScript( blueprint.validators[0].compiledCode, [] ); const scriptAddr = serializePlutusScript( { code: scriptCbor, version: "V3" }, ).address; return { scriptCbor, scriptAddr }; } // reusable function to get a transaction builder export function getTxBuilder() { return new MeshTxBuilder({ fetcher: blockchainProvider, submitter: blockchainProvider, }); } // reusable function to get a UTxO by transaction hash export async function getUtxoByTxHash(txHash: string): Promise { const utxos = await blockchainProvider.fetchUTxOs(txHash); if (utxos.length === 0) { throw new Error("UTxO not found"); } return utxos[0]; } ### Locking funds into the contract[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#locking-funds-into-the-contract) Now that we can read our validator, we can make our first transaction to lock funds into the contract. The datum must match the representation expected by the validator (and as specified in the blueprint), so this is a constructor with a single field that is a byte array. As value for that byte array, we provide a hash digest of our public key (pubKeyHash) from the wallet created with our `me.sk`. This will be needed to unlock the funds. lock.ts import { Asset, deserializeAddress, mConStr0 } from "@meshsdk/core"; import { getScript, getTxBuilder, wallet } from "./common"; async function main() { // these are the assets we want to lock into the contract const assets: Asset[] = [\ {\ unit: "lovelace",\ quantity: "1000000",\ },\ ]; // get utxo and wallet address const utxos = await wallet.getUtxos(); const walletAddress = (await wallet.getUsedAddresses())[0]; const { scriptAddr } = getScript(); // hash of the public key of the wallet, to be used in the datum const signerHash = deserializeAddress(walletAddress).pubKeyHash; // build transaction with MeshTxBuilder const txBuilder = getTxBuilder(); await txBuilder .txOut(scriptAddr, assets) // send assets to the script address .txOutDatumHashValue(mConStr0([signerHash])) // provide the datum where `"constructor": 0` .changeAddress(walletAddress) // send change back to the wallet address .selectUtxosFrom(utxos) .complete(); const unsignedTx = txBuilder.txHex; const signedTx = await wallet.signTx(unsignedTx); const txHash = await wallet.submitTx(signedTx); console.log(`1 tADA locked into the contract at Tx ID: ${txHash}`); } main(); You can run the excerpt above by executing: npx tsx lock.ts If everything went well, you should see something like this: 1 tADA locked into the contract at Tx ID: 8559f57234407204d8e9a6bf57ef6943c65ec7119eb1c2ca6224f8bad8e71c1e #### Inspecting the transaction[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#inspecting-the-transaction) Now is a good moment to pause and have a look at CardanoScan. Here's [an example of a _Hello World_ transaction (opens in a new tab)](https://preview.cardanoscan.io/transaction/8559f57234407204d8e9a6bf57ef6943c65ec7119eb1c2ca6224f8bad8e71c1e?tab=utxo) that we generated using this tutorial. If you notice the small icon next to the contract output address, we can even [inspect the datum (opens in a new tab)](https://preview.cardanoscan.io/datumInspector?datum=d8799f581c10073fd2997d2f7dc6dadcf24966bd06b01930e5210e5de7aebf792dff) : d8799f581c10073fd2997d2f7dc6dadcf24966bd06b01930e5210e5de7aebf792dff { "constructor": 0, "fields": [\ {\ "bytes": "4d871c3f74db9ea19e2ca678ac92672ada301a0d8ce2dc6091692a30"\ }\ ] } ### Unlocking funds from the contract[](https://aiken-lang.org/example--hello-world/end-to-end/mesh#unlocking-funds-from-the-contract) Finally, as a last step: we now want to spend the UTxO that is locked by our `hello-world` contract. To be valid, our transaction must meet two conditions: * it must provide "Hello, World!" as a redeemer; and * it must be signed by the key referenced as datum (i.e. the owner). Let's make a new file `hello-world-unlock.ts` and copy over some of the boilerplate from the first one. unlock.ts import { deserializeAddress, mConStr0, stringToHex, } from "@meshsdk/core"; import { getScript, getTxBuilder, getUtxoByTxHash, wallet } from "./common"; async function main() { // get utxo, collateral and address from wallet const utxos = await wallet.getUtxos(); const walletAddress = (await wallet.getUsedAddresses())[0]; const collateral = (await wallet.getCollateral())[0]; const { scriptCbor } = getScript(); // hash of the public key of the wallet, to be used in the datum const signerHash = deserializeAddress(walletAddress).pubKeyHash; // redeemer value to unlock the funds const message = "Hello, World!"; // get the utxo from the script address of the locked funds const txHashFromDesposit = process.argv[2]; const scriptUtxo = await getUtxoByTxHash(txHashFromDesposit); } main(); Now, let's add the bits to unlock the funds in the contract. We'll need the transaction identifier (i.e. `Tx ID`) obtained when you ran the previous script (`hello-world-lock.ts`) That transaction identifier (a.k.a. transaction hash), and the corresponding output index (here, `0`) uniquely identify the UTxO (Unspent Transaction Output) in which the funds are currently locked. And that's the one we're about to unlock. ⚠️ Note that we need to explicitly add a signer using `.setRequiredSigners` so that it gets added to the `extra_signatories` of our transaction and becomes accessible for our script. unlock.ts import { deserializeAddress, mConStr0, stringToHex, } from "@meshsdk/core"; import { getScript, getTxBuilder, getUtxoByTxHash, wallet } from "./common"; async function main() { // get utxo, collateral and address from wallet const utxos = await wallet.getUtxos(); const walletAddress = (await wallet.getUsedAddresses())[0]; const collateral = (await wallet.getCollateral())[0]; const { scriptCbor } = getScript(); // hash of the public key of the wallet, to be used in the datum const signerHash = deserializeAddress(walletAddress).pubKeyHash; // redeemer value to unlock the funds const message = "Hello, World!"; // get the utxo from the script address of the locked funds const txHashFromDesposit = process.argv[2]; const scriptUtxo = await getUtxoByTxHash(txHashFromDesposit); // build transaction with MeshTxBuilder const txBuilder = getTxBuilder(); await txBuilder .spendingPlutusScript("V3") // we used plutus v3 .txIn( // spend the utxo from the script address scriptUtxo.input.txHash, scriptUtxo.input.outputIndex, scriptUtxo.output.amount, scriptUtxo.output.address ) .txInScript(scriptCbor) .txInRedeemerValue(mConStr0([stringToHex(message)])) // provide the required redeemer value `Hello, World!` .txInDatumValue(mConStr0([signerHash])) // only the owner of the wallet can unlock the funds .requiredSignerHash(signerHash) .changeAddress(walletAddress) .txInCollateral( collateral.input.txHash, collateral.input.outputIndex, collateral.output.amount, collateral.output.address ) .selectUtxosFrom(utxos) .complete(); const unsignedTx = txBuilder.txHex; const signedTx = await wallet.signTx(unsignedTx); const txHash = await wallet.submitTx(signedTx); console.log(`1 tADA unlocked from the contract at Tx ID: ${txHash}`); } main(); Run this script as usual, but this time, also passing the transaction id obtained from the previous command locking the funds. For example: npx tsx unlock.ts 48b8178e3a8842227dfbb0f73669efc163f73fd7c8758b7dafc0a5a5f07a5445 If everything worked as planned you should see something resembling the following output: 1 tADA unlocked from the contract at Tx ID: af3e7d83e5ee5324a612de9126636ef55505ffb9c468dacaf419635921b7a91c And, tada 🎉! We can inspect our [redeeming transaction on CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/transaction/d3d5e828a3989691b0960d22a265c8c9ae4723134b52aa05ec0fb7d40f060392?tab=contracts) and see that it successfully executed our _Hello World_ contract. You can find the [full source code (opens in a new tab)](https://github.com/MeshJS/examples/tree/main/aiken-hello-world) for this tutorial on MeshJS's GitHub. [First steps](https://aiken-lang.org/example--hello-world/basics "First steps") [with PyCardano (Python)](https://aiken-lang.org/example--hello-world/end-to-end/pycardano "with PyCardano (Python)") --- # Aiken | Custom types [Language Tour](https://aiken-lang.org/language-tour) Custom Types Custom types ============ Defining custom types[](https://aiken-lang.org/language-tour/custom-types#defining-custom-types) ------------------------------------------------------------------------------------------------- ### Basics[](https://aiken-lang.org/language-tour/custom-types#basics) Aiken's custom types are named collections of keys and/or values. They are similar to objects in object-oriented languages, though they don't have methods. Custom types are defined with the `type` keyword. They may contain named fields, or not; but they cannot mix. // With named fields type Datum { Datum { signer: ByteArray, count: Int } } // With unnamed fields type DatumNameless { DatumNameless(ByteArray, Int) } Here we have defined two custom types called `Datum` and `DatumNameless` respectively. The constructor of `Datum` is called `Datum` and it has two fields: A `signer` field which is a `ByteArray`, and a `count` field which is an `Int`. Likewise, the constructor of `DatumNameless` also has two fields of types `ByteArray` and `Int`. Once defined the custom type can be used in functions to create values by calling their constructors. fn datums() { // Named fields can be given in any order let named_1 = Datum { signer: #[0xAA, 0xBB], count: 2001 } let named_2 = Datum { count: 1805, signer: #[0xAA, 0xCC] } // Nameless fields are given as positional arguments let nameless_1 = DatumNameless(#[0xAA, 0xBB], 2001) //Event-named fields can be given as positional arguments. let named_3 = Datum(#[0xAA, 0xBB], 2001) (named_1, named_2, nameless_1, named_3) } ### Shorthand notation[](https://aiken-lang.org/language-tour/custom-types#shorthand-notation) Because single constructors are quite common, there exists a special shorthand notation when the type and the constructor have the same name. So instead of the above, one can write: type Datum { signer: ByteArray, count: Int } These two notations (with or without the constructor) are synonyms. With the shorthand, we implicitly indicate that there's a single constructor named `Datum` which can be used for constructing values of type `Datum`, or can also be used when destructuring (see below). ### Multiple constructors[](https://aiken-lang.org/language-tour/custom-types#multiple-constructors) Custom types in Aiken can be defined with multiple constructors (a.k.a variants), making them a way of modeling data that can be one of a few different variants. We've already seen custom types with multiple constructors in the Language Tour like [`Bool`](https://aiken-lang.org/language-tour/primitive-types#bool) or [`Option`](https://aiken-lang.org/language-tour/primitive-types#option) . Aiken's built-in `Bool` type is defined like this: /// A Bool is a value that is either `True` or `False` type Bool { False True } It's a simple custom type with constructors that takes no arguments at all! Use it to answer yes/no questions and to indicate whether something is `True` or `False`. The records created by different constructors for a custom type can contain different values. For example, a `User` custom type could have a `LoggedIn` constructor that creates records with a name, and a `Guest` constructor which creates records without any contained values. type User { LoggedIn { username: ByteArray } // A logged in user Guest // A guest user with no details } let user1 = LoggedIn { username: "Alice" } let user2 = LoggedIn { username: "Bob" } let visitor = Guest ### Generics[](https://aiken-lang.org/language-tour/custom-types#generics) Custom types can be parameterized with other types, making their contents variable. We've seen that with the [`Option`](https://aiken-lang.org/language-tour/primitive-types#option) . Let's consider another example with a `Box` type, which is a simple record that holds a single value. type Box { Box(inner: inner_type) } The type of the field `inner` is `inner_type`, which is a parameter of the `Box` type. If it holds an `Int` the box's type is `Box`, if it holds a string the box's type is `Box`. fn foo() { let a = Box(420) // type is Box let b = Box("That's my ninja way!") // type is Box } Inspecting custom types[](https://aiken-lang.org/language-tour/custom-types#inspecting-custom-types) ----------------------------------------------------------------------------------------------------- ### Named accessors[](https://aiken-lang.org/language-tour/custom-types#named-accessors) If a custom type **has only one constructor** and **named fields** they can be accessed using the _dot_ symbol (`.`), followed by the name of the field. For example, considering a type `Dog`: type Dog { name: ByteArray, cuteness: Int, age: Int, } We can access any of its fields using `.name`, `.cuteness` and `.age` respectively. let dog = Dog { name: "bob", cuteness: 2001, age: 6 } dog.name // This returns "bob" dog.cuteness // This returns 2001 dog.age // This returns 6 ### Destructuring[](https://aiken-lang.org/language-tour/custom-types#destructuring) Values can be _destructured_ in Aiken. Destructuring is the opposite of constructing a value and uses a similar syntax albeit reversed. When **constructing**, constructors and fields appears on the **right-hand side** of an assignment. When **destructuring**, they appear on the **left-hand side**. To keep rolling with our `Dog` example, we have the following equivalence: // Constructing let dog = Dog { name: "bob", cuteness: 2001, age: 6 } // Destructuring let Dog { name, cuteness, age } = dog name == "bob" // True cuteness == 2001 // True age == 6 // True // Equivalent to let name = dog.name let cuteness = dog.cuteness let age = dog.age As you can see, the second expression introduces three new bindings in scope for `name`, `cuteness` and `dog` respectively. Destructuring works here because the associated type has a single constructor and the identifiers we introduce have the same names as the fields. #### (Re)naming fields[](https://aiken-lang.org/language-tour/custom-types#renaming-fields) If needed, we can also rename fields using a colon symbol (`:`), like so: // Destructuring let Dog { name: its_name, cuteness, age: its_age } = dog its_name == "bob" // True its_age == 6 // True Note that we can also destructure constructors whose fields are nameless by introducing identifiers for each of the fields. Like for constructing, when treated as nameless fields, the arguments are positional. // Destructuring nameless let Dog(name, cuteness, age) = dog name == "bob" // True cuteness == 2001 // True age == 6 // True // Destructuring nameless, arguments swapped. let Dog(age, name, cuteness) = dog age == "bob" // Confusing, but True name == 2001 // Confusing, but True cuteness == 6 // Confusing, but True ### Pattern-matching[](https://aiken-lang.org/language-tour/custom-types#pattern-matching) For nameless fields, one must resort to _pattern-matching_ using the `when *expr* is` keywords. This syntax allows the inspection of a value following the various branches defined by a type, ensuring that all possible paths are properly handled. Said differently, it is like asking the compiler "If the data has this shape then do that", for all possible shapes. Recall our `User` type from before? type User { LoggedIn { username: ByteArray } Guest } Let's write a function `get_name` that pulls out the name of a `User`: fn get_name(user: User) -> ByteArray { when user is { LoggedIn { username } -> username Guest -> "Guest user" } } The `when *expr* is` block forces us to exhaustively handle every constructor in the type definition. #### Wildcard[](https://aiken-lang.org/language-tour/custom-types#wildcard) Patterns always need to be complete, but enumerating every single fields or constructor can sometime be cumbersome. For these situations, Aiken allows the use of wildcards. A wildcard is like a fallback pattern, denoted `_` and it can be used in place of a pattern to match any remaining patterns. For example: fn get_name(user: User) -> ByteArray { when user is { LoggedIn { username } -> username _ -> "Guest user" } } Note that wildcards can also be named. And generally speaking, any identifier that starts with an underscore `_` will be treated as a wildcard. Beware though that wildcards are usually not recommended because they make code more brittle. Indeed, if you were to add a new constructor to the type `User`, this function would generate no warnings or errors at compilation because the wildcard `_` would swallow all the remaining constructors. Yet, imagine the following: type User { LoggedInAsAdministrator { username: ByteArray } LoggedIn { username: ByteArray } Guest } With the wildcard, `get_name` would compile just fine and return `"Guest user"` for users logged in as administrator! So, use wildcard only when you cannot do otherwise. In many situations, it is better to list all patterns explicitly. Wildcards also works when destructuring, should you need to only bring specific fields in scope but not all. let Dog(name, _cuteness, _age) = dog // equivalent to let name = dog.name #### Alternative patterns[](https://aiken-lang.org/language-tour/custom-types#alternative-patterns) To avoid repeating the same bits of logic across multiple branches, Aiken provides a syntax for handling multiple patterns at once using the pipe symbol `|`. This works particularly well when patterns introduce the same identifiers with the same respective types in scope. fn get_name(user: User) -> ByteArray { when user is { LoggedInAsAdministrator { username } | LoggedIn { username } -> username Guest -> "Guest user" } } #### Spread operator[](https://aiken-lang.org/language-tour/custom-types#spread-operator) In a similar fashion, it is sometimes useful to pull only specific named fields out of a constructor or even, none at all. For these situations, Aiken provides the spread operator `..` as a way to indicates that anything else is ignored. For example, let's pretend we added a field `age: Int` to our `LoggedIn` constructor variant. fn is_authorized(user: User) -> Bool { when user is { LoggedInAsAdministrator { .. } -> True LoggedIn { days_of_activity, .. } -> days_of_activity > 30 Guest -> False } } In the function above, you can see how we authorize any administrator or any logged-in user so long as they have at least 30 days of activity. The spread operator is used to indicate that other named fields of the records are ignored. The spread operator also works for destructuring, should you need to only bring specific fields in scope but not all. let Dog { name, .. } = dog // equivalent to let name = dog.name #### List[](https://aiken-lang.org/language-tour/custom-types#list) Pattern-matching also works on lists with a syntax of their own. In Aiken, lists are linked-lists, so they are virtually equivalent to a custom-type with two constructors: either it is an empty list, or it is a value and another list, possibly empty. When matching on lists, one may use wildcard and spread operators. Yet in the case of lists, the spread operator can be named to explicitly capture the tail of the list. Let's walk through some examples: fn get_head(xs: List) -> Option { when xs is { [] -> None [a, ..] -> Some(a) } } fn is_empty(xs: List) -> Bool { when xs is { [] -> True [_, ..] -> False } } fn get_tail(xs: List) -> List { when xs is { [] -> [] // debatable [_, ..tail] -> tail } } #### Nested patterns[](https://aiken-lang.org/language-tour/custom-types#nested-patterns) Patterns aren't limited to the first level of a type structure only. It is possible to pattern any compound type as deep as necessary. fn get_name_with_default(dog: Option, default: ByteArray) -> ByteArray { when dog is { Some(Dog { name, .. }) -> name _ -> default } } You can also pattern-match on nested fields within a record. For this, use the field labels and the `:` operator, like so: fn get_age_of_oscar(dog: Dog) -> Option { when dog is { Dog { name: "Oscar", age, ..} -> Some(age) _ -> None } } #### Assigning names to sub-patterns[](https://aiken-lang.org/language-tour/custom-types#assigning-names-to-sub-patterns) Sometimes when pattern-matching we want to assign a name to a value while specifying its shape at the same time. We can do this using the `as` keyword. when xs is { [[_, ..] as inner_list] -> inner_list _other -> [] } Updating custom types[](https://aiken-lang.org/language-tour/custom-types#updating-custom-types) ------------------------------------------------------------------------------------------------- Aiken provides a dedicated syntax for updating some of the fields of a custom type record. type Person { name: ByteArray, shoe_size: Int, age: Int, is_happy: Bool, } fn have_birthday(person) { // It's this person's birthday, so increment their age and // make them happy Person { ..person, age: person.age + 1, is_happy: True } } The update syntax created a new record with the values of the initial record. It replaces the given binding with their new values. Relationship with Data[](https://aiken-lang.org/language-tour/custom-types#relationship-with-data) --------------------------------------------------------------------------------------------------- At runtime custom types become an opaque Plutus' Data. In Aiken's type system `Data` matches with any user-defined type (but with none of the primitive types). ### Upcasting[](https://aiken-lang.org/language-tour/custom-types#upcasting) Thus, it's also possible to cast any custom type **into** `Data` (a.k.a _upcasting_). This implicit conversion is handy for interacting with builtin functions that operate on raw `Data`. Any function that accepts `Data` will automatically work with any custom type. type Datum { count: Int, } let datum: Datum = Datum { count: 1 } // fn(Data) -> ByteArray builtin.serialise_data(datum) // Or similarly, by providing an annotation. let as_data: Data = datum ### Downcasting[](https://aiken-lang.org/language-tour/custom-types#downcasting) Extracting **from** `Data` into a custom type (a.k.a _downcasting_) however requires the use of [`expect`](https://aiken-lang.org/language-tour/control-flow#expect) or an [`if *pattern* is`](https://aiken-lang.org/language-tour/control-flow#if-is) as explained in the [next section: Control Flow](https://aiken-lang.org/language-tour/control-flow) . While upcasting is always possible and safe, downcasting is not and can fail. How one chooses to handle failure drives the choice between `expect` (fail immediately and loudly) and `if/is` (fallback gracefully). In summary: | From | To | Crash on failure? | How | | --- | --- | --- | --- | | _any custom type_ | `Data` | \- _(cannot fail)_ | let-binding + type-annotation | | `Data` | _any custom type_ | no | [`if *pattern* is`](https://aiken-lang.org/language-tour/control-flow#if-is) | | `Data` | _any custom type_ | yes | [`expect`](https://aiken-lang.org/language-tour/control-flow#expect) | Type aliases[](https://aiken-lang.org/language-tour/custom-types#type-aliases) ------------------------------------------------------------------------------- Finally, it is also possible to define mere aliases for types. A type alias lets you create a name that is identical to another type, without any additional information. type MyNumber = Int Aliases are fully interchangeable and bear no difference at runtime. They are most useful for simplifying type signatures. type Person = (String, Int) fn create_person(name: String, age: Int) -> Person { (name, age) } While they are fully erased at runtime, type-aliases information are preserved during type-checking and when generating the validator(s) final blueprint(s). Said differently, type aliases will appear in error messages and generated documentation as well. Documentation[](https://aiken-lang.org/language-tour/custom-types#documentation) --------------------------------------------------------------------------------- You may add user-facing documentation to any data-type definition and constructor with a documentation comment starting with `///`. Markdown is supported and this text will be included with the module's entry in generated HTML documentation. /// A **incredibly useful** comment to tell you that this type represents: a person. type Person { /// The number of years elapsed since birth. age: Int, /// The word other people shout to catch this person's attention name: String, } When exported as module documentation, types-definition are alphabetically sorted (in ascending order). Customizing Encoding[](https://aiken-lang.org/language-tour/custom-types#customizing-encoding) ----------------------------------------------------------------------------------------------- There is syntax available for controlling the expected encoding of the underlying `PlutusData` representation of your custom data types. All Aiken types are represented under the hood with `PlutusData` which has a cbor encoding. The builtin Aiken type `Data` is exactly that. `Data` is a way to express that some value can be _any_ `PlutusData`. If `PlutusData` itself was an Aiken type, it would look something like this: type PlutusData { Constr { tag: Int, fields: List } PlutusList(List) Map(List<(PlutusData, PlutusData)>) I(Int) B(ByteArray) } Please keep in mind, this is just an example so that you can better visualize what is happening under the hood. Actually creating such a type is useless but enabling `when/is` to pattern match on `Data` to manually pull apart the constructors is something we could see in a future version of Aiken because this would need special treatment within the compiler. So now, considering that `PlutusData` is used to represent all custom types, let's look at some commonly used types and learn about how they are represented under the hood. As a first example, let's consider the `Bool` type which is defined like so: type Bool { False True } This type would be represented using the `Constr` form of `PlutusData`. This means that to build a correct `PlutusData` payload representing `Bool` you'd do this: **False** Constr { tag: 0, fields: [] } **True** Constr { tag: 1, fields: [] } Aiken comes with some neat built in tools to see the cbor representation of your data types directly. To learn more [check out this section](https://aiken-lang.org/language-tour/troubleshooting) . Most notably, `False` can be said to be "constr with a tag **0**" and `True` can be said to be "constr with a tag **1**"". The key take away here is that all constructors in Aiken will be represented with `Constr` and the tag will be based on the order as written at the definition site. Since `False` comes first it is of course _tagged_ as **0**. Aiken `v1.1.19` introduced the ability to control the number of the _tag_. Here is what that looks like: type Bool { @tag(1) True @tag(0) False } It may be a bit subtle but we've swapped the order in which the two constructors are defined but preserved their original tag which will be respected when casting to and from `Data`. This decouples the textual representation of the type from the way it is encoded under the hood. Most of the time, you should rely on the default which is the definition order but if you ever need to control the tags of a type's constructors, you can. It's now a good time to turn our attention to the shorthand notation for a type with a single constructor matching the name of the type itself. Naturally, with what we now know it's easy to conclude that they _must_ all use _tag_ **0** and contain one or more fields which are recursively `PlutusData` themselves. Let's bring back the `Datum` type from earlier: type Datum { signer: ByteArray, count: Int } Normally, this would look like so, as `PlutusData` under the hood: Constr { tag: 0, fields: [B(#"aabbcc"), I(5)] } You are free to customize the tag here as well: @tag(9000) type Datum { signer: ByteArray, count: Int } Lastly, when you have this kind of type, you can go as far as skipping `Constr` all together and using `PlutusList` which doesn't require a _tag_ and can in many cases perform better than `Constr` due to not needing to call `un_constr_data` and `snd_pair` under the hood to access the inner fields. @list type Datum { signer: ByteArray, count: Int } Resulting in this kind of encoding: PlutusList([B(#"aabbcc"), I(5)]) > "With great power comes great responsibility" - Uncle Ben Please lean on the defaults and only reach for these when you know what you are doing. [Functions](https://aiken-lang.org/language-tour/functions "Functions") [Control Flow](https://aiken-lang.org/language-tour/control-flow "Control Flow") --- # Aiken | Hello, World! - with PyCardano Hello, World! End-to-End with PyCardano (Python) Hello, World! - with PyCardano ============================== Covered in this tutorial[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#covered-in-this-tutorial) ---------------------------------------------------------------------------------------------------------------------- * [x] Interact with a validator on the `Preview` network; * [x] Using [PyCardano (opens in a new tab)](https://github.com/Python-Cardano/pycardano) through [Blockfrost (opens in a new tab)](https://blockfrost.io/) ; * [x] Getting test funds from the [Cardano Faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) ; * [x] Using web explorers such as [CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/) . Pre-requisites[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#pre-requisites) -------------------------------------------------------------------------------------------------- We assume that you have followed the _Hello, World!_'s [First steps](https://aiken-lang.org/example--hello-world/basics) and thus, have Aiken installed an ready-to-use. We will also use [PyCardano (opens in a new tab)](https://github.com/Python-Cardano/pycardano) , so make sure you have your dev environment ready for somePython (3). We recommend to setup a new Python environment for [PyCardano (opens in a new tab)](https://github.com/Python-Cardano/pycardano) as follows: curl -LsSf https://astral.sh/uv/install.sh | sh uv init uv add setuptools pycardano Getting funds[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#getting-funds) ------------------------------------------------------------------------------------------------ For this tutorial, we will use the validator we built in [First steps](https://aiken-lang.org/example--hello-world/basics) . Yet, before moving one, we'll need some funds, and a public/private key pair to hold them. We can generate a private key and an address using PyCardano. Let's write our first script as `generate-credentials.py`: generate-credentials.py from pycardano import Address, Network, PaymentSigningKey, PaymentVerificationKey signing_key = PaymentSigningKey.generate() signing_key.save("me.sk") verification_key = PaymentVerificationKey.from_signing_key(signing_key) address = Address(payment_part=verification_key.hash(), network=Network.TESTNET) with open("me.addr", "w") as f: f.write(str(address)) You can run the instructions above using: uv run generate-credentials.py Now, we can head to [the Cardano faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) to get some funds on the preview network to our newly created address (inside `me.addr`). ![](https://aiken-lang.org/faucet_preview.webp) 👉 Make sure to select _"Preview Testnet"_ as network. Using [CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/) we can watch for the faucet sending some ADA our way. This should be pretty fast (a couple of seconds). Using the contract[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#using-the-contract) ---------------------------------------------------------------------------------------------------------- Now that we have some funds, we can lock them in our newly created contract. We'll use [PyCardano (opens in a new tab)](https://github.com/Python-Cardano/pycardano) to construct and submit our transaction through [Blockfrost (opens in a new tab)](https://blockfrost.io/) . This is only one example of possible setup using tools we love. For more tools, make sure to check out the [Cardano Developer Portal (opens in a new tab)](https://developers.cardano.org/tools) ! ### Setup[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#setup) First, we setup PyCardano with Blockfrost as a provider. This will allow us to let PyCardano handle transaction building for us, which includes managing changes. It also gives us a direct way to submit the transaction later on. Create a file named `hello-world-lock.py` in the root of your project and add the following code: hello-world-lock.py from pycardano import ( BlockFrostChainContext, ) import os context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) ⚠️ Note that the highlighted line above looks for an environment variable named `BLOCKFROST_PROJECT_ID` which value must be set to your Blockfrost project id. You can define a new environment variable in your terminal by running (in the same session you're also executing the script!): export BLOCKFROST_PROJECT_ID=preview... Replace `preview...` with your actual project id. Next, we'll need to read the validator from the blueprint (`plutus.json`) we generated earlier. We'll also need to convert it to a format that PyCardano understands. This is done by serializing the validator and then converting it to an hexadecimal text string as shown below: hello-world-lock.py from pycardano import ( BlockFrostChainContext, PaymentSigningKey, PlutusV3Script, ScriptHash, ) import json import os def read_validator() -> dict: with open("plutus.json", "r") as f: validator = json.load(f) script_bytes = PlutusV3Script( bytes.fromhex(validator["validators"][0]["compiledCode"]) ) script_hash = ScriptHash(bytes.fromhex(validator["validators"][0]["hash"])) return { "type": "PlutusV3", "script_bytes": script_bytes, "script_hash": script_hash, } context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) signing_key = PaymentSigningKey.load("me.sk") validator = read_validator() ### Locking funds into the contract[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#locking-funds-into-the-contract) Now that we can read our validator, we can make our first transaction to lock funds into the contract. The datum must match the representation expected by the validator (and as specified in the blueprint), so this is a constructor with a single field that is a byte array. As value for that byte array, we provide a hash digest of our public me. This will be needed to unlock the funds. hello-world-lock.py from dataclasses import dataclass from pycardano import ( Address, BlockFrostChainContext, Network, PaymentSigningKey, PaymentVerificationKey, PlutusData, PlutusV3Script, ScriptHash, TransactionBuilder, TransactionOutput, ) from pycardano.hash import ( VerificationKeyHash, TransactionId, ScriptHash, ) import json import os def read_validator() -> dict: with open("plutus.json", "r") as f: validator = json.load(f) script_bytes = PlutusV3Script( bytes.fromhex(validator["validators"][0]["compiledCode"]) ) script_hash = ScriptHash(bytes.fromhex(validator["validators"][0]["hash"])) return { "type": "PlutusV3", "script_bytes": script_bytes, "script_hash": script_hash, } def lock( amount: int, into: ScriptHash, datum: PlutusData, signing_key: PaymentSigningKey, context: BlockFrostChainContext, ) -> TransactionId: # read addresses with open("me.addr", "r") as f: input_address = Address.from_primitive(f.read()) contract_address = Address( payment_part = into, network=Network.TESTNET, ) # build transaction builder = TransactionBuilder(context=context) builder.add_input_address(input_address) builder.add_output( TransactionOutput( address=contract_address, amount=amount, datum=datum, ) ) signed_tx = builder.build_and_sign( signing_keys=[signing_key], change_address=input_address, ) # submit transaction context.submit_tx(signed_tx) return signed_tx.id @dataclass class HelloWorldDatum(PlutusData): CONSTR_ID = 0 owner: bytes context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) signing_key = PaymentSigningKey.load("me.sk") validator = read_validator() owner = PaymentVerificationKey.from_signing_key(signing_key).hash() datum = HelloWorldDatum(owner=owner.to_primitive()) tx_hash = lock( amount=2_000_000, into=validator["script_hash"], datum=datum, signing_key=signing_key, context=context, ) print( f"2 tADA locked into the contract\n\tTx ID: {tx_hash}\n\tDatum: {datum.to_cbor_hex()}" ) You can run the excerpt above by executing: python hello-world-lock.py The above code requires you to: * have a `BLOCKFROST_PROJECT_ID` environment variable set. You can get one by [signing up for a Blockfrost account (opens in a new tab)](https://blockfrost.io/) . * have the file `hello-world-lock.py` placed at the root of your `hello-world` folder. At this stage, your folder should looks roughly like this: ./hello_world │ ├── README.md ├── aiken.toml ├── plutus.json ├── generate-credentials.py ├── hello-world-lock.py ├── me.addr ├── me.sk ├── lib │   └── ... └── validators    └── hello-world.ak If everything went well, you should see something like this: 2 tADA locked into the contract Tx ID: 2ea959e4b51b2b6046931fab80957b39e534f1c954d326e506814d3ca47726c6 Datum: d8799f581c1defd7502e25b312e48dc57712c434d317ea16a57728cd3c31828ea1ff #### Inspecting the transaction[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#inspecting-the-transaction) Now is a good moment to pause and have a look at CardanoScan. Here's [an example of an _Hello World_ transaction (opens in a new tab)](https://preview.cardanoscan.io/transaction/2ea959e4b51b2b6046931fab80957b39e534f1c954d326e506814d3ca47726c6?tab=utxo) that we generated using this tutorial. If you notice the small icon next to the contract output address, we can even [inspect the datum (opens in a new tab)](https://preview.cardanoscan.io/datumInspector?datum=d8799f581c1defd7502e25b312e48dc57712c434d317ea16a57728cd3c31828ea1ff) : d8799f581c10073fd2997d2f7dc6dadcf24966bd06b01930e5210e5de7aebf792dff { "constructor": 0, "fields": [\ {\ "bytes": "1defd7502e25b312e48dc57712c434d317ea16a57728cd3c31828ea1"\ }\ ] } ### Unlocking funds from the contract[](https://aiken-lang.org/example--hello-world/end-to-end/pycardano#unlocking-funds-from-the-contract) Finally, as a last step: we now want to spend the UTxO that is locked by our `hello-world` contract. To be valid, our transaction must meet two conditions: * it must provide "Hello, World!" as a redeemer; and * it must be signed by the key referenced as datum (i.e the owner). Let's make a new file `hello-world-unlock.py` and copy over some of the boilerplate from the first one. hello-world-unlock.py from pycardano import ( BlockFrostChainContext, PaymentSigningKey, PlutusV3Script, ScriptHash, ) import json import os def read_validator() -> dict: with open("plutus.json", "r") as f: validator = json.load(f) script_bytes = PlutusV3Script( bytes.fromhex(validator["validators"][0]["compiledCode"]) ) script_hash = ScriptHash(bytes.fromhex(validator["validators"][0]["hash"])) return { "type": "PlutusV3", "script_bytes": script_bytes, "script_hash": script_hash, } context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) signing_key = PaymentSigningKey.load("me.sk") validator = read_validator() Now, let's add the bits to unlock the funds in the contract. We'll need the transaction identifier (i.e. `Tx ID`) obtained when you ran the previous script (`hello-world-lock.py`) That transaction identifier (a.k.a. transaction hash), and the corresponding output index (here, `0`) uniquely identify the UTxO (Unspent Transaction Output) in which the funds are currently locked. And that's the one we're about to unlock. Since we know we have created a single UTxO, we'll take a little shortcut and simply look for the transaction id. hello-world-unlock.py from pycardano import ( Address, BlockFrostChainContext, Network, PaymentSigningKey, PlutusV3Script, ScriptHash, UTxO, ) import json import os import sys def read_validator() -> dict: with open("plutus.json", "r") as f: validator = json.load(f) script_bytes = PlutusV3Script( bytes.fromhex(validator["validators"][0]["compiledCode"]) ) script_hash = ScriptHash(bytes.fromhex(validator["validators"][0]["hash"])) return { "type": "PlutusV3", "script_bytes": script_bytes, "script_hash": script_hash, } def get_utxo_from_str(tx_id: str, contract_address: Address) -> UTxO: for utxo in context.utxos(str(contract_address)): if str(utxo.input.transaction_id) == tx_id: return utxo raise Exception(f"UTxO not found for transaction {tx_id}") context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) signing_key = PaymentSigningKey.load("me.sk") validator = read_validator() # get utxo to spend utxo = get_utxo_from_str(sys.argv[1], Address( payment_part = validator["script_hash"], network=Network.TESTNET, )) Finally, we can add the rest of the code and build a transaction that unlock the funds we previously locked. We'll need to construct a redeemer this time. ⚠️ Note that we need to explicitly add a signer using `.addSigner` so that it gets added to the `extra_signatories` of our transaction -- and becomes accessible for our script. Furthermore, `.add_input_address` is necessary to ensure the transaction builder adds inputs to cover for fees and collateral. hello-world-unlock.py from dataclasses import dataclass from pycardano import ( Address, BlockFrostChainContext, Network, PaymentSigningKey, PaymentVerificationKey, PlutusData, PlutusV3Script, Redeemer, ScriptHash, TransactionBuilder, TransactionOutput, UTxO, ) from pycardano.hash import ( VerificationKeyHash, TransactionId, ScriptHash, ) import json import os import sys def read_validator() -> dict: with open("plutus.json", "r") as f: validator = json.load(f) script_bytes = PlutusV3Script( bytes.fromhex(validator["validators"][0]["compiledCode"]) ) script_hash = ScriptHash(bytes.fromhex(validator["validators"][0]["hash"])) return { "type": "PlutusV3", "script_bytes": script_bytes, "script_hash": script_hash, } def unlock( utxo: UTxO, from_script: PlutusV3Script, redeemer: Redeemer, signing_key: PaymentSigningKey, owner: VerificationKeyHash, context: BlockFrostChainContext, ) -> TransactionId: # read addresses with open("me.addr", "r") as f: input_address = Address.from_primitive(f.read()) # build transaction builder = TransactionBuilder(context=context) builder.add_script_input( utxo=utxo, script=from_script, redeemer=redeemer, ) builder.add_input_address(input_address) builder.add_output( TransactionOutput( address=input_address, amount=utxo.output.amount.coin, ) ) builder.required_signers = [owner] signed_tx = builder.build_and_sign( signing_keys=[signing_key], change_address=input_address, ) # submit transaction context.submit_tx(signed_tx) return signed_tx.id def get_utxo_from_str(context, tx_id: str, contract_address: Address) -> UTxO: for utxo in context.utxos(str(contract_address)): if str(utxo.input.transaction_id) == tx_id: return utxo raise Exception(f"UTxO not found for transaction {tx_id}") @dataclass class HelloWorldRedeemer(PlutusData): CONSTR_ID = 0 msg: bytes context = BlockFrostChainContext( project_id=os.environ["BLOCKFROST_PROJECT_ID"], base_url="https://cardano-preview.blockfrost.io/api/", ) signing_key = PaymentSigningKey.load("me.sk") validator = read_validator() # get utxo to spend utxo = get_utxo_from_str(context, sys.argv[1], Address( payment_part = validator["script_hash"], network=Network.TESTNET, )) # build redeemer redeemer = Redeemer(data=HelloWorldRedeemer(msg=b"Hello, World!")) # execute transaction tx_hash = unlock( utxo=utxo, from_script=validator["script_bytes"], redeemer=redeemer, signing_key=signing_key, owner=PaymentVerificationKey.from_signing_key(signing_key).hash(), context=context, ) print( f"2 tADA unlocked from the contract\n\tTx ID: {tx_hash}\n\tRedeemer: {redeemer.to_cbor_hex()}" ) Run this script as usual, but this time, also passing the transaction id obtained from the previous command locking the funds. For example: python hello-world-unlock.py 2ea959e4b51b2b6046931fab80957b39e534f1c954d326e506814d3ca47726c6 If everything worked as planned you should see something resembling the following output: 2 tADA unlocked from the contract Tx ID: d3d5e828a3989691b0960d22a265c8c9ae4723134b52aa05ec0fb7d40f060392 Redeemer: 840000d8799f4d48656c6c6f2c20576f726c6421ff82198ee61a00bd3334 And, tada 🎉! We can inspect our [redeeming transaction on CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/transaction/c014620fcf6c108fdd2dc696dc3a8f8abfe58e5d685f4119c4eb2992548acf64?tab=contracts) and see that it successfully executed our _Hello World_ contract. [with Mesh (TypeScript)](https://aiken-lang.org/example--hello-world/end-to-end/mesh "with Mesh (TypeScript)") [with cardano-cli (CLI)](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli "with cardano-cli (CLI)") --- # Aiken | Hello, World! - with cardano-cli Hello, World! End-to-End with cardano-cli (CLI) Hello, World! - with cardano-cli ================================ Covered in this tutorial[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#covered-in-this-tutorial) ------------------------------------------------------------------------------------------------------------------------ * [x] Interact with a validator on the `Preview` network; * [x] Using [cardano-cli (opens in a new tab)](https://github.com/IntersectMBO/cardano-cli) and [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) ; * [x] Getting test funds from the [Cardano Faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) ; * [x] Using web explorers such as [CardanoScan (opens in a new tab)](https://preview.cardanoscan.io/) . Pre-requisites[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#pre-requisites) ---------------------------------------------------------------------------------------------------- We assume that you have followed the _Hello, World!_'s [First steps](https://aiken-lang.org/example--hello-world/basics) and thus, have Aiken installed an ready-to-use. We will also use [cardano-cli (opens in a new tab)](https://github.com/IntersectMBO/cardano-cli) , which is a command-line tool to interact with a running [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) , and we'll assume you have installed both tools so you have a `cardano-node` for the `Preview` network up and running locally, and `cardano-cli` is in your `PATH`. `cardano-cli` and `cardano-node` are usually packaged together and installed when installing a cardano-node. The details of the installation process is beyond the scope of that tutorial, please refer to the [Developer portal (opens in a new tab)](https://developers.cardano.org/docs/get-started/cardano-node/installing-cardano-node/) documentation for the details. We'll also make use of another command-line tool, [cardano-address (opens in a new tab)](https://github.com/IntersectMBO/cardano-addresses) , which you will need to install. Getting funds[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#getting-funds) -------------------------------------------------------------------------------------------------- For this tutorial, we will use the validator we built in [First steps](https://aiken-lang.org/example--hello-world/basics) . Yet, before moving one, we'll need some funds, and a public/private key pair to hold them. We can generate a private key and an address on the command-line: cardano-cli address key-gen --verification-key-file me.vk --signing-key-file me.sk This will create two files, `me.vk` and `me.sk`, containing respectively the public and private keys we'll use in this tutorial. From these keys we need to derive an address in bech32 format that will be stored in `me.addr`: cardano-cli conway address build --testnet-magic 2 --payment-verification-key-file me.vk | tee me.addr This should output some address similar to: addr_test1vr69qqlwgw7jty8m2wqvyxytnntufxhcjjur44rqd3t4hfgfq2ne0 Now, we can head to [the Cardano faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) to get some funds on the `Preview` network to our newly created address (inside `me.addr`). ![](https://aiken-lang.org/faucet_preview.webp) 👉 Make sure to select _"Preview Testnet"_ as network. After successful request, some funds should have been sent to our address and we should have some UTxO available. We can check this on the command-line: cardano-cli conway query utxo --address $(cat me.addr) --testnet-magic 2 --socket-path node.socket 2178565983a3125c608f7dea381d5b70d064be6b21879de86d4765e8ad3361f1 0 10000000 lovelace + TxOutDatumNone assuming a `cardano-node` is started and exposing a local socket at path `node.socket`. Using the contract[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#using-the-contract) ------------------------------------------------------------------------------------------------------------ Now that we have some funds, we can lock them in our newly created contract. We'll use [cardano-cli (opens in a new tab)](https://github.com/IntersectMBO/cardano-cli) to construct, sign, and submit our transaction to our locally running [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) . ### Setup[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#setup) First, we'll generate a _script file_ in a format [cardano-cli (opens in a new tab)](https://github.com/IntersectMBO/cardano-cli) can understand using `aiken blueprint` command: aiken blueprint convert > hello.script The `compiledCode` field in the blueprint file `plutus.json` cannot be used as-is because the cardano-cli uses a CBOR-in-CBOR encoding wrapped in a simple `TextEnvelope` JSON file. `aiken blueprint convert` correctly encodes the script. From the `hello.script` file we can compute the script address we'll use to lock: cardano-cli address build --testnet-magic 2 --payment-script-file hello.script | tee hello.addr This should output an address that looks like `addr_test1wqd988jgwwa5kjc2q4e03rrnrvqvlwz6c7wlyazymhd87mc2x3pjs`. ### Locking funds into the contract[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#locking-funds-into-the-contract) Now that we can read our validator, we can make our first transaction to lock funds into the contract. The datum must match the representation expected by the validator (and as specified in the blueprint), so this is a constructor with a single field that is a byte array. The datum must contain the _public key hash_ of the key that's authorized to unlock the contract. We can compute this public key hash as: cardano-cli conway address build --testnet-magic 2 --payment-verification-key-file test.vk \ | cardano-address address inspect \ | jq -r .spending_key_hash \ | tee me.hash The file `me.hash` should contain the hash represented as an hexadecimal string, eg. `f45003ee43bd2590fb5380c2188b9cd7c49af894b83ad4606c575ba5`. We can now create the datum JSON file that respects the datum schema from the blueprint, usign the popular [jq (opens in a new tab)](https://jqlang.github.io/) tool: jq -c '{constructor:0,fields:[{bytes:.}]}' < me.hash {"constructor":0,"fields":[{"bytes":"f45003ee43bd2590fb5380c2188b9cd7c49af894b83ad4606c575ba5"}]} We then proceed to building the transaction: cardano-cli conway transaction build \ --tx-in 2178565983a3125c608f7dea381d5b70d064be6b21879de86d4765e8ad3361f1#0 \ --tx-out $(cat hello.addr)+1100000 \ --tx-out-inline-datum-file datum.json \ --change-address $(cat me.addr) \ --socket-path node.socket --testnet-magic 2 \ --out-file tx.lock.raw Note that every UTxO requires a minimum Ada value to prevent users from abusing storage capabilities offered by the system. The 1100000 locked value in the output comes from running the command with a lower value (typically 1000000) and getting an error telling us what's the minimum Ada value to set. And we can sign it: cardano-cli conway transaction sign --tx-file tx.lock.raw --out-file tx.lock.signed --signing-key-file test.sk Before submitting the transaction, it's a good idea to inspect and double-check it: cardano-cli debug transaction view --tx-file tx.signed { "auxiliary scripts": null, "certificates": null, "collateral inputs": [], "currentTreasuryValue": null, "era": "Conway", "fee": "172145 Lovelace", "governance actions": [], "inputs": [\ "2842d9914aa07933fee6ac539be51ed19aa8f4d65473bf1e7afc30ba16536f79#1"\ ], "metadata": null, "mint": null, "outputs": [\ {\ "address": "addr_test1wqd988jgwwa5kjc2q4e03rrnrvqvlwz6c7wlyazymhd87mc2x3pjs",\ "address era": "Shelley",\ "amount": {\ "lovelace": 1100000\ },\ "datum": {\ "constructor": 0,\ "fields": [\ {\ "bytes": "f45003ee43bd2590fb5380c2188b9cd7c49af894b83ad4606c575ba5"\ }\ ]\ },\ "network": "Testnet",\ "payment credential script hash": "1a539e4873bb4b4b0a0572f88c731b00cfb85ac79df27444ddda7f6f",\ "reference script": null,\ "stake reference": null\ },\ {\ "address": "addr_test1vpf26vappzgz5zvjv362pzaag27c4dg4tu7qq7n07e686dsy5669m",\ "address era": "Shelley",\ "amount": {\ "lovelace": 40624362\ },\ "network": "Testnet",\ "payment credential key hash": "52ad33a108902a09926474a08bbd42bd8ab5155f3c007a6ff6747d36",\ "reference script": null,\ "stake reference": null\ }\ ], "redeemers": [], "reference inputs": [], "required signers (payment key hashes needed for scripts)": null, "return collateral": null, "total collateral": null, "treasuryDonation": 0, "update proposal": null, "validity range": { "lower bound": null, "upper bound": null }, "voters": {}, "withdrawals": null, "witnesses": [\ {\ "key": "VKey (VerKeyEd25519DSIGN \"19f61663433ebe8c46bed4b31e3f0f54130592ab4c757c7dd925f284149059c6\")",\ "signature": "SignedDSIGN (SigEd25519DSIGN \"716fb368ca0a196048e009be80a674214a9a3daad2c5ae2e9b35d90c1d45222bc1864415385a1e14480bfa0eec12d6ce433cbc834d9afaa4559be2ba82639408\")"\ }\ ] } Finally, we are able to submit the transaction to our local node. cardano-cli conway transaction submit --tx-file tx.lock.signed --socket-path node.socket --testnet-magic 2 Which should result in the message: Transaction successfully submitted The local [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) you are running will validate your transaction against its current state before propagating it, so it's perfectly possible to experiment without putting funds at risk (actually, this is all on `Preview` anyway which does not use real Adas so there's no risk, but it's good practice to think about it anyway). At this stage, your folder should looks roughly like this: ./hello_world │ ├── README.md ├── aiken.toml ├── plutus.json ├── generate-credentials.py ├── hello-world-lock.py ├── hello.script ├── hello.addr ├── me.addr ├── me.hash ├── me.sk ├── me.vk ├── tx.lock.raw ├── tx.lock.signed ├── lib │   └── ... └── validators    └── hello-world.ak ### Unlocking funds from the contract[](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli#unlocking-funds-from-the-contract) Finally, as a last step: we now want to spend the UTxO that is locked by our `hello-world` contract. To be valid, our transaction must meet two conditions: * it must provide "Hello, World!" as a redeemer; and * it must be signed by the key referenced in the datum (i.e the owner). Now, let's add the bits to unlock the funds in the contract. We'll need the transaction identifier (i.e. `Tx ID`) of the locking transaction. That transaction identifier (a.k.a. transaction hash), and the corresponding output index (here, `0`) uniquely identify the UTxO (Unspent Transaction Output) in which the funds are currently locked. And that's the one we're about to unlock. Since we know we have created a single UTxO, and transaction identifiers are uniquely derived from the transaction's content, we can simply compute this information locally: cardano-cli conway transaction txid --tx-file tx.signed which let us infer the UTxO we are interested in is `9609c8dd442e3d72023e09790263210dfb203bdb2a38e0796382976818e52675#0`. We can also query the [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) for UTxOs locked at specific addresses, which gives us: cardano-cli conway query utxo --address $(cat hello.addr) --testnet-magic 2 --socket-path node.socket --output-json which would yield the following JSON structure: { "9609c8dd442e3d72023e09790263210dfb203bdb2a38e0796382976818e52675#0": { "address": "addr_test1wqd988jgwwa5kjc2q4e03rrnrvqvlwz6c7wlyazymhd87mc2x3pjs", "datum": null, "inlineDatum": { "constructor": 0, "fields": [\ {\ "bytes": "f45003ee43bd2590fb5380c2188b9cd7c49af894b83ad4606c575ba5"\ }\ ] }, "inlineDatumRaw": "d8799f581c52ad33a108902a09926474a08bbd42bd8ab5155f3c007a6ff6747d37ff", "inlineDatumhash": "8a27f6e6fd3b1c07f86480306e3edfced1f799bea8c3215dd89eb16bbca386b0", "referenceScript": null, "value": { "lovelace": 1100000 } } } Our validator's first requirement is a redeemer that contains the string `Hello, World!`, so let's construct the corresponding JSON file. jq -c '{constructor:0,fields:[{bytes:.}]}' <<< "\"$(echo 'Hello, World!' | xxd -g1 | cut -d ' ' -f2-14 | tr -d ' ')\"" | tee redeemer.json this will output the content of the `redeemer.json` file which should be {"constructor":0,"fields":[{"bytes":"48656c6c6f2c20576f726c6421"}]} To build our unlocking transactions, we'll need the current _protocol parameters_ extracted from the node: cardano-cli conway query protocol-parameters --testnet-magic 1 --socket-path node.socket > pparams.json ⚠️ The protocol parameters are indispensable for: 1. computing the transaction fees, 2. adding corresponding hash to the transaction for double-checking purpose and avoid `PPViewHashesDontMatch` error. As we'll build our transaction without automatic balancing and fees computation, we need to compute in advance the execution units for running our validator. Luckily, `aiken check` always dump this information when running! > aiken check Compiling pankzsoft/legal 0.0.0 (.) Compiling aiken-lang/stdlib v2.2.0 (./build/packages/aiken-lang-stdlib) Collecting all tests scenarios across all modules Testing ... ┍━ hello_world ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ PASS [mem: 32451, cpu: 11921833] hello_world_example │ · with traces │ | redeemer: Hello, World! ┕━━━━━━━━━━━━━━━━━━━━━━━ 1 tests | 1 passed | 0 failed At last we are able to build the unlocking transaction: cardano-cli conway transaction build-raw \ --tx-in 9609c8dd442e3d72023e09790263210dfb203bdb2a38e0796382976818e52675#1 \ --tx-in 9609c8dd442e3d72023e09790263210dfb203bdb2a38e0796382976818e52675#0 \ --tx-in-collateral 9609c8dd442e3d72023e09790263210dfb203bdb2a38e0796382976818e52675#1 \ --tx-in-script-file hello.script \ --tx-in-inline-datum-present \ --tx-in-redeemer-file redeemer.json \ --tx-in-execution-units '(12000000,34000)' \ --tx-out $(cat me.addr)+856000000 \ --fee 1000000 \ --protocol-params-file pparams.json \ --out-file tx.unlock.raw \ --required-signer me.sk We cannot use the `build` command here because it will try to evaluate the script to compute the execution units and transaction fees, which will fail because the transaction is not yet signed and therefore the script will fail to check the transaction's signatories match its datum. The `build-raw` command does not automatically balance and compute the transaction's fees, so we need to run it twice to adjust the `--fee` parameter's value and the second output's value. To compute the "exact" fees, run: cardano-cli conway transaction calculate-min-fee --tx-body-file tx.unlock.raw --protocol-params-file pparams.json --witness-count 1 which should output an amount in lovelaces, e.g `198028 Lovelace`. Use this amount to adjust the change output's value and the `--fee` parameter to the `build-raw` command. ⚠️ In some rare circumstances it's possible those adjustments yield a transaction that still does not have enough fees. In this case, recompute the fees from the adjusted transaction's body. And we are now able to sign and submit our transaction: cardano-cli conway transaction sign --tx-file tx.unlock.raw --out-file tx.unlock.signed --signing-key-file test.sk cardano-cli conway transaction submit --tx-file tx.unlock.signed --socket-path node.socket --testnet-magic 2 And, tada 🎉! We can check our transaction was successfully submitted and that it successfully executed our _Hello World_ contract by querying the [cardano-node (opens in a new tab)](https://github.com/IntersectMBO/cardano-node) for the UTxO paying to our script's address: cardano-cli conway query utxo --address $(cat hello.addr) --testnet-magic 2 --socket-path node.socket --output-json which should result in an empty output: TxHash TxIx Amount -------------------------------------------------------------------------------------- [with PyCardano (Python)](https://aiken-lang.org/example--hello-world/end-to-end/pycardano "with PyCardano (Python)") [with Mesh (JavaScript)](https://aiken-lang.org/example--vesting/mesh "with Mesh (JavaScript)") --- # Aiken | Vesting Vesting with Mesh (JavaScript) Vesting ======= Armed with our recently acquired knowledge from the _Hello, World!_ contract, let's increase the difficulty and write a slightly more challenging one. A vesting contract is a common type of contract that allows funds to be locked for a period of time and unlocked later—once a specified time has passed. Typically, a vesting contract defines a beneficiary who may be different from the original owner. Covered in this tutorial[](https://aiken-lang.org/example--vesting/mesh#covered-in-this-tutorial) -------------------------------------------------------------------------------------------------- * [x] Writing non-trivial Aiken validators, with complex datums. * [x] Using more advanced Aiken features (type-aliases, pattern-matches). * [x] Writing unit tests with Aiken and mocktail. * [x] Managing time on-chain through transaction validity ranges. 📘 When encountering an unfamiliar syntax or concept, do not hesitate to refer to the [language-tour](https://aiken-lang.org/language-tour) for details and extra examples. Setup[](https://aiken-lang.org/example--vesting/mesh#setup) ------------------------------------------------------------ In a similar fashion to what we did for the _Hello, World!_ contract, we'll need some credentials (and funds) to play around with. Here, we define an extra key for the beneficiary. Again, use the [Cardano faucet (opens in a new tab)](https://docs.cardano.org/cardano-testnet/tools/faucet) to receive test funds. Refer to [Hello, World! :: Getting Funds](https://aiken-lang.org/example--hello-world/end-to-end/lucid#getting-funds) in case you have any doubts on the procedure. generate-credentials.mjs import fs from 'node:fs'; import { MeshWallet, } from "@meshsdk/core"; // Generate a secret key for the owner wallet and beneficiary wallet const owner_secret_key = MeshWallet.brew(true); const beneficiary_secret_key = MeshWallet.brew(true); //Save secret keys to files fs.writeFileSync('owner.sk', owner_secret_key); fs.writeFileSync('beneficiary.sk', beneficiary_secret_key); const owner_wallet = new MeshWallet({ networkId: 0, key: { type: 'root', bech32: owner_secret_key, }, }); const beneficiary_wallet = new MeshWallet({ networkId: 0, key: { type: 'root', bech32: beneficiary_secret_key, }, }); // Save unused addresses to files fs.writeFileSync('owner.addr', (await owner_wallet.getUnusedAddresses())[0]); fs.writeFileSync('beneficiary.addr', (await beneficiary_wallet.getUnusedAddresses())[0]); On-Chain code[](https://aiken-lang.org/example--vesting/mesh#on-chain-code) ---------------------------------------------------------------------------- Let's write our time lock validator as `validators/vesting.ak`, starting with the definition of its interface (i.e. its datum's shape). validators/vesting.ak use aiken/crypto.{VerificationKeyHash} pub type VestingDatum { /// POSIX time in milliseconds, e.g. 1672843961000 lock_until: Int, /// Owner's credentials owner: VerificationKeyHash, /// Beneficiary's credentials beneficiary: VerificationKeyHash, } Dependency[](https://aiken-lang.org/example--vesting/mesh#dependency) ---------------------------------------------------------------------- An additional dependency we will add to this project is [vodka (opens in a new tab)](https://github.com/sidan-lab/vodka) . To include vodka in our Aiken project, lets update our aiken.toml file to specify it as a dependency. aiken.toml [[dependencies]] name = "sidan-lab/vodka" version = "0.1.1-beta" source = "github" Using vodka as a dependency provides access to several utility functions designed for common contract validation needs. In our vesting.ak file, we will use two specific functions from vodka: * **`key_signed`**: Verifies that a specific key has signed the transaction. This ensures only authorized users can interact with the contract. * **`valid_after`**: Checks that a transaction is only valid after a designated time. This is useful for setting time-based constraints within your contract logic. As we can see the script's datum serves as configuration and contains the different parameters of our vesting operation. Remember that these elements are set when locking funds in the contract; combined with the script they define the conditions by which the funds can be released. From there, lets define the `spend` validator itself. validators/vesting.ak use cardano/transaction.{OutputReference, Transaction} use vodka_extra_signatories.{key_signed} use vodka_validity_range.{valid_after} use aiken/crypto.{VerificationKeyHash} pub type VestingDatum { /// POSIX time in milliseconds, e.g. 1672843961000 lock_until: Int, /// Owner's credentials owner: VerificationKeyHash, /// Beneficiary's credentials beneficiary: VerificationKeyHash, } validator vesting { // In principle, scripts can be used for different purpose (e.g. minting // assets). Here we make sure it's only used when 'spending' from a eUTxO spend( datum_opt: Option, _redeemer: Data, _input: OutputReference, tx: Transaction, ) { expect Some(datum) = datum_opt or { key_signed(tx.extra_signatories, datum.owner), and { key_signed(tx.extra_signatories, datum.beneficiary), valid_after(tx.validity_range, datum.lock_until), }, } } else(_) { fail } } The key feature here is the time-based check, which is abstracted by the valid\_after function. In fact, transactions can have validity intervals that define from when and until the transaction is considered valid. Validity bounds are checked by the ledger prior to executing a script and only does so if the bounds are legit. This is meant to give scripts a notion of time, while preserving determinism from within the context of a script. For example, in this scenario, given a lower bound `A` on the transaction, we can deduce that the current time is _at least_ `A`. Note that because we don't control the upper-bound, it could very much be that this transaction is executed 30 years after the vesting delay. Yet, from the perspective of the vesting script, this is perfectly okay. In Aiken, values that are not used directly can be prefixed with an underscore (`_`) to indicate they are intentionally ignored. In this validator, `_redeemer` and `_input` are marked as unused inputs, making the intent clear and improving readability. This practice is particularly helpful in contracts that may require multiple parameters for different purposes, such as minting or spending, while not all parameters are always relevant to the current logic. ### Testing[](https://aiken-lang.org/example--vesting/mesh#testing) Okay, now before deploying our contract in the wild and risking collapsing the economy with some unforeseen bug, let's write a simple test. Aiken has builtin support for tests, which are very much like functions that takes no argument and must return a `Bool`. In the test below, we also leverage the **mocktail** module provided by the **vodka** dependency. This module offers various utility functions that simplify unit testing for our smart contracts. Tests can use any function, constant or types defines in our module but beware, they cannot reference other tests! validators/vesting.ak // ^^^ Code above is unchanged. ^^^ // The mocktail module comes from the vodka dependency. // These dependencies should be added at the top of the file with the other imported modules. use mocktail.{complete, invalid_before, mocktail_tx, required_signer_hash} use mocktail/virgin_key_hash.{mock_pub_key_hash} use mocktail/virgin_output_reference.{mock_utxo_ref} type TestCase { is_owner_signed: Bool, is_beneficiary_signed: Bool, is_lock_time_passed: Bool, } fn get_test_tx(test_case: TestCase) { let TestCase { is_owner_signed, is_beneficiary_signed, is_lock_time_passed } = test_case mocktail_tx() |> required_signer_hash(is_owner_signed, mock_pub_key_hash(1)) |> required_signer_hash(is_beneficiary_signed, mock_pub_key_hash(2)) |> invalid_before(is_lock_time_passed, 1672843961001) |> complete() } fn vesting_datum() { VestingDatum { lock_until: 1672843961000, owner: mock_pub_key_hash(1), beneficiary: mock_pub_key_hash(2), } } test success_unlocking() { let output_reference = mock_utxo_ref(0, 1) let datum = Some(vesting_datum()) let test_case = TestCase { is_owner_signed: True, is_beneficiary_signed: True, is_lock_time_passed: True, } let tx = get_test_tx(test_case) vesting.spend(datum, Void, output_reference, tx) } 💡 You can run tests with `aiken check`; Aiken will collect and run all tests found in your modules, and give you some statistics about the execution units (CPU and memory) required by the test. ### Building[](https://aiken-lang.org/example--vesting/mesh#building) It's now time to build our on-chain contract! Simply do: aiken build This generate a [CIP-0057 Plutus blueprint (opens in a new tab)](https://github.com/cardano-foundation/CIPs/pull/258) as `plutus.json` at the root of your project. This blueprint describes your on-chain contract and its binary interface. In particular, it contains the generated on-chain code that will be executed by the ledger, and a hash of your validator(s) that can be used to construct addresses. Let's see the validator in action! Off-Chain code[](https://aiken-lang.org/example--vesting/mesh#off-chain-code) ------------------------------------------------------------------------------ ### Setup[](https://aiken-lang.org/example--vesting/mesh#setup-1) First, let's install the dotenv package([https://www.npmjs.com/package/dotenv (opens in a new tab)](https://www.npmjs.com/package/dotenv) ), which allows us to import our API key from a .env file. Next, we'll create a directory called common and, within it, a file named common.mjs. This file will house utility functions used for both locking and unlocking assets. In this setup, we will import our [BLOCKFROST\_API (opens in a new tab)](https://blockfrost.dev/overview/getting-started) key from the .env file. common/common.mjs import 'dotenv/config'; import { MeshWallet, BlockfrostProvider, MeshTxBuilder, serializePlutusScript, } from "@meshsdk/core"; import { applyParamsToScript } from "@meshsdk/core-csl"; import fs, { read } from 'fs'; export const blockchainProvider = new BlockfrostProvider(process.env.BLOCKFROST_API); export const owner_wallet = new MeshWallet({ networkId: 0, fetcher: blockchainProvider, submitter: blockchainProvider, key: { type: "root", bech32: fs.readFileSync("owner.sk").toString(), }, }); export const beneficiary_wallet = new MeshWallet({ networkId: 0, fetcher: blockchainProvider, submitter: blockchainProvider, key: { type: "root", bech32: fs.readFileSync("beneficiary.sk").toString(), }, }); export function getTxBuilder() { return new MeshTxBuilder({ fetcher: blockchainProvider, submitter: blockchainProvider, verbose: true, // <-- you can remove this if you dont want to see logs }); } const blueprint = JSON.parse(fs.readFileSync("./plutus.json")); export const scriptCbor = applyParamsToScript(blueprint.validators[0].compiledCode, []); export const scriptAddr = serializePlutusScript( { code: scriptCbor, version: "V3" }, undefined, 0 ).address; ### Locking funds into the contract[](https://aiken-lang.org/example--vesting/mesh#locking-funds-into-the-contract) First, we will set up the depositFundTx function. This function will encapsulate the core logic for locking funds into the smart contract, ensuring that the deposit process is handled efficiently and securely. vesting\_lock.mjs import { mConStr0 } from "@meshsdk/common"; import { deserializeAddress } from "@meshsdk/core"; import { getTxBuilder, owner_wallet, beneficiary_wallet, scriptAddr, } from "./common/common.mjs"; async function depositFundTx(amount, lockUntilTimeStampMs) { const utxos = await owner_wallet.getUtxos(); const { pubKeyHash: ownerPubKeyHash } = deserializeAddress( owner_wallet.addresses.baseAddressBech32 ); const { pubKeyHash: beneficiaryPubKeyHash } = deserializeAddress( beneficiary_wallet.addresses.baseAddressBech32 ); const txBuilder = getTxBuilder(); await txBuilder .txOut(scriptAddr, amount) .txOutInlineDatumValue( mConStr0([lockUntilTimeStampMs, ownerPubKeyHash, beneficiaryPubKeyHash]) ) .changeAddress(owner_wallet.addresses.baseAddressBech32) .selectUtxosFrom(utxos) .complete(); return txBuilder.txHex; } Now that we have built the core logic let's setup the main function that will handle signing and submitting the transaction. It will also call depositFundTx with the arguments it expects. vesting\_lock.mjs // ^^^ Code above is unchanged. ^^^ async function main() { const assets = [\ {\ unit: "lovelace",\ quantity: "3000000",\ },\ ]; const lockUntilTimeStamp = new Date(); lockUntilTimeStamp.setMinutes(lockUntilTimeStamp.getMinutes() + 1); const unsignedTx = await depositFundTx(assets, lockUntilTimeStamp.getTime()); const signedTx = await owner_wallet.signTx(unsignedTx); const txHash = await owner_wallet.submitTx(signedTx); //Copy this txHash. You will need this hash in vesting_unlock.mjs console.log("txHash", txHash); } main(); 💡 You can run the instructions above using Node via: node vesting_lock.mjs If all went according to plan, you should see the transaction identifier in the console. Make sure you copy this hash, you will need it in vesting\_unlock.mjs file. ### Unlocking funds from the contract[](https://aiken-lang.org/example--vesting/mesh#unlocking-funds-from-the-contract) Now want to spend the UTxO that is locked by our `vesting` contract. To be valid, our transaction must meet one of two conditions: * it must be signed by the owner referenced as "owner" in the datum; or * It must be signed by the beneficiary, who is referenced as "beneficiary" in the datum, and the transaction must occur after a specific time threshold. This threshold is defined as one minute beyond the current time when the lock condition is set. Let's make a new file `vesting_unlock.mjs` and add the bits to unlock the funds in the contract. This file contains the function withdrawFundTx, which allows the beneficiary to unlock funds from a vesting contract. The function handles the necessary transaction construction and ensures that the funds can only be accessed after the specified conditions are met. vesting\_unlock.mjs import { deserializeAddress, deserializeDatum, unixTimeToEnclosingSlot, SLOT_CONFIG_NETWORK, } from "@meshsdk/core"; import { getTxBuilder, beneficiary_wallet, scriptAddr, scriptCbor, blockchainProvider, } from "./common/common.mjs"; async function withdrawFundTx(vestingUtxo) { const utxos = await beneficiary_wallet.getUtxos(); const beneficiaryAddress = beneficiary_wallet.addresses.baseAddressBech32; const collateral = await beneficiary_wallet.getCollateral(); const collateralInput = collateral[0].input; const collateralOutput = collateral[0].output; const { pubKeyHash: beneficiaryPubKeyHash } = deserializeAddress( beneficiary_wallet.addresses.baseAddressBech32 ); const datum = deserializeDatum(vestingUtxo.output.plutusData); const invalidBefore = unixTimeToEnclosingSlot( Math.min(datum.fields[0].int, Date.now() - 19000), SLOT_CONFIG_NETWORK.preview ) + 1; const txBuilder = getTxBuilder(); await txBuilder .spendingPlutusScript("V3") .txIn( vestingUtxo.input.txHash, vestingUtxo.input.outputIndex, vestingUtxo.output.amount, scriptAddr ) .spendingReferenceTxInInlineDatumPresent() .spendingReferenceTxInRedeemerValue("") .txInScript(scriptCbor) .txOut(beneficiaryAddress, vestingUtxo.output.amount) .txInCollateral( collateralInput.txHash, collateralInput.outputIndex, collateralOutput.amount, collateralOutput.address ) .invalidBefore(invalidBefore) .requiredSignerHash(beneficiaryPubKeyHash) .changeAddress(beneficiaryAddress) .selectUtxosFrom(utxos) .complete(); return txBuilder.txHex; } In this section, we define a `main` function that retrieves the transaction hash generated when we executed the `vesting_lock.mjs` file. This hash will be used to fetch the corresponding UTxO (Unspent Transaction Output) for withdrawal. vesting\_unlock.mjs // ^^^ Code above is unchanged. ^^^ async function main() { const txHashFromDesposit = //This is the hash that we generated in the locking file when we submitted the transaction. "ed7559c7aa5a8bfcba9ec8d75fb2ee1902da8b909722ca4726261d35e8250645"; const utxo = await getUtxoByTxHash(txHashFromDesposit); if (utxo === undefined) throw new Error("UTxO not found"); const unsignedTx = await withdrawFundTx(utxo); const signedTx = await beneficiary_wallet.signTx(unsignedTx); const txHash = await beneficiary_wallet.submitTx(signedTx); console.log("txHash", txHash); } async function getUtxoByTxHash(txHash) { const utxos = await blockchainProvider.fetchUTxOs(txHash); if (utxos.length === 0) { throw new Error("UTxO not found"); } return utxos[0]; } main(); 💡 As you imagine, we can run this script with the following incantation: node vesting_unlock.mjs This should be the projects structure. ./vesting │ ├── README.md ├── aiken.toml └── common    └── common.mjs ├── beneficiary.addr ├── beneficiary.sk ├── .env ├── owner.addr ├── owner.sk ├── node_modules ├── package.json ├── package-lock.json ├── plutus.json └── validators    └── vesting.ak ├── vesting_lock.mjs ├── vesting_unlock.mjs Assuming everything went well... congratulations 🎉! [with cardano-cli (CLI)](https://aiken-lang.org/example--hello-world/end-to-end/cardano-cli "with cardano-cli (CLI)") [Gift Card](https://aiken-lang.org/example--gift-card "Gift Card") --- # Aiken | Untyped Plutus Core Untyped Plutus Core Untyped Plutus Core =================== One key feature of Aiken is how it helps you manipulate Untyped Plutus Core (abbrev. UPLC in short). UPLC is ultimately the format whereby codes gets executed on-chain. This is pretty-much as low-level as you can get when it comes to Cardano smart contracts. Note however that on-chain, UPLC code is encoded in a binary encoding for conciseness. Understanding how UPLC works, and having the right tools to troubleshoot UPLC programs can be handy when developing contracts on Cardano. Fortunately, this is something Aiken can help you with. ![Aiken <-> Plutus compileation pipeline diagram](https://aiken-lang.org/aiken-plutus-pipeline-light.png) While UPLC has erased any _explicit_ notion of types; functions, variables and constants are still _implicitly_ typed and, an interpreter will raise errors when encountering a type mismatch. For the sake of simplicity, we might speak about the type-signature of builtin functions such as `addInteger` which, in principle, only has a concrete meaning in Typed Plutus Core. Hence, even though they are _untyped_, we often think of UPLC programs has having implicit types, as if they were originally _typed_ programs whose types had simply been erased (in fact, that's exactly what they are). [Gift Card](https://aiken-lang.org/example--gift-card "Gift Card") [Syntax](https://aiken-lang.org/uplc/syntax "Syntax") --- # Aiken | Syntax [Untyped Plutus Core](https://aiken-lang.org/uplc) Syntax Syntax ====== Let's start with a little reminder about the syntax. The complete syntax for Untyped Plutus Core comes from the original [Formal Specification of the Plutus Core Language (opens in a new tab)](https://plutus.cardano.intersectmbo.org/resources/plutus-core-spec.pdf) . Primitive Types[](https://aiken-lang.org/uplc/syntax#primitive-types) ---------------------------------------------------------------------- Plutus Core has 7 primitive types (a.k.a. constants): `unit`, `bool`, `integer`, `bytestring`, `string`, `pair` and `list`. One can construct constants using the `con` keyword, followed by the name of the primitive type and its value. * Unit is denoted `()`; * Bool are `True` or `False`; * Bytestrings are denoted with a leading `#` followed by an hexadecimal sequence; * Strings are UTF-8 text strings, between double quotes `"` `"`; * Pair and lists are encapsulated between brackets `[` and `]`. Note that each constant is named after its type. For pairs and lists -- which are compound types --, the type of their elements is specified between chevrons `<` and `>`. | Primitive Type | Example | | --- | --- | | `unit` | `con unit ()` | | `bool` | `con bool True` | | `integer` | `con integer 42` | | `bytestring` | `con bytestring #41696b656e` | | `string` | `con string "Aiken"` | | `pair` | `con pair [True, 42]` | | `list` | `con list [#00, #aa]` | Functions[](https://aiken-lang.org/uplc/syntax#functions) ---------------------------------------------------------- A function (or simply, lambda) is constructed with the keyword `lam` followed by a variable name, and a term (i.e. a constant, another function, etc..). One can apply variables to a function using squared brackets `[ ]`. For example: `[ (lam x x) (con integer 42) ]`. This little excerpt constructs a function that takes an argument `x` and returns it; to which we immediately apply the constant `42`. If we were to evaluate this program, it would simply output: `42`. Builtins[](https://aiken-lang.org/uplc/syntax#builtins) -------------------------------------------------------- Plutus Core comes with a set of builtins functions which comes in handy to define certain operations. Incidentally, there's no _operator_ even for basic arithmetic operations, everything comes as a builtin. You'll notice also that some builtins are very domain specific and tailored to operations you'd expect a smart contract to perform on a blockchain. Hence, new builtins may be added in the future to address specific use cases that emerge. Builtins are called with the keyword `builtin` followed by their names. They may take one, two, three or really any number of arguments. Here is the complete list of [builtin functions](https://aiken-lang.org/uplc/builtins) . Delay & Force[](https://aiken-lang.org/uplc/syntax#delay--force) ----------------------------------------------------------------- Plutus Core has the notion of type abstractions and type instantiations. That is, like lambdas are functions over term values, abstractions are functions over types. These abstractions allow to represent polymorphic types (such as, a list of elements, or an option type). UPLC has gotten rid of the types, but introduces two new keywords in order to preserve the abstractions in some form. * `force` can be used on a polymorphic function to instantiate one type-parameter. For example, the branches of a builtin `ifThenElse` can be of any type -- though they have to be the same for both branches. In fact, `ifThenElse` has one type parameter. To be called, it must therefore be forced once: `[ [ [ (force (builtin ifThenElse)) p ] x ] y ]` * Similarly, `delay` can be used to defer the evaluation of a certain term; this allows to artificially construct or preserve type abstractions, but also, to introduce a certain level of laziness in parts of the program. Data[](https://aiken-lang.org/uplc/syntax#data) ------------------------------------------------ In addition to primitive types, Plutus Core also has a more generalized `data` data-type which is meant to represent any possible data-type in a program. > **TODO**: Give additional detail about how the serialization is done and how to construct a Data. > > In particular, revisit after [#34 (opens in a new tab)](https://github.com/txpipe/aiken/issues/34) > since the introduction of "list" and "pair" keywords may come in handy. Programs[](https://aiken-lang.org/uplc/syntax#programs) -------------------------------------------------------- Finally, UPLC programs are wraps in a `program` declaration, which indicates the version (e.g. `1.0.0`) of Plutus Core that this programs uses. You don't have to worry about that too much. Aiken supports the latest Plutus version (`2.0.0`). [Untyped Plutus Core](https://aiken-lang.org/uplc "Untyped Plutus Core") [Command-line utilities](https://aiken-lang.org/uplc/cli "Command-line utilities") --- # Aiken | Tests [Language Tour](https://aiken-lang.org/language-tour) Tests Tests ===== Aiken has first-class support for unit tests and property-based tests. This means that you can write tests in Aiken directly and execute them on the fly. Hence, the toolkit (`aiken check`) can parse tests, collect them, run them, and display a report with (hopefully) helpful details. Unit tests[](https://aiken-lang.org/language-tour/tests#unit-tests) -------------------------------------------------------------------- ### Writing unit tests[](https://aiken-lang.org/language-tour/tests#writing-unit-tests) To write a unit test, use the `test` keyword: test foo() { 1 + 1 == 2 } A unit test is a named function that takes no arguments and returns a boolean. More specifically, a test is considered _valid_ (i.e. it passes) if it returns `True`. You can write tests anywhere in an Aiken module, and they can make calls to functions and use constants the same way. One exciting thing about tests is that they use the same virtual machine as the one for executing contracts on-chain. Said differently, they are snippets of on-chain code you can run and reason about in the same context as your production code. ### Unit test reports[](https://aiken-lang.org/language-tour/tests#unit-test-reports) Let's write a simple function with some unit tests as an example. lib/example.ak fn add_one(n: Int) -> Int { n + 1 } test add_one_1() { add_one(0) == 1 } test add_one_2() { add_one(-42) == -41 } Running `aiken check` on our project gives us the following report: ![](https://aiken-lang.org/_next/static/media/language_tour_tests_fig_1.c9b3f4a7.png) As you can see, the report groups tests by module and gives you the memory and CPU execution units needed for each test. That means tests can also be used as benchmarks if you need to experiment with different approaches and compare their execution costs. Tests can be arbitrarily complex; unlike on-chain scripts, they do not have any execution limit -- or, more specifically, their limit is sufficiently large. ### Automatic diffing[](https://aiken-lang.org/language-tour/tests#automatic-diffing) Aiken's test runner is (trying to be) intelligent and helpful, especially on test failures. If a test fails, the test runner will do its best to provide information about what went wrong. This is particularly efficient if you write your tests as assertions using binary operators (`==`, `>=`, `!=` etc..). For example, let's add a failing test to our example above: lib/example.ak // ... rest of the file is unchanged test add_one_3() { add_one(1) == 1 } ![](https://aiken-lang.org/_next/static/media/language_tour_tests_fig_2.8b2d174b.png) Brilliant! We get to see what both operands are evaluated to, and the test runner points us to the problem. Property-based test[](https://aiken-lang.org/language-tour/tests#property-based-test) -------------------------------------------------------------------------------------- ### Short introduction[](https://aiken-lang.org/language-tour/tests#short-introduction) One of Aiken's unique selling points is its property-based testing framework with integrated shrinking. Property-based testing is the art of generating test cases by exploring the realm of possible inputs and looking for general behaviours rather than specific cases. For example, if you consider a function `list.reverse` that reverses the order of elements in a list, it has an excellent property: calling it twice puts the list back in its original order. You can approach testing that function by generating random list samples and checking that they satisfy the property. If a counterexample to the property is found, the framework tries to simplify it to a minimal counterexample so that it is easier to digest and reason about. This is a crucial step since exploring an input domain randomly can lead to arbitrarily large sample values that may obfuscate the real problem. Imagine a function that operates on a list of integers and fails for negative integer values. In this example the list `[12, 441, 0, 7863, -2, 1213]` is a valid counterexample but `[-1]` is arguably a much better one. It allows for the precise pinning down of the issue more directly. In classic property-based testing frameworks, finding a smaller counterexample can be tedious and is referred to as _shrinking_. Developers writing properties must define how to get to smaller counterexamples from an initial value. In Aiken, the framework integrates and automatically manages this process. ### Writing properties[](https://aiken-lang.org/language-tour/tests#writing-properties) A property-based test is a test with a single argument that specifies a `Fuzzer`. A fuzzer, or generator, is an abstraction that specifies how to generate (pseudo-) random values from a source of randomness. We provide -- _and strongly recommend using_ -- a core library for fuzzers: [`aiken/fuzz` (opens in a new tab)](https://github.com/aiken-lang/fuzz) . This library contains valuable primitives that go through the hassle of abstracting the management of the pseudo-randomness on your behalf so that writing fuzzers becomes bliss. Use it without moderation! use aiken/fuzz test prop_is_non_negative(n: Int via fuzz.int()) { n >= 0 } A `Fuzzer` is introduced as a special annotation for the argument using the `via` keyword and must be of type `Fuzzer` (although `a` must be instantiated to a concrete type!). In the example above, it is of type `Fuzzer`. There's thus a direct relation between the type of an argument and the type carried by its fuzzer! Therefore, the type annotation for the argument is entirely optional since it is redundant with the fuzzer. #### Composing fuzzers[](https://aiken-lang.org/language-tour/tests#composing-fuzzers) Fuzzers can be composed directly inline after the via keyword: use aiken/fuzz test prop_list(xs: List via fuzz.list(fuzz.int())) { todo } They can also be defined into separate functions and called by names: use aiken/fuzz fn my_fuzzer() -> Fuzzer> { fuzz.list(fuzz.int()) } test prop_list(xs: List via my_fuzzer()) { todo } ### Property test reports[](https://aiken-lang.org/language-tour/tests#property-test-reports) Akin to unit tests, properties are executed using the `aiken check` command. They provide a report highlighting the number of random samples checked and a simplified counterexample in case of failure. For example, if we run our `prop_is_non_negative` above operating on int, we get the following: ![](https://aiken-lang.org/_next/static/media/language_tour_tests_fig_3.87735c76.png) As you can see, the property is invalidated by negative values generated by the `int()` fuzzer. The counterexample is shown directly in the test report, along with the number of tests that ran until a counterexample was found. ### Labelling[](https://aiken-lang.org/language-tour/tests#labelling) Often, one wants to assess that specific paths are being explored in a random walk. This is particularly true of complex generators that explore a large domain space. The [`aiken/fuzz` (opens in a new tab)](https://github.com/aiken-lang/fuzz) library provides labels for that purpose that are automatically gathered by the framework. Upon success, their distribution across all test runs is shown in the report. Labelling can also be useful to assess a fuzzer's correctness, and we strongly recommend _testing your fuzzers_! A common pitfall of property-based testing is properties that do not actually test anything due to wrongly defined fuzzers. For example, let's inspect the `bool()` fuzzer from the [`aiken/fuzz` (opens in a new tab)](https://github.com/aiken-lang/fuzz) library: test prop_bool_distribution(predicate via fuzz.bool()) { fuzz.label( if predicate { @"True" } else { @"False" }, ) True } As we can see, the generator is mostly uniform between `True` and `False` (phew!). Note that it isn't necessarily exactly 50% due to the random nature of the sample, but it asymptotically converges towards that. ![](https://aiken-lang.org/_next/static/media/language_tour_tests_fig_4.f71d6e27.png) Note that we have passed the extra option `--max-success=1000` to the `check` command here to increase the number of tests run and get a better view of the random sample. Testing failures[](https://aiken-lang.org/language-tour/tests#testing-failures) -------------------------------------------------------------------------------- Sometimes, you need to assert that a particular execution path can fail. This is also known as an _"expected failure"_ and is a valid way of asserting the behaviour of a program. Fortunately, you can do this with Aiken, too, by adding `fail` after the test name. So, for example: lib/example.ak use aiken/math test must_fail() fail { expect Some(result) = math.sqrt(-42) result == -1 } The `fail` keyword here works for both unit and property tests, with a subtle difference for property tests. Indeed, a property that is expected to fail will still run multiple times and only be considered successful if ALL the execution of the property failed. This is useful to write non-properties, especially if they rely on `fail` or `expect` for checking pre-conditions under the hood. Said differently, a property test flagged as `fail` will stop at the first successful evaluation and be marked as failed. For properties, it's also possible to indicate `fail once` in order to run a property until it fails or until it reaches the maximum number of tries (default to 100). If the test runner finds 100 tests that satisfy a property expected to _fail once_, the entire test is considered a failure. Said differently, a property test flagged as `fail once` will stop at the first failed evaluation and be marked as a success. If none of the evaluation fails, it is marked as failed. Running specific tests[](https://aiken-lang.org/language-tour/tests#running-specific-tests) -------------------------------------------------------------------------------------------- `aiken check` supports flags that allow you to run subsets of all tests in your project. ### Examples[](https://aiken-lang.org/language-tour/tests#examples) `aiken check -m "aiken/list"` This only runs tests inside the module named `aiken/list`. * * * `aiken check -m "aiken/option.{flatten}"` This only runs tests within the `aiken/option` module that contains the word `flatten` in their name. * * * `aiken check -e -m "aiken/option.{flatten_1}"` You can force an exact match with `-e`. * * * `aiken check -e -m map_1` This only runs tests in the whole project that exactly match the name `map_1`. [Modules](https://aiken-lang.org/language-tour/modules "Modules") [Benchmarks](https://aiken-lang.org/language-tour/bench "Benchmarks") --- # Aiken | Ecosystem Overview Ecosystem Overview Ecosystem Overview ================== Within the Cardano community there has been a flourishing ecosystem of alternative languages for writing smart contracts. So naturally, one might ask about the differences between these and which they should use for their use case. There is also a big misconception about how writing smart contracts actually works on Cardano. In this document, we'll list some of the main alternatives along with their differences and similarities. Before we get into this though, let's discuss the misconception first so everyone is on the same page. The Misconception[](https://aiken-lang.org/ecosystem-overview#the-misconception) --------------------------------------------------------------------------------- **Cardano uses Haskell for smart contracts** This is **not** entirely true. The main Cardano node implementation does indeed happen to be written in Haskell. The virtual machine for executing smart contracts that comes baked into the node is then of course also implemented in Haskell. **But** that does not mean that it is Haskell itself which is executed by the smart contract virtual machine. Aiken actually has a fully working version of this [virtual machine (opens in a new tab)](https://github.com/aiken-lang/aiken/blob/main/crates/uplc/src/machine.rs#L63) written in Rust. So what's going on here? What is actually being executed? Well, there is something called [Untyped Plutus Core](https://aiken-lang.org/uplc) which is the lowest level representation of a smart contract and it is this low level representation that actually gets executed by the virtual machine. So contrary to popular knowledge, there isn't actually a coupling to Haskell. Armed with this knowledge one may now ask another question: **So what am I writing when I write Plutus?** In the wild, Plutus tends to refer to one of three things: 1. _Plutus Core_, the low-level interpreted code that is executed by the Cardano virtual machine. 2. _PlutusTx_, a Haskell framework that compiles to Plutus Core through the means of a GHC plugin. 3. The _Plutus Platform_, which more broadly includes _Plutus Core_, _PlutusTx_ and most of the tools developed around _Plutus Core_. Most of the time, when people say _Plutus_, they mean _PlutusTx_, which has led to a popular belief that Plutus is in fact Haskell. _PlutusTx_ being built as a GHC plugin means that you even use Haskell tooling like cabal for it. Even so, you are technically not writing Haskell. Code that one writes using _PlutusTx_ is consumed by the plugin and then transformed into _Untyped Plutus Core_. Essentially, it takes the intermediate representation of Haskell, GHC Core, and turns that into Untyped Plutus Core. This results in not needing to write a new parser and type checker. What you end up with is a kind of embedded language that looks and feels like Haskell but the target runtime is not GHC. The Alternatives[](https://aiken-lang.org/ecosystem-overview#the-alternatives) ------------------------------------------------------------------------------- Now that this misconception is out of the way it should be possible to see how other new languages can be created that ultimately compile to Untyped Plutus Core. The current alternatives range from full blown new languages to embedded Domain Specific Languages (abbrev. eDSLs.) Here is a list of the main ones: * [Aiken (opens in a new tab)](https://github.com/aiken-lang/aiken) * [opshin (opens in a new tab)](https://github.com/OpShin/opshin) * [Helios (opens in a new tab)](https://github.com/Hyperion-BT/Helios) * [Plutarch (opens in a new tab)](https://github.com/Plutonomicon/plutarch-plutus) * [plu-ts (opens in a new tab)](https://github.com/HarmonicLabs/plu-ts) * [Scalus (opens in a new tab)](https://github.com/nau/scalus) The creators of each of these projects all know each other and are in open communication with each other. ### Aiken[](https://aiken-lang.org/ecosystem-overview#aiken) Aiken is a brand new language with it's own syntax and compiler. It is not Rust. The compiler happens to be written in Rust but it is not Rust. Not only is Aiken a compiler for a new language but we've also developed everything in such a way that all the libraries we created in Rust are re-usable by people interested in doing more low-level things. One example of this is [Lucid (opens in a new tab)](https://github.com/spacebudz/lucid) , which uses Aiken's [uplc (opens in a new tab)](https://crates.io/crates/uplc) crate to evaluate transactions before submission to calculate exact redeemer ExUnits without using a node, ogmios, or blockfrost. As a language, Aiken is purely functional with static typing and type inference. This means most of the time the compiler is smart enough to know what the type of something is without you annotating it. It also let's you make custom types that are similar to records and enums. It does not have higher-kinded types or typeclasses because Aiken aims for simplicity. Writing smart contracts can be tedious, and we therefore believe that a language should remain simple to avoid silly mistakes. On-chain scripts are typically small in size and scope (relatively, compared to other kind of applications being written nowadays) and, therefore, do not require as much features as general-purpose languages that have to solve much harder problems. That being said Aiken may introduce more elaborate language features (such as type classes/traits) at a later time if it's found that they are extremely useful to developers. ### OpShin[](https://aiken-lang.org/ecosystem-overview#opshin) OpShin allows you to write Smart Contracts in 100% valid but restricted Python3. Since it uses normal Python, you have all the features that come with developing on Python, including widespread editor support, language servers, linters, testing frameworks and verification tools. It implements it's own type system and at compile time checks the types to be correct. As part of its development, python packages for [uplc (opens in a new tab)](https://github.com/OpShin/uplc) and [pluto (opens in a new tab)](https://github.com/OpShin/pluthon) have been externalized and are available for anyone that wants to build UPLC tooling in Python (such as optimizers). ### Helios[](https://aiken-lang.org/ecosystem-overview#helios) Helios is also a brand new language. One notable implementation difference is that it's compiler is written in a [single javascript file without dependencies (opens in a new tab)](https://github.com/Hyperion-BT/Helios/blob/main/helios.js) . According to the creator, the intention of that was to make the compiler implementation easier to audit. As a language, Helios is also purely functional but has limited type inference. It also supports custom types similar to records and enums. Another interesting thing is that because the compiler is a single javascript file it's pretty easy to use Helios from within a javascript project. ### Plutarch[](https://aiken-lang.org/ecosystem-overview#plutarch) Plutarch is **not** a new language. You can consider it an eDSL for creating smart contracts with Haskell. In some ways, Plutarch is what PlutusTx should have been. There is no [Template Haskell](https://wiki.haskell.org/Template_Haskell) involved. Since Plutarch is just Haskell, you have everything available to you. Type inference, typeclasses, higher-kinded types, etc. ### plu-ts[](https://aiken-lang.org/ecosystem-overview#plu-ts) plu-ts is **not** a new language. You can consider it an eDSL for creating smart contracts with Typescript. Because of this it's a bit closer to Plutarch conceptually than Aiken or Helios. It implements it's own type system and at compile time (js runtime) checks the types to be correct. ### Scalus[](https://aiken-lang.org/ecosystem-overview#scalus) A Scala implementation of Plutus. Scalus is a set of libraries to work with Cardano Untyped Plutus Core that works on both JVM and JavaScript. This includes: * Untyped Plutus Core (UPLC) data types and functions * Flat, CBOR, JSON serialization * CEK UPLC evaluation machine including execution cost calculation * UPLC parser and pretty printer * Type safe UPLC expression builder, think of Plutarch * Macros to generate UPLC code from Scala code, think of PlutusTx but simpler Which should you use?[](https://aiken-lang.org/ecosystem-overview#which-should-you-use) ---------------------------------------------------------------------------------------- Only you can decide for yourself which of these fits your needs the best. Each has made some different decisions around design and implementation. Aiken and Helios are on the **new language** end of the spectrum while Plutarch and plu-ts are on the eDSL end. Plutarch has the most expressive type system while Aiken's types are in between Plutarch and Helios. Embedded DSLs are nice because they integrate seamlessly with off-chain code and usually allow to reuse existing tools that already work on the host language. New languages are nice because they include bespoke checks and functionality specifically for Cardano smart contracts directly in their compilers. While they demand a lot of the tooling to be created anew, they also give the opportunity to address shortcomings of existing tooling in various languages. Which best serves your use case is for you to say. Being the maintainers behind Aiken, we can't be fully partial in providing an unbiaised answer. We encourage you to review the documentation, design decisions and overall project to make an informed decision. [Builtins](https://aiken-lang.org/uplc/builtins "Builtins") [Resources](https://aiken-lang.org/resources "Resources") --- # Aiken | Command-line utilities [Untyped Plutus Core](https://aiken-lang.org/uplc) Command-line utilities Command-line utilities ====================== Evaluation[](https://aiken-lang.org/uplc/cli#evaluation) --------------------------------------------------------- Let's consider the following basic program: **program\_1.uplc** (program 1.0.0 [ [ (builtin addInteger) (con integer 16) ] (con integer 26) ] ) We can evaluate this program using Aiken's cli via: aiken uplc eval program_1.uplc { "result": "(con integer 42)", "cpu": 321577, "mem": 602 } The output indicates the result of the evaluation (`42`) as well as the execution cost of that program, both in terms of CPU and memory usage. Note that the command also accepts arguments. So, for example, if we modify our program into a function that accepts an argument as follows: **program\_2.uplc** (program 1.0.0 (lam x [ [ (builtin addInteger) (con integer 16) ] x ]) ) You can then instrument Aiken to provide arguments upon calling the program by simply appending them to the `eval` command: aiken uplc eval program_2.uplc "(con integer 26)" { "result": "(con integer 42)", "cpu": 390577, "mem": 902 } Formatting[](https://aiken-lang.org/uplc/cli#formatting) --------------------------------------------------------- Because writing UPLC by hand can be a tedious task, Aiken provides a quick way to automatically format a UPLC program via the `fmt` command. By default, the command override the file given as input, but you can also simply prints the result to stdout using `--print`. For example: aiken uplc fmt program_2.uplc --print (program 1.0.0 (lam x [ [ (builtin addInteger) (con integer 16) ] x ]) ) Converting to/from binary encoding[](https://aiken-lang.org/uplc/cli#converting-tofrom-binary-encoding) -------------------------------------------------------------------------------------------------------- So far, we've been representing UPLC programs using a high-level syntax. In practice, however, UPLC programs are encoded into compact binary strings when submitted on-chain (using [flat (opens in a new tab)](http://quid2.org/docs/Flat.pdf) ). Aiken provides utilities to convert a high-level UPLC program into a low-level flat encoding − and vice-versa, via the `encode` and `decode` commands. For example: aiken uplc encode program_1.uplc --hex 01000023370090100009 The `encode` command prints everything on stdout and `--hex` turns the bytes into a hex encoded string so that we can read it. `aiken uplc encode program_1.uplc > program_1.flat` sends the raw "flat" bytes to a file named `program_1.flat` From there, one can recover a UPLC high-level syntax from a flat program using `unflat` as such: aiken uplc decode program_1.flat (program 1.0.0 [ [ (builtin addInteger) (con integer 16) ] (con integer 26) ] ) [Syntax](https://aiken-lang.org/uplc/syntax "Syntax") [Builtins](https://aiken-lang.org/uplc/builtins "Builtins") --- # Aiken | Builtins [Untyped Plutus Core](https://aiken-lang.org/uplc) Builtins Builtins ======== You can find the Aiken equivalents of those builtins in [aiken-lang/prelude (opens in a new tab)](https://aiken-lang.github.io/prelude/aiken/builtin.html) . | Builtins | Type Args | Term Args | Result | | --- | --- | --- | --- | | `ifThenElse`[1](https://aiken-lang.org/uplc/builtins#user-content-fn-1) | (α) | (Bool, (α), (α)) | α | | | | | | | `addInteger` | \- | (Integer, Integer) | Integer | | `subtractInteger` | \- | (Integer, Integer) | Integer | | `multiplyInteger` | \- | (Integer, Integer) | Integer | | `divideInteger` | \- | (Integer, Integer) | Integer | | `modInteger` | \- | (Integer, Integer) | Integer | | `quotientInteger` | \- | (Integer, Integer) | Integer | | `remainderInteger` | \- | (Integer, Integer) | Integer | | `equalsInteger` | \- | (Integer, Integer) | Bool | | `lessThanInteger` | \- | (Integer, Integer) | Bool | | `lessThanEqualsInteger` | \- | (Integer, Integer) | Bool | | `integerToByteString` | \- | (Bool, Integer, Integer) | Integer | | | | | | | `appendString` | \- | (String, String) | String | | `equalsString` | \- | (String, String) | Bool | | `encodeUtf8` | \- | (String) | Bytestring | | | | | | | `appendByteString` | \- | (Bytestring, Bytestring) | Bytestring | | `consByteString` | \- | (Integer, Bytestring) | Bytestring | | `indexByteString` | \- | (Bytestring, Integer) | Integer | | `sliceByteString` | \- | (Integer, Integer, Bytestring) | Bytestring | | `lengthOfByteString` | \- | (Bytestring) | Integer | | `equalsByteString` | \- | (Bytestring, Bytestring) | Bool | | `lessThanByteString` | \- | (Bytestring, Bytestring) | Bool | | `lessThanEqualsByteString` | \- | (Bytestring, Bytestring) | Bool | | `decodeUtf8` | \- | (Bytestring) | String | | `byteStringToInteger` | \- | (Bool, Bytestring) | Integer | | | | | | | `chooseData`[2](https://aiken-lang.org/uplc/builtins#user-content-fn-2) | (α) | (Data, (α), (α), (α), (α), (α)) | α | | `constrData` | \- | (Integer, List Data) | Data | | `unConstrData` | \- | Data | (Integer, List Data) | | `iData` | \- | (Integer) | Data | | `unIData` | \- | (Data) | Integer | | `bData` | \- | (Bytestring) | Data | | `unBData` | \- | (Data) | Bytestring | | `mapData` | \- | (List (Pair Data Data)) | Data | | `unMapData` | \- | (Data) | List (Pair Data Data) | | `listData` | \- | (List Data) | Data | | `unListData` | \- | (Data) | List Data | | `equalsData` | \- | (Data, Data) | Bool | | `serialiseData` | \- | (Data) | Bytestring | | | | | | | `chooseList`[3](https://aiken-lang.org/uplc/builtins#user-content-fn-3) | (α, β) | (List (α), (β), (β)) | β | | `mkNilData` | \- | (Unit) | List (Data) | | `mkNilPairData` | \- | (Unit) | List (Pair Data Data) | | `mkCons` | (α) | ((α), List (α)) | List (α) | | `headList` | (α) | (List (α)) | α | | `tailList` | (α) | (List (α)) | List (α) | | `nullList` | (α) | (List (α)) | Bool | | | | | | | `mkPairData` | \- | (Data, Data) | Pair (Data) (Data) | | `fstPair` | (α, β) | Pair (α) (β) | α | | `sndPair` | (α, β) | Pair (α) (β) | β | | | | | | | `blake2b_224` | \- | (Bytestring) | Bytestring | | `blake2b_256` | \- | (Bytestring) | Bytestring | | `keccak_256` | \- | (Bytestring) | Bytestring | | `sha2_256` | \- | (Bytestring) | Bytestring | | `sha3_256` | \- | (Bytestring) | Bytestring | | | | | | | `verifyEd25519Signature`[4](https://aiken-lang.org/uplc/builtins#user-content-fn-4) | \- | (Bytestring, Bytestring, Bytestring) | Bool | | `verifyEcdsaSecp256k1Signature`[4](https://aiken-lang.org/uplc/builtins#user-content-fn-4) | \- | (Bytestring, Bytestring, Bytestring) | Bool | | `verifySchnorrSecp256k1Signature`[4](https://aiken-lang.org/uplc/builtins#user-content-fn-4) | \- | (Bytestring, Bytestring, Bytestring) | Bool | | | | | | | `bls12_381_G1_add` | \- | (G1Element, G1Element) | G1Element | | `bls12_381_G1_scalarMul` | \- | (Integer, G1Element) | G1Element | | `bls12_381_G1_neg` | \- | G1Element | G1Element | | `bls12_381_G1_equal` | \- | (G1Element, G1Element) | Bool | | `bls12_381_G1_compress` | \- | G1Element | bytestring | | `bls12_381_G1_uncompress` | \- | bytestring | G1Element | | `bls12_381_G1_hashToGroup` | \- | (Bytestring, Bytestring) | G1Element | | `bls12_381_G2_add` | \- | (G2Element, G2Element) | G2Element | | `bls12_381_G2_scalarMul` | \- | (Integer, G2Element) | G2Element | | `bls12_381_G2_neg` | \- | G2Element | G2Element | | `bls12_381_G2_equal` | \- | (G2Element, G2Element) | Bool | | `bls12_381_G2_compress` | \- | G2Element | bytestring | | `bls12_381_G2_uncompress` | \- | Bytestring | G2Element | | `bls12_381_G2_hashToGroup` | \- | (Bytestring, Bytestring) | G2Element | | `bls12_381_millerLoop` | \- | (G1Element, G2Element) | MillerLoopResult | | `bls12_381_mulMlResult` | \- | (MillerLoopResult, MillerLoopResult) | MillerLoopResult | | `bls12_381_finalVerify` | \- | (MillerLoopResult, MillerLoopResult) | Bool | | | | | | | `trace` | (α) | (string, (α)) | α | Footnotes[](https://aiken-lang.org/uplc/builtins#footnote-label) ----------------------------------------------------------------- 1. Returns the second argument when the predicate is `True`, and the third argument when `False`. [↩](https://aiken-lang.org/uplc/builtins#user-content-fnref-1) 2. Each argument corresponds to each of the constructors of a builtin data (in this order): constr, map, list, integer and bytestring. The evaluation will continue with whatever branch actually corresponds to the given term value. [↩](https://aiken-lang.org/uplc/builtins#user-content-fnref-2) 3. Returns the second argument when the list is empty, and the third argument otherwise. [↩](https://aiken-lang.org/uplc/builtins#user-content-fnref-3) 4. Arguments are respectively: the public key, the message and the signature [↩](https://aiken-lang.org/uplc/builtins#user-content-fnref-4) [↩2](https://aiken-lang.org/uplc/builtins#user-content-fnref-4-2) [↩3](https://aiken-lang.org/uplc/builtins#user-content-fnref-4-3) [Command-line utilities](https://aiken-lang.org/uplc/cli "Command-line utilities") [Ecosystem Overview](https://aiken-lang.org/ecosystem-overview "Ecosystem Overview") --- # Aiken | Resources Resources Resources ========= Exploring Cardano[](https://aiken-lang.org/resources#exploring-cardano) ------------------------------------------------------------------------ * [Cardano Foundation's education course (opens in a new tab)](https://education.cardano.org/) * [The Developer Portal (opens in a new tab)](https://developers.cardano.org/) * [The Cardano Ledger (specs & CDDLs) (opens in a new tab)](https://github.com/input-output-hk/cardano-ledger/#readme) * [Aiken for Amateurs (opens in a new tab)](https://piefayth.github.io/blog/pages/aiken1/) * [Hello World with Mesh (opens in a new tab)](https://meshjs.dev/guides/aiken) Software Development Kit[](https://aiken-lang.org/resources#software-development-kit) -------------------------------------------------------------------------------------- | Language | Link | | --- | --- | | C# | [CardanoSharp (opens in a new tab)](https://cardanosharp.com/) | | Haskell | [Cardano API (opens in a new tab)](https://github.com/IntersectMBO/cardano-api) | | Java/Scala | [Bloxbean (opens in a new tab)](https://github.com/bloxbean/cardano-client-lib) | | JavaScript | [Mesh.js (opens in a new tab)](https://meshjs.dev/) | | Python | [PyCardano (opens in a new tab)](https://github.com/Python-Cardano/pycardano) | | Rust | [Pallas (opens in a new tab)](https://github.com/txpipe/pallas)
, [naumachia (opens in a new tab)](https://github.com/free-honey/naumachia) | | TypeScript | [Lucid (opens in a new tab)](https://lucid.spacebudz.io/)
, [Mesh (opens in a new tab)](https://meshjs.dev/) | Infrastructure[](https://aiken-lang.org/resources#infrastructure) ------------------------------------------------------------------ * [Demeter (cloud-based infrastructure for Cardano) (opens in a new tab)](https://demeter.run/) * [Maestro (opens in a new tab)](https://www.gomaestro.org/) * [Blockfrost (API query layer) (opens in a new tab)](https://blockfrost.io/) * [Koios (API query layer) (opens in a new tab)](https://www.koios.rest/) Building & Maintaining Aiken[](https://aiken-lang.org/resources#building--maintaining-aiken) --------------------------------------------------------------------------------------------- Below is a list of links to resources we used while building Aiken. * [The Gleam's compiler (opens in a new tab)](https://github.com/gleam-lang/gleam) * [The Official Plutus documentation (opens in a new tab)](https://plutus.readthedocs.io/en/latest/) * [The source code about encoding/Decoding UPLC (opens in a new tab)](https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Core/Instance/Flat.hs) * [The source code about core types (opens in a new tab)](https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Core/Type.hs) * [The original (albeit outdated) Plutus Core specification](https://aiken-lang.org/resources/plutus-core-specification.pdf) [Ecosystem Overview](https://aiken-lang.org/ecosystem-overview "Ecosystem Overview") --- # Aiken | Gift Card Gift Card Gift Card ========= Let's build a UI to send and redeem a gift card using smart contracts on Cardano. You can find code supporting this tutorial on [Aiken's main repository (opens in a new tab)](https://github.com/aiken-lang/aiken/tree/main/examples/gift_card) . Covered in this tutorial[](https://aiken-lang.org/example--gift-card#covered-in-this-tutorial) ----------------------------------------------------------------------------------------------- * [x] Writing `Aiken` inter-dependent `mint` & `spend` handlers. * [x] Parameterizing validators. * [x] [Weld (opens in a new tab)](https://github.com/Cardano-Forge/weld) for managing wallet connection. * [x] Using [Lucid Evolution (opens in a new tab)](https://github.com/Anastasia-Labs/lucid-evolution) with [Blockfrost (opens in a new tab)](https://blockfrost.io/) ★. ★ We'll once again be using the `Blockfrost` provider. So have your Blockfrost API key ready. * [x] Using [SvelteKit (opens in a new tab)](https://kit.svelte.dev/) ★. ★ Make sure you have Node.js installed. 📘 When encountering an unfamiliar syntax or concept, do not hesitate to refer to the [language-tour](https://aiken-lang.org/language-tour/primitive-types) for details and extra examples. What is a gift card?[](https://aiken-lang.org/example--gift-card#what-is-a-gift-card) -------------------------------------------------------------------------------------- In the context of this tutorial a gift card will involve locking some assets in a smart contract. While some assets are being locked, we'll mint an NFT in the same transaction. This NFT could be sent anywhere and the owner of the NFT can burn it to unlock the assets that were previously locked. We can think of the NFT as a gift card. Aiken is the easy part[](https://aiken-lang.org/example--gift-card#aiken-is-the-easy-part) ------------------------------------------------------------------------------------------- Let's go ahead and create a new `Aiken` project: aiken new my-org/gift-card cd gift-card `my-org` above can be replaced by any name. We recommend using the name of a Github organization or your own username. We've already covered what `aiken new` generates in a previous tutorial so let's jump right into some code. Go ahead and remove the `lib/` folder, we won't be needing that for this tutorial. rm -rf lib Now let's create a new file in the `validators/` folder called `oneshot.ak`. touch validators/oneshot.ak `oneshot.ak` could be named anything. Any file in `validators/` is allowed to export as many validators as you'd like. Now let's open the project folder in our favorite editor and define two empty validator functions. validators/oneshot.ak use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx validator gift_card { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(_rdmr: Data, policy_id: PolicyId, transaction: Transaction) -> Bool { todo @"mint and burn" } } The `gift_card` validator will be used to mint and burn the gift card NFT via the `mint` handler. The `spend` handler will be used to redeem the gift card and unlock the assets. The life cycle of this gift card will involve two transactions. The first transaction will mint the gift card as an NFT and it will send some assets to the `gift_card` validator's address. The gift card can be sent anywhere in the first transaction. The second transaction will burn the NFT and send the locked assets to the address that held the burned NFT. ### Minting a Gift Card[](https://aiken-lang.org/example--gift-card#minting-a-gift-card) Since this example is for a oneshot minting contract let's add some parameters to the validator that we can use to guarantee uniqueness. validators/oneshot.ak use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(_rdmr: Data, policy_id: PolicyId, transaction: Transaction) -> Bool { todo @"mint and burn" } } We'll use the `utxo_ref` parameter to ensure this validator will only allow a mint once. Since the Cardano ledger guarantees that utxos can only be spent once, we can leverage them to inherit similar guarantees in our validator. Next let's define a type for `rdmr`. We have two actions that this validator will perform. This validator can be used to mint and then burn an NFT. validators/oneshot.ak use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx type Action { CheckMint CheckBurn } validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(rdmr: Action, policy_id: PolicyId, transaction: Transaction) -> Bool { when rdmr is { CheckMint -> todo @"mint" CheckBurn -> todo @"burn" } } } Next we'll do these things in order so that we have everything we need to perform the final check. * pattern match on the `transaction` to get it's `inputs` and `mint` which holds minted assets * `expect` minted assets (`mint`) to only have one item which has an `asset_name` and an `amount` validators/oneshot.ak use aiken/collection/dict use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx type Action { CheckMint CheckBurn } validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(rdmr: Action, policy_id: PolicyId, transaction: Transaction) -> Bool { let Transaction { inputs, mint, .. } = transaction expect [Pair(asset_name, amount)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() when rdmr is { CheckMint -> todo @"mint" CheckBurn -> todo @"burn" } } } At this point we have all the data we need to perform the final check for the `CheckMint` action. For this validator to succeed we need to ensure that the `utxo_ref` parameter equals one of the `inputs` in the transaction. In addition to this, we need to ensure `amount` is equal to one because we're minting an NFT. For fun, we'll check that `asset_name` is equal to `token_name` from the parameters. validators/oneshot.ak use aiken/collection/dict use aiken/collection/list use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx type Action { CheckMint CheckBurn } validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(rdmr: Action, policy_id: PolicyId, transaction: Transaction) -> Bool { let Transaction { inputs, mint, .. } = transaction expect [Pair(asset_name, amount)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() when rdmr is { CheckMint -> { expect True = list.any(inputs, fn(input) { input.output_reference == utxo_ref }) amount == 1 && asset_name == token_name } CheckBurn -> todo @"burn" } } } We have everything we need in this validator to mint a Gift Card. Before we start making transactions though, we'll need to finish the `Burn` action and that will also be paired with the `spend` handler. ### Redeeming a Gift Card[](https://aiken-lang.org/example--gift-card#redeeming-a-gift-card) To redeem a gift card we'll want a transaction that uses two handlers at once. We'll use the `mint` handler with the `Burn` action to burn the NFT. We'll also use the `spend` handler to unlock the assets at that address. Let's finish the `Burn` action of the `mint` handler. We just need to check that `amount` is equal to negative one and that `asset_name` is equal to `token_name`. validators/oneshot.ak use aiken/collection/dict use aiken/collection/list use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx type Action { CheckMint CheckBurn } validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } mint(rdmr: Action, policy_id: PolicyId, transaction: Transaction) -> Bool { let Transaction { inputs, mint, .. } = transaction expect [Pair(asset_name, amount)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() when rdmr is { CheckMint -> { expect Some(_input) = list.find(inputs, fn(input) { input.output_reference == utxo_ref }) amount == 1 && asset_name == token_name } CheckBurn -> amount == -1 && asset_name == token_name } } } Now we can start working on the `spend` handler. validators/oneshot.ak use aiken/collection/dict use aiken/collection/list use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { todo @"redeem" } // ... mint handler ... } Let's add some boilerplate to this handler so that we can get the `asset_name` and the `amount` out of the transaction. validators/oneshot.ak use aiken/collection/dict use aiken/collection/list use cardano/address.{Script} use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { let Transaction { mint, inputs, .. } = transaction expect Some(own_input) = list.find(inputs, fn(input) { input.output_reference == own_ref }) expect Script(policy_id) = own_input.output.address.payment_credential expect [Pair(asset_name, amount)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() todo @"redeem" } // ... mint handler ... } Finally we need to confirm that `asset_name` is equal to `token_name` and that `amount` is equal to negative one. validators/oneshot.ak use aiken/collection/dict use aiken/collection/list use cardano/address.{Script} use cardano/assets.{PolicyId} use cardano/transaction.{OutputReference, Transaction} as tx validator gift_card(token_name: ByteArray, utxo_ref: OutputReference) { spend(_d, _r, own_ref: OutputReference, transaction: Transaction) -> Bool { let Transaction { mint, inputs, .. } = transaction expect Some(own_input) = list.find(inputs, fn(input) { input.output_reference == own_ref }) expect Script(policy_id) = own_input.output.address.payment_credential expect [Pair(asset_name, amount)] = mint |> assets.tokens(policy_id) |> dict.to_pairs() amount == -1 && asset_name == token_name } // ... mint handler ... } We should make sure this builds. You've been running `aiken check` along the way right?!? Jokes aside, you're probably using an editor integration. If the editor integration isn't giving you proper feed back or giving you a hard time please come talk to us so we can make things better. aiken build Building a frontend[](https://aiken-lang.org/example--gift-card#building-a-frontend) ------------------------------------------------------------------------------------- With the easy part out of the way we can start building a frontend to interact with our smart contracts in the browser. In this tutorial we'll be using SvelteKit to build the UI. ### Setting up[](https://aiken-lang.org/example--gift-card#setting-up) Let's generate a SvelteKit project in the same directory as our Aiken project. npx sv create . ⚠️ When prompted use the current directory, continue even though directory is not empty, choose a skeleton project, use Svelte 5, and enable typescript. Make sure to include tailwindcss. We need to add Lucid Evolution and Weld now. npm i @ada-anvil/weld @lucid-evolution/lucid vite-plugin-wasm vite-plugin-top-level-await Then make sure to update the `vite.config.js` file to include the new plugins. vite.config.js import { sveltekit } from "@sveltejs/kit/vite"; import wasm from "vite-plugin-wasm"; import topLevelAwait from "vite-plugin-top-level-await"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [sveltekit(), wasm(), topLevelAwait()], server: { fs: { // Allow serving files from one level up to the project root allow: ["plutus.json"], }, }, }); Let's also add some reusable components to our project. src/lib/components/Button.svelte src/lib/components/Input.svelte
### Home page[](https://aiken-lang.org/example--gift-card#home-page) Everything we'll be doing with validators and transactions will happen fully client side. This means we can just have our app render a single `+page.svelte` component and then we can write all of our code in this page component for the most part. Let's edit `src/routes/+page.svelte` to contain the following code. src/routes/+page.svelte One Shot

Make a one shot minting and lock contract

Gift Card Template

          TODO: Render non-parameterized gift_card validator
        
Oneshot
We've left a `TODO` in the code to remind us to render the validator. We'll render the compiled aiken code as a hex encoded string. There's not much of a reason to do this, it's just kinda cool to see. Next we should load the `plutus.json` file and get the compiled aiken code. Let's create a file called `lib/utils.ts` and add the following code. lib/utils.ts import blueprint from "../../plutus.json" assert { type: "json" }; export type Validators = { giftCard: string; }; export function readValidators(): Validators { const giftCard = blueprint.validators.find( (v) => v.title === "oneshot.gift_card.spend" ); if (!giftCard) { throw new Error("Gift Card validator not found"); } return { giftCard: giftCard.compiledCode, }; } There's nothing particularly special here. We're just reading the `plutus.json` file and finding the compiled code for the `gift_card` validator. We're also exporting a type for the validators so we can use it in our page later. Having this function potentially throw an error is just a way to signal to us that we've done something wrong. Let's import our new `readValidators` file into `src/routes/+page.server.ts` file and use it in a server side loader. This will allow us to access the data in the `+page.svelte` page component as page props which we'll then use to render the validator's compiled code. src/routes/+page.server.ts import { readValidators } from "$lib/utils"; import type { PageServerLoad } from "./$types"; export const load: PageServerLoad = async () => { const validator = readValidators().giftCard; return { validator }; }; src/routes/+page.svelte One Shot

Make a one shot minting and lock contract

Gift Card Template

{data.validator}
Oneshot
### The App[](https://aiken-lang.org/example--gift-card#the-app) It's about time we start the real party and we've made it to the juicy part. In this island we'll capture some user input, apply some params to our raw validator, and execute some transactions. To keep things simple we'll assume [eternl (opens in a new tab)](https://eternl.io/) is setup in your browser. Another thing we'll do to keep things simple is have the gift card be sent to ourselves when minted. This way we can test the redeeming of the gift card without having to send it to someone else or using a second wallet. #### Token name[](https://aiken-lang.org/example--gift-card#token-name) We need to capture the `token_name` so we can use it to apply some params to the raw validators. Lucid & Weld also requires initialization so let's get some boilerplate out of the way. src/lib/wallet.svelte.ts import { createWeldInstance, type WeldConfig } from "@ada-anvil/weld"; import { getContext, setContext } from "svelte"; export class Weld { weld = createWeldInstance(); // Use the $state rune to create a reactive object for each Weld store config = $state(this.weld.config.getState()); wallet = $state(this.weld.wallet.getState()); extensions = $state(this.weld.extensions.getState()); constructor(persist?: Partial) { this.weld.config.update({ updateInterval: 2000 }); if (persist) this.weld.persist(persist); $effect(() => { this.weld.init(); // Subscribe to Weld stores and update reactive objects when changse occur // Note: No need to use subscribeWithSelector as $state objects are deeply reactive this.weld.config.subscribe((s) => (this.config = s)); this.weld.wallet.subscribe((s) => (this.wallet = s)); this.weld.extensions.subscribe((s) => (this.extensions = s)); return () => this.weld.cleanup(); }); } } // Use the context API to scope weld stores and prevent unwanted sharing // of data between clients when rendering on the server const weldKey = Symbol("weld"); export function setWeldContext(persist?: Partial) { const value = new Weld(persist); setContext(weldKey, value); return value; } export function getWeldContext() { return getContext>(weldKey); } src/routes/+layout.svelte {@render children()} src/routes/+page.svelte One Shot

Make a one shot minting and lock contract

balance: {displayedBalance}

Gift Card Template

{data.validator}
{#if !lucid}
Blockfrost API Key
{:else}
Token Name {#if tokenName.length > 0} {/if}
{/if}
#### Apply params[](https://aiken-lang.org/example--gift-card#apply-params) We're going to use the `token_name` to apply some params to the raw validators. We can create a helper in `utils.ts` to do this for us. utils.ts import { applyDoubleCborEncoding, applyParamsToScript, Constr, fromText, validatorToAddress, validatorToScriptHash, type MintingPolicy, type OutRef, type SpendingValidator, } from "@lucid-evolution/lucid"; import blueprint from "../../plutus.json" assert { type: "json" }; // ... export type Validators ... // ... export function readValidators(): Validators ... export type AppliedValidators = { redeem: SpendingValidator; giftCard: MintingPolicy; policyId: string; lockAddress: string; }; export function applyParams( tokenName: string, outputReference: OutRef, validator: string ): AppliedValidators { const outRef = new Constr(0, [\ new Constr(0, [outputReference.txHash]),\ BigInt(outputReference.outputIndex),\ ]); const giftCard = applyParamsToScript(validator, [\ fromText(tokenName),\ outRef,\ ]); const policyId = validatorToScriptHash({ type: "PlutusV2", script: giftCard, }); const lockAddress = validatorToAddress("Preprod", { type: "PlutusV2", script: giftCard, }); return { redeem: { type: "PlutusV2", script: applyDoubleCborEncoding(giftCard) }, giftCard: { type: "PlutusV2", script: applyDoubleCborEncoding(giftCard) }, policyId, lockAddress, }; } Our `applyParams` function expects a `tokenName`, an `output_Reference` that we'll fetch using lucid in a moment, and a `validator` that we got in the props. First we create `outRef` which is `PlutusData` using `outputReference`. Then we apply the `tokenName` and `outRef` to the `giftCard` validator. We then use `lucid` to get the `policyId` so that we can apply `tokenName` and `policyId` to the `redeem` validator. Finally we use `lucid` to get the `lockAddress` so that we can return everything we need from the function. `lockAddress` is just the address of the `redeem` validator which is where we'll send some assets that can be redeemed with the gift card. At this point we won't need to touch `utils.ts` again. We can use this new function in `src/routes/+page.svelte` when a `tokenName` is submitted. src/routes/+page.svelte
{#if lucid && parameterizedContracts}

New Gift Card

          {parameterizedContracts.redeem.script}
        
{/if}
We now have the power to create validators, that are usable on-chain, **completely on the fly** powered by some user input. You may already be getting all kinds of ideas on how to use this. Before you go build the next big thing, let's use these newly generated validators in some transactions. #### Mint and lock[](https://aiken-lang.org/example--gift-card#mint-and-lock) We're going to mint some assets and lock them in the `lockAddress` that we got from `applyParams`. For the sake of keeping things simple, we'll only provide an input to capture some ADA amount to be locked. Technically the validators allow for any assets to be locked but it's easier to just support ADA for now. Along with an input, we want a button that when clicked will run a function that builds, signs, and submits a transaction. When the transaction is done we'll render the hash and have it link to cardano scan. src/routes/+page.svelte
With this code, we can now enter some ADA amount and then click a button to perform the transaction. The transaction will mint a new asset using our token and send the ADA to the validator's address, effectively locking the ADA. ⚠️ It may be tempting to run this right now, but unless you cache some of the data so far into local storage, you may find it hard to recover the locked assets. We'll be writing more code which will require the app to be reloaded and you will lose all your state including the uniquely parameterized `redeem` validator's compiled code. #### Burn and unlock[](https://aiken-lang.org/example--gift-card#burn-and-unlock) The final step in this example will be to redeem the gift card for the locked assets. Similar to the previous section, we'll drive the transaction execution with a button click. After the redeem button is clicked and the transaction finishes we'll render the hash and have it link to cardano scan like the previous section. src/routes/+page.svelte
{#if lucid && parameterizedContracts}
{#if lockTxHash}

ADA Locked

{lockTxHash}
{/if} {#if unlockTxHash}

ADA Unlocked

{unlockTxHash} {/if}
{/if}
We've now completed the example and have a fun little prototype. Conclusion[](https://aiken-lang.org/example--gift-card#conclusion) ------------------------------------------------------------------- Hopefully this gives you ideas on what you can build on Cardano. This example should also illustrate how most of the code in your dapp isn't even the validators. When designing applications that leverage Cardano it's always better to think about what kinds of transactions you'll need to construct and then writing your validators to enforce them. A full reference to this example can be found [here (opens in a new tab)](https://github.com/aiken-lang/aiken/tree/main/examples/gift_card) . [with Mesh (JavaScript)](https://aiken-lang.org/example--vesting/mesh "with Mesh (JavaScript)") [Untyped Plutus Core](https://aiken-lang.org/uplc "Untyped Plutus Core") --- # Unknown Formal Specification of the Plutus Core Language Plutus Team 6th November 2022 DRAFT Abstract This is intended to be a reference guide for developers who want to utilise the Plutus Core infras- tructure. We lay out the grammar and syntax of untyped Plutus Core terms, and their semantics and evaluation rules. We also describe the built-in types and functions. Appendix A includes a list of supported builtins in each era and the formally verified behaviour. This document only describes untyped Plutus Core: a subsequent version will also include the syntax and semantics of Typed Plutus Core and describe its relation to untyped Plutus Core. 1 Contents Contents2 1 Introduction4 2 Some Basic Notation4 2.1 Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3 The Grammar of Plutus Core5 3.1 Lexical grammar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.2 Grammar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 3.3 Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4 Interpretation of Built-in Types and Functions6 4.1 Built-in types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.1.1 Type variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.1.2 Polymorphic types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 4.1.3 Type assignments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 4.2 Built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 4.2.1 Inputs to built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 4.2.2 Signatures and denotations of built-in functions . . . . . . . . . . . . . . . . . . 9 4.2.3 Denotations of built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2.4 Results of built-in functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2.5 Parametricity for \*-polymorphic arguments . . . . . . . . . . . . . . . . . . . . 12 4.3 Evaluation of built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 4.3.1 Compatibility of inputs and signature entries . . . . . . . . . . . . . . . . . . . 12 4.3.2 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 5 Term Reduction13 5.1 Values in Plutus Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 5.2 Term reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 6 The CEK Machine17 6.1 Converting CEK evaluation results into Plutus Core terms . . . . . . . . . . . . . . . . . 18 7 Typed Plutus Core19 A Built-in Types and Functions Supported in the Alonzo Release20 A.1 Built-in types and type operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 A.2 Alonzo built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 A.3 Cost accounting for built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 B Built-in Types and Functions Supported in the Vasil Release26 B.1 Built-in types and type operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 B.2 Built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 C Formally Verified Behaviours27 2 D SerialisingdataObjects Using the CBOR Format27 D.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 D.2 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 D.3 The CBOR format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 D.4 Encoding and decoding the heads of CBOR items . . . . . . . . . . . . . . . . . . . . . 28 D.5 Encoding and decoding bytestrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 D.6 Encoding and decoding integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 D.7 Encoding and decodingdata. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 E Serialising Plutus Core Terms and Programs Using theflatFormat33 E.1 Encoding and decoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 E.1.1 Padding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 E.2 Basicflatencodings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 E.2.1 Fixed-width natural numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 E.2.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 E.2.3 Natural numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 E.2.4 Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 E.2.5 Bytestrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 E.2.6 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 E.3 Encoding and decoding Plutus Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 E.3.1 Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 E.3.2 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 E.3.3 Built-in types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 E.3.4 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 E.3.5 Built-in functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 E.3.6 Variable names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 E.4 Cardano-specific serialisation issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 E.4.1 Scope checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 E.4.2 CBOR wrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 E.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 References44 Index of Notation46 3 1 Introduction Plutus Core is an eagerly-evaluated version of the untyped lambda calculus extended with some “built-in” types and functions; it is intended for the implementation of validation scripts on the Cardano blockchain. This document presents the syntax and semantics of Plutus Core, a specification of an efficient evalua- tor, a description of the built-in types and functions available in the Alonzo release of Cardano, and a specification of the binary serialisation format used by Plutus Core. Since Plutus Core is intended for use in an environment where computation is potentially expensive and excessively long computations can be problematic we have also developed a costing infrastructure for Plutus Core programs. A description of this will be added in a later version of this document. We also have a typed version of Plutus Core which provides extra robustness when untyped Plutus Core is used as a compilation target, and we will eventually provide a specification of the type system and semantics of Typed Plutus Core here as well, together with its relationship to untyped Plutus Core. 2 Some Basic Notation We begin with some notation which will be used throughout the document. 2.1 Sets •The symbol⊎denotes a disjoint union of sets; for emphasis we often use this to denote the union of sets which we know to be disjoint. •Given a set푋,푋 ∗ denotes the set of finite sequences of elements of푋: 푋 ∗ = ‘ {푋 푛 ∶푛∈ℕ}. •ℕ= {0,1,2,3,...}. •ℕ + = {1,2,3,...}. •ℕ \[푎,푏\] = {푛∈ℕ∶푎≤푛≤푏}. •픹=ℕ \[0,255\] , the set of 8-bit bytes. •픹 ∗ is the set of all bytestrings. •ℤ= {...,−2,−1,0,1,2,...}. •핌denotes the set of Unicode scalar values, as defined in \[24, Definition D76\]. •핌 ∗ is the set of all Unicode strings. •We assume that there is a special symbol×which does not appear in any other set we mention. The symbol×is used to indicate that some sort of error condition has occurred, and we will often need to consider situations in which a value is either×or a member of some set푆. For brevity, if푆is a set then we define 푆 × ∶=푆 ⊎{×}. 4 2.2 Lists •The symbol\[\]denotes an empty list. •The notation\[푥 푚 ,...,푥 푛 \]denotes a list containing the elements푥 푚 ,...,푥 푛 . If푚 > 푛then the list is empty. •The length of a list퐿is denoted by퓁(퐿). •Given two lists퐿= \[푥 1 ,...,푥 푚 \]and퐿 ® = \[푦 1 ,...,푦 푛 \],퐿⋅퐿 ® denotes their concatenation\[푥 1 ,...,푥 푚 , 푦 1 ,...,푦 푛 \]. •Given an object푥and a list퐿= \[푥 1 ,...,푥 푛 \], we denote the list\[푥,푥 1 ,...,푥 푛 \]by푥⋅퐿. •Given a list퐿= \[푥 1 ,...,푥 푛 \]and an object푥, we denote the list\[푥 1 ,...,푥 푛 ,푥\]by퐿⋅푥. •Given a syntactic category푉, the symbol 푉denotes a possibly empty list\[푉 1 ,...,푉 푛 \]of elements 푉 푖 ∈푉. 3 The Grammar of Plutus Core This section presents the grammar of Plutus Core in a Lisp-like form. This is intended as a specification of the abstract syntax of the language; it may also by used by tools as a concrete syntax for working with Plutus Core programs, but this is a secondary use and we do not make any guarantees of its completeness when used in this way. The primary concrete form of Plutus Core programs is the binary format described in Appendix E. 3.1 Lexical grammar Name푛∶∶=\[a-zA-Z\]\[a-zA-Z0-9\_'\] \* name Var푥∶∶=푛term variable BuiltinName푏푛∶∶=푛built-in function name Version푣∶∶=\[0-9\] + .\[0-9\] + .\[0-9\] + version Constant푐∶∶=⟨literal constant⟩ Figure 1: Lexical grammar of Plutus Core 5 3.2 Grammar Term퐿,푀,푁∶∶=푥variable (conT푐)constant (builtin푏)builtin (lam푥 푀)휆abstraction \[푀 푁\]function application (delay푀)delay execution of a term (force푀)force execution of a term (error)error Program푃∶∶=(program푣 푀)versioned program Figure 2: Grammar of untyped Plutus Core 3.3 Notes Scoping.For simplicity,we assume throughout that the body of a Plutus Core program is a closed term, ie, that it contains no free variables. Thus(program 1.0.0 (lam x x))is a valid program but (program 1.0.0 (lam x y))is not, since the variableyis free. This condition should be checked before execution of any program commences, and the program should be rejected if its body is not closed. The assumption implies that any variable푥occurring in the body of a program must be bound by an occurrence oflamin some enclosing term; in this case, we always assume that푥refers to themost recent (ie, innermost) such binding. Iterated applications.An application of a term푀to a term푁is represented by\[푀 푁\]. We may occasionally write\[푀 푁 1 ...푁 푘 \]as an abbreviation for an iterated application\[...\[\[푀 푁 1 \]푁 2 \]...푁 푘 \], and tools may also use this as concrete syntax. Built-in types and functions.The language is parameterised by a setUofbuilt-in types(we some- times refer toUas theuniverse) and a setBofbuilt-in functions(builtinsfor short), both of which are sets of Names. Briefly, the built-in types represent sets of constants such as integers or strings; constant expressions(conT푐)represent values of the built-in types (the integer 123 or the string"string", for example), and built-in functions are functions operating on these values, and possibly also general Plutus Core terms. Precise details are given in Section 4. Plutus Core comes with a default universe and a default set of builtins, which are described in Appendix A. De Bruijn indices.The grammar defines names to be textual strings, but occasionally (specifically in Appendix E) we want to use de Bruijn indices (\[11\], \[4, C.3\]), and for this we redefine names to be natural numbers. In de Bruijn terms,휆-expressions do not need to bind a variable, but in order to re-use our existing syntax we arbitrarily use 0 for the bound variable, so that all휆-expresssions are of the form(lam 0푀); other variables (ie, those not appearing immediately after alambinder) are represented by natural number greater than zero. 4 Interpretation of Built-in Types and Functions As mentioned above, Plutus Core is generic over a universeUof types and a setBof built-in functions. As the terminology suggests, built-in functions are interpreted as functions over terms and elements of the 6 built-in types: in this section we make this interpretation precise by giving a specification of built-in types and functions in a set-theoretic denotational style. We require a considerable amount of extra notation in order to do this, and we emphasise that nothing in this section is part of the syntax of Plutus Core: it is meta-notation introduced purely for specification purposes. 4.1 Built-in types We require some extra syntactic notation for built-in types: see Figure 3. at∶∶=푛Atomic type op∶∶=푛Type operator T∶∶=atop(T,T,...,T)Built-in type Figure 3: Type names and operators We assume that we have a setU 0 ofatomic type namesand a setOoftype operator names. Each type operator nameop∈Ohas anargument count  op  ∈ℕ + , and a type nameop(T 1 ,...,T 푛 )is well-formed if and only if푛=  op  . We define theuniverseUto be the closure ofU 0 under repeated applications of operators inO: U 푖+1 =U 푖 ∪ {op(T 1 ,...,T  op  ) ∶op∈O,T 1 ,...,T  표푝  ∈U 푖 } U= Õ {U 푖 ∶푖∈ℕ + } The universeUconsists entirely ofnames, and the semantics of these names are given bydenotations. Each built-in typeT∈Uis associated with some mathematical setJTK, thedenotationofT. For example, we might haveJbooleanK= {헍헋헎햾,햿 햺헅헌햾}andJintegerK=ℤandJ횙횊횒횛(푎,푏)K=J푎K×J푏K. See Appendix A for a description of the built-in types and type operators available in the Alonzo release of Plutus Core. For non-atomic type namesT=op(T 1 ,...,T 푟 )we would generally expect the denotation ofTto be obtained in some uniform way (ie, parametrically) from the denotations ofT 1 ,...,T 푟 ; we do not insist on this though. 4.1.1 Type variables Built-in functions can be polymorphic, and to deal with this we needtype variables. An argument of a polymorphic function can be either restricted to built-in types or can be an arbitrary term, and we define two different kinds of type variables to cover these two situations. See Figure 4. TypeVariabletv∶∶=푛 ∗ fully polymorphic type variable 푛 # built-in-polymorphic type variable Figure 4: Type variables We denote the set of all possible type variables byV, the set of all fully-polymorphic type variables byV ∗ , and the set of all built-in-polymorphic type variables푣 # byV # . Note thatV∩U= ∅since the symbols ∗ and # do not occur in names inU. The two kinds of type variable are required because we have two different types of polymorphism. Later on we will see that built-in functions can take arguments which can be of a type which is unknown but must be inU, whereas other arguments can range over a larger set 7 of values such as the set of all Plutus Core terms. Type variables inV # are used in the former situation andV ∗ in the latter. Given a variable푣∈Vwe sometimes write 푣∶∶# if푣∈V # and 푣∶∶∗if푣∈V ∗ . 4.1.2 Polymorphic types We also need to talk about polymorphic types, and to do this we define an extended universe of polymor- phic typesU # by adjoiningV # toU 0 and closing under type operators as before: U #,0 =U 0 ∪V # U #,푖+1 =U #,푖 ∪ {op(T 1 ,...,T  op  ) ∶op∈O,T 1 ,...,T  표푝  ∈U #,푖 } U # = Õ {U #,푖 ∶푖∈ℕ + }. We will denote a typical element ofU # by the symbol푃(possibly subscripted). We define the set offree #-variablesof an element ofU # by 향햵 # (푃) = ∅if푃∈U 0 향햵 # (푣 # ) = {푣 # } 향햵 # (op(푃 1 ,...,푃 푘 )) =향햵 # (푃 1 ) ∪향햵 # (푃 2 ) ∪⋯∪향햵 # (푃 푟 ). Thus향햵 # (푃)⊆V # for all푃∈U. We say that a type name푃∈U # ismonomorphicif향햵 # (푃) = ∅(in which case we actually have푃∈U); otherwise푃ispolymorphic. The fact that type variables inU # are only allowed to come fromV # will ensure that values of polymorphic types such as lists and pairs can only contain values of built-in types: in particular, we will not be able to construct types representing things such as lists of Plutus Core terms. 4.1.3 Type assignments Atype assignmentis a function푆∶퐷→Uwhere퐷is some subset ofV # . As usual we say that퐷is the domainof푆and denote it bydom푆. We can extend a type assignment푆to a map ̂ 푆∶U # ⊎V ∗ →U # ⊎V ∗ by defining ̂ 푆(푣 # ) =푆(푣 # )if푣 # ∈ dom푆 ̂ 푆(푣 # ) =푣 # if푣 # ∈V # \\dom푆 ̂ 푆(푇) =푇if푇∈U 0 ̂ 푆(op(푃 1 ,...,푃 푛 )) =op( ̂ 푆(푃 1 ),..., ̂ 푆(푃 푛 )) ̂ 푆(푣 ∗ ) =푣 ∗ if푣 ∗ ∈V ∗ . If푃∈U # and푆is a type assignment with향햵 # (푃)⊆dom푆then in fact ̂ 푆(푃) ∈U; in this case we say that ̂ 푆(푃)is aninstanceor amonomorphisationof푃(via푆). If푇is an instance of푃then there is a unique smallest푆(with향햵 # (푃) = dom푆) such that푇= ̂ 푆(푃): we write푇⪯ 푆 푃to indicate that푇is an instance of푃via푆and푆is minimal. 8 Constructing type assignments.We say that a collection{푆 푖 ∶ 1≤푖≤푛}of type assignments is consistentif푆 푖  퐷 푖푗 =푆 푗  퐷 푖푗 for all푖and푗, wheredenotes function restriction and퐷 푖푗 = dom푆 푖 ∩ dom푆 푗 . If this is the case then (viewing functions as sets of pairs in the usual way)푆 1 ∪⋯∪푆 푛 is also a well-formed type assignment (each variable in its domain is associated with exactly one type). Given푇∈Uand푃∈U # it can be shown that푇⪯ 푆 푃if and only if one of the following holds: •푇=푃and푆= ∅. •푃∈V # and푆= {(푣 # ,푇)}. •–푇=op(푇 1 ,...,푇 푛 )with each푇 푖 ∈U. –푃=op(푃 1 ,...,푃 푛 )with each푃 푖 ∈U # . –푇 푖 ⪯ 푆 푖 푃 푖 for1≤푖≤푛. –{푆 1 ,...,푆 푛 }is consistent. –푆=푆 1 ∪⋯∪푆 푛 . This allows us to decide whether푇∈Uis an instance of푃∈U # and, if so, to construct an푆with 푇⪯ 푆 푃. 4.2 Built-in functions 4.2.1 Inputs to built-in functions To treat the typed and untyped versions of Plutus Core uniformly it is necessary to make the machinery of built-in functions generic over a setIofinputswhich are taken as arguments by built-in functions. In practiceIwill be the set of Plutus Core values or something very closely related. We requireIto have the following two properties: •Iis disjoint fromJTKfor allT∈U •There should be disjoint subsetsC T ⊆I(whereT∈U) ofconstants of type Tand mapsJ⋅K T ∶ C T →JTK(denotation) and⦃⋅⦄ T ∶JTK→C T (reification) such that⦃J푐K T ⦄ T =푐for all푐∈C T . We do not require these maps to be bijective (for example, there may be multiple inputs with the same denotation), but the condition implies thatJ⋅K T is surjective and⦃⋅⦄ T is injective. It is also convenient to letJIK=Iand define bothJ⋅K I and⦃⋅⦄ I to be the identity function. For example, we could takeIto be the set of all Plutus Core values (see Section 5.1),C T to be the set of all terms of the form(conT푐), andJ⋅K T to be the function which maps(conT푐)to푐. For simplicity we are assuming that mathematical entities occurring as members of type denotationsJTKare embedded directly as values푐in Plutus Core constant terms. In reality, tools which work with Plutus Core will need some concrete syntactic representation of constants; we do not specify this here, but see Section A.1 for suggested syntax for the built-in types included in the Alonzo release. 4.2.2 Signatures and denotations of built-in functions We will consistently use the symbol휏and subscripted versions of it to denote members ofU # ⊎V ∗ in the rest of the document; these indicate the types of values consumed and returned by built-in functions. We also define a class ofquantificationswhich are used to introduce type variables: a quantification is a symbol of the form∀푣with푣∈V; the set of of all possible quantifications is denoted byQ. 9 Signatures.Every built-in function푏∈Bhas asignature휎(푏)which describes the types of its argu- ments and its return value: a signature is of the form \[휄 1 ,...,휄 푛 \]→휏 with •휄 푗 ∈U # ⊎V ∗ ⊎Qfor all푗 •휏∈U # ⊎V ∗ •{푗∶휄 푗 ∉Q}≥1(so푛≥1) •If휄 푗 involves푣∈Vthen휄 푘 = ∀푣for some푘 < 푗, and similarly for휏; in other words, any type variable푣must be introduced by a quantification before it is used. (Here휄involves푣if either 휄=T∈U # and푣∈향햵 # (T)or휄=푣and푣∈V ∗ .) •If휏involves푣∈Vthen some휄 푗 must involve푣; this implies that향햵 # (휏)⊆ ⋃ {향햵 # (휄 푗 ) ∶휄 푗 ∈U # }. •If푗≠푘and휄 푗 ,휄 푘 ∈Qthen휄 푗 ≠휄 푘 ; ie, no quantification appears more than once. •If휄 푖 = ∀푣∈Qthen some푖 푗 ∉Qwith푗 > 푖must involve푣(signatures are not allowed to contain phantom type variables). For example, in our default set of built-in functions we have the functionsmkConswith signature\[∀푎 # ,푎 # , 횕횒횜횝(푎 # )\]→횕횒횜횝(푎 # )andifThenElsewith signature\[∀푎 ∗ ,횋횘횘횕횎횊횗,푎 ∗ ,푎 ∗ \]→푎 ∗ . When we use mkConsits arguments must be of built-in types, but the two final arguments ofifThenElsecan be any Plutus Core values. If푏has signature\[휄 1 ,...,휄 푛 \]→휏then we define thearityof푏to be 훼(푏) = \[휄 1 ,...,휄 푛 \]. We also define 휒(푏) =푛. We may abuse notation slightly by using the symbol휎to denote a specific signature as well as the function which maps built-in function names to signatures, and similarly with the symbol훼. Given a signature휎= \[휄 1 ,...,휄 푛 \]→휏, we define thereduced signaturē휎to be ̄휎= \[휄 푗 ∶휄 푗 ∉Q\]→휏 Here we have extended the usual set comprehension notation to lists in the obvious way, sō휎just denotes the signature휎with all quantifications omitted. We will often write a reduced signature in the form \[휏 1 ,...,휏 푚 \]→휏to emphasise that the entries aretypes, and∀does not appear. Also, given an arity= \[휄 1 ,...,휄 푛 \], thereduced arityis ̄훼= \[휄 푗 ∶휄 푗 ∉Q\]. 10 Commentary.What is the intended meaning of the notation introduced above? In Typed Plutus Core we have to instantiate polymorphic functions (both built-in functions and polymorphic lambda terms) at concrete types before they can be applied, and in Untyped Plutus Core instantiation is replaced by an application offorce. When we are applying a built-in function we supply its arguments one by one, and we can also applyforce(or perform type instantiation in the typed case) to a partially-applied builtin “between” arguments (and also after the final argument); no computation occurs until all arguments have been supplied and allforces have been applied. The arity (read from left to right) specifies what types of arguments are expected and how they should be interleaved with applications offorce, and휒(푏)tells you the total number of arguments and applications offorcethat a built-in function푏requires. A fully- polymorphic type variable푎 ∗ indicates that an arbitrary value fromIcan be provided, whereas a type from U # indicates that a value of the specified built-in type is expected. Occurrences of quantifications indicate thatforceis to be applied to a partially-applied builtin; we allow this purely so that partially-applied builtins can be treated in the same way as delayed lambda-abstractions:forcehas no effect unless it is the very last item in the signature. In Plutus Core, partially-applied builtins are values which can be treated like any others (for example, by being passed as an argument to alam-expression): see Section 5.1. 4.2.3 Denotations of built-in functions The basic idea is that a built-in function푏should represent some mathematical function on the denotations of the types of its inputs. However, this is complicated by the presence of polymorphism and we have to require that there is such a function for every possible monomorphisation of푏. More precisely, suppose that we have a builtin푏with reduced signature\[휏 1 ,...휏 푛 \]→휏. For every type assignment푆withdom푆=향햵 # (휏 1 ) ∪⋯∪향햵 # (휏 푛 )(which contains향햵 # (휏)by the conditions on signatures in Section 4.2.2) we require adenotation of푏at푆, a function J푏K 푆 ∶J ̂ 푆(휏 1 )K×⋯×J ̂ 푆(휏 푛 )K→J ̂ 푆(휏)K × . where J푣 ∗ K=Ifor푣 ∗ ∈V ∗ . This makes sense because ̂ 푆(휏 푖 ) ∈U⊎Ifor all푖, soJ ̂ 푆(휏 푖 )Kis always defined, and similarly for휏. If향햵 # (̄휎(푏)) = ∅(in which case we say that푏ismonomorphic) then the only relevant type assignment will be the empty one; in this case we have a single denotation J푏K ∅ ∶J휏 1 K×⋯×J휏 푛 K→J휏K × . Denotations of builtins are mathematical functions which terminate on every possible input; the symbol ×can be returned by a function to indicate that something has gone wrong, for example if an argument is out of range. In practice we expect most builtins to beparametrically polymorphic\[25, 22\], so that the denotationJ푏K 푆 will be the “same” for all type assignments푆; we do not insist on this though. 4.2.4 Results of built-in functions. If푟is the result of the evaluation of some built-in function there are thus three possibilities: 1.푟∈JTKfor someT∈U. 2.푟∈I. 3.푟=×. 11 In other words, 푟∈R∶= ‘ {JTK∶T∈U}⊎I⊎{×}. Our assumptions on the setI(Section 4.2.1) allow us define a function ⦃⋅⦄∶R→I × which converts results of built-in functions back into inputs (or the×symbol): 1. If푟∈JTK, then⦃푟⦄=⦃푟⦄ T ∈C T ⊆I. 2. If푟∈Ithen⦃푟⦄=푟. 3.⦃×⦄=×. 4.2.5 Parametricity for \*-polymorphic arguments A built-in function푏can only inspect arguments which are values of built-in types; other arguments (occurring as푎 ∗ in̄휎(푏)) are treated opaquely, and can be discarded or returned as (part of) a result, but cannot be altered or examined (in particular, they cannot be compared for equality):푏isparametrically polymorphicin such arguments. This implies that if a builtin returns a value푣∈I, then푣must have been an argument of the function. 4.3 Evaluation of built-in functions 4.3.1 Compatibility of inputs and signature entries The previous section describes how a built-in function is interpreted as a mathematical function. When a Plutus Core built-in function푏is applied to a sequence of arguments, the arguments must have types which are compatible with the signature of푏; for example, if푏has signature\[∀푎 # ,∀푏 # ,푎 # ,푏 # ,푎 # ,푐 ∗ ,푐 ∗ \]→푐 ∗ and푏is applied to a sequence of inputs푉 1 ,푉 2 ,푉 3 ,푉 4 ,푉 5 then푉 1 ,푉 2 , and푉 3 must all be constants of some monomorphic built-in types and the types of푉 1 and푉 3 must be the same;푉 4 and푉 5 can be arbitrary inputs. This section describes the conditions for type compatibility. In detail, given a reduced aritȳ훼= \[휏 1 ,...,휏 푛 \], a sequence ̄ 푉= \[푉 1 ,...,푉 푚 \], and a type assignment푆we say that ̄ 푉iscompatible with̄훼(via푆) if and only if푛=푚and, letting퐼= {푖∶ 1≤푖≤푛,휏 푖 ∈U # }(so 휏 푗 ∈V ∗ if푗∉퐼), there exist type assignments푆 푖 (1≤푖≤푛) such that all of the following are satisfied •For all푖∈퐼there exists푇 푖 ∈Usuch that푉 푖 ∈C 푇 푖 and푇 푖 ⪯ 푆 푖 휏 푖 . •{푆 푖 ∶푖∈퐼}is consistent (see Section 4.1.3). •푆= ⋃ {푆 푖 ∶푖∈퐼}. If these conditions are all satisfied then we can find suitable푆 푖 using the procedure described in Sec- tion 4.1.3 and this allows us to construct푆explicitly since the푆 푖 are consistent. Note that in this case dom푆= dom푆 1 ∪ ... ∪ dom푆 푛 =향햵 # (휏 1 ) ∪⋯∪향햵 # (휏 푛 ) =향햵 # (훼), so푆is minimal in the sense that no푆 ® withdom푆 ® strictly smaller thandom푆is sufficient to monomorphise all of the휏 푖 simultaneously. We write \[푉 1 ,...,푉 푚 \] ≈ 푆 \[휏 1 ,...,휏 푛 \] in this case. If ̄ 푉is not compatible with̄훼then we write ̄ 푉≉̄훼. 12 4.3.2 Evaluation For later use we define a function햤헏햺헅which attempts to evaluate an application of a built-in function푏 to a sequence of inputs\[푉 1 ,...,푉 푚 \]. This fails if the number of inputs is incorrect or if the inputs are not compatible with̄훼(푏): 햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \]) =×if\[푉 1 ,...,푉 푛 \] ≉̄훼(푏). Otherwise, the conditions for the existence of a denotation of푏are met and we can apply that denotation to the denotations of the inputs and then reify the result. If\[푉 1 ,...,푉 푛 \] ≈ 푆 ̄훼(푏) = \[휏 1 ,...,휏 푛 \], let푇 푖 = ̂ 푆(휏 푖 ) for1≤푖≤푛; then we define 햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \]) =⦃J푏K 푆 (J푉 1 K 푇 1 ,...,J푉 푛 K 푇 푛 )⦄. It can be checked that the compatibility condition guarantees that this makes sense according to the defi- nition ofJ푏K 푆 in Section 4.2.3. Notes. •All of the machinery which we have defined for built-in functions is parametric over the setIof inputs and the setsC 푇 ⊆Iof constants. This also applies to the햤헏햺헅function, so its meaning is not fully defined until we have given concrete definitions of the sets of inputs and constants. •The error value×can occur in two different ways: either because the arguments are not compatible with the signature, or because the builtin itself returns×to signal some error condition. •The symbol×is not part of Plutus Core; when we define reduction rules and evaluators for Plu- tus Core later some extra translation will be required to convert the result of햤헏햺헅into something appropriate to the context. 5 Term Reduction This section defines the semantics of (untyped) Plutus Core. 5.1 Values in Plutus Core The semantics of built-in functions in Plutus Core are obtained by instantiating the setsC T of constants of typeT(see Section 4.2.1) to be the expressions of the form(conT푐)and the setIto be the set of Plutus Corevalues, terms which cannot immediately undergo any further reduction, such as lambda terms and delayed terms. Values also include partial applications of built-in functions such as\[(builtin modInteger) (con integer 5)\], which cannot perform any computation until a second integer argu- ment is supplied. However, partial applications must also bewell-formed, in the sense that applications offorcemust be correctly interleaved with genuine arguments, and the arguments must themselves be values. We define syntactic classes푉of Plutus Core values and퐴of partial builtin applications simultane- ously: 13 Value푉∶∶=(conT푐) (delay푀) (lam푥 푀) 퐴 Figure 5: Values in Plutus Core Here퐴is the class of well-formed partial applications, and to define this we first define a class of possibly ill-formed iterated applications퐵for each built-in function푏∈B: 퐵∶∶=(builtin푏) \[퐵 푉\] (force퐵) Figure 6: Partial built-in function application We let햡denote the set of terms generated by the grammar in Figure 6 and we define a function훽which extracts the name of the built-in function occurring in a term in햡: 훽((builtin푏)) =푏 훽(\[퐵 푉\])=훽(퐵) 훽((force퐵)) =훽(퐵) We also define a function‖⋅‖which measures the size of a term퐵∈햡: ‖(builtin푏)‖= 0 ‖\[퐵 푉\]‖= 1 +‖퐵‖ ‖(force퐵)‖= 1 +‖퐵‖ Well-formed partial applications.A term퐵∈햡is an application of푏=훽(퐵)to a number of values in푆, interleaved with applications offorce. We now define what it means for퐵to be awell-formed partial application. Suppose that훼(푏) = \[휄 1 ,...,휄 푛 \]. Firstly we require that‖퐵‖< 푛, so that푏is not fully applied; in this case we put휄=휄 ‖퐵‖ , the element of푏’s signature which describes what kind of “argument” 푏currently expects. The definition is completed by induction on the structure of퐵: 1.퐵= (횋횞횒횕횝횒횗푏)is always well-formed. 2.퐵= \[퐵 ® 푉\]is well-formed if퐵 ® is well-formed and휄∈U # or휄∈V ∗ (equivalently,휄∉Q). 3.퐵= (횏횘횛회횎퐵 ® )is well-formed if퐵 ® is well-formed and휄∈Q. The definition of values in Figure 5 is now completed by defining퐴to be the syntactic class of well-formed partialbuilt-in function applications: 퐴= {퐵∈햡∶퐵is a well-formed partial application}. Note that this definition does not impose any requirements of type correctness. For example, with the types and functions defined in Appendix A the term푋=\[(builtin modInteger) (con string "blue")\] 14 is a valid value which could be treated like any other, for instance by being passed as an argument to a lamexpression. However, the evaluation rules described in the next section require that when a built-in function푏becomesfullyapplied the types of the arguments are checked against the signature of푏using the relation≈and the function햤헏햺헅defined in Sections 4.3.1 and 4.3.2, so an error would arise if the term 푋were ever applied to another argument. More notation.Suppose that퐴is a well-formed partial application with훼(훽(퐴)) = \[휄 1 ,...,휄 푛 \]. We define a function헇햾헑헍which extracts the next argument (orforce) expected by퐴: 헇햾헑헍(퐴) =휄 ‖퐴‖+1 . This makes sense because in a well-formed partial application퐴we have‖퐴‖< 푛. We also define a function햺헋헀헌which extracts the arguments which푏has received so far in퐴: 햺헋헀헌((builtin푏)) = \[\] 햺헋헀헌(\[퐴 푉\])=햺헋헀헌(퐴)⋅푉 햺헋헀헌((force퐴)) =햺헋헀헌(퐴). 5.2 Term reduction We define the semantics of Plutus Core using contextual semantics (or reduction semantics): see \[15\] or \[13\] or \[16, 5.3\], for example. We use퐴to denote a partial application of a built-in function as in Section 5.1 above. For builtin evaluation, we instantiate the setIof Section 4.2.1 to be the set of Plutus Core values. Thus all builtins take values as arguments and return a value or×. Since values are terms here, we can take⦃푉⦄=푉. The notation\[푉∕푥\]푀below denotes substitution of the value푉for the variable푥in푀. This iscapture- avoidingin that substitution is not performed on occurrences of푥inside subterms of푀of the form (lam푥 푁). 15 Frame푓∶∶=\[\_푀\]left application \[푉\_\]right application (force\_)force (a) Grammar of reduction frames for Plutus Core 푀→푀 ® Term푀reduces in one step to term푀 ® . \[(lam푥 푀)푉\]→\[푉∕푥\]푀 퓁(퐴) =휒(훽(퐴)) − 1헇햾헑헍(퐴) ∈U # ∪V ∗ \[퐴 푉\]→ 햤헏햺헅 ® (훽(퐴),햺헋헀헌(퐴)⋅푉) 퓁(퐴)< 휒(훽(퐴)) − 1헇햾헑헍(퐴) ∈U # ∪V ∗ \[퐴 푉\]→\[퐴 푉\] (force (delay푀))→푀 퓁(퐴) =휒(훽(퐴)) − 1헇햾헑헍(퐴) ∈Q (force퐴)→ 햤헏햺헅 ® (훽(퐴),햺헋헀헌(퐴)) 퓁(퐴)< 휒(훽(퐴)) − 1헇햾헑헍(퐴) ∈Q (force퐴)→퐴 푓{(error)}→(error) 푀→푀 ® 푓{푀}→푓{푀 ® } (b) Reduction via Contextual Semantics 햤헏햺헅 ® (푏,\[푉 1 ,...,푉 푛 \]) = T (error)if햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \]) =× 햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \])otherwise (c) Built-in function application Figure 7: Term reduction for Plutus Core It can be shown that any closed Plutus Core term whose evaluation terminates yields either(error)or a value. Recall from Section 3.3 that we require the body of every Plutus Core program to be closed. 16 6 The CEK Machine This section contains a description of an abstract machine for efficiently executing Plutus Core. This is based on the CEK machine of Felleisen and Friedman \[14\]. The machine alternates between two main phases: thecomputephase (⊳), where it recurses down the AST looking for values, saving surrounding contexts as frames (orreduction contexts) on a stack as it goes; and thereturnphase (⊲), where it has obtained a value and pops a frame off the stack to tell it how to proceed next. In addition there is an error state⬥which halts execution with an error, and a halting state◻which halts execution and returns a value to the outside world. To evaluate a program(program푣푀), we first check that the version number푣is valid, then start the machine in the state\[\];\[\]⊳푀. It can be proved that the transitions in Figure 10 always preserve validity of states, so that the machine can never enter a state such as\[\]⊲푀or푠,(force \_)⊲(lam푥 퐴 푀)which isn’t covered by the rules. If such a situation were to occur in an implementation then it would indicate that the machine was incorrectly implemented or that it was attempting to evaluate an ill-formed program (for example, one which attempts to apply a variable to some other term). StateΣ ∶∶=푠;휌 ⊳ 푀푠 ⊲ 푉⬥◻푉 Stack푠∶∶=푓 ∗ CEK value푉∶∶=〈conT푐〉〈delay푀 휌〉〈lam푥 푀 휌〉〈builtin푏 푉 휂〉 Environment휌∶∶= \[\]휌\[푥↦푉\] Expected builtin arguments휂∶∶= \[휄\]휄⋅휂 Figure 8: Grammar of CEK machine states for Plutus Core Frame푓∶∶=(force\_)force \[\_(푀,휌)\]left application \[푉\_\]right application Figure 9: Grammar of CEK stack frames Figures 8 and 9 define some notation forstatesof the CEK machine: these involve a modified type of value adapted to the CEK machine, environments which bind names to values, and a stack which stores partially evaluated terms whose evaluation cannot proceed until some more computation has been performed (for example, since Plutus Core is a strict language function arguments have to be reduced to values before application takes place, and because of this a lambda term may have to be stored on the stack while its argument is being reduced to a value). Environments are lists of the form휌= \[푥 1 ↦푉 1 ,...,푥 푛 ↦푉 푛 \] which grow by having new entries appended on the right; we say that푥is bound in the environment휌if휌 contains an entry of the form푥↦푉, and in that case we denote by휌\[푥\]the value푉in the rightmost (ie, most recent) such entry. ∗ ∗ The description of environments we use here is more general than necessary in that it permits a given variable to have multiple bindings; however, in what follows we never actually retrieve bindings other than the most recent one and we never remove bindings to expose earlier ones. The list-based definition has the merit of simplicity and suffices for specification purposes but in an imple- mentation it would be safe to use some data structure where existing bindings of a given variable are discarded when a new binding is added. 17 To make the CEK machine fit into the built-in evaluation mechanism defined in Section 4 we define I=푉andC T = {〈conT푐〉∶T∈U,푐∈JTK}. The rules in Figure 10 show the transitions of the machine; if any situation arises which is not included in these transitions (for example, if a frame\[〈conT푐〉\_\]is encountered or if an attempt is made to applyforceto a partial builtin application which is expecting a term argument), then the machine stops immediately in an error state. Σ↦Σ ® Machine takes one step from stateΣto stateΣ ® 푠;휌 ⊳ 푥↦푠 ⊲ 휌\[푥\]if푥is bound in휌 푠;휌 ⊳(conT푐)↦푠 ⊲〈conT푐〉 푠;휌 ⊳(lam푥 푀)↦푠 ⊲〈lam푥 푀 휌〉 푠;휌 ⊳(delay푀)↦푠 ⊲〈delay푀 휌〉 푠;휌 ⊳(force푀)↦(force\_)⋅푠;휌 ⊳ 푀 푠;휌 ⊳\[푀 푁\]↦\[\_(푁,휌)\]⋅푠;휌 ⊳ 푀 푠;휌 ⊳(builtin푏)↦푠 ⊲〈builtin푏\[\]훼(푏)〉 푠;휌 ⊳(error)↦⬥ \[\]⊲ 푉↦◻푉 \[\_(푀,휌)\]⋅푠 ⊲ 푉↦\[푉\_\]⋅푠;휌 ⊳ 푀 \[〈lam푥 푀 휌〉\_\]⋅푠 ⊲ 푉↦푠;휌\[푥↦푉\]⊳ 푀 (force\_)⋅푠 ⊲〈delay푀 휌〉↦푠;휌 ⊳ 푀 (force\_)⋅푠 ⊲〈builtin푏 푉(휄⋅휂)〉↦푠 ⊲〈builtin푏푉 휂〉if휄∈Q (force\_)⋅푠 ⊲〈builtin푏 푉\[휄\]〉↦ 햤헏햺헅 햢햤햪 (푠,푏,푉)if휄∈Q \[〈builtin푏 푉(휄⋅휂)〉\_\]⋅푠 ⊲ 푉↦푠 ⊲〈builtin푏(푉⋅푉)휂〉if휄∈U # ∪V ∗ \[〈builtin푏 푉\[휄\]〉\_\]⋅푠 ⊲ 푉↦ 햤헏햺헅 햢햤햪 (푠,푏,푉⋅푉)if휄∈U # ∪V ∗ (a) CEK machine transitions for Plutus Core 햤헏햺헅 햢햤햪 (푠,푏,\[푉 1 ,...,푉 푛 \]) = T ⬥if햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \]) =× 푠 ⊲햤헏햺헅(푏,\[푉 1 ,...,푉 푛 \])otherwise (b) Evaluation of built-in functions Figure 10: A CEK machine for Plutus Core 6.1 Converting CEK evaluation results into Plutus Core terms The purpose of the CEK machine is to evaluate Plutus Core terms, but in the definition in Figure 10 it does not return a Plutus Core term; instead the machine can halt in two different ways: •The machine can halt in the state◻푉for some CEK value푉. •The machine can halt in the state⬥. 18 To get a complete evaluation strategy for Plutus Core we must convert these states into Plutus Core terms. The term corresponding to⬥is(error), and to obtain a term from◻푉we perform a process which we refer to asdischargingthe CEK value푉(also known asunloading: see \[21, pp. 129–130\], \[12, pp. 71ff\]). This process substitutes bindings in environments for variables occurring in the value푉to obtain a termU(푉): see Figure 11a. Since environments contain bindings푥↦푊of variables to further CEK values, we have to recursively discharge those bindings first before substituting: see Figure 11b, which defines an operation@휌which does this. As before\[푁∕푥\]푀denotes the usual (capture-avoiding) process of substituting the term푁for all unbound occurrences of the variable푥in the term푀. Note that in Figure 11b we substitute the rightmost (ie, the most recent) bindings in the environment first. U(〈conT푐〉) =(conT푐) U(〈delay푀 휌〉) =(delay푀)@휌 U(〈lam푥 푀 휌〉) =(lam푥 푀)@휌 U(〈builtin푏 푉 1 푉 2 ...푉 푘 휂〉) =\[...\[\[(builtin푏)(U(푉 1 ))\](U(푉 2 ))\]...(U(푉 푘 ))\] (a) Discharging CEK values 푀@휌= \[(U(푉 1 ))∕푥 1 \]⋯\[(U(푉 푛 ))∕푥 푛 \]푀if휌= \[푥 1 ↦푉 1 ,...,푥 푛 ↦푉 푛 \] (b) Iterated substitution/discharging Figure 11: Discharging CEK values to obtain Plutus Core terms We can prove that if we evaluate a closed Plutus Core term in the CEK machine and then convert the result back to a term using the above procedure then we get the result that we should get according to the semantics in Figure 7. 7 Typed Plutus Core To follow. 19 Appendix A Built-in Types and Functions Supported in the Alonzo Release A.1 Built-in types and type operators The Alonzo release of the Cardano blockchain (September 2021) supports a default set of built-in types and type operators defined in Tables 1 and 2. We also include concrete syntax for these; the concrete syntax is not strictly part of the language, but may be useful for tools working with Plutus Core. TypeDenotationConcrete Syntax integerℤ-?\[0-9\]\* bytestring픹 ∗ , the set of sequences of bytes or 8-bit characters. #(\[0-9A-Fa-f\]\[0-9A-Fa-f\])\* string핌 ∗ , the set of sequences of Unicode char- acters. See note below. bool{true, false}True | False unit{()}() dataSee below.Not yet supported. Table 1: Atomic Types Operatorop  op  DenotationConcrete Syntax list1J횕횒횜횝(푡)K=J푡K ∗ Not yet supported pair2J횙횊횒횛(푡 1 ,푡 2 )K=J푡 1 K×J푡 2 KNot yet supported Table 2: Type Operators Concrete syntax for strings.Strings are represented as sequences of Unicode characters enclosed in double quotes, and may include standard escape sequences. Concrete syntax for higher-order types.Types such as횕횒횜횝(횒횗횝횎횐횎횛)and횙횊횒횛(횋횘횘횕,횜횝횛횒횗횐))are represented by application at the type level, thus:\[(con list) (con integer)\]and\[(con pair) (con bool) (con string)\]. Each higher-order type will need further syntax for representing con- stants of those types. For example, we might use\[\]for list values and(,)for pairs, so the list\[11,22,33\] might be written as (con \[(con list) (con integer)\] \[(con integer 11), (con integer 22), (con integer 33)\] ) and the pair (True, "Plutus") as (con \[(con pair) (con bool) (con string)\] ((con bool True), (con string "Plutus")) ). Note however that this syntax is not currently supported by most Plutus Core tools at the time of writing. 20 The획횊횝횊type.We provide a built-in type획횊횝횊which permits the encoding of simple data structures for use as arguments to Plutus Core scripts. This type is defined in Haskell as data Data = Constr Integer \[Data\] | Map \[(Data, Data)\] | List \[Data\] | I Integer | B ByteString In set-theoretic terms the denotation of획횊횝횊is defined to be the least fixed point of the endofunctor퐹on the category of sets given by퐹(푋) = (J횒횗횝횎횐횎횛K×푋 ∗ )⊎(푋×푋) ∗ ⊎푋 ∗ ⊎J횒횗횝횎횐횎횛K⊎J횋횢횝횎횜횝횛횒횗횐K, so that J획횊횝횊K= (J횒횗횝횎횐횎횛K×J획횊횝횊K ∗ )⊎(J획횊횝횊K×J획횊횝횊K) ∗ ⊎J획횊횝횊K ∗ ⊎J횒횗횝횎횐횎횛K⊎J횋횢횝횎횜횝횛횒횗횐K. We have injections inj 퐶 ∶J횒횗횝횎횐횎횛K×J획횊횝횊K ∗ →J획횊횝횊K inj 푀 ∶J획횊횝횊K×J획횊횝횊K ∗ →J획횊횝횊K inj 퐿 ∶J획횊횝횊K ∗ →J획횊횝횊K inj 퐼 ∶J횒횗횝횎횐횎횛K→J획횊횝횊K 푖푛푗 퐵 ∶J횋횢횝횎횜횝횛횒횗횐K→J획횊횝횊K and projections proj 퐶 ∶J획횊횝횊K→(J횒횗횝횎횐횎횛K×J획횊횝횊K ∗ ) × proj 푀 ∶J획횊횝횊K→(J획횊횝횊K×J획횊횝횊K ∗ ) × proj 퐿 ∶J획횊횝횊K→J획횊횝횊K ∗ × proj 퐼 ∶J획횊횝횊K→J횒횗횝횎횐횎횛K × proj 퐵 ∶J획횊횝횊K→J횋횢횝횎횜횝횛횒횗횐K × which extract an object of the relevant type from a획횊횝횊object퐷, returning×if퐷does not lie in the expected component of the disjoint union; also there are functions is 퐶 ,is 푀 ,is 퐿 ,is 퐼 ,is 퐵 ∶J획횊횝횊K→J횋횘횘횕K which determine whether a획횊횝횊value lies in the relevant component. Note:Constrtag values.TheConstrconstructor of thedatatype is intended to represent values from algebraic data types (also known as sum types and discriminated unions, among other things;dataitself is an example of such a type), where홲횘횗횜횝횛푖\[푑 1 ,...,푑 푛 \]represents a tuple of data items together with a tag푖indicating which of a number of alternatives the data belongs to. The definition above allows tags to be any integer value, but because of restrictions in the serialisation format fordata(see Section D.7) we recommend that in practiceonly tags푖with0≤푖≤2 64 − 1should be used: deserialisation will fail for dataitems (and programs which include such items) involving tags outside this range. 21 A.2 Alonzo built-in functions The default set of built-in functions for the Alonzo release is shown in Table 3. The table indicates which functions can fail during execution, and conditions causing failure are specified either in the denotation given in the table or in a relevant note. Recall also that a built-in function will fail if it is given an argument of the wrong type: this is checked in conditions involving the∼relation and the햤헏햺헅function in Figures 7 and 10. Note also the some of the functions are #-polymorphic. According to Section 4.2.3 we require a denotation for every possible monomorphisation of these; however all of these functions are parametrically polymorphic so to simplify notation we have given a single denotation for each of them with an implicit assumption that it applies at each possible monomorphisation in an obvious way. FunctionSignatureDenotationCanNote Fail? addInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛+ subtractInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛− multiplyInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛× divideInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛divYes1 modInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛modYes1 quotientInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛quotYes1 remainderInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횒횗횝횎횐횎횛remYes1 equalsInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횋횘횘횕= lessThanInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횋횘횘횕< lessThanEqualsInteger\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛\]→횋횘횘횕≤ appendByteString\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐\] →횋횢횝횎횜횝횛횒횗횐 (\[푐 1 ,...,푐 푚 \],\[푑 1 ,...,푑 푛 \]) ↦\[푐 1 ,...,푐 푚 ,푑 1 ,...,푑 푛 \] consByteString\[횒횗횝횎횐횎횛,횋횢횝횎횜횝횛횒횗횐\] →횋횢횝횎횜횝횛횒횗횐 (푐,\[푐 1 ,...,푐 푛 \]) ↦\[mod(푐,256),푐 1 ,...,푐 푛 \] sliceByteString\[횒횗횝횎횐횎횛,횒횗횝횎횐횎횛,횋횢횝횎횜횝횛횒횗횐\] →횋횢횝횎횜횝횛횒횗횐 (푠,푘,\[푐 0 ,...,푐 푛 \]) ↦\[푐 max(푠,0) ,...,푐 min(푠+푘−1,푛−1) \] 2 lengthOfByteString\[횋횢횝횎횜횝횛횒횗횐\]→횒횗횝횎횐횎횛\[\]↦0,\[푐 1 ,...,푐 푛 \]↦푛 indexByteString\[횋횢횝횎횜횝횛횒횗횐,횒횗횝횎횐횎횛\] →횒횗횝횎횐횎횛 (\[푐 0 ,...,푐 푛−1 \],푗) ↦ T 푐 푖 if0≤푗≤푛− 1 ×otherwise Yes equalsByteString\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐\] →횋횘횘횕 =3 lessThanByteString\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐\] →횋횘횘횕 <3 lessThanEqualsByteString\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐\] →횋횘횘횕 ≤3 appendString\[횜횝횛횒횗횐,횜횝횛횒횗횐\]→횜횝횛횒횗횐(\[푢 1 ,...,푢 푚 \],\[푣 1 ,...,푣 푛 \]) ↦\[푢 1 ,...,푢 푚 ,푣 1 ,...,푣 푛 \] equalsString\[횜횝횛횒횗횐,횜횝횛횒횗횐\]→횋횘횘횕= encodeUtf8\[횜횝횛횒횗횐\]→횋횢횝횎횜횝횛횒횗횐헎헍햿 ퟪ4 decodeUtf8\[횋횢횝횎횜횝횛횒횗횐\]→횜횝횛횒횗횐헎헍햿 ퟪ −1 Yes4 sha2\_256\[횋횢횝횎횜횝횛횒횗횐\]→횋횢횝횎횜횝횛횒횗횐Hash a횋횢횝횎횜횝횛횒횗횐usingSHA256. sha3\_256\[횋횢횝횎횜횝횛횒횗횐\]→횋횢횝횎횜횝횛횒횗횐Hash a횋횢횝횎횜횝횛횒횗횐usingSHA3- 256. blake2b\_256\[횋횢횝횎횜횝횛횒횗횐\]→횋횢횝횎횜횝횛횒횗횐Hash a횋횢횝횎횜횝횛횒횗횐using Blake2B256. Table 3: Built-in Functions 22 FunctionTypeDenotationCanNote Fail? verifyEd25519Signature\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐, 횋횢횝횎횜횝횛횒횗횐\]→횋횘횘횕 Verify anEd25519digital signa- ture. Yes5, 6 ifThenElse\[∀푎 ∗ ,횋횘횘횕,푎 ∗ ,푎 ∗ \]→푎 ∗ (횝횛횞횎,푡 1 ,푡 2 )↦푡 1 (횏횊횕횜횎,푡 1 ,푡 2 )↦푡 2 chooseUnit\[∀푎 ∗ ,횞횗횒횝,푎 ∗ \]→푎 ∗ ((),푡)↦푡 trace\[∀푎 ∗ ,횜횝횛횒횗횐,푎 ∗ \]→푎 ∗ (푠,푡)↦푡7 fstPair\[∀푎 # ,∀푏 # ,횙횊횒횛(푎 # ,푏 # )\]→푎 # (푥,푦)↦푥 sndPair\[∀푎 # ,∀푏 # ,횙횊횒횛(푎 # ,푏 # )\]→푏 # (푥,푦)↦푦 chooseList\[∀푎 # ,∀푏 ∗ ,횕횒횜횝(푎 # ),푏 ∗ ,푏 ∗ \]→푏 ∗ (\[\],푡 1 ,푡 2 )↦푡 1 , (\[푥 1 ,...,푥 푛 \],푡 1 ,푡 2 )↦푡 2 (푛≥1). mkCons\[∀푎 # ,푎 # ,횕횒횜횝(푎 # )\]→횕횒횜횝(푎 # )(푥,\[푥 1 ,...,푥 푛 \])↦\[푥,푥 1 ,...,푥 푛 \] headList\[∀푎 # ,횕횒횜횝(푎 # )\]→푎 # \[\]↦×,\[푥 1 ,푥 2 ,...,푥 푛 \]↦푥 1 Yes tailList\[∀푎 # ,횕횒횜횝(푎 # )\]→횕횒횜횝(푎 # )\[\]↦×, \[푥 1 ,푥 2 ,...,푥 푛 \]↦\[푥 2 ,...,푥 푛 \] Yes nullList\[∀푎 # ,횕횒횜횝(푎 # )\]→횋횘횘횕\[\]↦true,\[푥 1 ,...,푥 푛 \]↦false chooseData\[∀푎 ∗ ,획횊횝횊,푎 ∗ ,푎 ∗ ,푎 ∗ ,푎 ∗ ,푎 ∗ \]→푎 ∗ (푑,푡 퐶 ,푡 푀 ,푡 퐿 ,푡 퐼 ,푡 퐵 ) ↦ ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ 푡 퐶 ifis 퐶 (푑) 푡 푀 ifis 푀 (푑) 푡 퐿 ifis 퐿 (푑) 푡 퐼 ifis 퐼 (푑) 푡 퐵 ifis 퐵 (푑) constrData\[횒횗횝횎횐횎횛,횕횒횜횝(획횊횝횊)\]→획횊횝횊inj 퐶 mapData\[횕횒횜횝(횙횊횒횛(획횊횝횊,획횊횝횊)) →획횊횝횊 inj 푀 listData\[횕횒횜횝(획횊횝횊)\]→획횊횝횊inj 퐿 iData\[횒횗횝횎횐횎횛\]→획횊횝횊inj 퐼 bData\[횋횢횝횎횜횝횛횒횗횐\]→획횊횝횊inj 퐵 unConstrData\[획횊횝횊\] →횙횊횒횛(횒횗횝횎횐횎횛,횕횒횜횝(획횊횝횊)) proj 퐶 Yes unMapData\[획횊횝횊\] →횕횒횜횝(횙횊횒횛(획횊횝횊,획횊횝횊)) proj 푀 Yes unListData\[획횊횝횊\]→횕횒횜횝(획횊횝횊)proj 퐿 Yes unIData\[획횊횝횊\]→횒횗횝횎횐횎횛proj 퐼 Yes unBData\[획횊횝횊\]→횋횢횝횎횜횝횛횒횗횐proj 퐵 Yes equalsData\[획횊횝횊,획횊횝횊\]→횋횘횘횕= mkPairData\[획횊횝횊,획횊횝횊\] →횙횊횒횛(획횊횝횊,획횊횝횊) (푥,푦)↦(푥,푦) mkNilData\[횞횗횒횝\]→횕횒횜횝(획횊횝횊)()↦\[\] mkNilPairData\[횞횗횒횝\] →횕횒횜횝(횙횊횒횛(획횊횝횊,획횊횝횊)) ()↦\[\] Table 3: Built-in Functions (continued) Note 1. Integer division functions.We provide four integer division functions:divideInteger, modInteger,quotientInteger, andremainderInteger, whose denotations are mathematical func- tionsdiv,mod,quot, andremwhich are modelled on the corresponding Haskell operations. Each of these 23 takes two arguments and will fail (returning×) if the second one is zero. For all푎,푏∈ℤwith푏≠0we have div(푎,푏) ×푏+ mod(푎,푏) =푎 mod(푎,푏)<푏 and quot(푎,푏) ×푏+ rem(푎,푏) =푎 rem(푎,푏)<푏. Thedivandmodfunctions form a pair, as doquotandrem;divshould not be used in combination with mod, not shouldquotbe used withmod. For positive divisors푏,divtruncates downwards andmodalways returns a non-negative result (0≤ mod(푎,푏)≤푏−1). Thequotfunction truncates towards zero. Table 4 shows how the signs of the outputs of the division functions depend on the signs of the inputs;+means≥0and−means≤0, but recall that for푏= 0all of these functions return the error value×. a bdiv modquot rem + ++ ++ + − +− +− − + −− ++ + − −+ −+ − Table 4: Behaviour of integer division functions Note 2. ThesliceByteStringfunction.The application\[\[(builtin sliceByteString) (con integer푠)\] (con integer푘)\] (con bytestring푏)\]returns the substring of푏of length푘start- ing at position푠; indexing is zero-based, so a call with푠= 0returns a substring starting with the first element of푏,푠= 1returns a substring starting with the second, and so on. This function always succeeds, even if the arguments are out of range: if푏= \[푐 0 ,...,푐 푛−1 \]then the application above returns the substring \[푐 푖 ,...,푐 푗 \]where푖= max(푠,0)and푗= min(푠+푘− 1,푛− 1); if푗 < 푖then the empty string is returned. Note 3. Comparisons of bytestrings.Bytestrings are ordered lexicographically in the usual way. If we have푎= \[푎 1 ,...,푎 푚 \]and푏= \[푏 1 ,...,푏 푛 \]then (recalling that if푚= 0then푎= \[\], and similarly for푏), •푎=푏if and only if푚=푛and푎 푖 =푏 푖 for1≤푖≤푚. •푎≤푏if and only if one of the following holds: –푎= \[\] –푚,푛 >0and푎 1 < 푏 1 –푚,푛 >0and푎 1 =푏 1 and\[푎 2 ,...,푎 푚 \]≤\[푏 2 ,...,푏 푛 \]. •푎 < 푏if and only if푎≤푏and푎≠푏. The empty bytestring is equal only to itself and is strictly less than all other bytestrings. Note 4. Encoding and decoding bytestrings.TheencodeUtf8anddecodeUtf8functions convert between the횜횝횛횒횗횐type and the횋횢횝횎횜횝횛횒횗횐type. We have definedJ횜횝횛횒횗횐Kto consist of sequences 24 of Unicode characters without specifying any particular character representation, whereasJ횋횢횝횎횜횝횛횒횗횐K consists of sequences of 8-bit bytes. We define the denotation ofencodeUtf8to be the function 헎헍햿 ퟪ∶핌 ∗ →픹 ∗ which converts sequences of Unicode characters to sequences of bytes using the well-known UTF-8 char- acter encoding \[24, Definition D92\]. The denotation ofdecodeUtf8is the partial inverse function 헎헍햿 ퟪ −1 ∶픹 ∗ →핌 ∗ × . UTF-8 encodes Unicode characters encoded using between one and four bytes: thus in general neither function will preserve the length of an object. Moreover, not all sequences of bytes are valid represen- tations of Unicode characters, anddecodeUtf8will fail if it receives an invalid input (butencodeUtf8 will always succeed). Note 5. Digital signature verification functions.We use a uniform interface for digital signature verification algorithms. A digital signature verification function takes three bytestring arguments (in the given order): •a public keyvk(in this contextvkis also known as averification key) •a message푚 •a signature푠. A signature verification function may require one or more arguments to be well-formed in some sense (in particular an argument may need to be of a specified length), and in this case the function will fail (returning×) if any argument is malformed. If all of the arguments are well-formed then the verification function returnstrueif the private key corresponding tovkwas used to sign the message푚to produce푠, otherwise it returnsfalse. Note 6. Ed25519 signature verification.TheverifyEd25519Signaturefunction † performs crypto- graphic signature verification using the Ed25519 scheme \[5, 18\], and conforms to the interface described in Note 5. The arguments must have the following sizes: •vk: 32 bytes •푚: unrestricted •푠: 64 bytes. Note 7. Thetracefunction.An application\[(builtin trace)푠 푣\](푠astring,푣any Plutus Core value) returns푣. We do not specify the semantics any further. An implementation may choose to discard 푠or to perform some side-effect such as writing it to a terminal or log file. A.3 Cost accounting for built-in functions To follow. † verifyEd25519Signaturewas formerly calledverifySignaturebut was renamed to avoid ambiguity when further sig- nature verification functions were introduced in the Vasil release (see Section B.2). 25 Appendix B Built-in Types and Functions Supported in the Vasil Release The Vasil release of Cardano (June 2022) extends the set of built-in functions slightly. B.1 Built-in types and type operators The built-in types and type operators remain unchanged from the Alonzo release (Appendix A.1). B.2 Built-in functions The Vasil release continues to support the Alonzo built-in functions (Table 3) and adds three new ones: these are described in Table 5. FunctionSignatureDenotationCanNote Fail? serialiseData\[획횊횝횊\]→횋횢횝횎횜횝횛횒횗횐E 획횊횝횊 1 verifyEcdsaSecp256k1Signature\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐, 횋횢횝횎횜횝횛횒횗횐\]→횋횘횘횕 Verify an SECP-256k1 ECDSA signature Yes2 verifySchnorrSecp256k1Signature\[횋횢횝횎횜횝횛횒횗횐,횋횢횝횎횜횝횛횒횗횐, 횋횢횝횎횜횝횛횒횗횐\]→횋횘횘횕 Verify an SECP-256k1 Schnorr signature Yes3 Table 5: Built-in Functions Note 1. Serialising획횊횝횊objects.TheserialiseDatafunction takes a획횊횝횊object and converts it into a bytestring using a CBOR encoding. A full specification of the encoding (including the definition of E 획횊횝횊 ) is provided in Appendix D. Note 2. Secp256k1 ECDSA Signature verification.TheverifyEcdsaSecp256k1Signaturefunc- tion performs elliptic curve digital signature verification \[1, 2, 17\] over thesecp256k1curve \[9, §2.4.1\] and conforms to the interface described in Note 5 of Section A.2. The arguments must have the following sizes: •vk: 33 bytes •푚: 32 bytes •푠: 64 bytes. The public keyvkis expected to be in the 33-byte compressed form described in \[6\]. Moreover, the ECDSA scheme admits two distinct valid signatures for a given message and private key, and we follow the restriction imposed by Bitcoin (see \[20\],LOW\_S) andonly accept the smaller signature;verifyEcdsa- Secp256k1Signaturewill returnfalseif the larger one is supplied. Note 3. Secp256k1 Schnorr Signature verification.TheverifySchnorrSecp256k1Signature function performs verification of Schnorr signatures \[23, 19\] over thesecp256k1curve and conforms to the interface described in Note 5 of Section A.2. The arguments are expected to be of the forms specified in BIP-340 \[19\] and thus should have the following sizes: •vk: 32 bytes 26 •푚: unrestricted •푠: 64 bytes. Appendix C Formally Verified Behaviours To follow. Appendix D SerialisingdataObjects Using the CBOR Format D.1 Introduction In this section we define a CBOR encoding for thedatatype introduced in Section A.1. For ease of reference we reproduce the definition of the HaskellDatatype, which we may regard as the definition of the Plutusdatatype. Other representations are of course possible, but this is useful for the present discussion. data Data = Constr Integer \[Data\] | Map \[(Data, Data)\] | List \[Data\] | I Integer | B ByteString The CBOR encoding defined here uses basic CBOR encodings as defined in the CBOR standard \[8\], but with some refinements. Specifically •We use a restricted encoding for bytestrings which requires that bytestrings are serialised as se- quences of blocks, each block being at most 64 bytes long. Any encoding of a bytestring using our scheme is valid according to the CBOR specification, but the CBOR specification permits some encodings which we do not accept. The purpose of the size restriction is to prevent arbitrary data from bring stored on the blockchain. •Large integers (less than−2 64 or greater than2 64 − 1) are encoded via the restricted bytestring encoding; other integers are encoded as normal. Again, our restricted encodings are compatible with the CBOR specification. •TheConstrcase of thedatatype is encoded using a scheme which is an early version of a proposed extension of the CBOR specification to include encodings for discriminated unions. See \[10\] and \[7, Section 9.1\]. D.2 Notation We introduce some extra notation for use here and in Appendix E. The notation푓∶푋⇀푌indicates that푓is a partial map from푋to푌. We denote the empty bytestring by휖and (as in 2.2) use퓁(푠)to denote the length of a bytestring푠and⋅to denote the concatenation of two bytestrings, and also the operation of prepending or appending a byte to a bytestring. We will also make use of thedivandmodfunctions described in Note 1 in Appendix A. 27 Encoders and decoders.Recall that픹=ℕ \[0,255\] , the set of integral values that can be represented in a single byte, and that we identify bytestrings with elements of픹 ∗ . We will describe the CBOR encoding of thedatatype by defining families of encoding functions (orencoders) E 푋 ∶푋→픹 ∗ and decoding functions (ordecoders) D 푋 ∶픹 ∗ ⇀픹 ∗ ×푋 for various sets푋, such as the setℤof integers and the set of alldataitems. The encoding functionE 푋 takes an element푥∈푋and converts it to a bytestring, and the decoding functionD 푋 takes a bytestring푠, decodes some initial prefix of푠to a value푥∈푋, and returns the remainder of푠together with푥. Decoders for complex types will often be built up from decoders for simpler types. Decoders arepartialfunctions because they can fail, for instance, if there is insufficient input, or if the input is not well formed, or if a decoded value is outside some specified range. Many of the decoders which we define below involve a number of cases for different forms of input, and we implicitly assume that the decoder fails if none of the cases applies. We also assume that if a decoder fails then so does any other decoder which invokes it, so any failure when attempting to decode a particular data item in a bytestring will cause the entire decoding process to fail (immediately). D.3 The CBOR format A CBOR-encoded item consists of a bytestring beginning with aheadwhich occupies 1,2,3,5, or 9 bytes. Depending on the contents of the head, some sequence of bytes following it may also contribute to the encoded item. The first three bits of the head are interpreted as a natural number between 0 and 7 (the major type) which gives basic information about the type of the following data. The remainder of the head is called theargumentof the head and is used to encode further information, such as the value of an encoded integer or the size of a list of encoded items. Encodings of complex objects may occupy the bytes following the head, and these will typically contain further encoded items. D.4 Encoding and decoding the heads of CBOR items For푖∈ℕwe define a function햻 푖 ∶ℕ→픹which returns the푖-th byte of an integer, with the 0-th byte being the least significant: 햻 푖 (푛) = mod(div(푛,256 푖 ),256). We use this to define for each푘≥1a partial function햾 푘 ∶ℕ⇀픹 ∗ which converts a sufficiently small integer to a bytestring of length푘(possibly with leading zeros): 햾 푘 (푛) = \[햻 푘−1 (푛),...,햻 0 (푛)\]if푛≤256 푘 − 1. This function fails if the input is too large to fit into a푘-byte bytestring. We also define inverse functions햽 푘 ∶픹 ∗ ⇀ℕwhich decode a푘-byte natural number from the start of a bytestring, failing if there is insufficient input: 햽 푘 (푠) = (푠 ® , 푘−1 ... 푖=0 256 푖 푏 푖 )if푠= \[푏 푘−1 ,...,푏 0 \]⋅푠 ® . We now define an encoderE 헁햾햺햽 ∶ℕ \[0,7\] ×ℕ \[0,2 64 −1\] →픹 ∗ which takes a major type and a natural number and encodes them as a CBOR head using the standard encoding: 28 E 헁햾햺햽 (푚,푛) = ⎧ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎩ \[32푚+푛\]if푛≤23 (32푚+ 24)⋅햾 1 (푛)if24≤푛≤255 (32푚+ 25)⋅햾 2 (푛)if256≤푛≤256 2 − 1 (32푚+ 26)⋅햾 4 (푛)if256 2 ≤푛≤256 4 − 1 (32푚+ 27)⋅햾 8 (푛)if256 4 ≤푛≤256 8 − 1. The corresponding decoderD 헁햾햺햽 ∶픹 ∗ ⇀픹 ∗ ×ℕ \[0,7\] ×ℕ \[0,2 64 −1\] is given by D 헁햾햺햽 (푛⋅푠) = ⎧ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎩ (푠,div(푛,32),mod(푛,32))ifmod(푛,32)≤23 (푠 ® ,div(푛,32),푘)ifmod(푛,32) = 24and햽 1 (푠) = (푠 ® ,푘) (푠 ® ,div(푛,32),푘)ifmod(푛,32) = 25and햽 2 (푠) = (푠 ® ,푘) (푠 ® ,div(푛,32),푘)ifmod(푛,32) = 26and햽 4 (푠) = (푠 ® ,푘) (푠 ® ,div(푛,32),푘)ifmod(푛,32) = 27and햽 8 (푠) = (푠 ® ,푘). This function is undefined if the input is the empty bytestring휖, if the input is too short, or if its initial byte is not of the expected form. Heads for indefinite-length items.The functionsE 헁햾햺햽 andD 헁햾햺햽 defined above are used for a number of purposes. One use is to encode integers less than 64 bits, where the argument of the head is the relevant integer. Another use is for “definite-length” encodings of items such as bytestrings and lists, where the head contains the length푛of the object and is followed by some encoding of the object itself (for example a sequence of푛bytes for a bytestring or a sequence of푛encoded objects for the elements of a list). It is also possible to have “indefinite-length” encodings of objects such as lists and arrays, which do not specify the length of an object in advance: instead a special head with argument 31 is emitted, followed by the encodings of the individual items; the end of the sequence is marked by a “break” byte with value 255. We define an encoderE 헂헇햽햾햿 ∶ℕ \[2,5\] →픹 ∗ and a decoderD 헂헇햽햾햿 ∶픹 ∗ ⇀픹 ∗ ×ℕ \[2,5\] which deal with indefinite heads for a given major type: E 헂헇햽햾햿 (푚) = \[32푚+ 31\] D 헂헇햽햾햿 (푛⋅푠) = (푠,푚)if푛= 32푚+ 31. Note thatE 헂헇햽햾햿 andD 헂헇햽햾햿 are only defined for푚∈ {2,3,4,5}(and we shall only use them in these cases). The case푚= 31corresponds to the break byte and for푚∈ {0,1,6}the value is not well formed: see \[8, 3.2.4\]. D.5 Encoding and decoding bytestrings The standard CBOR encoding of bytestrings encodes a bytestring as either a definite-length sequence of bytes (the length being given in the head) or as an indefinite-length sequence of definite-length “chunks” (see \[8, §§3.1 and 3.4.2\]). We use a similar scheme, but only allow chunks of length up to 64. To this end, suppose that푎= \[푎 1 ,...,푎 64푘+푟 \] ∈픹 ∗ \\{휖}where푘≥0and0≤푟≤63. We define thecanonical 64-byte decomposition̄푎of푎to be ̄푎= \[\[푎 1 ,...,푎 64 \],\[푎 65 ,...,푎 128 \],...,\[푎 64(푘−1)+1 ,...,푎 64푘 \]\] ∈ (픹 ∗ ) ∗ if푟= 0and 29 ̄푎= \[\[푎 1 ,...,푎 64 \],\[푎 65 ,...,푎 128 \],...,\[푎 64(푘−1)+1 ,...,푎 64푘 \],\[푎 64푘+1 ,...,푎 64푘+푟 \]\] ∈ (픹 ∗ ) ∗ if푟 >0. The canonical decomposition of the empty list is̄휖= \[\]. We define the encoderE 픹 ∗ ∶픹 ∗ →픹 ∗ for bytestrings by encoding bytestrings of size up to 64 using the standard CBOR encoding and encoding larger bytestrings by breaking them up into 64-byte chunks (with the final chunk possibly being less than 64 bytes long) and encoding them as an indefinite-length list (major type 2 indicates a bytestring): E 픹 ∗ (푠) = ⎧ ⎪ ⎨ ⎪ ⎩ E 헁햾햺햽 (2,퓁(푠))⋅푠if퓁(푠)≤64 E 헂헇햽햾햿 (2)⋅E 헁햾햺햽 (2,퓁(푐 1 ))⋅푐 1 ⋅E 헁햾햺햽 (2,퓁(푐 2 ))⋅ ⋯ ⋯ ⋅푐 푛−1 ⋅E 헁햾햺햽 (2,퓁(푐 푛 ))⋅푐 푛 ⋅255if퓁(푠)>64and̄푠= \[푐 1 ,...,푐 푛 \]. The decoder is slightly more complicated. Firstly, for every푛≥0we define a decoderD (푛) 햻헒헍햾헌 ∶픹 ∗ ⇀ 픹 ∗ ×픹 ∗ which extracts an푛-byte prefix from its input (failing in the case of insufficient input): D (푛) 햻헒헍햾헌 (푠) = T (푠,휖)if푛= 0 (푠 ®® ,푏⋅푡)if푠=푏⋅푠 ® andD (푛−1) 햻헒헍햾헌 (푠 ® ) = (푠 ®® ,푡). Secondly, we define a decoderD 햻헅허햼헄 ∶픹 ∗ ⇀픹 ∗ ×픹 ∗ which attempts to extract a bytestring of length at most 64 from its input;D 햻헅허햼헄 (and any other function which calls it) will fail if it encounters a bytestring which is greater than 64 bytes. D 햻헅허햼헄 (푠) =D (푛) 햻헒헍햾헌 (푠 ® )ifD 헁햾햺햽 (푠) = (푠 ® ,2,푛)and푛≤64. Thirdly, we define a decoderD 햻헅허햼헄헌 ∶픹 ∗ ⇀픹 ∗ ×픹 ∗ which decodes a sequence of blocks and returns their concatenation. D 햻헅허햼헄헌 (푠) = T (푠 ® ,휖)if푠= 255⋅푠 ® (푠 ®® ,푡⋅푡 ® )ifD 햻헅허햼헄 (푠) = (푠 ® ,푡)andD 햻헅허햼헄헌 (푠 ® ) = (푠 ®® ,푡 ® ). Finally we define the decoderD 픹 ∗ ∶픹 ∗ ⇀픹 ∗ ×픹 ∗ for bytestrings by D 픹 ∗ (푠) = T (푠 ® ,푡)ifD 햻헅허햼헄 (푠) = (푠 ® ,푡) D 햻헅허햼헄헌 (푠 ® )ifD 헂헇햽햾햿 (푠) = (푠 ® ,2). This looks for either a single block or an indefinite-length list of blocks, in the latter case returning their concatenation. It will accept the output ofE 픹 ∗ but will reject bytestring encodings containing any blocks greater than 64 bytes long, even if they are valid bytestring encodings according to the CBOR specification. D.6 Encoding and decoding integers As with bytestrings we use a specialised encoding scheme for integers which prohibits encodings with overly-long sequences of arbitrary data. We encode integers inℕ \[−2 64 ,2 64 −1\] as normal (see \[8, §3.1\]: the major type is 0 for positive integers and 1 for negative ones) and larger ones by emitting a CBOR tag (major type 6; argument 2 for positive numbers and 3 for negative numbers) to indicate the sign, then converting the integer to a bytestring and emitting that using the encoder defined above. This encoding scheme is the same as the standard one except for the size limitations. 30 We firstly define conversion functions헂헍허헌∶ℕ→픹 ∗ and헌헍허헂∶픹 ∗ →ℕby 헂헍허헌(푛) = T 휖if푛= 0 헂헍허헌(div(푛,256))⋅mod(푛,256)if푛 >0. and 헌헍허헂(푙) = T 0if푙=휖 256 ×헌헍허헂(푙 ® ) +푛if푙=푙 ® ⋅푛with푛∈픹. The encoderE ℤ ∶ℤ→픹 ∗ for integers is now defined by E ℤ (푛) = ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ E 헁햾햺햽 (0,푛)if0≤푛≤2 64 − 1 E 헁햾햺햽 (6,2)⋅E 픹 ∗ (헂헍허헌(푛))if푛≥2 64 E 헁햾햺햽 (1,−푛− 1)if−2 64 ≤푛≤−1 E 헁햾햺햽 (6,3)⋅E 픹 ∗ (헂헍허헌(−푛− 1))if푛≤−2 64 − 1. The decoderD ℤ ∶픹 ∗ ⇀픹 ∗ ×ℤinverts this process. The decoder is in fact slightly more permissive than the encoder because it also accepts small integers encoded using the scheme for larger ones. However, the CBOR standard permits integer encodings which contain bytestrings longer than 64 bytes and it will not accept those. D ℤ (푠) = ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ (푠 ® ,푛)ifD 헁햾햺햽 (푠) = (푠 ® ,0,푛) (푠 ® ,−푛− 1)ifD 헁햾햺햽 (푠) = (푠 ® ,1,푛) (푠 ®® ,헌헍허헂(푏))ifD 헁햾햺햽 (푠) = (푠 ® ,6,2)andD 픹 ∗ (푠 ® ) = (푠 ®® ,푏) (푠 ®® ,−헌헍허헂(푏) − 1)ifD 헁햾햺햽 (푠) = (푠 ® ,6,3)andD 픹 ∗ (푠 ® ) = (푠 ®® ,푏). D.7 Encoding and decodingdata It is now quite straightforward to encode mostdatavalues. The main complication is in the encoding of constructor tags (the number푖in홲횘횗횜횝횛푖푙). The encoder.The encoder is given by E 획횊횝횊 (홼횊횙푙) =E 헁햾햺햽 (5,퓁(푙))⋅E (획횊횝횊 ퟸ ) ∗ (푙) E 획횊횝횊 (홻횒횜횝푙) =E 헂헇햽햾햿 (4)⋅E 획횊횝횊 ∗ (푙)⋅255 E 획횊횝횊 (홲횘횗횜횝횛푖푙) =E ctag (푖)⋅E 헂헇햽햾햿 (4)⋅E 획횊횝횊 ∗ (푙)⋅255 E 획횊횝횊 (홸푛)=E ℤ (푛) E 획횊횝횊 (홱푠)=E 픹 ∗ (푠). This definition uses encoders for lists of data items, lists of pairs of data items, and constructor tags as follows: E 획횊횝횊 ∗ (\[푑 1 ,...,푑 푛 \]) =E 획횊횝횊 (푑 1 )⋅ ⋯ ⋅E 획횊횝횊 (푑 푛 ) E (획횊횝횊 ퟸ ) ∗ (\[(푘 1 ,푑 1 ),...,(푘 푛 ,푑 푛 )\]) =E 획횊횝횊 (푘 1 )⋅E 획횊횝횊 (푑 1 )⋅ ⋯ ⋅E 획횊횝횊 (푘 푛 )⋅E 획횊횝횊 (푑 푛 ) E ctag (푖) = ⎧ ⎪ ⎨ ⎪ ⎩ E 헁햾햺햽 (6,121 +푖)if0≤푖≤6 E 헁햾햺햽 (6,1280 + (푖− 7))if7≤푖≤127 E 헁햾햺햽 (6,102)⋅E 헁햾햺햽 (4,2)⋅E ℤ (푖)otherwise. 31 In the final case ofE ctag we emit a head with major type 4 and argument 2. This indicates that an encoding of a list of length 2 will follow: the first element of the list is the constructor number and the second is the argument list of the constructor, which is actually encoded inE 획횊횝횊 . It might be conceptually more accurate to have a single encoder which would encode both the constructor tag and the argument list, but this would increase the complexity of the notation even further. Similar remarks apply toD ctag below. The decoder.The decoder is given by D 획횊횝횊 (푠) = ⎧ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎩ (푠 ®® ,홼횊횙푙)ifD 헁햾햺햽 (푠) = (푠 ® ,5,푛)andD (푛) (획횊횝횊 ퟸ ) ∗ (푠 ® ) = (푠 ®® ,푙) (푠 ® ,홻횒횜횝푙)ifD 획횊횝횊 ∗ (푠) = (푠 ® ,푙) (푠 ®® ,홲횘횗횜횝횛푖푙)ifD ctag (푠) = (푠 ® ,푖)andD 획횊횝횊 ∗ (푠 ® ) = (푠 ®® ,푙) (푠 ® ,홸푛)ifD ℤ (푠) = (푠 ® ,푛) (푠 ® ,홱푏)ifD 픹 ∗ (푠) = (푠 ® ,푏) where D 획횊횝횊 ∗ (푠) = T D (푛) 획횊횝횊 ∗ (푠 ® )ifD 헁햾햺햽 (푠) = (푠 ® ,4,푛) D 헂헇햽햾햿 획횊횝횊 ∗ (푠 ® )ifD 헂헇햽햾햿 (푠) = (푠 ® ,4) D (푛) 획횊횝횊 ∗ (푠) = T (푠,휖)if푛= 0 (푠 ®® ,푑⋅푙)ifD 획횊횝횊 (푠) = (푠 ® ,푑)andD (푛−1) 획횊횝횊 ∗ (푠 ® ) = (푠 ®® ,푙) D 헂헇햽햾햿 획횊횝횊 ∗ (푠) = T (푠 ® ,휖)if푠= 255⋅푠 ® (푠 ®® ,푑⋅푙)ifD 획횊횝횊 (푠) = (푠 ® ,푑)andD 헂헇햽햾햿 획횊횝횊 ∗ (푠 ® ) = (푠 ®® ,푙) D (푛) (획횊횝횊 ퟸ ) ∗ (푠) = ⎧ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎩ (푠,휖)if푛= 0 (푠 ®®® ,(푘,푑)⋅푙) ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ if푛 >0 andD 획횊횝횊 (푠) = (푠 ® ,푘) andD 획횊횝횊 (푠 ® ) = (푠 ®® ,푑) andD (푛−1) (획횊횝횊 ퟸ ) ∗ (푠 ®® ) = (푠 ®®® ,푙) D ctag (푠) = ⎧ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎩ (푠 ® ,푖− 121)ifD 헁햾햺햽 (푠) = (푠 ® ,6,푖)and121≤푖≤127 (푠 ® ,(푖− 1280) + 7)ifD 헁햾햺햽 (푠) = (푠 ® ,6,푖)and1280≤푖≤1400 (푠 ®®® ,푖) ⎧ ⎪ ⎪ ⎨ ⎪ ⎪ ⎩ ifD 헁햾햺햽 (푠) = (푠 ® ,6,102) andD 헁햾햺햽 (푠 ® ) = (푠 ®® ,4,2) andD ℤ (푠 ®® ) = (푠 ®®® ,푖) and0≤푖≤2 64 − 1. Note that the decoders forListandConstraccept both definite-length and indefinite-length lists of encodeddatavalues, but the decoder forMaponly accepts definite-length lists (and the length is the number ofpairsin the map). This is consistent with CBOR’s standard encoding of arrays and lists (major type 4) and maps (major type 5). Note also that the encoderE ctag accepts arbitrary integer values forConstrtags, but (for compatibility with \[10\]) the decoderD ctag only accepts tags inℕ \[0,2 64 −1\] . This means that some valid Plutus Core programs can be serialised but not deserialised, and is the reason for the recommendation in Section A.1 that only constructor tags between 0 and2 64 − 1should be used. 32 Appendix E Serialising Plutus Core Terms and Programs Using the flatFormat We use theflatformat \[3\] to serialise Plutus Core terms, and we regard this format as being the definitive concrete representation of Plutus Core programs. For compactness we generally (andalwaysfor scripts on the blockchain) replace names with de Bruijn indices (see Section 3.3) in serialised programs. We use bytestrings for serialisation, but it is convenient to define the serialisation and deserialisation process in terms of strings of bits. Some extra bits of padding are added at the end of the encoding of a program to ensure that the number of bits in the output is a multiple of 8, and this allows us to regard serialised programs as bytestrings in the obvious way. See Section E.4 for some restrictions on serialisation specific to the Cardano blockchain. Note:flatversus CBOR.Much of the Cardano codebase uses the CBOR format for serialisation; how- ever, it is important that serialised scripts not be too large. CBOR pays a price for being a self-describing format. The size of the serialised terms is consistently larger than a format that is not self-describing: benchmarks show thatflatencodings of Plutus Core scripts are smaller than CBOR encodings by about 35% (without using compression). E.1 Encoding and decoding Let핊= {ퟶ,ퟷ} ∗ , the set of all finite sequences of bits. For brevity we write a sequence of bits in the form푏 푛−1 ⋯푏 0 instead of\[푏 푛−1 ,...,푏 0 \]: thusퟶퟷퟷퟶퟶퟷinstead of\[ퟶ,ퟷ,ퟷ,ퟶ,ퟶ,ퟷ\]). We denote the empty sequence by휖, and use퓁(푠)to denote the length of a sequence of bits, and⋅to denote concatenation (or prepending or appending a single bit to a sequence of bits). Similarly to the CBOR encoding fordatadescribed in Appendix D, we will describe the flat encoding by defining families of encoding functions (orencoders) 햤 푋 ∶핊×푋→핊 and (partial) decoding functions (ordecoders) 햣 푋 ∶핊⇀핊×푋 for various sets푋, such as the setℤof integers and the set of all Plutus Core terms. The encoding function 햤 푋 takes a sequence푠∈핊and an element푥∈푋and produces a new sequence of bits by appending the encoding of푥to푠, and the decoding function햣 푋 takes a sequence of bits, decodes some initial prefix of 푠to a value푥∈푋, and returns the remainder of푠together with푥. Encoding functions basically operate by decomposing an object into subobjects and concatenating the encodings of the subobject; however it is sometimes necessary to add some padding between subobjects in order to make sure that parts of the output are aligned on byte boundaries, and for this reason (unlike the CBOR encoding fordata) all of our encoding functions have a first argument containing all of the previous output, so that it can be examined to determine how much alignment is required. As in the case of CBOR, decoding functions are partial: they can fail if, for instance, there is insufficient input, or if a decoded value is outside some specified range. To simplify notation we will mention any preconditions separately, with the assumption that the decoder will fail if the preconditions are not met; we also make a blanket assumption that all decoders fail if there is not enough input for them to proceed. Many of the definitions of decoders construct objects by calling other decoders to obtain subobjects which are then composed, and these are often introduced by a condition of the form “if햣 푋 (푠) =푥”. Conditions like this should be read as implicitly saying that if the decoder햣 푋 fails then the whole decoding process fails. 33 E.1.1 Padding The encoding functions mentioned above produce sequences ofbits, but we sometimes need sequences of bytes. To this end we introduce a functions헉햺햽∶핊→핊which adds a sequence ofퟶs followed by aퟷto a sequence푠to get a sequence whose length is a multiple of 8; if푠is a sequence such that퓁(푠)is already a multiple of 8 then헉햺햽still adds an extra byte of padding;헉햺햽is used both for internal alignment (for example, to make sure that the contents of a bytestring are aligned on byte boundaries) and at the end of a complete encoding of a Plutus Core program to to make the length a multiple of 8 bits. Symbolically, 헉햺햽(푠) =푠⋅헉 푘 if퓁(푠) = 8푛+푘with푛,푘∈ℕand0≤푘≤7 where 헉 0 =ퟶퟶퟶퟶퟶퟶퟶퟷ 헉 1 =ퟶퟶퟶퟶퟶퟶퟷ 헉 2 =ퟶퟶퟶퟶퟶퟷ 헉 3 =ퟶퟶퟶퟶퟷ 헉 4 =ퟶퟶퟶퟷ 헉 5 =ퟶퟶퟷ 헉 6 =ퟶퟷ 헉 7 =ퟷ. We also define a (partial) inverse function헎헇헉햺햽∶핊⇀핊which discards padding: 헎헇헉햺햽(푞⋅푠) =푠if푞=헉 푖 for some푖∈ {0,1,2,3,4,5,6,7}. This can fail if the padding is not of the expected form or if the input is the empty sequence휖. E.2 Basicflatencodings E.2.1 Fixed-width natural numbers We often wish to encode and decode natural numbers which fit into some fixed number of bits, and we do this simply by encoding them as their binary expansion (most significant bit first), adding leading zeros if necessary. More precisely for푛≥1we define an encoder 햤 푛 ∶핊×ℕ \[0,2 푛−1 −1\] →핊 by 햤 푛 (푠, 푛−1 ... 푖=0 푏 푖 2 푖 ) =푠⋅푏 푛−1 ⋯푏 0 (푏 푖 ∈ {0,1}) and a decoder 햣 푛 ∶핊⇀핊×ℕ \[0,2 푛−1 −1\] by 햣 푛 (푏 푛−1 ⋯푏 0 ⋅푠) = (푠, 푛−1 ... 푖=0 푏 푖 2 푖 ). As in Appendix D,ℕ \[푎,푏\] denotes the closed interval of integers{푛∈ℤ∶푎≤푛≤푏}. Note that푛here is a variable (not a fixed label) so we are defining whole families of encoders햤 1 ,햤 2 ,햤 3 ,...and and decoders 햣 1 ,햣 2 ,햣 3 .... 34 E.2.2 Lists Suppose that we have a set푋for which we have defined an encoder햤 푋 and a decoder햣 푋 ; we define an encoder ô⃗ 햤 푋 which encodes lists of elements of푋by emitting the encodings of the elements of the list, each preceded by aퟷbit, then emitting aퟶbit to mark the end of the list. ô⃗ 햤 푋 (푠,\[\]) =푠⋅ퟶ ô⃗ 햤 푋 (푠,\[푥 1 ,...,푥 푛 \]) = ô⃗ 햤 푋 (푠⋅ퟷ⋅햤 푋 (푥 1 ),\[푥 2 ,...,푥 푛 \]). The corresponding decoder is given by ôô⃗ 햣 푋 (ퟶ⋅푠) = (푠,\[\]) ôô⃗ 햣 푋 (ퟷ⋅푠) = (푠 ®® ,푥⋅푙)if퐷 푋 (푠) = (푠 ® ,푥)and ôô⃗ 햣 푋 (푠 ® ) = (푠 ®® ,푙). E.2.3 Natural numbers We encode natural numbers by splitting their binary representations into sequences of 7-bit blocks, then emitting these as a list with theleast significant block first: 햤 ℕ (푠, 푛−1 ... 푖=0 푘 푖 2 7푖 ) = ô⃗ 햤 7 (푠,\[푘 0 ,...,푘 푛−1 \]) (where푘 푖 ∈ℤand0≤푘 푖 ≤127). The decoder is 햣 ℕ (푠) = (푠 ® , 푛−1 ... 푖=0 푘 푖 2 7푖 )if ôô⃗ 햣 7 (푠) = (푠 ® ,\[푘 0 ,...,푘 푛−1 \]). E.2.4 Integers Signed integers are encoded by converting them to natural numbers using the zigzag encoding (0↦ 0,−1↦1,1↦2,−2↦3,2↦4,...) and then encoding the result using햤 ℕ : 햤 ℤ (푠,푛) = T 햤 ℕ (푠,2푛)if푛≥0 햤 ℕ (푠,2(1 −푛) + 1)if푛 <0. The decoder is 햣 ℤ (푠) = T (푠 ® , 푛 2 )if푛≡0 (mod 2) (푠 ® ,− 푛−1 2 − 1)if푛≡1 (mod 2) if햣 ℕ (푠) = (푠 ® ,푛). E.2.5 Bytestrings Bytestrings are encoded by dividing them into nonempty blocks of up to 255 bytes and emitting each block in sequence. Each block is preceded by a single unsigned byte containing its length, and the end of the encoding is marked by a zero-length block (so the empty bytestring is encoded just as a zero-length block). Before emitting a bytestring, the preceding output is padded so that its length (in bits) is a multiple of 8; if this is already the case a single padding byte is still added; this ensures that contents of the bytestring are aligned to byte boundaries in the output. Recall that픹denotes the set of 8-bit bytes,{0,1,...,255}. For specification purposes we may identify the set of bytestrings with the set픹 ∗ of (possibly empty) lists of elements of픹. We denote by퐶the set 35 ofbytestring chunksofnonemptybytestrings of length at most 255:퐶= {\[푏 1 ,...,푏 푛 \] ∶푏 푖 ∈픹,1≤푛≤ 255}, and define a function퐸 퐶 ∶퐶→핊by 퐸 퐶 (\[푏 1 ,...,푏 푛 \]) =햤 8 (푛)⋅햤 8 (푏 1 )⋅ ⋯ ⋅햤 8 (푏 푛 ). We define an encoder햤 퐶 ∗ for lists of chunks by 햤 퐶 ∗ (푠,\[푐 1 ,...,푐 푛 \]) =푠⋅퐸 퐶 (푐 1 )⋅ ⋯ ⋅퐸 퐶 (푐 푛 )⋅ퟶퟶퟶퟶퟶퟶퟶퟶ. Note that each푐 푖 is required to be nonempty but that we allow the case푛= 0, so that an empty list of chunks encodes asퟶퟶퟶퟶퟶퟶퟶퟶ. To encode a bytestring we decompose it into a list퐿of chunks and then apply햤 퐶 ∗ to퐿. However, there will usually be many ways to decompose a given bytestring푎into chunks. For definiteness we recommend (but do not demand) that푎is decomposed into a sequence of chunks of length 255 possibly followed by a smaller chunk. Formally, suppose that푎= \[푎 1 ,...,푎 255푘+푟 \] ∈픹 ∗ \\{휖}where푘≥0and0≤푟≤254. We define thecanonical 256-byte decompositioñ푎of푎to be ̃푎= \[\[푎 1 ,...,푎 255 \],\[푎 256 ,...,푎 510 \],...\[푎 255(푘−1)+1 ,...,푎 255푘 \]\] ∈퐶 ∗ if푟= 0and ̃푎= \[\[푎 1 ,...,푎 255 \],\[푎 256 ,...,푎 510 \],...\[푎 255(푘−1)+1 ,...,푎 255푘 \],\[푎 255푘+1 ,...,푎 255푘+푟 \]\] ∈퐶 ∗ if푟 >0. For the empty bytestring we define ̃휖= \[\]. Given all of the above, we define the canonical encoding function햤 픹 ∗ for bytestrings to be 햤 픹 ∗ (푠,푎) =퐸 퐶 ∗ (헉햺햽(푠), ̃푎). Non-canonical encodings can be obtained by replacing̃푎with any other decomposition of푎into nonempty chunks, and the decoder below will accept these as well. To define a decoder for bytestrings we first define a decoder햣 퐶 for bytestring chunks: 햣 퐶 (푠) =햣 (푛) 퐶 (푠 ® ,\[\])if햣 8 (푠) = (푠 ® ,푛) where 햣 (푛) 퐶 (푠,푙) = T (푠,푙)if푛= 0 햣 (푛−1) 퐶 (푠 ® ,푙⋅푥)if푛 >0and햣 8 (푠) = (푠 ® ,푥). Now we define 햣 퐶 ∗ (푠) = T (푠 ® ,\[\])if퐷 퐶 (푠) = (푠 ® ,\[\]) (푠 ®® ,푥⋅푙)if햣 퐶 (푠) = (푠 ® ,푥)with푥≠\[\]and햣 퐶 ∗ (푠 ® ) = (푠 ®® ,푙). The notation is slightly misleading here:햣 퐶 ∗ does not decode to a list of bytestring chunks, but to a single bytestring. We could alternatively decode to a list of bytestrings and then concatenate them later, but this would have the same overall effect. Finally, we define the decoder for bytestrings by 햣 픹 ∗ (푠) =햣 퐶 ∗ (헎헇헉햺햽(푠)). 36 E.2.6 Strings We have defined values of thestringtype to be sequences of Unicode characters. As mentioned earlier we do not specify any particular internal representation of Unicode characters, but for serialisation we use the UTF-8 representation to convert between strings and bytestrings and then use the bytestring encoder and decoder: 햤 핌 ∗ (푠,푢) =햤 픹 ∗ (푠,헎헍햿 ퟪ(푢)) 햣 핌 ∗ (푠) = (푠 ® ,헎헍햿 ퟪ −1 (푎))if햣 픹 ∗ (푠) = (푠 ® ,푎) where헎헍햿 ퟪand헎헍햿 ퟪ −1 are the UTF8 encoding and decoding functions mentioned in Appendix A. Recall that헎헍햿 ퟪ −1 is partial (not all bytestrings represent valid Unicode sequences), so햣 핌 ∗ may fail if the input is invalid. E.3 Encoding and decoding Plutus Core E.3.1 Programs A program is encoded by encoding the three components of the version number in sequence then encoding the body, and possibly adding some padding to ensure that the total number of bits in the output is a multiple of 8 (and hence the output can be viewed as a bytestring). 햤 헉헋허헀헋햺헆 ((program푎.푏.푐 푡)) =헉햺햽(햤 헍햾헋헆 (햤 ℕ (햤 ℕ (햤 ℕ (휖,푎),푏),푐),푡)). The decoding process is the inverse of the encoding process: three natural numbers are read to obtain the version number and then the body is decoded. After this we discard any padding in the remaining input and check that all of the input has been consumed. 햣 헉헋허헀헋햺헆 (푠) =(program푎.푏.푐 푡) ⎧ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎩ if햣 ℕ (푠) = (푠 ® ,푎) and햣 ℕ (푠 ® ) = (푠 ®® ,푏) and햣 ℕ (푠 ®® ) = (푠 ®®® ,푐) and햣 헍햾헋헆 (푠 ®®® ) = (푟,푡) and헎헇헉햺햽(푟) =휖. E.3.2 Terms Plutus Core terms are encoded by emitting a 4-bit tag identifying the type of the term (see Table 6; recall that\[\]denotes application) then emitting the encodings for any subterms. We currently only use eight of the sixteen available tags: the remainder are reserved for potential future expansion. 37 Term typeBinaryDecimal Variableퟶퟶퟶퟶ0 delayퟶퟶퟶퟷ1 lamퟶퟶퟷퟶ2 \[\]ퟶퟶퟷퟷ3 constퟶퟷퟶퟶ4 forceퟶퟷퟶퟷ5 errorퟶퟷퟷퟶ6 builtinퟶퟷퟷퟷ7 Table 6: Term tags The encoder for terms is given below: it refers to other encoders (for names, types, and constants) which will be defined later. 햤 헍햾헋헆 (푠,푥)=햤 헇햺헆햾 (푠⋅ퟶퟶퟶퟶ,푥) 햤 헍햾헋헆 (푠,(delay푡)) =햤 헍햾헋헆 (푠⋅ퟶퟶퟶퟷ,푡) 햤 헍햾헋헆 (푠,(lam푥 푡)) =햤 헍햾헋헆 (햤 휆헏햺헋 (푠⋅ퟶퟶퟷퟶ,푥),푡) 햤 헍햾헋헆 (푠,\[푡 1 푡 2 \])=햤 헍햾헋헆 (햤 헍햾헋헆 (푠⋅ퟶퟶퟷퟷ,푡 1 ),푡 2 ) 햤 헍햾헋헆 (푠,(const푡푛 푐)) =햤 푡푛 햼허헇헌헍햺헇헍 (햤 헍헒헉햾 (푠⋅ퟶퟷퟶퟶ,T),푐) 햤 헍햾헋헆 (푠,(force푡)) =햤 헍햾헋헆 (푠⋅ퟶퟷퟶퟷ,푡) 햤 헍햾헋헆 (푠,(error)) =푠⋅ퟶퟷퟷퟶ 햤 헍햾헋헆 (푠,(builtin푏)) =햤 햻헎헂헅헍헂헇 (푠⋅ퟶퟷퟷퟷ,푏). The decoder for terms is given below. To simplify the definition we use some pattern-matching syntax for inputs to decoders: for example the argumentퟶퟷퟶퟷ⋅푠indicates that when the input is a string beginning withퟶퟷퟶퟷthe definition after the=sign should be used (and the remainder of the input is available in푠 there). If the input is not long enough to permit the indicated decomposition then the decoder fails. The decoder also fails if the input begins with a prefix which is not listed; that does not happen here, but does in some later decoders. 햣 헍햾헋헆 (ퟶퟶퟶퟶ⋅푠) = (푠 ® ,푥)if햣 헇햺헆햾 (푠) = (푠 ® ,푥) 햣 헍햾헋헆 (ퟶퟶퟶퟷ⋅푠) = (푠 ® ,(delay푡))if햣 헍햾헋헆 (푠) = (푠 ® ,푡) 햣 헍햾헋헆 (ퟶퟶퟷퟶ⋅푠) = (푠 ®® ,(lam푥 푡))if햣 휆헏햺헋 (푠) = (푠 ® ,푥)and햣 헍햾헋헆 (푠 ® ) = (푠 ®® ,푡) 햣 헍햾헋헆 (ퟶퟶퟷퟷ⋅푠) = (푠 ®® ,\[푡 1 푡 2 \])if햣 헍햾헋헆 (푠) = (푠 ® ,푡 1 )and햣 헍햾헋헆 (푠 ® ) = (푠 ®® ,푡 2 ) 햣 헍햾헋헆 (ퟶퟷퟶퟶ⋅푠) = (푠 ®® ,(const푡푛 푐))if햣 헍헒헉햾 (푠) = (푠 ® ,T)and햣 T 햼허헇헌헍햺헇헍 (푠 ® ) = (푠 ®® ,푐) 햣 헍햾헋헆 (ퟶퟷퟶퟷ⋅푠) = (푠 ® ,(force푡))if햣 헍햾헋헆 (푠) = (푠 ® ,푡) 햣 헍햾헋헆 (ퟶퟷퟷퟶ⋅푠) = (푠,(error)) 햣 헍햾헋헆 (ퟶퟷퟷퟷ⋅푠) = (푠 ® ,푏)if햣 햻헎헂헅헍헂헇 (푠) = (푠 ® ,푏). E.3.3 Built-in types Constants from built-in types are essentially encoded by emitting a sequence of 4-bit tags representing the constant’s type and then emitting the encoding of the constant itself. However the encoding of types 38 is somewhat complex because it has to be able to deal with type operators such as횕횒횜횝and횙횊횒횛. The tags are given in Table 7: they include tags for the basic types together with a tag for a type application operator. TypeBinaryDecimal 횒횗횝횎횐횎횛ퟶퟶퟶퟶ0 횋횢횝횎횜횝횛횒횗횐ퟶퟶퟶퟷ1 횜횝횛횒횗횐ퟶퟶퟷퟶ2 횞횗횒횝ퟶퟶퟷퟷ3 횋횘횘횕ퟶퟷퟶퟶ4 횕횒횜횝ퟶퟷퟶퟷ5 횙횊횒횛ퟶퟷퟷퟶ6 (type application)ퟶퟷퟷퟷ7 획횊횝횊ퟷퟶퟶퟶ8 Table 7: Type tags We define auxiliary functions햾 헍헒헉햾 ∶U→ℕ ∗ and햽 헍헒헉햾 ∶ℕ ∗ ⇀ℕ ∗ ×U(햽 헍헒헉햾 is partial andUdenotes the universe of types used in Alonzo and Vasil) by 햾 헍헒헉햾 (횒횗횝횎횐횎횛) = \[0\] 햾 헍헒헉햾 (횋횢횝횎횜횝횛횒횗횐) = \[1\] 햾 헍헒헉햾 (횜횝횛횒횗횐) = \[2\] 햾 헍헒헉햾 (횞횗횒횝)= \[3\] 햾 헍헒헉햾 (횋횘횘횕)= \[4\] 햾 헍헒헉햾 (횕횒횜횝(푡)) = \[7,5\]⋅햾 헍헒헉햾 (푡) 햾 헍헒헉햾 (횙횊횒횛(푡 1 ,푡 2 )) = \[7,7,6\]⋅햾 헍헒헉햾 (푡 1 )⋅햾 헍헒헉햾 (푡 2 ) 햾 헍헒헉햾 (획횊횝횊)= \[8\]. 햽 헍헒헉햾 (0⋅푙)= (푙,횒횗횝횎횐횎횛) 햽 헍헒헉햾 (1⋅푙)= (푙,횋횢횝횎횜횝횛횒횗횐) 햽 헍헒헉햾 (2⋅푙)= (푙,횜횝횛횒횗횐)) 햽 헍헒헉햾 (3⋅푙)= (푙,횞횗횒횝) 햽 헍헒헉햾 (4⋅푙)= (푙,횋횘횘횕) 햽 헍헒헉햾 (\[7,5\]⋅푙) = (푙 ® ,횕횒횜횝(푡))if햽 헍헒헉햾 (푙) = (푙 ® ,푡) 햽 헍헒헉햾 (\[7,7,6\]⋅푙) = (푙 ®® ,횙횊횒횛(푡 1 ,푡 2 )) T if햽 헍헒헉햾 (푙) = (푙 ® ,푡 1 ) and햽 헍헒헉햾 (푙 ® ) = (푙 ®® ,푡 2 ) 햽 헍헒헉햾 (8⋅푙)= (푙,획횊횝횊). The encoder and decoder for types is obtained by combining햾 헍헒헉햾 and햽 헍헒헉햾 with ô⃗ 햤 4 and ôô⃗ 햣 4 , the encoder and decoder for lists of four-bit integers (see Section E.2). 39 햤 헍헒헉햾 (푠,푡) = ô⃗ 햤 4 (푠,햾 헍헒헉햾 (푡)) 햣 헍헒헉햾 (푠) = (푠 ® ,푡)if ôô⃗ 햣 4 (푠) = (푠 ® ,푙)and햽 헍헒헉햾 (푙) = (\[\],푡). E.3.4 Constants Values of built-in types can mostly be encoded quite simply by using encoders already defined. Note that the unit value(con unit ())does not have an explicit encoding: the type has only one possible value, so there is no need to use any space to serialise it. The획횊횝횊type is encoded by converting to a bytestring using the CBOR encoderE 획횊횝횊 described in Appendix D and then using햤 픹 ∗ . The decoding process is the opposite of this: a bytestring is obtained using햣 픹 ∗ and this is then decoded from CBOR usingD 획횊횝횊 to obtain a획횊횝횊object. 햤 횒횗횝횎횐횎횛 햼허헇헌헍햺헇헍 (푠,푛)=햤 ℤ (푠,푛) 햤 횋횢횝횎횜횝횛횒횗횐 햼허헇헌헍햺헇헍 (푠,푎) =햤 픹 ∗ (푠,푎) 햤 횜횝횛횒횗횐 햼허헇헌헍햺헇헍 (푠,푡)=햤 핌 ∗ (푠,푡) 햤 횞횗횒횝 햼허헇헌헍햺헇헍 (푠,푐)=푠 햤 횋횘횘횕 햼허헇헌헍햺헇헍 (푠,False) =푠⋅ퟶ 햤 횋횘횘횕 햼허헇헌헍햺헇헍 (푠,True) =푠⋅ퟷ 햤 횕횒횜횝(T) 햼허헇헌헍햺헇헍 (푠,푙)= ô⃗ 햤 T 햼허헇헌헍햺헇헍 (푠,푙) 햤 횙횊횒횛(T 1 ,T 2 ) 햼허헇헌헍햺헇헍 (푠,(푐 1 ,푐 2 )) =햤 T 2 햼허헇헌헍햺헇헍 (햤 T 1 햼허헇헌헍햺헇헍 (푠,푐 1 ),푐 2 ) 햤 획횊횝횊 햼허헇헌헍햺헇헍 (푠,푑)=햤 픹 ∗ (푠,E 획횊횝횊 (푑)). 햣 횒횗횝횎횐횎횛 햼허헇헌헍햺헇헍 (푠) =햣 ℤ (푠) 햣 횋횢횝횎횜횝횛횒횗횐 햼허헇헌헍햺헇헍 (푠) =햣 픹 ∗ (푠) 햣 횜횝횛횒횗횐 햼허헇헌헍햺헇헍 (푠) =햣 핌 ∗ (푠) 햣 횞횗횒횝 햼허헇헌헍햺헇헍 (푠) =푠 햣 횋횘횘횕 햼허헇헌헍햺헇헍 (ퟶ⋅푠) = (푠,False) 햣 횋횘횘횕 햼허헇헌헍햺헇헍 (ퟷ⋅푠) = (푠,True) 햣 횕횒횜횝(T) 햼허헇헌헍햺헇헍 (푠) = ôô⃗ 햣 T 햼허헇헌헍햺헇헍 (푠,푙) 햣 횙횊횒횛(T 1 ,T 2 ) 햼허헇헌헍햺헇헍 (푠) = (푠 ®® ,(푐 1 ,푐 2 )) T if햣 T 1 햼허헇헌헍햺헇헍 (푠) = (푠 ® ,푐 1 ) and햣 T 2 햼허헇헌헍햺헇헍 (푠 ® ) = (푠 ®® ,푐 2 ) 햣 획횊횝횊 햼허헇헌헍햺헇헍 (푠) = (푠 ® ,푑)if햣 픹∗ (푠) = (푠 ® ,푡)andD 획횊횝횊 (푡) = (푡 ® ,푑)for some푡 ® . E.3.5 Built-in functions Built-in functions are represented by seven-bit integer tags and encoded and decoded using햤 7 and햣 7 . The tags are specified in Tables 8 and 9. We assume that there are (partial) functions헍햺헀and헍햺헀 −1 which convert back and forth between builtin names and their tags. 40 햤 햻헎헂헅헍헂헇 (푠,푏) =햤 7 (푠,헍햺헀(푏)) 햣 햻헎헂헅헍헂헇 (푠) = (푠 ® ,헍햺헀 −1 (푛))if햣 7 (푠) = (푠 ® ,푛). BuiltinBinaryDecimalBuiltinBinaryDecimal addIntegerퟶퟶퟶퟶퟶퟶퟶ0ifThenElseퟶퟶퟷퟷퟶퟷퟶ26 subtractIntegerퟶퟶퟶퟶퟶퟶퟷ1chooseUnitퟶퟶퟷퟷퟶퟷퟷ27 multiplyIntegerퟶퟶퟶퟶퟶퟷퟶ2traceퟶퟶퟷퟷퟷퟶퟶ28 divideIntegerퟶퟶퟶퟶퟶퟷퟷ3fstPairퟶퟶퟷퟷퟷퟶퟷ29 quotientIntegerퟶퟶퟶퟶퟷퟶퟶ4sndPairퟶퟶퟷퟷퟷퟷퟶ30 remainderIntegerퟶퟶퟶퟶퟷퟶퟷ5chooseListퟶퟶퟷퟷퟷퟷퟷ31 modIntegerퟶퟶퟶퟶퟷퟷퟶ6mkConsퟶퟷퟶퟶퟶퟶퟶ32 equalsIntegerퟶퟶퟶퟶퟷퟷퟷ7headListퟶퟷퟶퟶퟶퟶퟷ33 lessThanIntegerퟶퟶퟶퟷퟶퟶퟶ8tailListퟶퟷퟶퟶퟶퟷퟶ34 lessThanEqualsIntegerퟶퟶퟶퟷퟶퟶퟷ9nullListퟶퟷퟶퟶퟶퟷퟷ35 appendByteStringퟶퟶퟶퟷퟶퟷퟶ10chooseDataퟶퟷퟶퟶퟷퟶퟶ36 consByteStringퟶퟶퟶퟷퟶퟷퟷ11constrDataퟶퟷퟶퟶퟷퟶퟷ37 sliceByteStringퟶퟶퟶퟷퟷퟶퟶ12mapDataퟶퟷퟶퟶퟷퟷퟶ38 lengthOfByteStringퟶퟶퟶퟷퟷퟶퟷ13listDataퟶퟷퟶퟶퟷퟷퟷ39 indexByteStringퟶퟶퟶퟷퟷퟷퟶ14iDataퟶퟷퟶퟷퟶퟶퟶ40 equalsByteStringퟶퟶퟶퟷퟷퟷퟷ15bDataퟶퟷퟶퟷퟶퟶퟷ41 lessThanByteStringퟶퟶퟷퟶퟶퟶퟶ16unConstrDataퟶퟷퟶퟷퟶퟷퟶ42 lessThanEqualsByteStringퟶퟶퟷퟶퟶퟶퟷ17unMapDataퟶퟷퟶퟷퟶퟷퟷ43 sha2\_256ퟶퟶퟷퟶퟶퟷퟶ18unListDataퟶퟷퟶퟷퟷퟶퟶ44 sha3\_256ퟶퟶퟷퟶퟶퟷퟷ19unIDataퟶퟷퟶퟷퟷퟶퟷ45 blake2b\_256ퟶퟶퟷퟶퟷퟶퟶ20unBDataퟶퟷퟶퟷퟷퟷퟶ46 verifyEd25519Signatureퟶퟶퟷퟶퟷퟶퟷ21equalsDataퟶퟷퟶퟷퟷퟷퟷ47 appendStringퟶퟶퟷퟶퟷퟷퟶ22mkPairDataퟶퟷퟷퟶퟶퟶퟶ48 equalsStringퟶퟶퟷퟶퟷퟷퟷ23mkNilDataퟶퟷퟷퟶퟶퟶퟷ49 encodeUtf8ퟶퟶퟷퟷퟶퟶퟶ24mkNilPairDataퟶퟷퟷퟶퟶퟷퟶ50 decodeUtf8ퟶퟶퟷퟷퟶퟶퟷ25 Table 8: Tags for Alonzo builtins BuiltinBinaryDecimal serialiseDataퟶퟷퟷퟶퟶퟷퟷ51 verifyEcdsaSecp256k1Signatureퟶퟷퟷퟶퟷퟶퟶ52 verifySchnorrSecp256k1Signatureퟶퟷퟷퟶퟷퟶퟷ53 Table 9: Extra tags for Vasil builtins E.3.6 Variable names Variable names are encoded and decoded using the햤 헇햺헆햾 and햣 헇햺헆햾 functions, and variables bound inlam expressions are encoded and decoded by the햤 휆헏햺헋 and햣 휆헏햺헋 functions. 41 De Bruijn indices.We use serialised de Bruijn-indexed terms for script transmission because this makes serialised scripts significantly smaller. Recall from Section 3.3 that when we want to use our syntax with de Bruijn indices we replace names with natural numbers and the bound variable in alamexpression with 0. During serialisation the zero is ignored, and during deserialisation no input is consumed and the index 0 is always returned: 햤 휆헏햺헋 (푠,푛) =푠 햣 휆헏햺헋 (푠) = 0. For variables we always use indices which are greater than zero, and our encoder and decoder for names are given by 햤 헇햺헆햾 =햤 ℕ and 햣 헇햺헆햾 (푠) = (푠 ® ,푛)if햣 ℕ = (푠 ® ,푛)and푛 >0. Other types of name.One can serialise code involving other types of name by providing suitable en- coders and decoders for name. For example, for textual names one could use햤 휆헏햺헋 =햤 헇햺헆햾 =햤 핌 ∗ and 햣 휆헏햺헋 =햣 헇햺헆햾 =햣 핌 ∗ . Depending on the method used to represent variable names it may also be neces- sary to check during deserialisation the more general requirement that variables are well-scoped, but this problem will not arise if de Bruijn indices are used. E.4 Cardano-specific serialisation issues E.4.1 Scope checking To execute a Plutus Core program on the blockchain it will be necessary to deserialise it to some in- memory representation, and during or immediately after deserialisation it should be checked that the body of the program is a closed term (see the requirement in Section 3.3); if this is not the case then evaluation should fail immediately. E.4.2 CBOR wrapping Plutus Core programs are not stored on the Cardano chain directly asflatbytestrings; for consistency with other objects used on the chain, theflatbytestrings are in fact wrapped in a CBOR encoding. This should not concern most users, but we mention it here to avoid possible confusion. E.5 Example Consider the program (program 5.0.2 \[ \[(builtin indexByteString)(con bytestring #1a5f783625ee8c)\] (con integer 54321) \]) Suppose this is stored inindex.uplc. We can convert it toflatby running $ cabal run exec uplc convert -- -i index.uplc --of flat -o index.flat The serialised program looks like this: 42 $ xxd -b index.flat 00000000: 00000101 00000000 00000010 00110011 01110001 11001001 ...3q. 00000006: 00010001 00000111 00011010 01011111 01111000 00110110 ...\_x6 0000000c: 00100101 11101110 10001100 00000000 01001000 00111000 %...H8 00000012: 10110100 00000001 10000001 Figure 12 shows how this encodes the original program. Sequences of bits are followed by explanatory comments and lines beginning with#provide further commentary on preceding bit sequences. 00000101 :Final integer chunk:0000101→5 00000000 :Final integer chunk:0000000→0 00000010 :Final integer chunk:0000000→2 #Version: 5.0.2 0011 :Term tag 3: apply 0011 :Term tag 3: apply 0111 :Term tag 7: builtin 0001110 :Builtin tag 14 # builtin indexByteString 0100 :Term tag 4: constant 1 :Start of type tag list 0001 :Type tag 1 0 :End of list #Type tags: \[1\]→bytestring 001 :Padding before bytestring 00000111 :Bytestring chunk size: 7 00011010 : 0x1a 01011111 : 0x5f 01111000 : 0x78 00110110 : 0x36 00100101 : 0x25 11101110 : 0xee 10001100 : 0x8c 00000000 :Bytestring chunk size: 0 (end of list of chunks) # con bytestring #1a5f783625ee8c 0100 :Term tag 4: constant 1 :Start of type tag list 0000 :Type tag 0 0 :End of list #Type tags: \[0\]→integer 11100010 :Integer chunk1100010(least significant) 11010000 :Integer chunk1010000 00000110 :Final integer chunk0000110(most significant) # 0000110⋅1010000⋅1100010→108642 decimal #Zigzag encoding: 108642/2→+54321 # con integer 54321 000001 :Padding Figure 12:flatencoding ofindex.uplc 43 References \[1\] ANSI. X9.62: Public Key Cryptography for the Financial Services Industry: the Elliptic Curve Digital Signature Algorithm (ECDSA), 2005. \[2\] ANSI. X9.142: Public Key Cryptography for the Financial Services Industry: the Elliptic Curve Digital Signature Algorithm (ECDSA), 2020. \[3\] Pasqualino ‘Titto’ Assini. Flat format specification.http://quid2.org/docs/Flat.pdf. \[4\] Hendrik Pieter Barendregt.The Lambda Calculus - its Syntax and Semantics, volume 103 ofStudies in Logic and the Foundations of Mathematics. North-Holland, 1985. \[5\] Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang. High-speed high- security signatures. InCHES, volume 6917 ofLecture Notes in Computer Science, pages 124–142. Springer, 2011. \[6\] Bitcoin Wiki. Elliptic Curve Digital Signature Algorithm, 2022. \[7\] Carsten Bormann.Notable CBOR Tags.https://www.ietf.org/archive/id/ draft-bormann-cbor-notable-tags-06.html. \[8\] Carsten Bormann and Paul E. Hoffman. RFC 8949: Concise Binary Object Representation (CBOR). https://www.rfc-editor.org/info/rfc8949, December 2020. \[9\] Certicom Research. Standards for Efficient Cryptography 2 (SEC 2).https://www.secg.org/ SEC2-Ver-2.0.pdf, 2010. \[10\] Duncan Coutts, Michael Peyton Jones, and Carsten Bormann. CBOR Tags for Discriminated Unions. https://github.com/cabo/cbor-discriminated-unions/. \[11\] N.G de Bruijn. Lambda calculus notation with nameless dummies, a tool for automatic formula manipulation, with application to the Church-Rosser theorem.Indagationes Mathematicae (Pro- ceedings), 75(5):381–392, 1972. \[12\] Matthias Felleisen. Programming languages and lambda calculi, 2007. \[13\] Matthias Felleisen, Robert Bruce Findler, and Matthew Flatt.Semantics Engineering with PLT Redex.MIT Press, 2009. \[14\] Matthias Felleisen and Daniel P. Friedman. Control operators, the SECD-machine, and the lambda- calculus. In3rd Working Conference on the Formal Description of Programming Concepts, August 1986. \[15\] Matthias Felleisen and Robert Hieb. The revised report on the syntactic theories of sequential control and state.Theor. Comput. Sci., 103(2):235–271, September 1992. \[16\] Robert Harper.Practical Foundations for Programming Languages. Cambridge University Press, New York, NY, USA, 2012. \[17\] Don Johnson, Alfred Menezes, and Scott A. Vanstone. The elliptic curve digital signature algorithm (ECDSA).Int. J. Inf. Sec., 1(1):36–63, 2001. \[18\] Simon Josefsson and Ilari Liusvaara. RFC 8032: Edwards-Curve Digital Signature Algorithm (Ed- DSA).https://www.rfc-editor.org/info/rfc8032, January 2017. 44 \[19\] Johnson Lau, Jonas Nick, and Tim Ruffing. Bitcoin Improvement Proposal 340: Schnorr Signatures for secp256k1.https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki, 2020. \[20\] Johnson Lau and Pieter Wuilie. Bitcoin Improvement Proposal 146: Dealing with signature encod- ing malleability.https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki, 2016. \[21\] Gordon D. Plotkin. Call-by-name, call-by-value and the lambda-calculus.Theor. Comput. Sci., 1(2):125–159, 1975. \[22\] John C. Reynolds. Types, abstraction and parametric polymorphism. In R. E. A. Mason, editor, Information Processing 83, Proceedings of the IFIP 9th World Computer Congress, Paris, France, September 19-23, 1983, pages 513–523. North-Holland/IFIP, 1983. \[23\] Claus-Peter Schnorr. Efficient identification and signatures for smart cards. In Gilles Brassard, editor, CRYPTO, volume 435 ofLecture Notes in Computer Science, pages 239–252. Springer, 1989. \[24\] The Unicode Consortium. The Unicode Standard.https://www.unicode.org/versions/ latest/. \[25\] Philip Wadler. Theorems for free! InFPCA ’89: Proceedings of the fourth international conference on Functional programming languages and computer architecture, pages 347–359, New York, NY, USA, 1989. ACM. 45 Index of Notation Sets 픹{푛∈ℤ∶ 0≤푛≤255}, 4 픹 ∗ The set of all bytestrings, 4 ℕ{0,1,2,3,...}, 4 ℕ + {1,2,3,...}, 4 ℕ \[푎,푏\] {푛∈ℕ∶푎≤푛≤푏}, 4 핊The set of strings of bits, 33 핌The set of Unicode scalar values, 4 핌 ∗ The set of Unicode strings, 4 ℤ{...,−2,−1,0,1,2,...}, 4 푆 × 푆 ⊎{×}(푆a set), 4 ⊎Disjoint union of sets, 4 푋 ∗ The set of all finite sequences of elements of a set푋, 4 Lists \[\]The empty list, 5 퐿⋅퐿 ® Concatenation of lists퐿and퐿 ® , 5 푥⋅퐿\[푥\]⋅퐿, 5 퐿⋅푥퐿⋅\[푥\], 5 푉A sequence\[푉 1 ,...,푉 푛 \], 5 퓁(⋅)Length of a list or bytestring, 5 휖The empty bytestring, 27 Plutus Core grammar 푏푛,푏The name of a built-in function, 5 푐A literal constant, 5 퐿,푀,푁A term, 5 푛A name, 5 푃A Plutus Core program, 5 푣Plutus Core version, 5 푥A variable name, 5 Built-in types C T Constants of built-in type푇, 9 OThe set of built-in type operator names, 7 UThe universe of built-in types, 7 U 0 The set of atomic type names, 7 U # The set of builtin-polymorphic types, 8 VThe set of all type variables,V # ∪V ∗ , 7 V ∗ The set of fully-polymorphic type variables, 7 V # The set of builtin-polymorphic type variables, 7 opA built-in type operator (an element ofO), 7 푣 ∗ A fully-polymorphic type variable, 7 46 푣 # A builtin-polymorphic type variable, 7 푃A builtin-polymorphic type, 8 푇A built-in type (an element ofU), 7 푇⪯ 푆 푃푇is an instance of푃via푆anddom푆=향햵 # (푃), 8 ̂ 푆The extension of a type assignment푆toU # ∪V ∗ , 8 푆A type assignment, 8 향햵 # (푃)Free #-variables of a polymorphic type푃∈U # , 8 J푇KDenotation of a type푇∈U, 7 J⋅K 푇 Denotation of constants of type푇, 9 ⦃⋅⦄ 푇 Reification of constants of type푇, 9 ⦃⋅⦄Reification of the result of a built-in function application, 12 Built-in functions BThe set of built-in functions, 6 IThe set of inputs to built-in functions, 9 QThe set of all type quantifications, 9 훼Arity of built-in function, 10 ̄훼Reduced arity, 10 휒Number of arguments of built-in function, 10 휄Signature item, 10 휎Signature of built-in function, 10 ̄휎Reduced signature, 10 휏A member ofU # ⊎V ∗ , ie a type or type variable, 9 ≈ 푆 Compatibility of built-in function arguments with function arity via푆, 12 ∀푣Type quantification, 9 J푏K 푆 The denotation of the built-in function푏at the type assignment푆, 11 햤헏햺헅Evaluation of built-in functions, 13 Term reduction 퐴Well-formed partial built-in function application, 14 퐵Partial built-in function application (possibly ill-formed), 14 햡Set of all partial built-in function applications, 14 푉Plutus Core value, 14 훽(퐵)Function in partial builtin application퐵, 14 ‖퐵‖Size of partial builtin application퐵, 14 헇햾헑헍(퐴)Next argument type (orforce) required by a partial builtin application퐴, 15 햺헋헀헌(퐴)Term arguments received so far by partial builtin application퐴, 15 \[푉∕푥\]푀Capture-avoiding substitution of value푉for variable푥in term푀, 15 푓Reduction frame for contextual semantics:\[\_푀\],\[푉\_\],(force\_), 16 CEK machine ⊳CEK compute phase, 17 ⊲CEK return phase, 17 ⬥CEK error state, 17 ◻CEK halting state, 17 ΣCEK machine state, 17 47 푠CEK machine stack, 17 푓CEK stack frame:(force\_),\[\_(푀,휌)\],\[푉\_\], 17 푉CEK value:〈conT푐〉,〈delay푀 휌〉,〈lam푥 푀 휌〉,〈builtin푏푉 휂〉, 17 휌CEK environment, 17 휌\[푥\]Value bound to variable푥in environment휌, 17 휂Arguments expected by partial builtin application, 17 U(푉)Discharge a CEK value푉to obtain a Plutus Core term, 19 푀@휌Discharge all variables bound by휌in the term푀, 19 Serialisation and deserialisation D 푋 CBOR decoder fordata, 28 E 푋 CBOR encoder fordata, 28 햣 푋 Flat decoder, 33 햤 푋 Flat encoder, 33 48 ---