-
Notifications
You must be signed in to change notification settings - Fork 4
Description of splitting Assets cryptocondition contract and dual evalcode token tx validation
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 in cryptocondition contracts.
The Assets contract is split into Tokens and Assets contracts. Tokens circulate both within the Tokens contract and other contracts using tokens due to dual eval vouts. Business logic rules are removed from the Tokens contract into other contracts using tokens 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 ensures '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 the other contract's XXXValidate().
The Tokens contract provides other contracts using tokens with AddTokensCCInputs() function which adds only valid uxtos as token inputs (skipping possible fake and incorrect formed tx).
For this thing to work, transactions in Tokens contract should have tokenid (assetid) in their opret:
opret: EVAL_TOKENS<funcid><tokenid><ccType>{<vout pubkey1>,...}<evalcode><other 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. More information about using these pubkeys is here: Token vouts validation in token transactions
The rest of the opret is the payload of any other contract using tokens. It is critical that first uint8_t field contains that other contract's eval code. It is the other contract responsible for adding its evalcode as the first element of the payload. The rest of the opret other contracts may format for their needs.
Note that the Tokens contract tokentransfer rpc function does not preserve other contracts payload when tokens are transferred. This function actually moves tokens between different contracts (each of which would attach its own token payload). The function tokenconvert is used to add the second eval code to token vouts, by this attaching tokens to other contracts.
The benefits of this Tokens contract are:
- Unification of token usage across all contracts
- Concentration of complex token-specific validation rules in the single Tokens contract.