Write down specification about Plutus contract blueprints.
Including meta-schemas and type definitions for existing "builtins" Plutus types.
Including meta-schemas and type definitions for existing "builtins" Plutus types.
---
CIP: 57
Title: Plutus Contract Blueprint
Status: Active
Category: Tools
Authors:
- KtorZ <[email protected]>
- scarmuega <[email protected]>
Implementors:
- Aiken <https://aiken-lang.org>
- Lucid <https://lucid.spacebudz.io/>
- Martify Labs <https://martify.io/>
Discussions:
- https://github.com/cardano-foundation/CIPs/pull/258
- https://discord.gg/yUkkhqBnyV
Created: 2022-05-15
License: CC-BY-4.0
---
## Abstract
This document specifies a language for documenting Plutus contracts in a machine-readable manner. This is akin to what [OpenAPI](https://swagger.io/specification) or [AsyncAPI](https://www.asyncapi.com/docs/specifications/v2.4.0) are for, documenting HTTP services and asynchronous services respectively. In a similar fashion, A Plutus contract has a binary interface which is mostly defined by its datum and redeemer.
This document is therefore a meta-specification defining the vocabulary and validation rules with which one can specify a Plutus contract interface, a.k.a **Plutus contract blueprint**.
## Motivation: why is this CIP necessary?
While publicly accessible, on-chain contracts are currently inscrutable. Ideally, one would want to get an understanding of transactions revolving around script executions. This is both useful to visualize and to control the evolution of a contract life-cycle; but also, as a user interacting with a contract, to ensure that one is authorizing a transaction to do what it's intended to. Having a machine-readable specification in the form of a JSON-schema makes it easier (or even, possible) to enable a wide variety of use cases from a single concise document, such as:
- Code generators for serialization/deserialization of Contract's elements
- Contract API Reference / Documentation, also automatically generated
- Extra automated transaction validation layers
- Better wallet UI / integration with DApps
- Automated Plutus-code scaffolding
Moreover, by making the effort to write a clear specification of their contracts, DApps developers make their contracts easier to audit (as they're able to specify the expected behavior).
## Specification
### Overview
This specification introduces the notion of a _Plutus contract blueprint_, as a JSON document which it itself a _JSON-schema_ as per the definition of given in [JSON Schema: A Media Type for Describing JSON Documents: Draft 2020-12](https://json-schema.org/draft/2020-12/json-schema-core.html).
Said differently, Plutus blueprints are first and foremost, valid JSON schemas (according to the specification linked above). This specification defines a core vocabulary and additional keywords which are tailored to the specification of Plutus contracts. Tools supporting this specification must implement the semantic and validation rules specified in this document.
Meta-schemas for Plutus blueprints (i.e. schemas used for validating Plutus blueprints themselves) are given [in annexe](./schemas/README.md).
A _Plutus contract blueprint_ is made of a single document describing one or more on-chain validators. By convention, the document is named `plutus.json` and should be located at the root of a project's repository to facilitate its discoverability.
### Document Structure
The document itself is a JSON object with the following fields:
| Fields | Description |
| --- | --- |
| [preamble](#preamble) | An object with meta-information about the contract |
| [validators](#validators) | An object of named validators |
| ?[definitions](#definitions) | A registry of definition re-used across the specification |
Note that examples of specifications are given later in the document to keep the specification succinct enough and not bloated with examples.
#### preamble
The `preamble` fields stores meta-information about the contract such as version numbers or a short description. This field is mainly meant for humans as a mean to contextualize a specification.
| Fields | Description |
| --- | --- |
| title | A short and descriptive title of the application |
| ?description | A more elaborate description |
| version | A version number |
| ?license | A license under which the specification and contract code is distributed |
#### validators
Where the essence of the specification lies. This section describes each validator involved in the contract (simple applications will likely have only a single validator). Each validator can be named, as a UTF-8 identifier. A validator is mainly defined by three things: a redeemer, a datum and some compiled code. Note that the datum is optional in the case of a minting policy.
| Fields | Description |
| --- | --- |
| title | A short and descriptive name for the validator |
| ?description | An informative description of the validator |
| purpose | The purpose of that redeemer, one of: `spend`, `mint`, `withdraw` or `publish` |
| redeemer | A description of the redeemer format expected by this validator |
| ?datum | A description of the datum format expected by this validator. Must be present when `purpose=spend` and absent otherwise |
| ?parameters | A list of parameters required by the script in addition of the datum and redeemer |
| ?compiledCode | The full compiled and cbor-encoded serialized flat script |
| ?hash | A blake2b-224 hash digest of the validator script, as found in addresses. Optional, but mandatory if `compiledCode` is provided |
#### definitions
A set of extra schemas to be re-used as references across the specification.
### Core vocabulary
Plutus blueprints ultimately describes on-chain data value that can be found at the validator's interface boundaries. This means that while we would generally operate at the level of _Plutus Data_, the vocabulary covers in practice any of the possible Untyped Plutus Core (abbrev. UPLC) primitives that can appear at a validator's boundary. Any UPLC primitive is therefore represented as a schema with a `dataType` keyword. The possible values for `dataType` are detailed just below. In addition, and depending on the value of `dataType`, we may find additional keywords in the vocabulary.
| dataType | UPLC Type | Description |
| --- | --- | --- |
| `integer` | Data | A signed integer at an arbitrary precision, wrapped as `iData`. |
| `bytes` | Data | A bytes string of an arbitrary length, wrapped as `bData`. |
| `list` | Data | An ordered list of Plutus data, wrapped as `listData` |
| `map` | Data | An associative list of Plutus data keys and values, wrapped as `mapData`. |
| `constructor` | Data | A constructor with zero, one or many fields, wrapped as `constrData`. |
| `#unit` | Unit | A builtin unit value (the unary constructor). |
| `#boolean` | Boolean | A builtin boolean value. |
| `#integer` | Integer | A builtin signed integer at an arbitrary precision. |
| `#bytes` | ByteString | A builtin bytes string of an arbitrary length. |
| `#string` | String | A builtin UTF-8 text string. |
| `#pair` | ProtoPair | A builtin pair of `Data` elements. |
| `#list` | ProtoList | A builtin list of `Data` elements. |
> **Warning**
>
> While they exist for completeness, frameworks are strongly discouraged to use any of the constructs starting with a `#` as they refer to Plutus Core builtins types used by the Plutus virtual machines but aren't meant to figure in outward-facing interfaces. Validators should, as much as possible, stick to `integer`, `bytes`, `list`, `map` and `constructor` (and any composition of those) for their binary interface.
Using these primitives, it becomes possible to represent the entire domain (i.e. possible values) which can be manipulated by Plutus contracts.
### Additional keywords
Similarly to JSON schemas, we provide extra validation keywords and keywords for applying subschemas with logic to further refine the definition of core primitives. Keywords allow to combine core data-types into bigger types and we'll later give some pre-defined definitions which we assume to be part of the core vocabulary and therefore, recognized by any tool supporting this standard.
When presented with a validation keyword with a malformed value (e.g. `"maxLength": "foo"`), programs are expected to return an appropriate error.
Beside, we define a _Plutus Data Schema_ as a JSON object with a set of fields depending on its corresponding data-type. When we refer to a _Plutus Data Schema_, we refer to the entire schema definition, with its validations and with the semantic of each keywords applied.
Unless otherwise specified, keywords are all considered optional.
Here below are detailed all the accepted keywords for each data-type.
#### For any data-type
> **Note** Keywords in this section applies to any instance data-type described above.
##### `dataType`
The value of this keyword must be a string, with one of the following value listed in the first column of the table above. This keyword is **mandatory for any instance** and defines the realm of other applicable keywords for that instance.
##### `title`
This keyword's value must be a string. This keyword can be used to decorate a user interface and qualify an instance with some short title.
##### `description`
This keyword's value must be a string. This keyword can be used to decorate a user interface and provide explanation about the purpose of the instance described by this schema.
##### `$comment`
This keyword's value must be a string. It is meant mainly for programmers and humans reading the specification. This keyword should be ignored by programs.
##### `allOf`
This keyword's value must be a non-empty array. Each item of the array MUST be a valid _Plutus Data Schema_. An instance validates successfully against this keyword if it validates successfully against all schemas defined by this keyword's value.
##### `anyOf`
This keyword's value must be a non-empty array. Each item of the array must be a valid _Plutus Data Schema_. An instance validates successfully against this keyword if it validates successfully against at least one schema defined by this keyword's value.
##### `oneOf`
This keyword's value must be a non-empty array. Each item of the array must be a valid _Plutus Data Schema_. An instance validates successfully against this keyword if it validates successfully against exactly one schema defined by this keyword's value.
##### `not`
This keyword's value must be a valid _Plutus Data Schema_. An instance is valid against this keyword if it fails to validate successfully against the schema defined by this keyword.
#### For `{ "dataType": "bytes" }`
> **Note** Keywords in this section only applies to `bytes`. Using them in conjunction with an invalid data-type should result in an error.
##### `enum`
The value of this keyword must be an array of hex-encoded string literals. An instance validates successfully against this keyword if once hex-encoded, its value matches one of the elements of the keyword's values.
##### `maxLength`
The value of this keyword must be a non-negative integer. A bytes instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
##### `minLength`
The value of this keyword must be a non-negative integer. A bytes instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
#### For `{ "dataType": "integer" }`
> **Note** Keywords in this section only applies to `integer`. Using them in conjunction with an invalid type should result in an error.
##### `multipleOf`
The value of "multipleOf" must be a integer, strictly greater than 0. The instance is valid if division by this keyword's value results in an integer.
##### `maximum`
The value of "maximum" must be a integer, representing an inclusive upper limit. This keyword validates only if the instance is less than or exactly equal to "maximum".
##### `exclusiveMaximum`
The value of "exclusiveMaximum" must be an integer, representing an exclusive upper limit. The instance is valid only if it has a value strictly less than (not equal to) "exclusiveMaximum".
##### `minimum`
The value of "minimum" must be an integer, representing an inclusive lower limit. This keyword validates only if the instance is greater than or exactly equal to "minimum".
# Plutus Contract Blueprint - Meta-Schemas
In these folders you'll find meta JSON-schemas for CIP-0057; Meta-schemas are JSON schemas describing how a Plutus Contract Blueprint should be structured. They also define several common data-types that can be referenced when writing your own specification.
Schema | Description
--- | ---
[plutus-blueprint.json](./plutus-blueprint.json) | The meta-schema for the blueprint specification document itself
[plutus-data.json](./plutus-data.json) | Definitions of the _Plutus Data Schema_ and the various supported keywords
[plutus-builtin.json](./plutus-builtin.json) | Definitions of the Untyped Plutus Core builtin types
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cips.cardano.org/cips/cip57/schemas/plutus-blueprint.json",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true,
"https://cips.cardano.org/cips/cip57": true
},
"type": "object",
"required": [
"preamble",
"validators"
],
"properties": {
"preamble": {
"$ref": "#/$defs/Preamble"
},
"validators": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/Validator"
}
},
"definitions": {
"type": "object",
"additionalProperties": true
}
},
"$defs": {
"Preamble": {
"type": "object",
"additionalProperties": false,
"required": [
"title",
"version"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"version": {
"type": "string"
},
"license": {
"type": "string"
}
}
},
"Validator": {
"type": "object",
"required": [
"title",
"purpose",
"redeemer"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"purpose": {
"$ref": "#/$defs/Purpose"
},
"compiledCode": {
"$ref": "#/$defs/CompiledCode"
},
"hash": {
"$ref": "#/$defs/HashDigest"
},
"datum": {
"oneOf": [
{ "$ref": "plutus-data.json" },
{ "$ref": "plutus-builtin.json" }
]
},
"redeemer": {
"oneOf": [
{ "$ref": "plutus-data.json" },
{ "$ref": "plutus-builtin.json" }
]
},
"parameters": {
"oneOf": [
{ "$ref": "plutus-data.json" },
{ "$ref": "plutus-builtin.json" }
]
}
}
},
"CompiledCode": {
"type": "string",
"contentEncoding": "base16",
"description": "A cbor-serialised flat-encoded Plutus script",
"example": "01450100002601"
},
"HashDigest": {
"type": "string",
"contentEncoding": "base16",
"minLength": 56,
"maxLength": 56
},
"Purpose": {
"type": "string",
"enum": ["spend", "mint", "withdraw", "publish"]
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cips.cardano.org/cips/cip57/schemas/plutus-builtin.json",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true,
"https://cips.cardano.org/cips/cip57": true
},
"allOf": [
{
"$ref": "#/$defs/annotation"
},
{
"anyOf": [
{
"$ref": "#/$defs/primitive"
},
{
"$ref": "#/$defs/applicator"
}
]
}
],
"$defs": {
"primitive": {
"oneOf": [
{
"$ref": "#/$defs/_unit"
},
{
"$ref": "#/$defs/_boolean"
},
{
"$ref": "#/$defs/_integer"
},
{
"$ref": "#/$defs/_bytes"
},
{
"$ref": "#/$defs/_string"
},
{
"$ref": "#/$defs/_pair"
},
{
"$ref": "#/$defs/_list"
}
]
},
"_unit": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "#unit"
}
}
},
"_boolean": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "#boolean"
}
}
},
"_integer": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "#integer"
}
}
},
"_bytes": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "#bytes"
}
}
},
"_string": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "#string"
}
}
},
"_list": {
"type": "object",
"required": [
"dataType",
"items"
],
"properties": {
"dataType": {
"type": "string",
"const": "#list"
},
"items": {
"$ref": "plutus-data.json"
}
}
},
"_pair": {
"dataType": "object",
"required": [
"dataType",
"left",
"right"
],
"properties": {
"dataType": {
"type": "string",
"const": "#pair"
},
"left": {
"$ref": "plutus-data.json"
},
"right": {
"$ref": "plutus-data.json"
}
}
},
"annotation": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
}
}
},
"applicator": {
"type": "object",
"maxProperties": 1,
"minProperties": 1,
"properties": {
"allOf": {
"$ref": "#/$defs/schemaArray"
},
"anyOf": {
"$ref": "#/$defs/schemaArray"
},
"oneOf": {
"$ref": "#/$defs/schemaArray"
},
"not": {
"$ref": "#"
}
}
},
"schemaArray": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#"
}
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cips.cardano.org/cips/cip57/schemas/plutus-data.json",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true,
"https://cips.cardano.org/cips/cip57": true
},
"allOf": [
{
"$ref": "#/$defs/annotation"
},
{
"anyOf": [
{
"$ref": "#/$defs/primitive"
},
{
"$ref": "#/$defs/applicator"
}
]
}
],
"$defs": {
"primitive": {
"oneOf": [
{
"$ref": "#/$defs/integer"
},
{
"$ref": "#/$defs/bytes"
},
{
"$ref": "#/$defs/list"
},
{
"$ref": "#/$defs/map"
},
{
"$ref": "#/$defs/constructor"
}
]
},
"integer": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "integer"
}
}
},
"bytes": {
"type": "object",
"required": [
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"const": "bytes"
}
}
},
"list": {
"type": "object",
"required": [
"dataType",
"items"
],
"properties": {
"dataType": {
"type": "string",
"const": "list"
},
"items": {
"$ref": "#"
}
}
},
"map": {
"dataType": "object",
"required": [
"dataType",
"keys",
"values"
],
"properties": {
"dataType": {
"type": "string",
"const": "map"
},
"keys": {
"$ref": "#"
},
"values": {
"$ref": "#"
}
}
},
"constructor": {
"type": "object",
"required": [
"dataType",
"index",
"fields"
],
"properties": {
"dataType": {
"type": "string",
"const": "constructor"
},
"index": {
"type": "integer",
"minimum": 0
},
"fields": {
"type": "array",
"items": {
"$ref": "#"
}
}
}
},
"annotation": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
}
}
},
"applicator": {
"type": "object",
"maxProperties": 1,
"minProperties": 1,
"properties": {
"allOf": {
"$ref": "#/$defs/schemaArray"
},
"anyOf": {
"$ref": "#/$defs/schemaArray"
},
"oneOf": {
"$ref": "#/$defs/schemaArray"
},
"not": {
"$ref": "#"
}
}
},
"schemaArray": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#"
}
}
}
}