On-chain Code Documentation
The on-chain component is written in Aiken, a functional programming language designed for Cardano smart contracts.
Overview
The cage smart contract manages MPF (Merkle Patricia Forestry) tokens on Cardano. It implements a "caging" pattern where tokens are locked at a script address with their MPF root stored in the datum.
Module Structure
types.ak
Defines the core data types used throughout the contract:
Mint
Represents the type of minting operation:
- Boot - Creates a new MPF token with an empty root
- Destroy - Burns an existing MPF token
MintRedeemer
Redeemer for minting policy validation:
UpdateRedeemer
Redeemer for spending validator (state updates):
State
Represents the current state of an MPF token:
type State {
owner: ByteArray, // Public key hash of the owner
root: Hash<Blake2b_256, ByteArray> // Current MPF root hash
}
Operation
Defines the operations that can be applied to an MPF:
type Operation {
Insert { new_value: ByteArray }
Delete { old_value: ByteArray }
Update { old_value: ByteArray, new_value: ByteArray }
}
Request
A pending modification request:
type Request {
tokenId: TokenId, // Target token identifier
owner: ByteArray, // Request owner's pub key hash
key: ByteArray, // MPF key to modify
operation: Operation // The requested operation
}
CageDatum
The datum type for UTxOs at the cage address:
type CageDatum {
RequestDatum { request: Request } // A pending request
StateDatum { state: State } // Token state
}
lib.ak
Helper functions for token manipulation:
quantity(value, policy, name)
Extracts the quantity of a specific asset from a Value.
assetName(value, policy)
Retrieves the asset name of the first token found for a given policy.
valueFromToken(policy, name, quantity)
Creates a Value containing the specified token.
tokenFromValue(value, policy)
Extracts token information (name and quantity) from a Value.
extractTokenFromInputs(inputs, policy)
Finds and extracts a token from a list of transaction inputs.
cage.ak
The main validator implementing both minting policy and spending validator.
Minting Policy
The minting policy controls token creation and destruction:
Boot (Minting)
When creating a new token: 1. The token ID must be unique (derived from a spent UTxO) 2. Exactly one token is minted 3. A new State UTxO is created at the cage address with: - The minted token - A null (empty) MPF root - The signer as the owner
Destroy (Burning)
When destroying a token: 1. The token is burned (quantity: -1) 2. The owner must sign the transaction
Spending Validator
The spending validator handles four cases based on the redeemer:
End (ConStr0)
Terminates the token: - Validates ownership - Token is burned via the minting policy
Contribute (ConStr1)
Spends a request UTxO in an update transaction: - Verifies the referenced state UTxO is being consumed - Part of batch request processing
Modify (ConStr2)
Updates the MPF state: - Validates ownership - Verifies all proofs match the requests being consumed - Computes the new root from applied operations - Ensures the output contains the updated state
Retract (ConStr3)
Allows request owners to reclaim their request UTxOs: - Only the request owner can retract - Returns the locked ADA
Validation Flow
sequenceDiagram
participant User
participant Validator
participant MPF
User->>Validator: Boot Transaction
Validator->>Validator: Verify unique token ID
Validator->>MPF: Create empty state
User->>Validator: Request Transaction
Validator->>MPF: Store request at cage address
User->>Validator: Update Transaction
Validator->>Validator: Verify ownership
Validator->>Validator: Validate proofs
Validator->>MPF: Apply changes, update root
User->>Validator: Retract Transaction
Validator->>Validator: Verify request ownership
Validator->>User: Return locked ADA
Security Properties
- Ownership: Only the token owner can modify or destroy the token
- Integrity: All MPF modifications include cryptographic proofs
- Uniqueness: Token IDs are derived from spent UTxOs, ensuring uniqueness
- Retractability: Request owners can always reclaim their pending requests