# State Machine

### Introduction

The **State Machine** is a core protocol component responsible for maintaining and updating the state of the blockchain as it progresses. It represents the collective state of all accounts, validators, and other relevant data stored on the blockchain.

### Accounts

An account is a structure that holds funds and can send or receive transactions. Each account has a unique address and a balance, think of a bank account, but managed by the blockchain instead of a bank.

```protobuf
message Account {
  // address: the short version of a public key
  bytes address = 1;
  // amount: the balance of funds the account has
  uint64 amount = 2;
}
```

The native cryptocurrency is able to be transferred to and from accounts from a digital signature from the user's private key that corresponds to the account `Address`.

```protobuf
message MessageSend {
  // from_address: is the sender of the funds
  bytes from_address = 1;
  // to_address: is the recipient of the funds
  bytes to_address = 2;
  // amount: is the amount of tokens in micro-denomination
  uint64 amount = 3;
}
```

### Pool

A pool is like an account without an owner, holding funds that are managed directly by the blockchain protocol. It's very similar to an account, but instead of an address, it has a unique ID and operates based on predefined blockchain rules rather than individual control.

```protobuf
message Pool {
  // id: the unique identifier of the pool
  uint64 id = 1;
  // amount: the balance of funds the pool has
  uint64 amount = 2;
}
```

For example:

* **DAO Treasury Pool**
* **Committee Funds Pool**
* **Committee Escrow Pool**

### Validators

A Validator is an actor in the blockchain network responsible for verifying transactions, creating new blocks, and maintaining the blockchain's integrity and security. In Canopy, Validators provide this service for multiple chains by restaking their tokens across multiple 'committees' and run a blockchain client for each.&#x20;

The native cryptocurrency is able to be surety-bonded by participants to become validators. When staking (bonding), validators are able to choose a variety of configurations:

* **Amount**: The amount of tokens to be removed from the sender account and locked as a surety bond against bad behavior during BFT. The entirety of this 'stake' is re-used (restaked) in each Committee BFT at the same time.
* **Committees**: The list of chains the validator is [restaking](https://canopy-network.gitbook.io/docs/canopy-network/core-features/capital-efficient-restaking) their tokens towards.
* **Delegate**: A non-delegate is registering for active participation in the committee consensus, whereas a delegate is a passive participant who contributes their tokens to influence subsidized status.
* **Compounding**: Automatically compounds rewards to the stake or early withdraws them to the reward address for a penalty.
* **Output Address:** The address where early-withdrawal rewards and the unstaking surety bond are transferred to.

⇨ The more tokens the validator bonds, the more voting power it has in consensus operations. However, the more staked, the more at risk if ‘slashed’ for bad behavior like double or non-signing during consensus.

{% hint style="success" %}
Both the Operator and the Output private key may sign transactions on behalf of the Validator, but only the\
Output private key may change the Output address.
{% endhint %}

At any time a validator is able to update their stake information like committees and compounding\
status by submitting an `edit_stake` transaction:

```protobuf
message MessageEditStake {
  // must be greater than or equal to the previous staked amount
  // new_balance = (old.Staked_Tokens - message.amount)
  uint64 amount = 1;
  // update which chains the validator is restaking their tokens for
  repeated uint64 committees = 2;
  // the update to the tcp peer-to-peer address of the peer
  string net_address = 3;
  // the update to the reward address
  bytes output_address = 4;
  // the update to the auto-compounding status
  bool compound = 5;
}
```

In addition to this, the validator may also pause operations across all committees by submitting a `pause_tx`, temporarily removing it from service, and submit an `unpause_tx` to resume committee membership.

```protobuf
// temporary stop participation
message MessagePause {
  bytes address = 1;
}

// restart participation
message MessageUnpause {
   bytes address = 1;
}
```

➩ To exit completely the validator may submit an `unstake_tx`, permanently leaving the committees it is staked for. A Validator may be Paused up to `MaxPauseBlocks` before it's automatically unstaked.

```protobuf
// starts exit.'stake_return_block' saved in Validator structure after submit
message MessageUnstake {
  bytes address = 1;
}
```

{% hint style="warning" %}
For short and long-range attack protection, the validator must wait for governance-controlled `unstaking_blocks` before their bond is fully returned, during which they are still eligible for slashing for bad behavior.
{% endhint %}

### Committee

A validator is eligible to provide shared security services to any Nested Chain it chooses.&#x20;

{% hint style="info" %}
The quorum of Validators that participate in the BFT process are called `Committees`.
{% endhint %}

Once staked for a Committee, the validator connects with the other members of that Committee via the P2P layer. Together, the committee executes consensus for the Nested-Chain, producing immediately final Nested-Chain blocks.&#x20;

{% hint style="info" %}
The Root-Chain functions as just another Nested Chain, leveraging the network's shared security. This design allows validators to secure other sub-chains without being required to validate the Root-Chain itself.&#x20;
{% endhint %}

➩ For each Nested-Chain block, the elected leader submits a **Certificate Result** **Transaction** to the Canopy Root-Chain, encapsulating the results of the quorum.

```protobuf
message CertificateResult {
  // reward_recipients: the recipients who are rewarded based on the quorum decision
  // specifically who the committee agreed to reward from the committee pool
  RewardRecipients reward_recipients = 1;
  // slash_recipients: the recipients who are penalized (slashed) by quorum decision
  // specifically who the committee agreed to slash due to evidence of bad behavior
  SlashRecipients slash_recipients = 2;
  // orders: contains information regarding the 'buying side' of sell orders
  // including actions like 'buy/reserve order' or 'close/complete order'
  Orders orders = 3;
  // checkpoint: contains block information to allow provide Checkpoint-as-a-Service
  Checkpoint checkpoint = 4;
  // retired: tombstone a committee by quorum decision
  bool retired = 5;
}
```

{% hint style="danger" %}
Details of how Validators perform BFT for Nested Chains are found in the [utility](https://canopy-network.gitbook.io/docs/canopy-network/how-does-canopy-work/utility "mention") section.
{% endhint %}

### Subsidization

Each Root-Chain block, new mint is created and is equally sent to the **auto-subsidized** committees' '[Committee Funds Pool](#pool)'.

Committees qualify as `AutoSubsidized` using the following formula:<br>

```go
// calculate the total amount of stake
TotalStake = AllValidatorStake + AllDelegateStake
// calculate the amount of restake for the committee
CommitteeRestake = ValidatorStakeForChain + DelegateStakeForChain
// calculate the restake percentage using the committee restake and the total stake
RestakePercentage = (CommitteeRestake / TotalStake) * 100
// is % restake greater than or equal to the governance parameter?
IsAutoSubsidized = RestakePercentage >= governance.SubsidizationThreshold
```

<details>

<summary> <strong>Example</strong></summary>

Subsidization Threshold:&#x20;

* 33%

Stakes (Total=200):&#x20;

* **V1**=50,&#x20;
* **V2**=50,&#x20;
* **V3**=100

Committees:

* **C1** → V1 + V2 = 100/200 = 50% ✅ (auto-subsidized)
* **C2** → V1 = 50/200 = 25% ❌ (not-auto-subsidized)
* **C3** → V3 = 100/200 = 50% ✅ (auto-subsidized)

</details>

Patrons may manually subsidize the Committee Funds Pool by directly sending tokens to it with an `OpCode` instruction on distribution rules (if applicable)

```protobuf
message MessageSubsidy {
  // the sender of the funds
  bytes address = 1;
  // the id of the committee the funds are being sent to
  uint64 chain_id = 2;
  // the amount of funds that are taken from the sender and transferred to the recipient pool
  uint64 amount = 3;
  // a generic code field to allow fund designation instructions
  string opcode = 4;
}
```

From the **Committee Funds Pool**, the Committee may distribute these rewards at the quorum's discretion.

### Rewards

The tokens for the `RewardRecipients` are distributed from the Committee [#pool](#pool "mention") — which is funded by (auto & manual) [subsidies](https://canopy-network.gitbook.io/docs/canopy-network/canopy-economics/subsidies "mention").

{% hint style="info" %}
If there are multiple Certificate Result Transactions included in a single Root-Chain block — the rewards are normalized based on the number of transactions.
{% endhint %}

Each committee may choose Reward Recipients as determined by their respective software—but the default is:

* 70% → **Block Producer**: The Root-Chain Validator who produced the Nested-Chain block.
* 10% → **Root-Chain Delegate**: Randomly selected by stake-weight from Delegates staked for the `NestedChainID` on the Root-Chain.
* 10% → **Nested-Chain Validator**: Randomly selected by stake-weight from Validators staked for the `NestedChainID` on the Nested-Chain (will perform BFT once Nested-Chain is independent).
* 10% → **Nested-Chain Delegate**: Randomly selected by stake-weight from Delegates staked for the `NestedChainID` on the Nested-Chain (will influence subsidization once Nested-Chain is independent).

{% hint style="success" %}
The Certificate Result is included in the Nested-Chain block — so Reward Recipients are processed on both chains! Meaning, tokens are distributed proportionally to the same 4 actors **on both chains**.
{% endhint %}

### Slashes

**SlashRecipients** are burned when processing each Certificate Result. By default, slashes only occur for 2 reasons:

* A Validator didn't sign `MaxNonSign` blocks within the `NonSignWindow` of blocks.
* A Validator Double-Signed blocks — violating BFT safety.

Both of these Slashes have respective burn amounts, which are parameterized.

{% hint style="warning" %}
Unlike rewards, Slashes are processed only on the Root-Chain and are applied directly without normalization by transaction count.
{% endhint %}

A governance parameter, `MaxSlashPerCommittee` limits the number of slashes **per Committee per block**.&#x20;

⇨ If this limit is exceeded, slashes are capped, and a safety mechanism automatically ejects Validators from the Committee — removing their eligibility to participate and receive slashes, preventing cascading failures.

### Token Swaps

1. A Seller may submit `CreateOrder` transactions that create a **sell order** on the Root-Chain. \
   \
   When submitting a CreateOrder transaction, the 'Root-Chain asset' is transferred to a Committee controlled **escrow pool.**<br>

   ```protobuf
   message MessageCreateOrder {
     // the id of the committee that is responsible for the 'buyer asset'
     uint64 chain_id = 1;
     // how much 'Root-Chain asset' listed for sale, transferred to escrow
     uint64 amount_for_sale = 2;
     // how much 'buyer asset' the buyer must send to the seller
     uint64 requested_amount = 3;
     // the address of the seller where the 'buyer asset' is received
     bytes sellers_receive_address = 4;
     // the Root-Chain address the seller is selling and signing from
     bytes sellers_send_address = 5;
   }
   ```

2. The Buyer may **reserve** an Order by submitting a transaction to themselves on the 'buyer asset' chain with a `LockOrder` embedded in the transaction (via SigScript, OpData, Memo etc).<br>

   ```protobuf
   message LockOrder {
     // the number id that is unique to this committee to identify the order
     uint64 order_id = 1;
     // the Root-Chain address where the 'Root-Chain asset' may be received
     bytes buyer_receive_address = 2;
     // the 'buyer asset' address where the tokens will be sent from
     bytes buyer_send_address = 3;
     // 'buyer asset' must be sent by 'buyer-chain' height or the Lock is voided
     uint64 buyer_chain_deadline = 4;
   }
   ```

3. The Committee **witnesses** the Lock by deterministically parsing the buyer chain block. \
   \
   The Committee marks the on-chain SellOrder as 'Locked' by recording the **Buyer's information** and setting the **BuyerChainDeadline**.<br>

   ```protobuf
   message SellOrder {
     ...
     // amount of 'Root-Chain asset' for sale
     uint64 amount_for_sale = 3;
     // amount of 'buyer-asset' to receive
     uint64 requested_amount = 4;
     // the seller's 'buyer-chain' address to receive the 'buyer-asset'
     bytes seller_receive_address = 5;
     // the address the buyer will be transferring the funds from
     bytes buyer_send_address = 6;
     // the buyer's 'Root-Chain' address to receive the 'Root-Chain asset'
     bytes buyer_receive_address = 7;
     // the 'buyer-chain' height deadline to send the 'tokens'
     uint64 buyer_chain_deadline = 8;
    ...
   }
   ```

4. The Buyer **witnesses** their information in the SellOrder on the Root-Chain and directly sends the funds to the Seller — embedding a `CloseOrder` in the transaction.<br>

   ```protobuf
   message CloseOrder {
     // the tag to represent the intent to embed a close order
     bool close_order = 1;
     // the number id that is unique to this committee to identify the order
     uint64 order_id = 2;
   }
   ```

5. The Committee **witnesses** the CloseOrder by deterministically parsing the buyer chain block and releases the Seller's escrowed funds to the Buyer. <br>

   ```protobuf
   // SWAP COMPLETE: in as little as 2 Root-Chain blocks
   ```

   <br>

6. The Seller may `EditOrder` or `DeleteOrder` if the order is not yet locked.<br>

   ```protobuf
   // If an order is already 'locked' the order may not be modified
   message MessageEditOrder {
     // the number id that is unique to this committee to identify the order
     uint64 order_id = 1;
     // the id of the committee that is responsible for the 'buyer asset'
     uint64 chain_id = 2;
     // update order amount, a reduction returns tokens to the Seller
     uint64 amount_for_sale = 3;
     // update how much 'buyer asset' the buyer must send to the seller
     uint64 requested_amount = 4;
     // the address of the seller where the 'buyer asset' is received
     bytes seller_receive_address = 5;
   }

   // If an order is not 'locked' the funds are returned to the seller
   message MessageDeleteOrder {
     // the number id that is unique to this committee to identify the order
     uint64 order_id = 1;
     // the id of the committee that is responsible for the 'buyer asset'
     uint64 chain_id = 2;
   }
   ```

### Governance

Canopy is pre-built with 2 different on-chain governance mechanisms:

* Polling: Gather community sentiment by executing an on-chain vote of Accounts and Validators.
* Proposals: Change [governance parameters ](https://canopy-network.gitbook.io/docs/canopy-network/how-does-canopy-work/specifications/governance-params)and distribute tokens from the [dao-treasury-fund](https://canopy-network.gitbook.io/docs/canopy-network/canopy-economics/dao-treasury-fund "mention") by completing an on-chain referendum of Validators staked for the Root-Chain committee.&#x20;

#### Polling

{% hint style="success" %}
Polls don't actually execute any action — rather they are an on-chain community sentiment gathering tool.
{% endhint %}

Polling transactions come in 2 flavors:

* StartPoll: Encoded in Root-Chain `SendTransactions` — an Account or Validator begins an on-chain poll. StartPoll transactions cost a higher fee as dictated by governance parameters to prevent spam.<br>

  ```go
  type StartPoll struct {
    StartPoll string // hash(PollJSON)
    Url       string // the forum url of the poll
    EndBlock  uint64 // the end height of the poll
  }
  ```

* VotePoll: Encoded in Root-Chain `SendTransactions` — an Account or Validator signals their sentiment with a vote on an existing poll.<br>

  ```go
  type VotePoll struct {
    VotePoll string // hash(PollJSON)
    Approve  bool   // approve or not
  }
  ```

<figure><img src="https://2343293880-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLrZDX2RTu3ClIHkvyP7T%2Fuploads%2FhVxNs4gkPizNPLR5lxru%2Fimage.png?alt=media&#x26;token=5d254be1-129d-4baf-9b6a-7666c991fecf" alt="" width="563"><figcaption><p>Polls are initiated through the built-in decentralized web-wallet</p></figcaption></figure>

The result of a poll are automatically recorded by the Canopy node deterministically parsing the block for `StartPoll` and `VotePoll` transactions.&#x20;

⇨ The results are updated per block as Account and Validator funds change and may be viewed in the web wallet. Results are locked in after `EndBlock` and are pruned after a node-configurable expiration height.

<figure><img src="https://2343293880-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLrZDX2RTu3ClIHkvyP7T%2Fuploads%2FrUAYleOrzqATNsiRgcRR%2Fimage.png?alt=media&#x26;token=34f2bc25-2a05-495a-b2c9-1df4228ae44f" alt="" width="563"><figcaption></figcaption></figure>

#### Proposals

Canopy has 2 different flavors of Proposal:

* **Change Parameter:** update on-chain [governance parameters](https://canopy-network.gitbook.io/docs/canopy-network/how-does-canopy-work/specifications/governance-params) that allow 'on-the-fly' adjustments to the protocol without any software changes.
* **DAO Transfer:** Distribute treasury funds from the DAO pool to any account

<mark style="background-color:yellow;">Proposals require approval from +⅔ of the Voting Power of the Root-Chain committee to be finalized.</mark>

1. A **Proposal Transaction** is created by any community member.<br>

<figure><img src="https://2343293880-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLrZDX2RTu3ClIHkvyP7T%2Fuploads%2Frecb7QTPLUfL2btbH1h9%2Fimage.png?alt=media&#x26;token=d6fadbd2-82b4-4b4c-811f-79a8104207b0" alt="" width="563"><figcaption><p>Via built in decentralized Web Wallet</p></figcaption></figure>

2. This proposal is exported in JSON format and may be distributed via governance forum.\ <br>

   ```json
   {
   "type": "changeParameter",
   "msg": {
   "parameterSpace": "val",
   "parameterKey": "unstakingBlocks",
   "parameterValue": 100,
   "startHeight": 1,
   "endHeight": 100,
   "signer": "851e90eaef1fa27debaee2c2591503bdeec1d123"
   },
   "signature": {
   "publicKey": "b88a5928e54cbf0a36e0b98f5bcf02...",
   "signature": "b85dbe5bf883fb984dbd180ed81ea7..."
   },
   "time": 1745787266877817,
   "createdHeight": 1,
   "fee": 10000,
   "networkID": 1,
   "chainID": 1
   }
   ```

   \
   Once exported, each Validator configures their node to approve or reject the proposal via the web interface. This configures the node to accept or reject this **proposal hash** when validating the transaction at the Mempool and BFT level.

<figure><img src="https://2343293880-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLrZDX2RTu3ClIHkvyP7T%2Fuploads%2FP3aDj2OaMkAbo8v1cfaP%2Fimage.png?alt=media&#x26;token=cd42c99f-b6a9-44b3-bf7e-ec532f74fa47" alt="" width="563"><figcaption></figcaption></figure>

3. Once +⅔ of the Voting Power has approved the proposal, the Proposal Transaction may be submitted by any community member to be included in a block. Importantly, the transaction may never be sent before the `StartHeight` ensuring sufficient time to achieve quorum.

{% hint style="info" %}
It's an expected pattern to execute a poll to check Validator votes and then execute a proposal to execute an action.
{% endhint %}

### Supply Tracker

A supply tracker is a structure that keeps track of the total amount of tokens or funds available across the entire blockchain.&#x20;

➤ It provides a single source of truth for the overall token supply managed by the network. It conveniently buckets the tokens into useful categories, allowing transparency and consistency.

```protobuf
message Supply {
  // total: the total tokens existing in the system - minted tokens are added, burned tokens are removed
  uint64 total = 1;
  // staked: the total locked tokens in the protocol - this includes delegated
  uint64 staked = 2;
  // delegated_only: the total locked tokens that are delegated only
  uint64 delegated_only = 3;
  // committee_staked: a list of committees and their 'committed (staked + delegated)' tokens, sorted by stake
  repeated Pool committee_staked = 4; 
  // committee_delegated_only: a list of committees and their 'delegated only' tokens, sorted by stake
  repeated Pool committee_delegated_only = 5; 
}
```

{% hint style="info" %}
'staked' includes delegations, to calculate stake without delegations, simply staked - delegated\_only
{% endhint %}
