Skip to content

Description of splitting Assets cryptocondition contract and cross contract token validation

dimxy edited this page Jan 11, 2019 · 17 revisions

The previous implementation of token based cryptocondition contracts (like Assets, Gateways,...) based on conversion of tokens by tokenconvert rpc, moving tokens from the base Assets contract to other contracts, was hard to maintain and error-prone. Things became even more complicated when the fix for 'fake tokens' problem was implemented which supposed each token transaction should be balanced on its token value, otherwise it would be considered as a 'fake tx' (see also 'Fake tokens' problem description).

This is a description of the change of the token implementation approach.

The conversion of tokens from Assets contract to other contracts by the tokenconvert rpc is deprecated. The Assets contract is split into Tokens and Assets contracts. Tokens are issued and remain only within the Tokens contract and never should be converted outside. Business logic is removed from the Tokens contract to other contracts like Assets, Gateways, Heir etc, and the Tokens contract becomes a basis for token support for any other contracts which need tokens.

In a way we may call this Tokens contract as 'bitcoin for tokens' as its sole purpose is only enforcing token issuance and transfer.

The Tokens contract should contain only basic operations with tokens like create ('c'), transfer tokens ('t') and in future may be burn tokens ('b') and add more tokens ('a').

The Tokens contract enforces 'fake token' detection (to prevent token injection or leakage) by validating token value balance for token transactions' cc inputs and cc outputs. To allow enforcing both tokens and the other contract rules token vouts should contain both eval codes (as cc eval cryptoconditions). This would trigger validation functions TokensValidate and OtherContractValidate.

The Tokens contract provides other contracts, using tokens, with AddTokensCCInputs() function which adds only valid uxtos as token inputs (skipping possible fake tx).

For this thing to work, transactions in Tokens contract should have tokenid (assetid) in their opret:

opret: <evalcode><funcid><tokenid><ccType>{<vout pubkey1>,...}<other contract payload...>

As we can see, the Tokens contract's opret contains evalcode of some other contract and tokenid. Apart of that it contains several vout pubkeys, with which token transactions' vouts are validated, that is, if they belong to EVAL_TOKENS address space. It is done to control token leakage to other contracts (which is prohibited from now on). Currently uint8_t ccType may take values of '0' (no pubkeys following), '1' (one pubkey) or '2' (two pubkeys for 1of2 cryptoconditions). That is, it is important for token validation to work that the contract creating a token transaction attaches one or two destination pubkeys to the tx opret.

The rest of the opret is the payload of any other contract using tokens. Those other contracts may format this opret's additional payload for their needs.

Note that the Tokens contract tokentransfer rpc function does not forward (but simply drops) other contracts payload when tokens are transferred. This is not necessary and this is actually allows to move tokens between contracts of different type (each of which may attach its own token payload)


The benefits of this Tokens contract are:

  • Unification of token usage across all contracts
  • Separation of token address space and other contracts cc address space which avoids the need of balancing token cc value in those other contracts (what leads to contracts' simplification, errors decreasing).
Clone this wiki locally