QIP: 7
Layer: Consensus (hard fork)
Title: Qi UTXO Ledger
Author: wizeguyy <[email protected]>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/quainetwork/qips/wiki/Comments:QIP-0007
Status: Draft
Type: Standards Track
Created: 2023-12-05
License: BSD-2-Clause
This specification proposes a UTXO ledger for one of the two tokens in the Quai two-token system.
The Quai protocol is designed to be a two-token system. The first token ledger is designed to be highly programmable, including a smart contract VM and an account based ledger to compliment the VM. The second token ledger, will sacrifice programability in order to achieve maximum computational and communication efficiency. The second token ledger will use a UTXO accounting system, due to its improved resource efficiency. This specification describes the design of that ledger.
The design of the UTXO ledger requires consideration of the following details:
- What is the structure of a transaction output?
- What is the structure of a UTXO transaction?
- How are those transactions recorded in a block?
These questions will be answered in the following sections.
Each transaction output will indicate a denomination of coins, as well as an address which is allowed to spend those coins. Specifically, each output will contain the following data:
{
denomination: uint8,
address: []byte,
}
The value field of a transaction output is constrained to 1 of 16 denominations, where each denomination represents a quantity of Qi. The denominations are given below:
denomination | number of Qi |
---|---|
0 | 0.001 Qi |
1 | 0.005 Qi |
2 | 0.01 Qi |
3 | 0.05 Qi |
4 | 0.1 Qi |
5 | 0.25 Qi |
6 | 0.5 Qi |
7 | 1 Qi |
8 | 5 Qi |
9 | 10 Qi |
10 | 20 Qi |
11 | 50 Qi |
12 | 100 Qi |
13 | 1000 Qi |
14 | 10000 Qi |
15 | 100000 Qi |
16 | 1000000 Qi |
Every transaction must reference a set of unspent transaction outputs as inputs to be spent, and create new transaction outputs to be added to the UTXO set. Transaction inputs will be referenced by the hash of the transaction which minted them and the index of the output in that transaction. Additionally, each transaction must contain a valid aggregate signature from each UTXO key.
Putting it all together, transactions will contain the following data, RLP encoded:
transaction: {
chainId: uint256, // for replay protection
inputs: [
{
txhash: []byte, // hash of the transaction which minted the output
index: uint16, // output index of that transaction
pubkey: []byte, // public key corresponding to the output's address
},
...
],
outputs: [
{
denomination: uint8, // which denomination of coins this output represents
address: []byte, // owner's address
},
...
],
signature: []byte, // aggregate signature
}
- Each transaction input must not have been spent by any other transaction.
- The cumulative sum of all created transaction outputs must not be greater than the cumulative sum of all destroyed input UTXOs.
- Each new output must have a valid coin denomination.
- Each input key must match the address of the input UTXO.
- Each transaction must contain a valid aggregate signature of from each input key.
In order for miners to claim the block reward, we create a special virtual input to be spent by the coinbase transaction in each block. This virtual input will contain the value of the block reward + fees and the miner's address. Since this virtual input has no originating transaction, it cannot be referenced by transaction hash and index. Instead, the coinbase transaction will reference it by the parent block hash and index = 65535.
For example, the coinbase transaction at block n would be:
transaction: {
chainId: ...,
inputs: [
{ // coinbase virtual input
txhash: Hash(block[n-1]), // txhash is replaced by parent block hash
index: 65535,
pubkey: <miner address>,
}
... // possibly other inputs
],
outputs: [
{
denomination: ...,
address: <miner address>
},
... // possibly other outputs to satisfy denominations
],
signature: <miner signature>,
}
Each transaction must contain a valid aggregate signature, signing the hash of the transaction data.
The Quai network is a sharded system, where each 'zone chain' processes transactions related to its shard(s) of the ledger(s), as defined in qip-0002. Furthermore, qip-0005 describes how transactions may transmit value or data across shards, via an external transaction message (aka ETX). The The mechanics of transmitting and referencing ETXs across shards/chains is described fully in qip-0005. This document will just focus on the structure, processing, and emission of ETXs from a given shard of the Qi ledger.
Simply, an ETX is equivalent to a Qi UTXO. When an ETX becomes referencable at the destination shard, each ETX will be directly added to the UTXO set and made available for spending in the Qi ledger.
Conversely, when emitting an ETX, the ETX may contain extra data according to the transaction format of the ledger it is destined for. When a Qi transaction creates an output to an address which resides outside the shard which processed the transaction, that output will be treated as an ETX instead of being added as a new output in the UTXO set. This ETX will be added to the block's ETX list for cross-chain propagation (if the destination shard is across chains) as described in qip-0005.
Note, in the case of an ETX destined to the Quai account ledger, the ETX structure will be different according to the transaction format of that ledger.
A few additional pieces of data must be committed to in the block structure:
- List of UTXO transactions
- Transactions must be added to the block's transaction list, and committed to the transactions root hash in the header.
- Committment to updated set of UTXOs
- The UTXO set will be committed to in a merkle tree, and the merkle root will be included in the block header.
The UTXO ledger does not implement a metered VM in the traditional sense, but the cost of processing each transaction still needs to be accounted for, so that consensus may regulate block capacity. To determine the gas usage of a transaction, we give the following equation:
tx_gas = LOOKUP_COST x num_inputs
+ INSERTION_COST x num_outputs
+ VERIFICATION_COST
The constants in this equation are defined below:
name | gas cost |
---|---|
LOOKUP_COST | TBD |
INSERTION_COST | TBD |
VERIFICATION_COST | TBD |
- The first output in each block is the coinbase output, and will not have a corresponding transaction
- The sum of all created outputs must equal the sum of all spent inputs + fees + block reward
- Each transaction must be valid according to the transaction validation rules.
- UTXO transactions must be counted against the block gas limit according to its gas usage
Address reuse is an ill-advised practice, wherein multiple UTXOs may be encumbered by the same address. One common cause of this, is the case where a wallet creates a change output to the same address as the sender's input address. This practice significantly harms user privacy and fungibility of the token itself. It is impractical to fully disallow address reuse, but we impose one additional transaction rule to greatly mitigate address reuse, by making those common bad-practices impossible: every address in a transaction (inputs and outputs) must be unique.
This QIP licensed under the BSD 2-clause license.