Skip to main content

7 - Lifecycle of a Coin

You should now understand how to create Chialisp puzzles that lock up coins with a particular set of rules. You may be wondering how a coin gets locked up, where it is stored, how and when it gets spent, and who can spend it. Let's take a look at the steps a coin goes through from its creation to its destruction.

The Coin Set Model#

Chia's model of how coins are stored is called the coin set model and is closely modelled after Bitcoin's UTXO model. The idea is simple, every full node holds onto a database of coin records:

class Coin:  parent_coin_info: bytes32  puzzle_hash: bytes32  amount: uint64
class CoinRecord:  coin: Coin  confirmed_block_index: uint32  spent_block_index: uint32  spent: boolean  coinbase: boolean  timestamp: uint64

Note that the Coin object is part of the blockchain format and cannot be changed, while the CoinRecord object is part of the full node and can be modified by alternative implementations

This database is generated by looking at the blockchain from block height zero and processing all of the transactions until it is in sync with the current peak. When a full node processes a spend, it first makes sure that the coin being spent exists in its database, runs and validates the Chialisp conditions that are produced, marks the coin that was being spent as spent, and adds any new coins that were created as a result.

This approach makes the database very light as the blockchain grows, but still allows for complexity and smart contracts through Chialisp which is committed to inside the puzzle hash. It is also an important distinction from some other blockchains in that all state in Chialisp is stored in the coins. There is no special way to store data so that people can access it; the majority of that functionality comes from finding ways to reveal the proper information in puzzle reveals and by committing to predictable puzzle hashes.

Farming Rewards#

As you probably know, farmers create the entirety of new value in Chia. Every 18.75 seconds or so, a new block pops into existence that allows for a farmer to create a coin out of thin air, in two amounts of a very specific ratio: 1 to 7 (farmer to pool). Here are two actual farming rewards that were generated in block 10,000 of the blockchain:

[  {    "amount" : 1750000000000,    "parent_coin_info" : "0xccd5bb71183532bff220ba46c268991a0000000000000000000000000000270b",    "puzzle_hash" : "0x0b42a11b76d276f191026ae1a01c711cc0637e63d8ce0c2f62d6d079cc974920"  },  {    "amount" : 250000000000,    "parent_coin_info" : "0x3ff07eb358e8255a65c30a2dce0e5fbb0000000000000000000000000000270b",    "puzzle_hash" : "0x0b42a11b76d276f191026ae1a01c711cc0637e63d8ce0c2f62d6d079cc974920"  }]

Fun fact: The genesis challenge is the hash of a bitcoin block around the time when the Chia network was launched and the message from Bram Cohen: ""Operational error causes Fed payment system to crash" We stand on the shoulders of giants, now let's get farming!"

Notice that the parent_coin_info for both is a little strange. Because they are created out of thin air, the coins are assigned a special value as their parent coin: one half of the "genesis challenge", some filler zeroes, and the index of the block they are farmed in.

pool_parent_id = bytes32(genesis_challenge[:16] + block_height.to_bytes(16, "big"))farmer_parent_id = bytes32(genesis_challenge[16:] + block_height.to_bytes(16, "big"))

This information is usually not very relevant, but it is used. For example, in the pooling puzzles, the reward coin needs to make an announcement as it's being claimed and another puzzle needs to assert that announcement. It's also important to point out that since the genesis challenge is part of the parent info, and since the genesis challenge is different between mainnet and each testnet, it's nearly impossible to end up with two identical coin ids. This is, in part, to prevent signature replay attacks: coins that are signed on testnet won't be able to use the same signature on mainnet.

Spend Bundles#

Alright, so you've received some XCH and you want to deploy your first smart contract. To do that, you're first going to have to create a spend bundle. This is sometimes referred to colloquially as a "transaction". A spend bundle is a simple construct. It is an object that contains exactly two things: a list of coin spends and an aggregated signature.

class SpendBundle:  coin_solutions: List[CoinSpend]  aggregated_signature: G2Element

A coin spend contains exactly three things: The coin you are trying to spend (parent_coin_info, amount, puzzle_hash), the puzzle reveal (needs to tree hash to the puzzle hash), and the solution.

class CoinSolution:  coin: Coin  puzzle_reveal: SerializedProgram  solution: SerializedProgram

The aggregated signature is a BLS signature and needs to be a signature of exactly the public key/message pairs that are output by the conditions from puzzles in the bundle. You cannot aggregate extra signatures, nor can you leave out any. If there are no AGG_SIG_ME or AGG_SIG_UNSAFE conditions that are output, the aggregated signature must be an empty signature which is a "c" followed 191 zeroes.

Once you have constructed a spend bundle, you can push it to the /push_tx RPC endpoint on a Chia full node. Keep in mind a few things though:

  • Unless you farm the block, you cannot guarantee if or when your spend bundle will make it to the network. If two spends rely on each other some way, they must be in the same spend bundle and bound with signatures.
  • When using short absolute timelocks, make sure to leave in a reasonable amount of time for the bundle to reach the network. Sometimes, due to high fee pressure, it may take a while for a low fee transaction to get in. If your spend relies on having the coin available on the blockchain before the timelock passes, it's not a guarantee, so make sure to leave in a good amount of time just in case.
  • Nothing technically keeps the spends in a spend bundle together except for the aggregated signature and announcement logic. If you make a spend that does not assert an announcement from another coin in the spend AND does not require a signature, that spend can be excluded from the bundle by a malicious node or farmer. Also note that a spend bundle can be aggregated with another without your permission. In fact, the block creation code does something similar when it creates the generator, which we will talk about soon.

Remember that other nodes are handling this piece of data as well, and it's best to assume that they will try to change everything that they can. Make sure that if anything changes in a way you don't like, the whole bundle will fail.

Fees and the Mempool#

Once you create and push a spend bundle, your node will validate it and then start gossiping the information out to its peers. When each node receives a transaction it tries to validate it on its own and will reject it if it considers the spend to be invalid. It is extremely important to note that before it validates it, the node can run software that tries to change it. This is why it's very important that you create your spend bundles in a way that any changes will automatically invalidate the signature or the puzzle hash. The malicious node can push the changed transaction to other honest nodes, and if it's not invalid, the honest nodes will accept it.

Once it has validated the spend bundle, it will include the relevant information into its mempool. The mempool is a list of transactions that a node is holding onto and waiting to put into a block. Items in the mempool are sorted by fees. A fee is created when the sum of the CREATE_COIN outputs is less than the amount of the coin that is being spent. This means that no value can ever be destroyed in the Chia ecosystem: if you spend a coin and don't create any new coins, the farmer can add the value of the existing coin to their farming rewards.

When the mempool is deciding which transactions to put into a block, it looks to put in the transactions with the highest fee / cost ratio. We haven't talked much about cost yet, but you can find out more about it here and we'll talk about optimization more in a later section. The main idea is: if you make a complex and hard to run transaction with a lot of data, you will probably end up paying a higher fee to get it into a block in a reasonable time frame.

Transaction Generators#

When a farmer decides to include transactions in a block, they will aggregate all of them into something called a transaction generator. A generator is a clvm program that outputs the information in the spend bundle. It will result in a list of lists that contain:

  • The parent ID of the coin being spent
  • The puzzle and solution reveals
  • The coin amount

That program is then run through another Chialisp program that is a part of consensus and is used when validating every block that a node receives. That program parses the generator for puzzle and solution reveals, and returns a total list of conditions for every transaction in the block.

This is an important concept: all transactions in a block are run in parallel. The fact that all of these transactions are evaluated as one big program provides some valuable benefits:

Farmers cannot front-run a transaction at farm time because there is no order to the transactions. They can kick transactions and spend the coins that the kicked transactions were planning to spend if they'd like, but they cannot then add the transaction back into the set because the coin it was spending is now already spent.

Also, since all transactions are returned as a single list of conditions, your conditions can potentially rely on announcements from other transactions. If you know that a spend frequently happens that creates an announcement, your puzzles can assert that announcement without being the owner of that transaction! This is a potential implementation for oracles on the Chia Network. An entity can create a singleton, or a coin that there is verifiably only one of, and can make announcements every block that puzzles reliant on the announcements can assert.

Furthermore, generators also enable the compression of certain puzzles in a block to reduce their cost. This is entirely dependent on the farmers to make this compression happen, and as a Chialisp developer you don't have much control over it. However, it does incentivize the use of those specific compress-able puzzles. For example, when farmers create a block with the standard transaction format in them, they can compress those transactions down to a single pubkey and a reference of a block to look in to find the full uncompressed puzzle. If you look at the standard puzzle and decide that it's too complex to use as an inner puzzle for a puzzle you are writing, you may try to make a simpler puzzle to save cost. However, you may actually end up paying more cost because your puzzle may not be compressed whereas the standard transaction will!

Conclusion#

You should now have a pretty decent grasp of the environment that your puzzles will be running in. Writing secure Chialisp puzzles requires a somewhat unique way of looking at things that is not shared by many other programming languages or blockchains. Understanding of the network is inextricably intertwined with the creation of puzzles and while that creates a steep learning curve, it also enables incredible functionality in an incredibly compact format.

It is highly recommended that you fully understand this section before you decide to deploy any smart contract so that you can ensure they are secure and functional. Simply running the puzzle on your computer is no substitute for having it run on millions of nodes with indeterminable intent.