291 lines
9.0 KiB
Markdown
291 lines
9.0 KiB
Markdown
# Developing and Activating CLP Implementations
|
|
|
|
This guide explains how developers make code changes that activate only after an implementation has been approved by Contractless governance.
|
|
|
|
Governance does not download or execute source code. Developers release candidate software containing the proposed changes before activation. That software keeps the new behavior disabled until the approved activation block.
|
|
|
|
## Development Process
|
|
|
|
The complete process is:
|
|
|
|
1. A CLP is submitted through a ProposalKey transaction.
|
|
2. Eligible node operators approve the proposal.
|
|
3. Developers implement the approved proposal.
|
|
4. Developers publish an implementation document identifying the exact code and commits.
|
|
5. Node operators install and test candidate software containing both the original and proposed behavior.
|
|
6. Eligible node operators vote on that implementation.
|
|
7. An implementation reaching 85 percent approval is scheduled to activate 5,760 blocks later.
|
|
8. Candidate software begins using the new behavior at the activation block.
|
|
|
|
Candidate software must be available before activation voting finishes. Otherwise node operators cannot verify what they are being asked to activate.
|
|
|
|
## Implementation Document
|
|
|
|
The implementation document identifies the exact code presented for activation. It is separate from the original proposal.
|
|
|
|
It should include:
|
|
|
|
- The ProposalKey being implemented.
|
|
- The source repository and branch.
|
|
- Every relevant commit.
|
|
- The files and consensus paths changed.
|
|
- The original and proposed behavior.
|
|
- Transaction, block, or database format changes.
|
|
- Compatibility and migration requirements.
|
|
- Tests performed.
|
|
|
|
Activation votes hash the exact bytes of this document. Editing it creates a different implementation hash and therefore a different activation candidate.
|
|
|
|
Do not alter an implementation document after voting begins. Publish a new document for revised code.
|
|
|
|
## Activation Status
|
|
|
|
All consensus changes use one shared function:
|
|
|
|
```rust
|
|
use crate::common::governance::implementation_activation_status;
|
|
|
|
let (implementation_approved, activation_block) =
|
|
implementation_activation_status(
|
|
db,
|
|
PROPOSAL_KEY,
|
|
DEVELOPMENT_HASH,
|
|
)?;
|
|
```
|
|
|
|
The function returns:
|
|
|
|
```text
|
|
(false, 0)
|
|
```
|
|
|
|
when that proposal has no approved implementation.
|
|
|
|
It returns:
|
|
|
|
```text
|
|
(true, activation_block)
|
|
```
|
|
|
|
when the exact development hash was approved and scheduled.
|
|
|
|
It returns an error when the governance record is malformed or the proposal scheduled a different implementation than the software expects.
|
|
|
|
`PROPOSAL_KEY` is the ProposalKey transaction hash from the CLP `.id` file.
|
|
|
|
`DEVELOPMENT_HASH` is the hash of the exact implementation document approved by node operators.
|
|
|
|
## Standard Activation Check
|
|
|
|
Every affected code path uses the same condition:
|
|
|
|
```rust
|
|
if implementation_approved && block_height >= activation_block {
|
|
// New behavior
|
|
} else {
|
|
// Original behavior
|
|
}
|
|
```
|
|
|
|
The comparison must be:
|
|
|
|
```rust
|
|
block_height >= activation_block
|
|
```
|
|
|
|
The original rules remain active below the activation block. The new rules begin at the activation block.
|
|
|
|
If activation is scheduled for block `50,000`:
|
|
|
|
- Block `49,999` uses the original behavior.
|
|
- Block `50,000` uses the new behavior.
|
|
- Block `50,001` uses the new behavior.
|
|
|
|
## Which Height to Use
|
|
|
|
`block_height` must be the height of the specific block being processed.
|
|
|
|
Do not automatically use the node's currently saved chain height.
|
|
|
|
For example, a node at height `100,000` may be replaying block `40,000`. Block `40,000` must use the rules that applied at block `40,000`, not the rules active at the current chain tip.
|
|
|
|
| Operation | Height to compare |
|
|
|---|---|
|
|
| Validating a received block | Height of the received block |
|
|
| Saving a block | Height of the block being saved |
|
|
| Parsing historical transactions | Height containing those transactions |
|
|
| Initial synchronization | Height of each downloaded block |
|
|
| Orphan replay | Height of each block being replayed |
|
|
| Undo or rewind | Height of the block being undone |
|
|
| Creating the next local block | Height of the block being created |
|
|
|
|
## Changing Existing Behavior
|
|
|
|
Use an `if` and `else` when replacing an existing rule:
|
|
|
|
```rust
|
|
if implementation_approved && block_height >= activation_block {
|
|
validate_with_new_rules(transaction)?;
|
|
} else {
|
|
validate_with_original_rules(transaction)?;
|
|
}
|
|
```
|
|
|
|
Both paths must remain in the software. Historical blocks still require the original rules after activation.
|
|
|
|
## Adding New Behavior
|
|
|
|
Reject new functionality before activation:
|
|
|
|
```rust
|
|
if !implementation_approved || block_height < activation_block {
|
|
return Err("This feature has not been activated.".to_string());
|
|
}
|
|
|
|
validate_new_feature(transaction)?;
|
|
```
|
|
|
|
This prevents miners or users from introducing the new behavior before the approved block.
|
|
|
|
## Changing a Structure
|
|
|
|
Rust structure definitions cannot appear or disappear at runtime. Candidate software contains both formats and chooses the correct parser:
|
|
|
|
```rust
|
|
if implementation_approved && block_height >= activation_block {
|
|
NewTransaction::from_bytes(bytes)
|
|
} else {
|
|
OriginalTransaction::from_bytes(bytes)
|
|
}
|
|
```
|
|
|
|
Never parse historical bytes using a structure that did not apply when the block was created.
|
|
|
|
## Saving Records
|
|
|
|
Saving follows the same condition:
|
|
|
|
```rust
|
|
if implementation_approved && block_height >= activation_block {
|
|
save_with_new_rules(transaction, db)?;
|
|
} else {
|
|
save_with_original_rules(transaction, db)?;
|
|
}
|
|
```
|
|
|
|
The locally mined, live downloaded, initial-sync, and orphan-replay paths must produce the same records for the same block.
|
|
|
|
## Undoing and Rewinding
|
|
|
|
Undo logic must use the height of the block being undone:
|
|
|
|
```rust
|
|
let block_height = height_of_block_being_undone;
|
|
|
|
if implementation_approved && block_height >= activation_block {
|
|
undo_new_behavior(transaction, db)?;
|
|
} else {
|
|
undo_original_behavior(transaction, db)?;
|
|
}
|
|
```
|
|
|
|
If activation occurred at block `50,000`:
|
|
|
|
- Undoing block `50,000` uses the new undo behavior.
|
|
- Undoing block `49,999` uses the original undo behavior.
|
|
|
|
This allows orphan correction to move backward and forward across an activation boundary without corrupting chain state.
|
|
|
|
## Later Revisions
|
|
|
|
A later CLP may revise an earlier consensus change.
|
|
|
|
For example:
|
|
|
|
- Original rules apply through block `49,999`.
|
|
- The first implementation applies from block `50,000` through `99,999`.
|
|
- A revised implementation applies beginning at block `100,000`.
|
|
|
|
That code checks both activation boundaries:
|
|
|
|
```rust
|
|
if second_approved && block_height >= second_activation_block {
|
|
apply_second_rules(...)
|
|
} else if first_approved && block_height >= first_activation_block {
|
|
apply_first_rules(...)
|
|
} else {
|
|
apply_original_rules(...)
|
|
}
|
|
```
|
|
|
|
Do not remove older rule paths. Nodes require them for complete historical validation.
|
|
|
|
## Paths Developers Must Review
|
|
|
|
Apply the activation condition everywhere the changed consensus rule is interpreted:
|
|
|
|
- Transaction parsing.
|
|
- Mempool validation.
|
|
- Received-block validation.
|
|
- Local block saving.
|
|
- Initial synchronization.
|
|
- Record and balance updates.
|
|
- Pending effects.
|
|
- Orphan replay.
|
|
- Block undo and chain rewinds.
|
|
- Database reconstruction.
|
|
- Validation CLI tools.
|
|
|
|
A change is incomplete if one path applies the new rule while another still applies the original rule at the same block height.
|
|
|
|
## Creating an Activation Vote
|
|
|
|
After reviewing and installing the candidate implementation, eligible node operators use:
|
|
|
|
```bash
|
|
./create_activation_vote_tx \
|
|
<proposal_key> \
|
|
<implementation_document> \
|
|
<repository_document_location> \
|
|
yes
|
|
```
|
|
|
|
The first vote for a ProposalKey and development hash creates that implementation candidate.
|
|
|
|
Voting `yes` approves the exact implementation-document hash. Voting `no` rejects that candidate without rejecting every future implementation of the proposal.
|
|
|
|
## Checking Activation
|
|
|
|
Use:
|
|
|
|
```bash
|
|
./lookup_governance_proposal <proposal_key>
|
|
```
|
|
|
|
The response shows each implementation candidate, its development hash, vote totals, activation state, and activation block.
|
|
|
|
The possible states are:
|
|
|
|
- `open`: The implementation has not reached approval.
|
|
- `scheduled`: It was approved, but its activation block has not arrived.
|
|
- `active`: The local chain tip has reached or passed its activation block.
|
|
|
|
The displayed state is informational. Consensus code uses `implementation_activation_status` and compares the specific block being processed with the returned activation block.
|
|
|
|
## Testing Requirements
|
|
|
|
Before asking node operators to approve an implementation, test:
|
|
|
|
- Blocks immediately before and at activation.
|
|
- Blocks created after activation.
|
|
- Synchronization from before the activation block.
|
|
- Historical validation across the activation block.
|
|
- Undo from after activation to before activation.
|
|
- Replay from before activation through activation.
|
|
- Orphan correction across the boundary.
|
|
- Candidate nodes operating before activation.
|
|
- Malformed or mismatched activation records.
|
|
|
|
The same block must produce the same result whether it was mined locally, received live, downloaded during sync, or replayed during orphan correction.
|
|
|