Design overview
Most coins that are part of the Circuit protocol are structured similarly. Generally, their authorization is proved through lineage — sometimes with Chia's standard singleton puzzle, sometimes using custom logic. A protocol coin usually has a collection of operations, each one governing a way the coin can be spent.
Operations
The majority of protocol coin types have operations stored by module hash in curried parameters. Precisely how these operations are formatted appears ad hoc, based on factors like access control. Here are a few examples.
Collateral vaults have separate
OWNER_OPERATIONS
andKEEPER_OPERATIONS
lists, and the collateral-vault puzzle manages access control for which operations can be invoked.Atom announcers have all operations stored in an
OPERATIONS
list, despite having both keeper and owner operations. The atom announcer passes whether the spender is the owner or keeper as a parameter to the operation, which can choose to fail if the spender is not authorized.The oracle coin only has two operations, taking their module hashes explicitly as parameters (
MUTATION_PROGRAM_HASH
andOUTLIER_RESOLUTION_PROGRAM_HASH
) instead of in a list.
Regardless of how these operations are stored, the general pattern for how coins are spent is the same. The spender reveals an operation to execute and provides necessary data. The coin confirms the operation is permitted and executes the module. Then, the return value is used to determine the final spend conditions.
Coins obtain the target operation and parameters in two main ways.
For the keeper operations, this data is provided directly in the solution. See the collateral vault's keeper operations or all statutes operations for examples.
Coins that have owners (like governance CRTs) obtain the operation and parameters from
REMARK
conditions when the inner puzzle is solved.
The precise format of operation results also varies across coins. For example, the governance operations return both a bill and a list of conditions. The oracle operations just return the exact list of conditions for the spend. The statutes operations output a list of seven items, including the new statutes and additional spend conditions.
Authorization
One of the key challenges in the protocol is distinguishing between trusted coins, carrying meaningful state, and coins that are not relevant to the protocol (or are malicious). Chia provides a few primitives for maintaining logical state on chain, like singleton and CAT. But these tools are not always easily applicable. For instance, the governance coins need to be stateful while holding CAT balance at the same time. This is addressed by implementing custom singleton-like behavior.
Since this pattern is so common across protocol coins, we will quickly outline how it works. It would be convenient if we could verify a coin's authorization simply by verifying that it comes from a correct puzzle hash. But this is not always possible, because coins can have stateful curried parameters. What stops an attacker from creating a new coin with the same puzzle hash and choosing these maliciously?
A solution is to carefully limit which coins can be spent. For instance, if we want to enforce requirements on a coin's creation (such as initial state, minimum balance, approval by governance, etc.), we require that either
the coin is created by a coin of the same type, or
the coin was created in accordance with these requirements
in order to spend it. Then, we carefully enforce what coins our trusted ones can create. For example, a collateral coin can only be spent if a) it was created by a collateral coin (which can set a nonzero collateral), or b) it has zero collateral. Thus, every collateral coin spent originates from a collateral coin with zero collateral.
Condition filtering
The protocol coins communicate through announcements, but there are situations where the spender needs to add their own. For example, when withdrawing collateral, the spender needs to tie the receipt of XCH to the collateral coin spent. Otherwise, the spend bundle might be split by an attacker to take the XCH for themselves. So protocol coins have to allow spenders to add announcements while preventing them from forging protocol communication.
This is accomplished by protecting specific condition formats. In particular, announcements and messages that begin with a specific byte and are 33 bytes long cannot be provided by spenders. Remarks that begin with that same byte are also protected.