Protocol Upgrades
Kaizen uses a hard-fork model for protocol upgrades, similar to Ethereum. Upgrades are scheduled at specific block heights and are compiled into the node binary.
Overview
Block 499,999 (v1.0.0)
│
▼
Block 500,000 (upgrade height)
│
├─→ Migrations run (if any)
│
▼
Block 500,000+ (v1.1.0)
│
└─→ New rules activeKey Concepts
Protocol Version
Each block is produced under a specific protocol version (e.g., v1.0.0). The version is:
- Stored in
BlockHeader.protocol_version - Included in block hash calculation
- Used to gate new features
// Check version before enabling new feature
if version.at_least(1, 1, 0) {
// New feature available
}Upgrade Schedule
Upgrades are defined in code, NOT in configuration:
// crates/app/src/upgrade.rs
const MAINNET_UPGRADES: &[UpgradeDefinition] = &[
UpgradeDefinition {
name: "v1.1-new-features",
height: 1_000_000,
version: (1, 1, 0),
},
];This ensures all nodes have the same schedule without coordination.
Migrations
Migrations transform existing data when an upgrade activates:
impl Migration for MigrateToV1_1 {
fn migrate(&self, state: &mut StateManager) -> Result<(), MigrationError> {
// Transform data here
// Runs once at upgrade height
}
}Adding an Upgrade
1. Define the Upgrade
// crates/app/src/upgrade.rs
const MAINNET_UPGRADES: &[UpgradeDefinition] = &[
UpgradeDefinition {
name: "v1.1-my-upgrade",
height: 500_000, // Activation height
version: (1, 1, 0),
},
];2. Implement Logic Changes
Version-gate new behavior in the engine:
// crates/engine/src/...
fn execute(&mut self, ...) -> Result<...> {
let version = self.state.current_protocol_version()?;
if version.at_least(1, 1, 0) {
// New behavior
} else {
// Old behavior
}
}3. Add Migration (if needed)
Only required if state schema changes:
// crates/app/src/upgrade.rs
pub mod migrations {
pub struct MigrateToV1_1;
impl Migration for MigrateToV1_1 {
fn version(&self) -> ProtocolVersion {
ProtocolVersion::new(1, 1, 0)
}
fn name(&self) -> &'static str {
"v1.1-add-new-field"
}
fn migrate(&self, state: &mut StateManager) -> Result<(), MigrationError> {
// Your migration logic
Ok(())
}
}
}Register in UpgradeRegistry::new():
handler.register_migration(Box::new(migrations::MigrateToV1_1));4. Bump Version
// crates/types/src/upgrade.rs
impl ProtocolVersion {
pub const CURRENT: Self = Self { major: 1, minor: 1, patch: 0 };
}5. Release
- Merge changes
- Tag release (e.g.,
v1.1.0) - Announce upgrade height and deadline
- Operators upgrade nodes before activation
Network Configuration
Set network in config to use the correct schedule:
[node]
network = "mainnet" # or "testnet", "local"| Network | Upgrade Schedule |
|---|---|
mainnet | Production upgrades |
testnet | Test upgrades (more frequent) |
local | No scheduled upgrades |
Compatibility
Node Version Check
On startup, nodes verify compatibility:
✓ Protocol version check passed
network: mainnet
node_version: v1.1.0
chain_version: v1.0.0If incompatible:
✗ Node version v1.0.0 is incompatible with chain at height 500000
Required: v1.1.0Upgrade Timeline
- T-2 weeks: Announce upgrade
- T-1 week: Release new node version
- T-0: Upgrade activates at scheduled height
File Locations
| Purpose | File |
|---|---|
| Upgrade schedule | crates/app/src/upgrade.rs |
| Migrations | crates/app/src/upgrade.rs (migrations module) |
| Version-gated logic | crates/engine/src/*.rs |
| Version constants | crates/types/src/upgrade.rs |
| Upgrade handler | crates/engine/src/upgrade.rs |
