tip | title | description | author | discussions-to | status | type | layer | created | requires |
---|---|---|---|---|---|---|---|---|---|
18 |
Multi-Asset Ledger and ISC Support |
Transform IOTA into a multi-asset ledger that supports running IOTA Smart Contracts |
Levente Pap (@lzpap) <[email protected]> |
Draft |
Standards |
Core |
2021-11-04 |
TIP-19, TIP-20, TIP-21 and TIP-22 |
This document proposes new output types and transaction validation rules for the IOTA protocol to support native tokenization and smart contract features.
Native tokenization refers to the capability of the IOTA ledger to track the ownership and transfer of user defined tokens, so-called native tokens, thus making it a multi-asset ledger. The scalable and feeless nature of IOTA makes it a prime candidate for tokenization use cases.
The IOTA Smart Contract Protocol (ISCP) is a layer 2 extension of the IOTA protocol that adds smart contract features to the Tangle. Many so-called smart contract chains, which anchor their state to the base ledger, can be run in parallel. Users wishing to interact with smart contract chains can send requests to layer 1 chain accounts either as regular transactions or directly to the chain, but chains may also interact with other chains in a trustless manner through the Tangle.
This TIP presents output types that realize the required new features:
- Smart contract chains have a new account type, called alias account, represented by an alias output.
- Requests to smart contract chains can be carried out using the configurable new output type called extended output.
- Native tokens have their own supply control policy enforced by foundry outputs.
- Layer 1 native non-fungible tokens (unique tokens with attached metadata) are introduced via NFT outputs.
IOTA transitioned from an account based ledger model to an unspent transaction output (UTXO) model with the upgrade to Chrysalis phase 2. In this model, transactions explicitly reference funds produced by previous transactions to be consumed. This property is desired for scalability: transaction validation does not depend on the shared global state and, as such, transactions can be validated in parallel. Double-spends can easily be detected as they spend the very same output more than once.
The UTXO model becomes even more powerful when unlocking criteria (validation) of outputs is extended as demonstrated by the EUTXO model (Chakravarty et al., 2020): instead of requiring only a valid signature for the output's address to unlock it, additional unlocking conditions can be programmed into outputs. This programmability of outputs is the main idea behind the new output types presented in this document.
Today, outputs in the IOTA protocol are designed for one specific use case: the single asset cryptocurrency. The aim of this TIP is to design several output types for the use cases of:
- Native Tokenization Framework,
- ISCP style smart contracts,
- seamless interoperability between layer 1 and layer 2 tokenization concepts.
Users will be able to mint their own native tokens directly in the base ledger, which can then be transferred without any fees just like regular IOTA coins. Each native token has its own supply control policy enforced by the protocol. These policies are transparent to all network participants. Issuers will be able to store metadata about their tokens on-ledger, accessible to anyone.
Non-fungible tokens can be minted and transferred with zero fees. The validated issuers of such NFTs are immutably attached to the tokens, making it impossible to counterfeit them.
Users will be able to interact with smart contracts by posting requests through the Tangle. Requests can carry commands to smart contracts and can additionally also transfer native tokens and NFTs. By depositing native tokens to smart contracts, their features can be greatly enhanced and programmed to specific use cases.
The proposal in this TIP not only makes it possible to transfer native tokens to layer 2 smart contracts, but tokens that originate from layer 2 smart contract chains can also be wrapped into their respective layer 1 representation. Smart contract chains may transfer tokens between themselves through this mechanism, and they can also post requests to other chains.
Composability of smart contracts extends the realm of one smart contract chain, as smart contracts residing on different chains can call each other in a trustless manner.
In conclusion, the IOTA protocol will become a scalable general purpose multi-asset DLT with the addition of smart contracts and native tokenization frameworks. The transition is motivated by the ever-growing need for a scalable and affordable decentralized application platform.
Outputs in the UTXO model are essential, core parts of the protocol. The new output types introduce new validation and unlocking mechanisms, therefore the protocol needs to be adapted. The structure of the remaining sections is as follows:
- Introduction to ledger programmability
- Data types, subschemas and protocol constants
- Transaction Payload changes compared to Chrysalis Part 2
- New concepts of output design
- Detailed design of new output types
- New unlocking mechanisms
- Discussion
The current UTXO model only provides support to transfer IOTA coins. However, the UTXO model presents a unique opportunity to extend the range of possible applications by programming outputs.
Programming the base ledger of a DLT is not a new concept. Bitcoin uses the UTXO model and attaches small executables (scripts) that need to be executed during transaction validation. The bitcoin script language is however not Turing-complete as it can only support a small set of instructions that are executed in a stack based environment. As each validator has to execute the same scripts and arrive at the same conclusion, such scripts must terminate very quickly. Also, as transaction validation happens in the context of the transaction and block, the scripts have no access to the global shared state of the system (all unspent transaction outputs).
The novelty of Ethereum was to achieve quasi Turing-completeness by employing an account based model and gas to limit resource usage during program execution. As the amount of gas used per block is limited, only quasi Turing-completeness can be achieved. The account based model of Ethereum makes it possible for transactions to have access to the global shared state of the system, furthermore, transactions are executed one-after-the-other. These two properties make Ethereum less scalable and susceptible to high transaction fees.
Cardano achieves UTXO programmability by using the EUTXO model. This makes it possible to represent smart contracts in a UTXO model as state machines. In EUTXO, states of the machine are encoded in outputs, while state transition rules are governed by scripts. Just like in bitcoin, these scripts may only use a limited set of instructions.
It would be quite straightforward to support EUTXO in IOTA too, except that IOTA transactions are feeless. There is no reward to be paid out to validators for validating transactions, as all nodes in the network validate all transactions. Due to the unique data structure of the Tangle, there is no need for miners to explicitly choose which transactions are included in the ledger, but there still has to be a notion of objective validity of transactions. Since it is not possible without fees to penalize scripts that consume excessive network resources (node CPU cycles) during transaction validation, IOTA has to be overly restrictive about what instructions are supported on layer 1.
It must also be noted that UTXO scripts are finite state machines with the state space restricted by the output and transaction validation rules. It makes expressiveness of UTXO scripts inherently limited. In the context of complicated application logic required by use cases such as modern DeFi, this leads to unconventional and complicated architectures of the application, consisting of many interacting finite state machines. Apart from complexity and UX costs, it also has performance and scalability penalties.
For the reason mentioned above, IOTA chooses to support configurable yet hard-coded scripts for output and transaction validation on layer 1. The general full-scale quasi Turing-complete programmability of the IOTA ledger is achieved by extending the ledger state transition function with layer 2 smart contract chains. This not only makes it possible to keep layer 1 scalable and feeless, but also allows to support any type of virtual machine on layer 2 to program advanced business logic and features.
Below, several new output types are discussed that implement their own configurable script logic. They can be viewed as UTXO state machines in which the state of the machine is encoded as data inside the output. The state transition rules are defined by the output type and by the parameters chosen upon deployment.
Data types and subschemas used throughout this TIP are defined in draft TIP-21.
Global protocol parameters used throughout this TIP are defined in draft TIP-22.
The new output types and unlocking mechanisms require new transaction validation rules, furthermore some protocol rules have been modified compared to Chrysalis Part 2 Transaction Payload TIP-7.
Draft TIP-20 replaces aforementioned TIP-7 with the new transaction layout and validation rules. The updated version is the basis for output validation in this TIP.
- Deprecating SigLockedSingleOutput and SigLockedDustAllowanceOutput.
- The new dust protection mechanism does not need a distinct output type, therefore SigLockedDustAllowanceOutput will be deprecated. One alternative is that during migration to the new protocol version, all dust outputs sitting on an address will be merged into an ExtendedOutput together with their respective SigLockedDustAllowanceOutputs to create the snapshot for the updated protocol. The exact migration strategy will be decided later.
- Adding new output types to Transaction Payload.
- Adding new unlock block types to Transaction Payload.
- Inputs and Outputs of a transaction become a list instead of a set. Binary duplicate inputs are not allowed as they anyway mean double-spends, but binary duplicate outputs are allowed.
- There can be many outputs created to the same address in the transaction.
- Confirming milestone supplies notion of time to semantic transaction validation.
New output types add new features to the protocol and hence new transaction validation rules. While some of these new features are specifically tied to one output type, some are general, LEGO like building blocks that may be put in several types of outputs.
Below is a summary of such new features and the validation rules they introduce.
Outputs are records in the UTXO ledger that track ownership of funds. Thus, each output must be able to specify which funds it holds. With the addition of the Native Tokenization Framework, outputs may also carry user defined native tokens, that is, tokens that are not IOTA coins but were minted by foundries and are tracked in the very same ledger. Therefore, every output must be able to hold not only IOTA coins, but also native tokens.
Dust protection applies to all outputs, therefore it is not possible for outputs to hold only native tokens, the dust requirements must be covered via IOTA coins.
User defined tokens are called Native Tokens on protocol level. The maximum supply of a particular native token
is defined by the representation chosen on protocol level for defining their amounts in outputs. Since native tokens
are also a vehicle to wrap layer 2 tokens into layer 1 tokens, the chosen representation must take into account the
maximum possible supply of layer 2 tokens. Solidity, the most popular layer 2 smart contract language defines the
maximum supply of an ERC-20 token as MaxUint256
, therefore it should be possible to represent such huge amount of
assets on layer 1.
Outputs must have the following fields to define the balance of native tokens they hold:
Name | Type | Description | |||||||||
Native Tokens Count | uint16 | The number of native tokens present in the output. | |||||||||
Native Tokens optAnyOf |
Native Token
|
Native Tokens
must be lexicographically sorted based onToken ID
.- Each Native Token must be unique in the set of
Native Tokens
based on itsToken ID
. No duplicates are allowed. Amount
of any Native Token must not be0
.
- The transaction is balanced in terms of native tokens, that is, the sum of native token balances in consumed outputs equals that of the created outputs.
- When the transaction is imbalanced, the foundry outputs controlling outstanding native token balances must be present in the transaction. The validation of the foundry output(s) determines if the outstanding balances are valid.
The programmability of outputs opens the door for implementing new features for the base protocol. While some outputs were specifically designed for such new features, some are optional additions that may be used with any outputs that support them.
These new features are grouped into two categories:
- Unlock Conditions and
- Feature Blocks.
The Output Design section lists all supported Unlock Conditions and Feature Blocks for each output type.
New output features that introduce unlocking conditions, that is, they define constraints on how the output can be unlocked and spent, are grouped under the field Unlock Conditions.
Each output must not contain more than one unlock condition of each type and not all unlock condition types are supported for each output type.
It is merely a layout change that the previously defined Address
field of outputs (TIP-7)
is represented as an Address Unlock Condition. Unlocking an Ed25519 Address doesn't change, it has to
be performed via a Signature Unlock Block in a transaction by signing the hash of the transaction essence.
Transaction validation rules are detailed in draft TIP-18.
New additions are the Alias Address and NFT Address types, which have to be unlocked with their corresponding unlock blocks, as defined in Unlocking Chain Script Locked Outputs.
Address Unlock Block
Defines the Address that owns this output, that is, it can unlock it with the proper Unlock Block in a transaction.
Name | Type | Description | |||||||||||||||||||||||||||
Unlock Condition Type | uint8 | Set to value 0 to denote an Address Unlock Condition. | |||||||||||||||||||||||||||
Address |
Ed25519 Address
Alias Address
NFT Address
|
ℹ️ Good to know about address format |
---|
The Address Type byte of a raw address has an effect on the starting character of the bech32 encoded address, which is the recommended address format for user facing applications.
A usual bech32 encoded mainnet address starts with iota1
, and continues with the bech32 encoded bytes of the address.
By choosing Address Type as a multiple of 8 for different address types, the first character after the 1
separator in the bech32 address will always be different.
Address | Type Byte as uint8 |
Bech32 Encoded |
---|---|---|
Ed25519 | 0 | iota1q... |
Alias | 8 | iota1p... |
NFT | 16 | iota1z... |
A user can identify by looking at the address whether it is a signature backed address, a smart contract chain account or an NFT address.
This unlock condition is employed to achieve conditional sending. An output that has Dust Deposit Unlock Condition
specified can only be consumed in a transaction that deposits Return Amount
IOTA coins into Return Address
. When
several of such outputs are consumed, their return amounts per Return Addresses
are summed up and the output side
must deposit this total sum per Return Address
.
Return Amount
must be ≥ thanMinimum Dust Deposit
and must not be0
.Return Amount
in a Dust Deposit Unlock Condition must be ≤ than the required dust deposit of the output.
- An output that has Dust Deposit Unlock Condition specified must only be consumed and unlocked in a transaction
that deposits
Return Amount
IOTA coins toReturn Address
via an output that has no additional spending constraints. (ExtendedOutput with only an Address Unlock Condition) - When several outputs with Dust Deposit Unlock Condition and the same
Return Address
are consumed, their return amounts perReturn Addresses
are summed up and the output side of the transaction must deposit this total sum perReturn Address
.
Dust Deposit Return Unlock Condition
Defines the amount of IOTAs used as dust deposit that have to be returned to Return Address.
Name | Type | Description | |||||||||||||||||||||||||||
Unlock Condition Type | uint8 | Set to value 1 to denote a Dust Deposit Return Unlock Condition. | |||||||||||||||||||||||||||
Return AddressoneOf |
Ed25519 Address
Alias Address
NFT Address
|
||||||||||||||||||||||||||||
Return Amount | uint64 | Amount of IOTA tokens the consuming transaction should deposit to Return Address. |
This unlock condition makes it possible to send small amounts of IOTA coins or native tokens to addresses without having to lose control of the required dust deposit. It is also a vehicle to send on-chain requests to ISCP chains that do not require fees. To prevent the receiving party from blocking access to the dust deposit, it is advised to be used together with the Expiration Unlock Conditions. The receiving party then has a sender-defined time window to agree to the transfer by consuming the output, or the sender regains total control after expiration.
The notion of time in the Tangle is introduced via milestones. Each milestone carries the current milestone index and the unix timestamp corresponding to that index. Whenever a new milestone appears, nodes perform the white-flag ordering and transaction validation on its past cone. The timestamp and milestone index of the confirming milestone provide the time as an input parameter to transaction validation.
An output that contains a Timelock Unlock Condition can not be unlocked before the specified timelock has expired. The timelock is expired when the timestamp and/or milestone index of the confirming milestone is equal or past the timestamp and/or milestone defined in the Timelock Unlock Condition.
The timelock can be specified as a unix timestamp or as a milestone index. When specified in both ways, both conditions have to pass in order for the unlock to be valid. The zero value of one if the timestamp fields signals that it should be ignored during validation.
The two time representations help to protect against the possible downtime of the Coordinator. If the Coordinator is down, "milestone index clock" essentially stops advancing, while "real time clock" does not. An output that specifies time in both clocks must satisfy both conditions (AND relation).
- If both
Milestone Index
andUnix Time
fields are0
, the unlock condition, and hence the output and transaction that contain it, is invalid.
- An output that has Timelock Unlock Condition specified must only be consumed and unlocked in a
transaction, if the confirming milestone index is ≥ than the
Milestone Index
specified in the unlock condition. - An output that has Timelock Unlock Condition specified must only be consumed and unlocked in a
transaction, if the timestamp of the confirming milestone is equal or past the
Unix Time
specified in the unlock condition.
Timelock Unlock Condition
Defines a milestone index and/or unix timestamp until which the output can not be unlocked.
Name | Type | Description |
Unlock Condition Type | uint8 | Set to value 2 to denote a Timelock Milestone Index Unlock Condition. |
Milestone Index | uint32 | The milestone index starting from which the output can be consumed. |
Unix Time | uint32 | Unix time (seconds since Unix epoch) starting from which the output can be consumed. |
The expiration feature of outputs makes it possible for the return address to reclaim an output after a given expiration time has been passed. The expiration might be specified as a unix timestamp or as a milestone index. When specified in both ways, both conditions have to pass in order for the unlock to be valid.
The expiration feature can be viewed as an opt-in receive feature, because the recipient loses access to the received funds after the output expires, while the return address specified by the sender regains control over them. This feature is a big help for on-chain smart contract requests. Those that have expiration set and are sent to dormant smart contract chains can be recovered by their senders. Not to mention the possibility to time requests by specifying both a timelock and an expiration unlock condition.
- If both
Milestone Index
andUnix Time
fields are0
, the unlock condition, and hence the output and transaction that contain it, is invalid.
- If
Milestone Index
!=0
, an output that has Expiration Unlock Condition set must only be consumed and unlocked by the targetAddress
(defined in Address Unlock Condition) in a transaction that has a confirming milestone index < than theMilestone Index
defined in the unlock condition.0
value of theMilestone Index
field is a special flag that signals to the validation that this check must be ignored. - If
Milestone Index
!=0
, an output that has Expiration Unlock Condition set must only be consumed and unlocked byReturn Address
in a transaction that has a confirming milestone index ≥ than theMilestone Index
defined in the unlock condition.0
value of theMilestone Index
field is a special flag that signals to the validation that this check must be ignored. - If
Unix Time
!=0
, an output that has Expiration Unlock Condition set must only be consumed and unlocked by the targetAddress
(defined in Address Unlock Condition) in a transaction that has a confirming milestone timestamp earlier than theUnix Time
defined in the unlock condition.0
value of theUnix Time
field is a special flag that signals to the validation that this check must be ignored. - If
Unix Time
!=0
, an output that has Expiration Unlock Condition set must only be consumed and unlocked by theReturn Address
in a transaction that has a confirming milestone timestamp same or later than theUnix Time
defined in the unlock condition.0
value of theUnix Time
field is a special flag that signals to the validation that this check must be ignored. - Semantic validation of an output that has Expiration Unlock Condition set and is unlocked by the
Return Address
must ignore:
Expiration Unlock Condition
Defines a milestone index and/or unix time until which only Address, defined in Address Unlock Condition, is allowed to unlock the output. After the milestone index and/or unix time, only Return Address can unlock it.
Name | Type | Description | |||||||||||||||||||||||||||
Unlock Condition Type | uint8 | Set to value 3 to denote a Expiration Unlock Condition. | |||||||||||||||||||||||||||
Return Address oneOf |
Ed25519 Address
Alias Address
NFT Address
|
||||||||||||||||||||||||||||
Milestone Index | uint32 | Before this milestone index, Address Unlock Condition is allowed to unlock the output, after that only the address defined in Return Address. | |||||||||||||||||||||||||||
Unix Time | uint32 | Before this unix time, Address Unlock Condition is allowed to unlock the output, after that only the address defined in Return Address. |
An unlock condition defined solely for Alias Output. It is functionally equivalent to an
Address Unlock Condition, however there are additional transition constraints defined for the Alias UTXO state
machine that can only be carried out by the State Controller Address
, hence the distinct unlock condition type.
State Controller Address Unlock Block
Defines the State Controller Address that owns this output, that is, it can unlock it with the proper Unlock Block in a transaction that state transitions the alias output.
Name | Type | Description | |||||||||||||||||||||||||||
Unlock Condition Type | uint8 | Set to value 4 to denote an State Controller Address Unlock Condition. | |||||||||||||||||||||||||||
Address |
Ed25519 Address
Alias Address
NFT Address
|
The additional constraints are defined in Alias Output Design section.
An unlock condition defined solely for Alias Output. It is functionally equivalent to an
Address Unlock Condition, however there are additional transition constraints defined for the Alias UTXO state
machine that can only be carried out by the Governor Address
, hence the distinct unlock condition type.
Governor Address Unlock Block
Defines the Governor Address that owns this output, that is, it can unlock it with the proper Unlock Block in a transaction that governance transitions the alias output.
Name | Type | Description | |||||||||||||||||||||||||||
Unlock Condition Type | uint8 | Set to value 5 to denote an Governor Address Unlock Condition. | |||||||||||||||||||||||||||
Address |
Ed25519 Address
Alias Address
NFT Address
|
The additional constraints are defined in Alias Output Design section.
New output features that do not introduce unlocking conditions, but rather add new functionality and add constraints on output creation are grouped under Feature Blocks.
Each output must not contain more than one block of each type and not all block types are supported for each output type.
Every transaction consumes several elements from the UTXO set and creates new outputs. However, certain applications (smart contracts) require to associate each output with exactly one sender address. Here, the sender block is used to specify the validated sender of an output.
Outputs that support the Sender Block may specify a Sender
address which is validated by the protocol during
transaction validation.
- The Sender Block, and hence the output and transaction that contain it, is valid, if and only if an output
with the corresponding
Address
is consumed and unlocked in the transaction. IfAddress
is either Alias Address or NFT Address, their corresponding outputs (defined byAlias ID
andNFT ID
) must be unlocked in the transaction.
Sender Block
Identifies the validated sender of the output.
Name | Type | Description | |||||||||||||||||||||||||||
Block Type | uint8 | Set to value 0 to denote a Sender Block. | |||||||||||||||||||||||||||
Sender oneOf |
Ed25519 Address
Alias Address
NFT Address
|
The issuer block is a special case of the sender block that is only supported by outputs that implement a UTXO state
machine with chain constraint (alias, NFT).
Only when the state machine is created (e.g. minted) it is checked during transaction validation that an output
corresponding to the Issuer
address is consumed. In every future transition of the state machine, it is instead
checked that the issuer block is still present and unchanged.
- When an Issuer Block is present in an output representing the initial state of an UTXO state machine, the
transaction that contains this output is valid, if and only if an output with the corresponding
Address
is consumed and unlocked in the transaction. IfIssuer
is either Alias Address or NFT Address, their corresponding outputs (defined byAlias ID
andNFT ID
) must be unlocked in the transaction.
The main use case is proving authenticity of NFTs. Whenever an NFT is minted as an NFT output, the creator (issuer) can fill the Issuer Block with their address that they have to unlock in the transaction. Issuers then can publicly disclose their addresses to prove the authenticity of the NFT once it is in circulation.
Issuer Block
Identifies the validated issuer of the output.
Name | Type | Description | |||||||||||||||||||||||||||
Block Type | uint8 | Set to value 1 to denote an Issuer Block. | |||||||||||||||||||||||||||
Issuer oneOf |
Ed25519 Address
Alias Address
NFT Address
|
Whenever a chain account mints an NFT on layer 1 on behalf of some user, the Issuer
field can only contain the
chain's address, since user does not sign the layer 1 transaction. As a consequence, artist would have to mint NFTs
themselves on layer 1 and then deposit it to chains if they want to place their own address in the Issuer
field.
Outputs may carry additional data with them that is interpreted by higher layer applications built on the Tangle. The protocol treats this metadata as pure binary data, it has no effect on the validity of an output except that it increases the required dust deposit. ISC is a great example of a higher layer protocol that makes use of Metadata Block: smart contract request parameters are encoded in the metadata field of outputs.
- An output with Metadata Block is valid, if and only if 0 <
Data Length
≤Max Metadata Length
.
Metadata Block
Defines metadata (arbitrary binary data) that will be stored in the output.
Name | Type | Description |
Block Type | uint8 | Set to value 2 to denote a Metadata Block. |
Data Length | uint32 | Length of the following data field in bytes. |
Data | ByteArray | Binary data. |
A Tag Block makes it possible to tag outputs with an index, so they can be retrieved through an indexer API not
only by their address, but also based on the the Tag
. The combination of a Tag Block, a
Metadata Block and a Sender Block makes it possible to retrieve data associated to an address and stored
in outputs that were created by a specific party (Sender
) for a specific purpose (Tag
).
An example use case is voting on the Tangle via the participation plugin.
Storing indexed data in outputs however incurs greater dust deposit for such outputs, because they create look-up entries in nodes' databases.
- An output with Tag Block is valid, if and only if 0 <
Tag Length
≤Max Tag Length
.
Tag Block
Defines an indexation tag to which the output can be indexed by additional node plugins.
Name | Type | Description |
Block Type | uint8 | Set to value 3 to denote a Tag Block. |
Tag Length | uint8 | Length of the following tag field in bytes. |
Tag | ByteArray | Binary indexation tag. |
Previously created transaction outputs are destroyed when they are consumed in a subsequent transaction as an input. The chain constraint makes it possible to carry the UTXO state machine state encoded in outputs across transactions. When an output with chain constraint is consumed, that transaction has to create a single subsequent output that carries the state forward. The state can be updated according to the transition rules defined for the given type of output and its current state. As a consequence, each such output has a unique successor, and together they form a path or chain in the graph induced by the UTXO spends. Each chain is identified by its globally unique identifier.
Alias outputs, foundry outputs and NFT outputs all use this chain constraint concept and define their own unique identifiers.
In the following, we define four new output types. They are all designed with specific use cases in mind:
- Extended Output: transfer of funds with attached metadata and optional spending restrictions. Main use cases are on-ledger ISC requests, native asset transfers and indexed data storage in the UTXO ledger.
- Alias Output: representing ISC chain accounts on L1 that can process requests and transfer funds.
- Foundry Output: supply control of user defined native tokens. A vehicle for cross-chain asset transfers and asset wrapping.
- NFT Output: an output that represents a Non-fungible token with attached metadata and proof-of-origin. A NFT is represented as an output so that the token and metadata are transferred together, for example as a smart contract requests. NFTs are possible to implement with native tokens as well, but then ownership of the token does not mean ownership of the foundry that holds its metadata.
The validation of outputs is part of the transaction validation process. There are two levels of validation for transactions: syntactic and semantic validation. The former validates the structure of the transaction (and outputs), while the latter validates whether protocol rules are respected in the semantic context of the transaction. Outputs hence are validated on both levels:
- Transaction Syntactic Validation: validates the structure of each output created by the transaction.
- Transaction Semantic Validation:
- For consumed outputs: validates whether the output can be unlocked in a transaction given the semantic transaction context.
- For created outputs: validates whether the output can be created in a transaction given the semantic transaction context.
Each new output type may add its own validation rules which become part of the transaction validation rules if the output is placed inside a transaction. Unlock Conditions and Feature Blocks described previously also add constraints to transaction validation when they are placed in outputs.
ExtendedOutput can hold native tokens and might have several unlock conditions and optional feature blocks. The combination of several features provide the base functionality for the output to be used as an on-ledger smart contract request:
- Verified
Sender
, - Attached
Metadata
that can encode the request payload for layer 2, Return Amount
to get back the dust deposit,Timelock
to be able to time requests,Expiration
to recover funds in case of chain inactivity.
Besides, the Tag Block feature is a tool to store arbitrary, indexed data with verified origin in the ledger.
Note, that an ExtendedOutput in its simplest possible form with only an Address Unlock Condition and without feature blocks or native tokens is functionally equivalent to a SigLockedSingleOutput: it has an address and an IOTA balance. Therefore, aforementioned output type, that was introduced for Chrysalis Part 2 via TIP-7 is deprecated with the replacement of the draft TIP-20 Transaction Payload.
Extended Output
Describes an extended output with optional features.
Name | Type | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Output Type | uint8 | Set to value 3 to denote an Extended Output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Amount | uint64 | The amount of IOTA coins to held by the output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens Count | uint16 | The number of native tokens held by the output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens optAnyOf |
Native Token
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions Count | uint8 | The number of unlock conditions following. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions atMostOneOfEach |
Address Unlock Condition
Dust Deposit Return Unlock ConditionDefines the amount of IOTAs used as dust deposit that have to be returned to Return Address.
Timelock Unlock ConditionDefines a milestone index and/or unix timestamp until which the output can not be unlocked.
Expiration Unlock ConditionDefines a milestone index and/or unix time until which only Address, defined in Address Unlock Condition, is allowed to unlock the output. After the milestone index and/or unix time, only Return Address can unlock it.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks Count | uint8 | The number of feature blocks following. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks atMostOneOfEach |
Sender BlockIdentifies the validated sender of the output.
Metadata BlockDefines metadata (arbitrary binary data) that will be stored in the output.
Tag BlockDefines an indexation tag to which the output can be indexed by additional node plugins.
|
Amount
field must fulfill the dust protection requirements and must not be0
.Native Tokens Count
must not be greater thanMax Native Tokens Count
.Native Tokens
must be lexicographically sorted based onToken ID
.- Each Native Token must be unique in the set of
Native Tokens
based on itsToken ID
. No duplicates are allowed. Amount
of any Native Token must not be0
.- It must hold true that
1
≤Unlock Conditions Count
≤4
. Unlock Condition Type
of an Unlock Condition must define on of the following types:- Address Unlock Condition
- Dust Deposit Return Unlock Condition
- Timelock Unlock Condition
- Expiration Unlock Condition
- Unlock Conditions must be sorted in ascending order based on their
Unlock Condition Type
. - Syntactic validation of all present unlock conditions must pass.
- Address Unlock Condition must be present.
- It must hold true that
0
≤Blocks Count
≤3
. Block Type
of a Block must define on of the following types:- Sender Block
- Metadata Block
- Tag Block
- Blocks must be sorted in ascending order based on their
Block Type
. - Syntactic validation of all present feature blocks must pass.
- The unlock block of the input must correspond to
Address
field in the Address Unlock Condition and the unlock must be valid. - The unlock is valid if and only if all unlock conditions and feature blocks present in the output validate.
- All Unlock Condition imposed transaction validation criteria must be fulfilled.
- All Feature Block imposed transaction validation criteria must be fulfilled.
The Alias Output is a specific implementation of a UTXO state machine. Alias ID
, the unique identifier of an
instance of the deployed state machine, is generated deterministically by the protocol and is not allowed to change in
any future state transitions.
Alias Output represents an alias account in the ledger with two control levels and a permanent
Alias Address. The account owns other outputs that are locked under Alias Address. The account keeps
track of state transitions (State Index
counter), controlled foundries (Foundry Counter
) and anchors the layer 2
state as metadata into the UTXO ledger.
Alias Output
Describes an alias account in the ledger that can be controlled by the state and governance controllers.
Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Output Type | uint8 | Set to value 4 to denote a Alias Output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Amount | uint64 | The amount of IOTA tokens held by the output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens Count | uint16 | The number of native tokens held by the output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens optAnyOf |
Native Token
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Alias ID | ByteArray[20] | Unique identifier of the alias, which is the BLAKE2b-160 hash of the Output ID that created it. Alias Address = Alias Address Type || Alias ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
State Index | uint32 | A counter that must increase by 1 every time the alias is state transitioned. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
State Metadata Length | uint32 | Length of the following State Metadata field. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
State Metadata | ByteArray | Metadata that can only be changed by the state controller. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Foundry Counter | uint32 | A counter that denotes the number of foundries created by this alias account. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions Count | uint8 | The number of unlock conditions following. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions atMostOneOfEach |
State Controller Address Unlock Condition
Governor Address Unlock Condition
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks Count | uint8 | The number of feature blocks following. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks atMostOneOfEach |
Sender BlockIdentifies the validated sender of the output.
Issuer BlockIdentifies the validated issuer of the alias output.
Metadata BlockDefines metadata (arbitrary binary data) that will be stored in the output.
|
Amount
field must fulfill the dust protection requirements and must not be0
.Native Tokens Count
must not be greater thanMax Native Tokens Count
.Native Tokens
must be lexicographically sorted based onToken ID
.- Each Native Token must be unique in the set of
Native Tokens
based on itsToken ID
. No duplicates are allowed. Amount
of any Native Token must not be0
.- It must hold true that
Unlock Conditions Count
=2
. Unlock Condition Type
of an Unlock Condition must define one of the following types:- State Controller Address Unlock Condition
- Governor Address Unlock Condition
- Unlock Conditions must be sorted in ascending order based on their
Unlock Condition Type
. - Syntactic validation of all present unlock conditions must pass.
- It must hold true that
0
≤Blocks Count
≤3
. Block Type
of a Block must define on of the following types:- Sender Block
- Issuer Block
- Metadata Block
- Syntactic validation of all present feature blocks must pass.
- When
Alias ID
is zeroed out,State Index
andFoundry Counter
must be0
. State Metadata Length
must not be greater thanMax Metadata Length
.Address
of State Controller Address Unlock Condition andAddress
of Governor Address Unlock Condition must be different from the alias address derived fromAlias ID
.
- Explicit
Alias ID
:Alias ID
is taken as the value of theAlias ID
field in the alias output. - Implicit
Alias ID
: When an alias output is consumed as an input in a transaction andAlias ID
field is zeroed out whileState Index
andFoundry Counter
are zero, take the BLAKE2b-160 hash of theOutput ID
of the input asAlias ID
. - The BLAKE2b-160 hash function outputs a 20 byte hash as opposed to the 32 byte hash size of BLAKE2b-256.
Alias ID
is sufficiently secure and collision free with 20 bytes already, as it is actually a hash of anOutput ID
, which already contains the 32 byte hashTransaction ID
. - For every non-zero explicit
Alias ID
on the output side there must be a corresponding alias on the input side. The corresponding alias has the explicit or implicitAlias ID
equal to that of the alias on the output side.
Whenever an alias output is consumed in a transaction, it means that the alias is transitioned into its next state. The
current state is defined as the consumed alias output, while the next state is defined as the alias
output with the same explicit AliasID
on the output side. There are two types of transitions: state transition
and governance transition
.
- State transition:
- A state transition is identified by an incremented
State Index
. - The
State Index
must be incremented by 1. - The unlock block must correspond to the
Address
of State Controller Address Unlock Condition. - State transition can only change the following fields in the next state:
IOTA Amount
,Native Tokens
,State Index
,State Metadata Length
,State Metadata
,Foundry Counter
andSender Block
. Foundry Counter
field must increase by the number of foundry outputs created in the transaction that map toAlias ID
. TheSerial Number
fields of the created foundries must be the set of natural numbers that cover the open-ended interval between the previous and next values of theFoundry Counter
field in the alias output.- The created foundry outputs must be sorted in the list of outputs by their
Serial Number
. Note, that any foundry that maps toAlias ID
and has aSerial Number
that is less or equal to theFoundry Counter
of the input alias is ignored when it comes to sorting. - Newly created foundries in the transaction that map to different aliases can be interleaved when it comes to sorting.
- A state transition is identified by an incremented
- Governance transition:
- A governance transition is identified by an unchanged
State Index
in next state. If there is no alias output on the output side with a corresponding explicitAlias ID
, the alias is being destroyed. The next state is the empty state. - The unlock block must correspond to the
Address
of Governor Address Unlock Condition. - Governance transition must only change the following fields:
Address
of State Controller Address Unlock Condition,Address
of Governor Address Unlock Condition,Metadata Block
,Sender Block
. - The
Metadata Block
is optional, the governor can put additional info about the chain here, for example chain name, fee structure, supported VMs, list of access nodes, etc., anything that helps clients to fetch info (i.e. account balances) about the layer 2 network.
- A governance transition is identified by an unchanged
- When a consumed alias output has an
Issuer Block
and a corresponding alias output on the output side,Issuer Block
is not allowed to change.
- When Issuer Block is present in an output and explicit
Alias ID
is zeroed out, an input withAddress
field that corresponds toIssuer
must be unlocked in the transaction.
- Governor Address Unlock Condition field is made mandatory for now to help formal verification. When the same entity is defined for state and governance controllers, the output is self governed. Later, for compression reasons, it is possible to make the governance controller optional and define a self-governed alias as one that does not have the governance Governor Address Unlock Condition set.
- Indexers and nodeplugins shall map the alias address of the output derived with
Alias ID
to the regular address -> output mapping table, so that given an Alias Address, its most recent unspent alias output can be retrieved.
- Is an immutable data field needed for alias output?
A foundry output is an output that controls the supply of user defined native tokens. It can mint and burn tokens
according to the policy defined in the Token Scheme
field of the output. Foundries can only be created and
controlled by aliases.
The concatenation of Address
|| Serial Number
|| Token Scheme Type
fields defines the unique identifier of the
foundry, the Foundry ID
.
Upon creation of the foundry, the alias defined in the Address
field must be unlocked in the same transaction, and
its Foundry Counter
field must increment. This incremented value defines Serial Number
, while the Token Scheme
can be chosen freely.
Foundry ID
is not allowed to change after deployment, therefore neither Address
, nor Serial Number
or
Token Scheme
can change during the lifetime of the foundry.
Foundries control the supply of tokens with unique identifiers, so-called Token IDs
. The Token ID
of tokens
controlled by a specific foundry is the concatenation of Foundry ID
|| Token Tag
.
Foundry Output
Describes a foundry output that is controlled by an alias.
Name | Type | Description | ||||||||||||||||||
Output Type | uint8 | Set to value 5 to denote a Foundry Output. | ||||||||||||||||||
Amount | uint64 | The amount of IOTA coins to held by the output. | ||||||||||||||||||
Native Tokens Count | uint16 | The number of different native tokens held by the output. | ||||||||||||||||||
Native Tokens optAnyOf |
Native Token
|
|||||||||||||||||||
Serial Number | uint32 | The serial number of the foundry with respect to the controlling alias. | ||||||||||||||||||
Token Tag | ByteArray[12] | Data that is always the last 12 bytes of ID of the tokens produced by this foundry. | ||||||||||||||||||
Circulating Supply | uint256 | Circulating supply of tokens controlled by this foundry. | ||||||||||||||||||
Maximum Supply | uint256 | Maximum supply of tokens controlled by this foundry. | ||||||||||||||||||
Token Scheme oneOf |
Simple Token Scheme
|
|||||||||||||||||||
Unlock Conditions Count | uint8 | The number of unlock conditions following. | ||||||||||||||||||
Unlock Conditions atMostOneOfEach |
Address Unlock Condition
|
|||||||||||||||||||
Blocks Count | uint8 | The number of feature blocks following. | ||||||||||||||||||
Blocks atMostOneOfEach |
Metadata BlockDefines metadata (arbitrary binary data) that will be stored in the output.
|
Amount
field must fulfill the dust protection requirements and must not be0
.Native Tokens Count
must not be greater thanMax Native Tokens Count
.Native Tokens
must be lexicographically sorted based onToken ID
.- Each Native Token must be unique in the set of
Native Tokens
based on itsToken ID
. No duplicates are allowed. Amount
of any Native Token must not be0
.- It must hold true that
Unlock Conditions Count
=1
. Unlock Condition Type
of an Unlock Condition must define on of the following types:- Address Unlock Condition
- Address Unlock Condition might only define an Alias Address as
Address
. - It must hold true that
0
≤Blocks Count
≤1
. Block Type
of a Block must define on of the following types:- Metadata Block
- Syntactic validation of all present feature blocks must pass.
Token Scheme Type
must match one of the supported schemes. Any other value results in invalid output.Circulating Supply
must not be greater thanMaximum Supply
.Maximum Supply
must be larger than zero.
A foundry is essentially a UTXO state machine. A transaction might either create a new foundry with a unique
Foundry ID
, transition an already existing foundry or destroy it. The current and next states of the state machine
are encoded in inputs and outputs respectively.
- The current state of the foundry with
Foundry ID
X
in a transaction is defined as the consumed foundry output whereFoundry ID
=X
. - The next state of the foundry with
Foundry ID
X
in a transaction is defined as the created foundry output whereFoundry ID
=X
. Foundry Diff
is the pair of the current and next state of the foundry output in the transaction.
A transaction that... | Current State | Next State |
---|---|---|
Creates the foundry | Empty | Output with Foundry ID |
Transitions the foundry | Input with Foundry ID |
Output with Foundry ID |
Destroys the foundry | Input with Foundry ID |
Empty |
- The foundry output must be unlocked like any other output type where the Address Unlock Condition defines an Alias Address, by transitioning the alias in the very same transaction. See section alias unlocking for more details.
- When the current state of the foundry with
Foundry ID
is empty, it must hold true forSerial Number
in the next state, that:Foundry Counter(InputAlias) < Serial Number <= Foundry Counter(OutputAlias)
- An alias can create several new foundries in one transaction. It was written for the alias output that freshly
created foundry outputs must be sorted in the list of outputs based on their
Serial Number
. No duplicates are allowed. - The two previous rules make sure that each foundry output produced by an alias has a unique
Serial Number
, hence eachFoundry ID
is unique.
- Native tokens present in a transaction are all native tokens present in inputs and outputs of the transaction. Native
tokens of a transaction must be a set based on their
Token ID
. - There must be at most one
Token ID
in the native token set of the transaction that maps to a specificFoundry ID
. - Let
Token Diff
denote the difference between native token balances of the input and the output side of the transaction of the singleToken ID
that maps to theFoundry ID
. Minting results in excess of tokens on the output side (positive diff), burning results in excess on the input side (negative diff). Now, the following conditions must hold forToken Diff
:Current State(Circulating Supply) + Token Diff = Next State(Circulating Supply)
.- When
Current State
is empty,Current State(Circulating Supply) = 0
. - When
Next State
is empty,Next State(Circulating Supply) = 0
.
Token Scheme Validation
takesToken Diff
andFoundry Diff
and validates if the scheme constraints are respected. This can include validatingToken Tag
part of theToken IDs
and theToken Scheme
fields inside the foundry output.Simple Token Scheme
validates that theToken Tag
part of theToken ID
(last 12 bytes) matches theToken Tag
field of the foundry output.- Additional token schemes will be defined that make use of the
Foundry Diff
as well, for example validating that a certain amount of tokens can only be minted/burned after a certain date.
- When neither
Current State
norNext State
is empty:Maximum Suppply
field must not change.Address
field of Address Unlock Condition must not change.Serial Number
must not change.Token Tag
must not change.Token Scheme Type
must not change.
- A token scheme is a list of hard coded constraints. It is not feasible at the moment to foresee the future
needs/requirements of hard coded constraints, so it is impossible to design token schemes as any possible combination
of those constraints. A better design would be to have a list of possible constraints (and their related fields) from
which the user can choose. The chosen combination should still be encoded as a bitmask inside the
Token ID
. - For now, only token scheme
0
is supported. Additional token schemes will be designed iteratively while doing the testnet implementation. - The
Foundry ID
of a foundry output should have a global mapping table in nodes, so that given aFoundry ID
, theOutput ID
of the foundry output can be retrieved.Foundry ID
behaves like an address that can't unlock anything. While it is not necessarily needed for the protocol, it is needed for client side operations (what is the current state of the foundry? accessing token metadata in foundry based onFoundry ID
derived fromTokend ID
).
Non-fungible tokens in the ledger are implemented with a special output type, the so-called NFTOutput.
Each NFT output gets assigned a unique identifier NFT ID
upon creation by the protocol. NFT ID
is BLAKE2b-160 hash
of the Output ID that created the NFT. The address of the NFT is the concatenation of NFT Address Type
||
NFT ID
.
The NFT may contain immutable metadata set upon creation, and a verified Issuer
. The output type supports all
non-alias specific (state controller, governor) unlock conditions and optional feature blocks so that the output can be
sent as a request to smart contract chain accounts.
NFT Output
Describes an NFT output, a globally unique token with metadata attached.
Name | Type | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Output Type | uint8 | Set to value 6 to denote a NFT Output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Amount | uint64 | The amount of IOTA tokens held by the output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens Count | uint16 | The number of native tokens held by the output. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Native Tokens optAnyOf |
Native Token
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NFT ID | ByteArray[20] | Unique identifier of the NFT, which is the BLAKE2b-160 hash of the Output ID that created it. NFT Address = NFT Address Type || NFT ID | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Immutable Metadata Length | uint32 | Length of the following Immutable Metadata field in bytes. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Immutable Metadata | ByteArray | Binary metadata attached immutably to the NFT. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions Count | uint8 | The number of unlock conditions following. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Unlock Conditions atMostOneOfEach |
Address Unlock Condition
Dust Deposit Return Unlock ConditionDefines the amount of IOTAs used as dust deposit that have to be returned to Return Address.
Timelock Unlock ConditionDefines a milestone index and/or unix timestamp until which the output can not be unlocked.
Expiration Unlock ConditionDefines a milestone index and/or unix time until which only Address, defined in Address Unlock Condition, is allowed to unlock the output. After the milestone index and/or unix time, only Return Address can unlock it.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks Count | uint8 | The number of feature blocks following. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks atMostOneOfEach |
Sender BlockIdentifies the validated sender of the output.
Issuer BlockIdentifies the validated issuer of the NFT output.
Metadata BlockDefines metadata (arbitrary binary data) that will be stored in the output.
Tag BlockDefines an indexation tag to which the output can be indexed by additional node plugins.
|
Amount
field must fulfill the dust protection requirements and must not be0
.Native Tokens Count
must not be greater thanMax Native Tokens Count
.Native Tokens
must be lexicographically sorted based onToken ID
.- Each Native Token must be unique in the set of
Native Tokens
based on itsToken ID
. No duplicates are allowed. Amount
of any Native Token must not be0
.- It must hold true that
0
≤Unlock Conditions Count
≤4
. Unlock Condition Type
of an Unlock Condition must define on of the following types:- Address Unlock Condition
- Dust Deposit Return Unlock Condition
- Timelock Unlock Condition
- Expiration Unlock Condition
- Unlock Conditions must be sorted in ascending order based on their
Unlock Condition Type
. - Syntactic validation of all present unlock conditions must pass.
- Address Unlock Condition must be present.
- It must hold true that
0
≤Blocks Count
≤4
. Block Type
of a Block must define on of the following types:- Sender Block
- Issuer Block
- Metadata Block
- Tag Block
- Blocks must be sorted in ascending order based on their
Block Type
. - Syntactic validation of all present feature blocks must pass.
Immutable Metadata Length
fields can not be greater thanMaxMetadataLength
.Address
field of the Address Unlock Condition must not be the same as the NFT address derived fromNFT ID
.
- Explicit
NFT ID
:NFT ID
is taken as the value of theNFT ID
field in the NFT output. - Implicit
NFT ID
: When an NFT output is consumed as an input in a transaction andNFT ID
field is zeroed out, take the BLAKE2b-160 hash of theOutput ID
of the input asNFT ID
. - For every non-zero explicit
NFT ID
on the output side there must be a corresponding NFT on the input side. The corresponding NFT has the explicit or implicitNFT ID
equal to that of the NFT on the output side.
- The unlock block of the input corresponds to
Address
field of the Address Unlock Condition and the unlock is valid. - The unlock is valid if and only if all unlock conditions and feature blocks present in the output validate.
- When a consumed NFT output has a corresponding NFT output on the output side,
Immutable Metadata Length
andImmutable Data
fields are not allowed to change. - When a consumed NFT output has an
Issuer Block
and a corresponding NFT output on the output side,Issuer Block
is not allowed to change. - When a consumed NFT output has no corresponding NFT output on the output side, the NFT it is being burned. Funds and assets inside the burned NFT output must be redistributed to other outputs in the burning transaction.
Other outputs in the ledger that are locked to the address of the NFT can only be unlocked by including the NFT itself in the transaction. If the NFT is burned, such funds are locked forever. It is strongly advised to always check and sweep what the NFT owns in the ledger before burning it.
- When
Issuer Block
is present in an output and explicitNFT ID
is zeroed out, an input withAddress
field that corresponds toIssuer
must be unlocked in the transaction. IfAddress
is either Alias Address or NFT Address, their corresponding outputs (defined byAlias ID
andNFT ID
) must be unlocked in the transaction. - All Unlock Condition imposed transaction validation criteria must be fulfilled.
- All Feature Block imposed transaction validation criteria must be fulfilled.
- It would be possible to have two-step issuer verification: First NFT is minted, and then metadata can be immutably
locked into the output. The metadata contains an issuer public key plus a signature of the unique
NFT ID
. This way a smart contract chain can mint on behalf of the user, and then push the issuer signature in a next step.
Two of the introduced output types (Alias, NFT) implement the so-called UTXO chain
constraint. These outputs receive their unique identifiers upon creation, generated by the protocol, and carry it
forward with them through transactions until they are destroyed. These unique identifiers (Alias ID
, NFT ID
) also
function as global addresses for the state machines, but unlike Ed25519 Addresses, they are not backed by private
keys that could be used for signing. The rightful owners who can unlock these addresses are defined in the outputs
themselves.
Since such addresses are accounts in the ledger, it is possible to send funds to these addresses. The unlock mechanism of such funds is designed in a way that proving ownership of the address is reduced to the ability to unlock the corresponding output that defines the address.
A transaction may consume a (non-alias) output that belongs to an Alias Address by also consuming (and thus
unlocking) the alias output with the matching Alias ID
. This serves the exact same purpose as providing a signature
to unlock an output locked under a private key backed address, such as Ed25519 Addresses.
On protocol level, alias unlocking is done using a new unlock block type, called Alias Unlock Block.
Alias Unlock Block
Points to the unlock block of a consumed alias output.
Name | Type | Description |
Unlock Block Type | uint8 | Set to value 2 to denote a Alias Unlock Block. |
Alias Reference Unlock Index | uint16 | Index of input and unlock block corresponding to an alias output. |
This unlock block is similar to the Reference Unlock Block. However, it is valid if and only if the input of the
transaction at index Alias Reference Unlock Index
is an alias output with the same Alias ID
as the one derived from
the Address
field of the to-be unlocked output.
Additionally, the Alias Unlock Blocks must also be ordered to prevent circular dependencies:
If the i-th Unlock Block of a transaction is an Alias Unlock Block and has Alias Reference Unlock Index
set to k,
it must hold that i > k. Hence, an Alias Unlock Block can only reference an Unlock Block (unlocking the
corresponding alias) at a smaller index.
For example the scenario where Alias A
is locked to the address of Alias B
while Alias B
is in locked to the
address of Alias A
introduces a circular dependency and is not well-defined. By requiring the Unlock Blocks to be
ordered as described above, a transaction consuming Alias A
as well as Alias B
can never be valid as there would
always need to be one Alias Unlock Block referencing a greater index.
- It must hold that 0 ≤
Alias Reference Unlock Index
<Max Inputs Count
.
- The address of the unlocking condition of the input being unlocked must be an Alias Address.
- The index
i
of the Alias Unlock Block is the index of the input in the transaction that it unlocks.Alias Reference Unlock Index
must be <i
. Alias Reference Unlock Index
defines a previous input of the transaction and its unlock block. This input must be an Alias Output withAlias ID
that refers to the Alias Address being unlocked.- The referenced Alias Output must be unlocked for state transition.
NFT ID
field is functionally equivalent to Alias ID
of an alias output. It is generated the same way, but it can
only exist in NFT outputs. Following the same analogy as for alias addresses, NFT addresses are iota addresses that are
controlled by whoever owns the NFT output itself.
Outputs that are locked under NFT Address
can be unlocked by unlocking the NFT output in the same transaction that
defines NFT Address
, that is, the NFT output where NFT Address Type Byte || NFT ID = NFT Address
.
An NFT Unlock Block looks and behaves like an Alias Unlock Block, but the referenced input at the index must
be an NFT output with the matching NFT ID
.
NFT Unlock Block
Points to the unlock block of a consumed NFT output.
Name | Type | Description |
Unlock Block Type | uint8 | Set to value 3 to denote a NFT Unlock Block. |
NFT Reference Unlock Index | uint16 | Index of input and unlock block corresponding to an NFT output. |
An NFT Unlock Block is only valid if the input in the transaction at index NFT Reference Unlock Index
is the NFT
output with the same NFT ID
as the one derived from the Address
field of the to-be unlocked output.
If the i-th Unlock Block of a transaction is an NFT Unlock Block and has NFT Reference Unlock Index
set to k, it
must hold that i > k. Hence, an NFT Unlock Block can only reference an Unlock Block at a smaller index.
- It must hold that 0 ≤
NFT Reference Unlock Index
<Max Inputs Count
.
- The address of the input being unlocked must be an NFT Address.
- The index
i
of the NFT Unlock Block is the index of the input in the transaction that it unlocks.NFT Reference Unlock Index
must be <i
. NFT Reference Unlock Index
defines a previous input of the transaction and its unlock block. This input must be an NFT Output withNFT ID
that refers to the NFT Address being unlocked.
- New output types increase transaction validation complexity, however it is still bounded.
- Outputs take up more space in the ledger, UTXO database size might increase.
- It is possible to intentionally deadlock aliases and NFTs, however client side software can notify users when they perform such action. Deadlocked aliases and NFTs can not be unlocked, but this is true for any funds locked into unspendable addresses.
- Time based output locking conditions can only be evaluated after attachment to the Tangle, during milestone confirmation.
- IOTA ledger can only support hard-coded scripts. Users can not write their own scripts because there is no way currently to charge them based on resource usage, all IOTA transactions are feeless by nature.
- Aliases can be destroyed even if there are foundries alive that they control. Since only the controlling alias can unlock the foundry, such foundries and the supply of the tokens remain forever locked in the Tangle.
- Token schemes and needed supply control rules are unclear.
The feeless nature of IOTA makes it inherently impossible to implement smart contracts on layer 1. A smart contract platform shall not only be capable of executing smart contracts, but also to limit their resource usage and make users pay validators for the used resources. IOTA has no concept of validators, neither fees. While it would technically be possible to run EUTXO smart contracts on the layer 1 Tangle, it is not possible to properly charge users for executing them.
The current design aims to combine the best of both worlds: Scalable and feeless layer 1 and Turing-complete smart contracts on layer 2. Layer 1 remains scalable because of parallel transaction validation, feeless because the bounded hard-coded script execution time, and layer 2 can offer support for all kinds of virtual machines, smart contracts and advanced tokenization use cases.
- List of supported Token Schemes is not complete.
- Deflationary token scheme
- Inflationary token scheme with scheduled minting
- etc.
- Adapt the current congestion control, i.e. Message PoW, to better match the validation complexity of the different outputs and types.
Copyright and related rights waived via CC0.