|
| 1 | +# PeoChain Validator Bond Runtime Module |
| 2 | + |
| 3 | +## Architecture Overview |
| 4 | + |
| 5 | +The Validator Bond Runtime Module implements a truly permissionless validator registration and bonding system with no administrative control points. This document outlines the core architecture, design principles, and implementation details. |
| 6 | + |
| 7 | +## Core Principles |
| 8 | + |
| 9 | +1. **Cryptographic Verification Only**: All validation is performed through cryptographic proof, not subjective judgment |
| 10 | +2. **No Administrative Control**: There are no approval queues, status transitions, or administrative overrides |
| 11 | +3. **Deterministic Subnet Assignment**: Validator assignment to subnets is fully deterministic and unpredictable |
| 12 | +4. **Objective Performance Tracking**: Performance is measured through cryptographically verifiable consensus participation only |
| 13 | + |
| 14 | +## Module Structure |
| 15 | + |
| 16 | +The runtime module consists of the following key components: |
| 17 | + |
| 18 | +### Storage Items |
| 19 | + |
| 20 | +```rust |
| 21 | +decl_storage! { |
| 22 | + trait Store for Module<T: Config> as ValidatorBond { |
| 23 | + /// All validators self-register with cryptographic proof only. |
| 24 | + /// No approval queue, no status transitions. |
| 25 | + Validators get(fn validator): map hasher(blake2_128_concat) ValidatorId => Option<ValidatorEscrow<T::AccountId, BalanceOf<T>, T::BlockNumber>>; |
| 26 | + |
| 27 | + /// Purely deterministic subnet assignments. |
| 28 | + SubnetAssignments get(fn subnet_assignments): map hasher(blake2_128_concat) (SubnetId, EpochNumber) => Vec<ValidatorId>; |
| 29 | + |
| 30 | + /// Current epoch number. |
| 31 | + CurrentEpoch get(fn current_epoch): EpochNumber; |
| 32 | + |
| 33 | + /// Performance records for validators. |
| 34 | + ValidatorPerformance get(fn validator_performance): map hasher(blake2_128_concat) (ValidatorId, EpochNumber) => PerformanceRecord; |
| 35 | + |
| 36 | + /// Total number of validators. |
| 37 | + ValidatorCount get(fn validator_count): u32; |
| 38 | + |
| 39 | + /// Validators by subnet. |
| 40 | + ValidatorsBySubnet get(fn validators_by_subnet): map hasher(blake2_128_concat) (SubnetId, EpochNumber) => Vec<ValidatorId>; |
| 41 | + } |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Dispatchable Functions |
| 46 | + |
| 47 | +The module exposes the following dispatchable functions: |
| 48 | + |
| 49 | +1. **`bond_validator`**: Register as a validator with cryptographic proof of escrow |
| 50 | +2. **`submit_performance`**: Submit cryptographically verifiable performance proof |
| 51 | +3. **`rotate_subnet`**: Trigger deterministic subnet rotation (permissionless) |
| 52 | +4. **`unbond`**: Release validator bond after timelock period |
| 53 | + |
| 54 | +### Key Types |
| 55 | + |
| 56 | +```rust |
| 57 | +/// Validator identifier derived from public key. |
| 58 | +pub struct ValidatorId(pub [u8; 32]); |
| 59 | + |
| 60 | +/// Escrow proof provided by validators during registration. |
| 61 | +pub struct ProofOfEscrow<AccountId, Balance, BlockNumber, Signature> { |
| 62 | + /// The escrow address (typically a multisig or threshold signature address). |
| 63 | + pub escrow_address: [u8; 32], |
| 64 | + /// The amount bonded in the escrow. |
| 65 | + pub amount: Balance, |
| 66 | + /// The timelock expiry height. |
| 67 | + pub timelock_height: BlockNumber, |
| 68 | + /// Cryptographic proof of escrow control. |
| 69 | + pub proof: Signature, |
| 70 | + /// The validator account that controls the escrow. |
| 71 | + pub controller: AccountId, |
| 72 | +} |
| 73 | + |
| 74 | +/// Performance proof submitted by validators. |
| 75 | +pub struct PerformanceProof<Signature> { |
| 76 | + /// The validator's unique identifier. |
| 77 | + pub validator_id: ValidatorId, |
| 78 | + /// The epoch number this proof applies to. |
| 79 | + pub epoch: EpochNumber, |
| 80 | + /// The block range this proof covers. |
| 81 | + pub block_range: (u32, u32), |
| 82 | + /// The participation bitmap (1 bit per block). |
| 83 | + pub participation: Vec<u8>, |
| 84 | + /// Cryptographic proof of participation. |
| 85 | + pub proof: Signature, |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +## Key Workflows |
| 90 | + |
| 91 | +### Validator Registration |
| 92 | + |
| 93 | +1. Validator creates a non-custodial escrow (either multisig or threshold signature) |
| 94 | +2. Validator generates cryptographic proof of escrow control |
| 95 | +3. Validator submits registration transaction with public key and escrow proof |
| 96 | +4. Runtime verifies the proof cryptographically (no human judgment) |
| 97 | +5. Validator is immediately registered and assigned to a subnet deterministically |
| 98 | + |
| 99 | +```rust |
| 100 | +// Example registration flow |
| 101 | +fn bond_validator( |
| 102 | + origin, |
| 103 | + public_key: [u8; 32], |
| 104 | + proof: ProofOfEscrow<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Signature>, |
| 105 | +) -> DispatchResult { |
| 106 | + let sender = ensure_signed(origin)?; |
| 107 | + |
| 108 | + // Generate deterministic validator ID |
| 109 | + let validator_id = Self::derive_validator_id(&public_key); |
| 110 | + |
| 111 | + // Ensure validator is not already registered |
| 112 | + ensure!(!Validators::<T>::contains_key(validator_id), Error::<T>::ValidatorAlreadyRegistered); |
| 113 | + |
| 114 | + // Ensure bond amount meets minimum requirement |
| 115 | + ensure!(proof.amount >= T::MinimumBond::get(), Error::<T>::BondTooLow); |
| 116 | + |
| 117 | + // Validate escrow cryptographically - no human judgment |
| 118 | + ensure!(Self::verify_escrow_proof(&proof, &public_key), Error::<T>::InvalidEscrowProof); |
| 119 | + |
| 120 | + // Create and store validator |
| 121 | + // ... |
| 122 | + |
| 123 | + // Assign to subnet deterministically |
| 124 | + // ... |
| 125 | + |
| 126 | + Ok(()) |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +### Deterministic Subnet Assignment |
| 131 | + |
| 132 | +Subnet assignment is fully deterministic and based on a cryptographic hash of the validator ID and epoch number: |
| 133 | + |
| 134 | +```rust |
| 135 | +fn assign_validator_to_subnet(validator_id: &ValidatorId, epoch: EpochNumber) -> SubnetId { |
| 136 | + // Create a deterministic but unpredictable assignment |
| 137 | + let mut input = Vec::with_capacity(36); |
| 138 | + input.extend_from_slice(&validator_id.0); |
| 139 | + input.extend_from_slice(&epoch.to_be_bytes()); |
| 140 | + |
| 141 | + let hash = sp_io::hashing::blake2_256(&input); |
| 142 | + |
| 143 | + // Convert to subnet ID using modulo of subnet count |
| 144 | + let subnet_index = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) % T::SubnetCount::get(); |
| 145 | + |
| 146 | + SubnetId(subnet_index) |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +### Performance Verification |
| 151 | + |
| 152 | +Performance is verified through cryptographic proof only: |
| 153 | + |
| 154 | +```rust |
| 155 | +fn verify_performance_proof(proof: &PerformanceProof<T::Signature>) -> bool { |
| 156 | + // In a real implementation, this would verify: |
| 157 | + // 1. The validator participated in the claimed blocks |
| 158 | + // 2. The proof signature is valid |
| 159 | + |
| 160 | + let message = ( |
| 161 | + proof.validator_id, |
| 162 | + proof.epoch, |
| 163 | + proof.block_range, |
| 164 | + proof.participation.clone(), |
| 165 | + ).encode(); |
| 166 | + |
| 167 | + // Get the validator's public key |
| 168 | + if let Some(escrow) = Self::validator(proof.validator_id) { |
| 169 | + // Convert public key to account ID for verification |
| 170 | + let account_id = T::AccountId::decode(&mut &escrow.public_key[..]) |
| 171 | + .unwrap_or_default(); |
| 172 | + |
| 173 | + // Verify the signature |
| 174 | + proof.proof.verify(&message[..], &account_id) |
| 175 | + } else { |
| 176 | + false |
| 177 | + } |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +## Subnet Rotation |
| 182 | + |
| 183 | +Subnet assignments are rotated periodically to ensure fair distribution of validators: |
| 184 | + |
| 185 | +```rust |
| 186 | +fn do_subnet_rotation(new_epoch: EpochNumber) { |
| 187 | + // Get all validators |
| 188 | + let validators: Vec<_> = Validators::<T>::iter() |
| 189 | + .map(|(id, _)| id) |
| 190 | + .collect(); |
| 191 | + |
| 192 | + // Clear previous subnet assignments |
| 193 | + // ... |
| 194 | + |
| 195 | + // Assign validators to subnets deterministically |
| 196 | + for validator_id in validators { |
| 197 | + let subnet_id = Self::assign_validator_to_subnet(&validator_id, new_epoch); |
| 198 | + |
| 199 | + // Add validator to subnet |
| 200 | + // ... |
| 201 | + } |
| 202 | +} |
| 203 | +``` |
| 204 | + |
| 205 | +## Security Considerations |
| 206 | + |
| 207 | +### Non-custodial Escrow |
| 208 | + |
| 209 | +The validator bond is held in a non-custodial escrow, which can be implemented as: |
| 210 | + |
| 211 | +1. **2-of-2 Multisig**: Requiring signatures from both the validator and the network |
| 212 | +2. **Threshold Signature**: Requiring a threshold of signatures from a set of participants |
| 213 | + |
| 214 | +### Timelock Period |
| 215 | + |
| 216 | +Validator bonds are subject to a timelock period to prevent rapid unbonding: |
| 217 | + |
| 218 | +```rust |
| 219 | +// Ensure timelock has expired |
| 220 | +let current_block = <frame_system::Module<T>>::block_number(); |
| 221 | +ensure!(current_block >= escrow.timelock_height, Error::<T>::TimelockNotExpired); |
| 222 | +``` |
| 223 | + |
| 224 | +### Cryptographic Verification |
| 225 | + |
| 226 | +All operations are verified cryptographically, with no subjective judgment: |
| 227 | + |
| 228 | +```rust |
| 229 | +// Validate escrow cryptographically - no human judgment |
| 230 | +ensure!(Self::verify_escrow_proof(&proof, &public_key), Error::<T>::InvalidEscrowProof); |
| 231 | +``` |
| 232 | + |
| 233 | +## Integration with CLI |
| 234 | + |
| 235 | +The runtime module is designed to be used with the CLI tool, which provides a direct interface for validators to interact with the chain without administrative intermediaries. |
| 236 | + |
| 237 | +## Conclusion |
| 238 | + |
| 239 | +The Validator Bond Runtime Module implements a truly permissionless validator registration and bonding system with no administrative control points. By relying on cryptographic verification and deterministic assignment, it ensures that the validator marketplace is fair, transparent, and resistant to centralization. |
0 commit comments