/ ledger / validator / src / clp.rs
clp.rs
   1  // Copyright (c) 2025 ADnet Contributors
   2  // This file is part of the AlphaVM library.
   3  
   4  // Licensed under the Apache License, Version 2.0 (the "License");
   5  // you may not use this file except in compliance with the License.
   6  // You may obtain a copy of the License at:
   7  
   8  // http://www.apache.org/licenses/LICENSE-2.0
   9  
  10  // Unless required by applicable law or agreed to in writing, software
  11  // distributed under the License is distributed on an "AS IS" BASIS,
  12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  // See the License for the specific language governing permissions and
  14  // limitations under the License.
  15  
  16  //! # Continuous Liveness Proof (CLP) System (F-V10 to F-V13)
  17  //!
  18  //! Implements the CLP attestation system for validator liveness verification.
  19  //!
  20  //! ## Attestation Tiers
  21  //!
  22  //! 1. Intel SGX (hardware TEE)
  23  //! 2. AMD SEV-SNP (hardware TEE)
  24  //! 3. TPM 2.0 Measured Boot
  25  //! 4. Software-only attestation
  26  //!
  27  //! All tiers receive equal rewards - tier choice is validator discretion.
  28  //!
  29  //! ## Requirements
  30  //!
  31  //! - Validators must submit CLP attestation every 24 hours
  32  //! - 24-hour grace period after expiry before penalties
  33  //! - Failure penalties: ejection, 5-epoch disqualification, 3% stake loss
  34  
  35  use crate::console::{prelude::*, types::Field};
  36  
  37  use std::collections::HashMap;
  38  
  39  // ============================================================================
  40  // Constants
  41  // ============================================================================
  42  
  43  /// CLP attestation validity period in seconds (24 hours)
  44  pub const CLP_VALIDITY_PERIOD_SECS: u64 = 24 * 60 * 60;
  45  
  46  /// Grace period after CLP expiry before penalties (24 hours)
  47  pub const CLP_GRACE_PERIOD_SECS: u64 = 24 * 60 * 60;
  48  
  49  /// Disqualification period after CLP failure (5 epochs)
  50  pub const CLP_DISQUALIFICATION_EPOCHS: u64 = 5;
  51  
  52  /// Stake slashing percentage for CLP failure (3% = 300 basis points)
  53  pub const CLP_FAILURE_SLASH_BPS: u16 = 300;
  54  
  55  // ============================================================================
  56  // Attestation Tier
  57  // ============================================================================
  58  
  59  /// CLP attestation tier (security level)
  60  #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
  61  pub enum AttestationTier {
  62      /// Intel SGX hardware TEE - highest hardware security
  63      IntelSgx,
  64      /// AMD SEV-SNP hardware TEE - high hardware security
  65      AmdSevSnp,
  66      /// TPM 2.0 Measured Boot - hardware-backed measurement
  67      Tpm2,
  68      /// Software-only attestation - cryptographic proof without hardware TEE
  69      #[default]
  70      SoftwareOnly,
  71  }
  72  
  73  impl AttestationTier {
  74      /// Get human-readable name for the tier
  75      pub fn name(&self) -> &'static str {
  76          match self {
  77              AttestationTier::IntelSgx => "Intel SGX",
  78              AttestationTier::AmdSevSnp => "AMD SEV-SNP",
  79              AttestationTier::Tpm2 => "TPM 2.0",
  80              AttestationTier::SoftwareOnly => "Software",
  81          }
  82      }
  83  
  84      /// Get tier number (1-4)
  85      pub fn tier_number(&self) -> u8 {
  86          match self {
  87              AttestationTier::IntelSgx => 1,
  88              AttestationTier::AmdSevSnp => 2,
  89              AttestationTier::Tpm2 => 3,
  90              AttestationTier::SoftwareOnly => 4,
  91          }
  92      }
  93  
  94      /// Check if this is a hardware TEE tier
  95      pub fn is_hardware_tee(&self) -> bool {
  96          matches!(self, AttestationTier::IntelSgx | AttestationTier::AmdSevSnp)
  97      }
  98  }
  99  
 100  impl std::fmt::Display for AttestationTier {
 101      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 102          write!(f, "{}", self.name())
 103      }
 104  }
 105  
 106  // ============================================================================
 107  // Attestation Status
 108  // ============================================================================
 109  
 110  /// Status of a CLP attestation
 111  #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
 112  pub enum AttestationStatus {
 113      /// Attestation is valid and current
 114      Valid,
 115      /// Attestation has expired but within grace period
 116      Expired,
 117      /// Attestation failed verification
 118      Invalid,
 119      /// No attestation submitted (new validator or after failure)
 120      #[default]
 121      Missing,
 122      /// Validator is disqualified due to CLP failure
 123      Disqualified,
 124  }
 125  
 126  impl std::fmt::Display for AttestationStatus {
 127      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 128          match self {
 129              AttestationStatus::Valid => write!(f, "valid"),
 130              AttestationStatus::Expired => write!(f, "expired"),
 131              AttestationStatus::Invalid => write!(f, "invalid"),
 132              AttestationStatus::Missing => write!(f, "missing"),
 133              AttestationStatus::Disqualified => write!(f, "disqualified"),
 134          }
 135      }
 136  }
 137  
 138  // ============================================================================
 139  // CLP Attestation
 140  // ============================================================================
 141  
 142  /// A CLP attestation proof
 143  #[derive(Clone, Debug)]
 144  pub struct ClpAttestation<N: Network> {
 145      /// Validator ID this attestation is for
 146      validator_id: Field<N>,
 147      /// Attestation tier used
 148      tier: AttestationTier,
 149      /// Software version hash being attested
 150      software_hash: Field<N>,
 151      /// Attestation quote/proof data (tier-specific)
 152      quote_data: Vec<u8>,
 153      /// Signature from TEE or signing key
 154      signature: Vec<u8>,
 155      /// Timestamp when attestation was created
 156      created_at: u64,
 157      /// Timestamp when attestation expires
 158      expires_at: u64,
 159      /// Block height when submitted on-chain
 160      submitted_block: Option<u32>,
 161  }
 162  
 163  impl<N: Network> ClpAttestation<N> {
 164      /// Create a new CLP attestation
 165      pub fn new(
 166          validator_id: Field<N>,
 167          tier: AttestationTier,
 168          software_hash: Field<N>,
 169          quote_data: Vec<u8>,
 170          signature: Vec<u8>,
 171          current_time: u64,
 172      ) -> Self {
 173          Self {
 174              validator_id,
 175              tier,
 176              software_hash,
 177              quote_data,
 178              signature,
 179              created_at: current_time,
 180              expires_at: current_time + CLP_VALIDITY_PERIOD_SECS,
 181              submitted_block: None,
 182          }
 183      }
 184  
 185      // Getters
 186      pub fn validator_id(&self) -> &Field<N> {
 187          &self.validator_id
 188      }
 189  
 190      pub fn tier(&self) -> AttestationTier {
 191          self.tier
 192      }
 193  
 194      pub fn software_hash(&self) -> &Field<N> {
 195          &self.software_hash
 196      }
 197  
 198      pub fn quote_data(&self) -> &[u8] {
 199          &self.quote_data
 200      }
 201  
 202      pub fn signature(&self) -> &[u8] {
 203          &self.signature
 204      }
 205  
 206      pub fn created_at(&self) -> u64 {
 207          self.created_at
 208      }
 209  
 210      pub fn expires_at(&self) -> u64 {
 211          self.expires_at
 212      }
 213  
 214      pub fn submitted_block(&self) -> Option<u32> {
 215          self.submitted_block
 216      }
 217  
 218      /// Set the block height when submitted
 219      pub fn set_submitted_block(&mut self, block: u32) {
 220          self.submitted_block = Some(block);
 221      }
 222  
 223      /// Check if attestation is expired
 224      pub fn is_expired(&self, current_time: u64) -> bool {
 225          current_time >= self.expires_at
 226      }
 227  
 228      /// Check if attestation is within grace period
 229      pub fn is_in_grace_period(&self, current_time: u64) -> bool {
 230          current_time >= self.expires_at && current_time < self.expires_at + CLP_GRACE_PERIOD_SECS
 231      }
 232  
 233      /// Check if attestation has completely failed (past grace period)
 234      pub fn is_failed(&self, current_time: u64) -> bool {
 235          current_time >= self.expires_at + CLP_GRACE_PERIOD_SECS
 236      }
 237  
 238      /// Get time remaining until expiry (0 if already expired)
 239      pub fn time_remaining(&self, current_time: u64) -> u64 {
 240          self.expires_at.saturating_sub(current_time)
 241      }
 242  
 243      /// Get time remaining in grace period (0 if not in grace or past it)
 244      pub fn grace_time_remaining(&self, current_time: u64) -> u64 {
 245          if !self.is_in_grace_period(current_time) {
 246              return 0;
 247          }
 248          let grace_end = self.expires_at + CLP_GRACE_PERIOD_SECS;
 249          grace_end - current_time
 250      }
 251  }
 252  
 253  // ============================================================================
 254  // Trusted Root Keys
 255  // ============================================================================
 256  
 257  /// Trusted root key for attestation verification
 258  #[derive(Clone, Debug)]
 259  pub struct TrustedRoot<N: Network> {
 260      /// Root key identifier
 261      id: Field<N>,
 262      /// Tier this root is valid for
 263      tier: AttestationTier,
 264      /// Public key or certificate hash
 265      public_key: Vec<u8>,
 266      /// Issuer name (e.g., "Intel", "AMD", manufacturer)
 267      issuer: String,
 268      /// Whether this root is currently active
 269      active: bool,
 270      /// Activation timestamp
 271      activated_at: u64,
 272      /// Revocation timestamp (if revoked)
 273      revoked_at: Option<u64>,
 274  }
 275  
 276  impl<N: Network> TrustedRoot<N> {
 277      /// Create a new trusted root
 278      pub fn new(id: Field<N>, tier: AttestationTier, public_key: Vec<u8>, issuer: String, current_time: u64) -> Self {
 279          Self { id, tier, public_key, issuer, active: true, activated_at: current_time, revoked_at: None }
 280      }
 281  
 282      // Getters
 283      pub fn id(&self) -> &Field<N> {
 284          &self.id
 285      }
 286  
 287      pub fn tier(&self) -> AttestationTier {
 288          self.tier
 289      }
 290  
 291      pub fn public_key(&self) -> &[u8] {
 292          &self.public_key
 293      }
 294  
 295      pub fn issuer(&self) -> &str {
 296          &self.issuer
 297      }
 298  
 299      pub fn is_active(&self) -> bool {
 300          self.active
 301      }
 302  
 303      /// Revoke this root
 304      pub fn revoke(&mut self, current_time: u64) {
 305          self.active = false;
 306          self.revoked_at = Some(current_time);
 307      }
 308  }
 309  
 310  // ============================================================================
 311  // Validator CLP Record
 312  // ============================================================================
 313  
 314  /// CLP record for a validator
 315  #[derive(Clone, Debug)]
 316  pub struct ValidatorClpRecord<N: Network> {
 317      /// Validator ID
 318      validator_id: Field<N>,
 319      /// Current attestation (if any)
 320      current_attestation: Option<ClpAttestation<N>>,
 321      /// Current status
 322      status: AttestationStatus,
 323      /// Preferred tier (may differ from current if hardware unavailable)
 324      preferred_tier: AttestationTier,
 325      /// Historical attestation count
 326      attestation_count: u64,
 327      /// Consecutive successful attestations
 328      consecutive_success: u64,
 329      /// Last failure timestamp (if any)
 330      last_failure: Option<u64>,
 331      /// Disqualification end epoch (if disqualified)
 332      disqualified_until_epoch: Option<u64>,
 333      /// Total times disqualified
 334      disqualification_count: u32,
 335  }
 336  
 337  impl<N: Network> ValidatorClpRecord<N> {
 338      /// Create a new CLP record for a validator
 339      pub fn new(validator_id: Field<N>) -> Self {
 340          Self {
 341              validator_id,
 342              current_attestation: None,
 343              status: AttestationStatus::Missing,
 344              preferred_tier: AttestationTier::SoftwareOnly,
 345              attestation_count: 0,
 346              consecutive_success: 0,
 347              last_failure: None,
 348              disqualified_until_epoch: None,
 349              disqualification_count: 0,
 350          }
 351      }
 352  
 353      // Getters
 354      pub fn validator_id(&self) -> &Field<N> {
 355          &self.validator_id
 356      }
 357  
 358      pub fn current_attestation(&self) -> Option<&ClpAttestation<N>> {
 359          self.current_attestation.as_ref()
 360      }
 361  
 362      pub fn status(&self) -> AttestationStatus {
 363          self.status
 364      }
 365  
 366      pub fn preferred_tier(&self) -> AttestationTier {
 367          self.preferred_tier
 368      }
 369  
 370      pub fn current_tier(&self) -> Option<AttestationTier> {
 371          self.current_attestation.as_ref().map(|a| a.tier())
 372      }
 373  
 374      pub fn attestation_count(&self) -> u64 {
 375          self.attestation_count
 376      }
 377  
 378      pub fn consecutive_success(&self) -> u64 {
 379          self.consecutive_success
 380      }
 381  
 382      pub fn disqualified_until_epoch(&self) -> Option<u64> {
 383          self.disqualified_until_epoch
 384      }
 385  
 386      pub fn disqualification_count(&self) -> u32 {
 387          self.disqualification_count
 388      }
 389  
 390      /// Set preferred tier
 391      pub fn set_preferred_tier(&mut self, tier: AttestationTier) {
 392          self.preferred_tier = tier;
 393      }
 394  
 395      /// Check if validator is disqualified
 396      pub fn is_disqualified(&self, current_epoch: u64) -> bool {
 397          match self.disqualified_until_epoch {
 398              Some(until) => current_epoch < until,
 399              None => false,
 400          }
 401      }
 402  
 403      /// Submit a new attestation
 404      pub fn submit_attestation(&mut self, attestation: ClpAttestation<N>) {
 405          self.current_attestation = Some(attestation);
 406          self.status = AttestationStatus::Valid;
 407          self.attestation_count += 1;
 408          self.consecutive_success += 1;
 409      }
 410  
 411      /// Update status based on current time
 412      pub fn update_status(&mut self, current_time: u64, current_epoch: u64) {
 413          // Check disqualification first
 414          if self.is_disqualified(current_epoch) {
 415              self.status = AttestationStatus::Disqualified;
 416              return;
 417          }
 418  
 419          // Clear disqualification if expired
 420          if self.disqualified_until_epoch.is_some() && !self.is_disqualified(current_epoch) {
 421              self.disqualified_until_epoch = None;
 422          }
 423  
 424          // Check attestation status
 425          match &self.current_attestation {
 426              None => {
 427                  self.status = AttestationStatus::Missing;
 428              }
 429              Some(attestation) => {
 430                  if attestation.is_failed(current_time) {
 431                      self.status = AttestationStatus::Invalid;
 432                  } else if attestation.is_expired(current_time) {
 433                      // Covers both in-grace-period and just-expired states
 434                      self.status = AttestationStatus::Expired;
 435                  } else {
 436                      self.status = AttestationStatus::Valid;
 437                  }
 438              }
 439          }
 440      }
 441  
 442      /// Record a CLP failure and apply disqualification
 443      pub fn record_failure(&mut self, current_time: u64, current_epoch: u64) {
 444          self.last_failure = Some(current_time);
 445          self.consecutive_success = 0;
 446          self.disqualified_until_epoch = Some(current_epoch + CLP_DISQUALIFICATION_EPOCHS);
 447          self.disqualification_count += 1;
 448          self.status = AttestationStatus::Disqualified;
 449          self.current_attestation = None;
 450      }
 451  
 452      /// Check if attestation needs renewal (within 4 hours of expiry)
 453      pub fn needs_renewal(&self, current_time: u64) -> bool {
 454          match &self.current_attestation {
 455              None => true,
 456              Some(attestation) => {
 457                  let renewal_threshold = 4 * 60 * 60; // 4 hours before expiry
 458                  attestation.time_remaining(current_time) <= renewal_threshold
 459              }
 460          }
 461      }
 462  }
 463  
 464  // ============================================================================
 465  // CLP Verifier
 466  // ============================================================================
 467  
 468  /// Result of CLP verification
 469  #[derive(Clone, Debug)]
 470  pub struct VerificationResult {
 471      /// Whether verification passed
 472      pub valid: bool,
 473      /// Error message if invalid
 474      pub error: Option<String>,
 475      /// Verified tier (if valid)
 476      pub verified_tier: Option<AttestationTier>,
 477  }
 478  
 479  impl VerificationResult {
 480      /// Create a successful result
 481      pub fn success(tier: AttestationTier) -> Self {
 482          Self { valid: true, error: None, verified_tier: Some(tier) }
 483      }
 484  
 485      /// Create a failure result
 486      pub fn failure(error: impl Into<String>) -> Self {
 487          Self { valid: false, error: Some(error.into()), verified_tier: None }
 488      }
 489  }
 490  
 491  /// CLP attestation verifier
 492  #[derive(Clone, Debug)]
 493  pub struct ClpVerifier<N: Network> {
 494      /// Trusted roots by tier
 495      trusted_roots: HashMap<AttestationTier, Vec<TrustedRoot<N>>>,
 496      /// Expected software hash (current valid version)
 497      expected_software_hash: Field<N>,
 498      /// Whether to allow software-only attestations
 499      allow_software_only: bool,
 500  }
 501  
 502  impl<N: Network> ClpVerifier<N> {
 503      /// Create a new CLP verifier
 504      pub fn new(expected_software_hash: Field<N>) -> Self {
 505          Self { trusted_roots: HashMap::new(), expected_software_hash, allow_software_only: true }
 506      }
 507  
 508      /// Add a trusted root
 509      pub fn add_trusted_root(&mut self, root: TrustedRoot<N>) {
 510          self.trusted_roots.entry(root.tier()).or_default().push(root);
 511      }
 512  
 513      /// Remove/revoke a trusted root
 514      pub fn revoke_root(&mut self, tier: AttestationTier, root_id: &Field<N>, current_time: u64) {
 515          if let Some(roots) = self.trusted_roots.get_mut(&tier) {
 516              for root in roots.iter_mut() {
 517                  if root.id() == root_id {
 518                      root.revoke(current_time);
 519                  }
 520              }
 521          }
 522      }
 523  
 524      /// Update expected software hash (for upgrades)
 525      pub fn set_expected_software_hash(&mut self, hash: Field<N>) {
 526          self.expected_software_hash = hash;
 527      }
 528  
 529      /// Set whether software-only attestations are allowed
 530      pub fn set_allow_software_only(&mut self, allow: bool) {
 531          self.allow_software_only = allow;
 532      }
 533  
 534      /// Verify an attestation
 535      pub fn verify(&self, attestation: &ClpAttestation<N>, current_time: u64) -> VerificationResult {
 536          // Check if attestation is expired
 537          if attestation.is_expired(current_time) {
 538              return VerificationResult::failure("Attestation expired");
 539          }
 540  
 541          // Check software hash matches expected
 542          if attestation.software_hash() != &self.expected_software_hash {
 543              return VerificationResult::failure("Software hash mismatch");
 544          }
 545  
 546          // Tier-specific verification
 547          match attestation.tier() {
 548              AttestationTier::SoftwareOnly => {
 549                  if !self.allow_software_only {
 550                      return VerificationResult::failure("Software-only attestations disabled");
 551                  }
 552                  // For software-only, verify the signature is valid
 553                  // In production, this would verify against the validator's registered key
 554                  if attestation.signature().is_empty() {
 555                      return VerificationResult::failure("Missing signature");
 556                  }
 557                  VerificationResult::success(AttestationTier::SoftwareOnly)
 558              }
 559              tier => {
 560                  // Hardware attestation - verify against trusted roots
 561                  let roots = match self.trusted_roots.get(&tier) {
 562                      Some(r) => r,
 563                      None => return VerificationResult::failure("No trusted roots for tier"),
 564                  };
 565  
 566                  // Check if any active root can verify this attestation
 567                  let has_valid_root = roots.iter().any(|r| r.is_active());
 568                  if !has_valid_root {
 569                      return VerificationResult::failure("No active trusted roots");
 570                  }
 571  
 572                  // In production, this would:
 573                  // 1. Parse the quote_data for the specific TEE format
 574                  // 2. Verify the quote signature against the trusted root
 575                  // 3. Check enclave measurements match expected values
 576                  // For now, we do basic validation
 577                  if attestation.quote_data().is_empty() {
 578                      return VerificationResult::failure("Missing quote data");
 579                  }
 580                  if attestation.signature().is_empty() {
 581                      return VerificationResult::failure("Missing signature");
 582                  }
 583  
 584                  VerificationResult::success(tier)
 585              }
 586          }
 587      }
 588  }
 589  
 590  // ============================================================================
 591  // CLP Registry
 592  // ============================================================================
 593  
 594  /// Central CLP registry managing all validator attestations
 595  #[derive(Clone, Debug)]
 596  pub struct ClpRegistry<N: Network> {
 597      /// Validator CLP records
 598      records: HashMap<Field<N>, ValidatorClpRecord<N>>,
 599      /// CLP verifier
 600      verifier: ClpVerifier<N>,
 601      /// Current timestamp
 602      current_time: u64,
 603      /// Current epoch
 604      current_epoch: u64,
 605  }
 606  
 607  impl<N: Network> ClpRegistry<N> {
 608      /// Create a new CLP registry
 609      pub fn new(expected_software_hash: Field<N>, current_time: u64, current_epoch: u64) -> Self {
 610          Self {
 611              records: HashMap::new(),
 612              verifier: ClpVerifier::new(expected_software_hash),
 613              current_time,
 614              current_epoch,
 615          }
 616      }
 617  
 618      /// Get the verifier for configuration
 619      pub fn verifier_mut(&mut self) -> &mut ClpVerifier<N> {
 620          &mut self.verifier
 621      }
 622  
 623      /// Register a validator for CLP tracking
 624      pub fn register_validator(&mut self, validator_id: Field<N>) {
 625          if !self.records.contains_key(&validator_id) {
 626              self.records.insert(validator_id, ValidatorClpRecord::new(validator_id));
 627          }
 628      }
 629  
 630      /// Get validator CLP record
 631      pub fn get_record(&self, validator_id: &Field<N>) -> Option<&ValidatorClpRecord<N>> {
 632          self.records.get(validator_id)
 633      }
 634  
 635      /// Get mutable validator CLP record
 636      pub fn get_record_mut(&mut self, validator_id: &Field<N>) -> Option<&mut ValidatorClpRecord<N>> {
 637          self.records.get_mut(validator_id)
 638      }
 639  
 640      /// Submit and verify an attestation
 641      pub fn submit_attestation(&mut self, attestation: ClpAttestation<N>) -> Result<VerificationResult> {
 642          let validator_id = *attestation.validator_id();
 643  
 644          // Ensure validator is registered
 645          if !self.records.contains_key(&validator_id) {
 646              bail!("Validator not registered for CLP");
 647          }
 648  
 649          // Verify the attestation
 650          let result = self.verifier.verify(&attestation, self.current_time);
 651  
 652          if result.valid {
 653              // Update record with new attestation
 654              if let Some(record) = self.records.get_mut(&validator_id) {
 655                  record.submit_attestation(attestation);
 656              }
 657          }
 658  
 659          Ok(result)
 660      }
 661  
 662      /// Update time and check for expired attestations
 663      pub fn update_time(&mut self, new_time: u64, new_epoch: u64) {
 664          self.current_time = new_time;
 665          self.current_epoch = new_epoch;
 666  
 667          // Update all record statuses
 668          for record in self.records.values_mut() {
 669              record.update_status(self.current_time, self.current_epoch);
 670          }
 671      }
 672  
 673      /// Get validators with expired attestations (in grace period)
 674      pub fn get_expired_validators(&self) -> Vec<&Field<N>> {
 675          self.records.iter().filter(|(_, r)| r.status() == AttestationStatus::Expired).map(|(id, _)| id).collect()
 676      }
 677  
 678      /// Get validators with failed attestations (past grace period)
 679      pub fn get_failed_validators(&self) -> Vec<&Field<N>> {
 680          self.records
 681              .iter()
 682              .filter(|(_, r)| matches!(r.status(), AttestationStatus::Invalid | AttestationStatus::Missing))
 683              .filter(|(_, r)| {
 684                  // Check if past grace period
 685                  match r.current_attestation() {
 686                      Some(att) => att.is_failed(self.current_time),
 687                      None => true, // Missing attestation counts as failed
 688                  }
 689              })
 690              .map(|(id, _)| id)
 691              .collect()
 692      }
 693  
 694      /// Get disqualified validators
 695      pub fn get_disqualified_validators(&self) -> Vec<&Field<N>> {
 696          self.records.iter().filter(|(_, r)| r.is_disqualified(self.current_epoch)).map(|(id, _)| id).collect()
 697      }
 698  
 699      /// Process CLP failures and return validators to penalize
 700      /// Returns: Vec of (validator_id, should_slash)
 701      pub fn process_failures(&mut self) -> Vec<(Field<N>, bool)> {
 702          let mut to_penalize = Vec::new();
 703  
 704          let failed = self.get_failed_validators().into_iter().cloned().collect::<Vec<_>>();
 705  
 706          for validator_id in failed {
 707              if let Some(record) = self.records.get_mut(&validator_id) {
 708                  // Only penalize if not already disqualified
 709                  if !record.is_disqualified(self.current_epoch) {
 710                      record.record_failure(self.current_time, self.current_epoch);
 711                      to_penalize.push((validator_id, true));
 712                  }
 713              }
 714          }
 715  
 716          to_penalize
 717      }
 718  
 719      /// Get CLP statistics
 720      pub fn get_statistics(&self) -> ClpStatistics {
 721          let mut stats = ClpStatistics::default();
 722  
 723          for record in self.records.values() {
 724              stats.total_validators += 1;
 725  
 726              match record.status() {
 727                  AttestationStatus::Valid => stats.valid_attestations += 1,
 728                  AttestationStatus::Expired => stats.expired_attestations += 1,
 729                  AttestationStatus::Invalid => stats.invalid_attestations += 1,
 730                  AttestationStatus::Missing => stats.missing_attestations += 1,
 731                  AttestationStatus::Disqualified => stats.disqualified_validators += 1,
 732              }
 733  
 734              if let Some(tier) = record.current_tier() {
 735                  match tier {
 736                      AttestationTier::IntelSgx => stats.intel_sgx_count += 1,
 737                      AttestationTier::AmdSevSnp => stats.amd_sev_count += 1,
 738                      AttestationTier::Tpm2 => stats.tpm2_count += 1,
 739                      AttestationTier::SoftwareOnly => stats.software_only_count += 1,
 740                  }
 741              }
 742          }
 743  
 744          stats
 745      }
 746  
 747      /// Get validator count
 748      pub fn validator_count(&self) -> usize {
 749          self.records.len()
 750      }
 751  }
 752  
 753  // ============================================================================
 754  // Statistics
 755  // ============================================================================
 756  
 757  /// CLP statistics for monitoring
 758  #[derive(Clone, Debug, Default)]
 759  pub struct ClpStatistics {
 760      /// Total registered validators
 761      pub total_validators: u64,
 762      /// Validators with valid attestations
 763      pub valid_attestations: u64,
 764      /// Validators with expired attestations (in grace)
 765      pub expired_attestations: u64,
 766      /// Validators with invalid attestations
 767      pub invalid_attestations: u64,
 768      /// Validators with missing attestations
 769      pub missing_attestations: u64,
 770      /// Disqualified validators
 771      pub disqualified_validators: u64,
 772      /// Count using Intel SGX
 773      pub intel_sgx_count: u64,
 774      /// Count using AMD SEV-SNP
 775      pub amd_sev_count: u64,
 776      /// Count using TPM 2.0
 777      pub tpm2_count: u64,
 778      /// Count using software-only
 779      pub software_only_count: u64,
 780  }
 781  
 782  impl ClpStatistics {
 783      /// Get percentage of valid attestations
 784      pub fn valid_percentage(&self) -> f64 {
 785          if self.total_validators == 0 {
 786              0.0
 787          } else {
 788              (self.valid_attestations as f64 / self.total_validators as f64) * 100.0
 789          }
 790      }
 791  
 792      /// Get percentage using hardware TEE
 793      pub fn hardware_tee_percentage(&self) -> f64 {
 794          let hardware = self.intel_sgx_count + self.amd_sev_count;
 795          let total_with_attestation = hardware + self.tpm2_count + self.software_only_count;
 796          if total_with_attestation == 0 { 0.0 } else { (hardware as f64 / total_with_attestation as f64) * 100.0 }
 797      }
 798  }
 799  
 800  // ============================================================================
 801  // Tests
 802  // ============================================================================
 803  
 804  #[cfg(test)]
 805  mod tests {
 806      use super::*;
 807      use crate::console::network::MainnetV0;
 808  
 809      type CurrentNetwork = MainnetV0;
 810  
 811      #[test]
 812      fn test_attestation_creation() {
 813          let attestation = ClpAttestation::<CurrentNetwork>::new(
 814              Field::from_u64(1),
 815              AttestationTier::IntelSgx,
 816              Field::from_u64(12345),
 817              vec![1, 2, 3, 4],
 818              vec![5, 6, 7, 8],
 819              1000,
 820          );
 821  
 822          assert_eq!(attestation.tier(), AttestationTier::IntelSgx);
 823          assert!(!attestation.is_expired(1000));
 824          assert!(attestation.is_expired(1000 + CLP_VALIDITY_PERIOD_SECS));
 825      }
 826  
 827      #[test]
 828      fn test_attestation_expiry_and_grace() {
 829          let attestation = ClpAttestation::<CurrentNetwork>::new(
 830              Field::from_u64(1),
 831              AttestationTier::SoftwareOnly,
 832              Field::from_u64(12345),
 833              vec![],
 834              vec![1, 2, 3],
 835              1000,
 836          );
 837  
 838          let expiry = 1000 + CLP_VALIDITY_PERIOD_SECS;
 839  
 840          // Not expired yet
 841          assert!(!attestation.is_expired(expiry - 1));
 842          assert!(!attestation.is_in_grace_period(expiry - 1));
 843  
 844          // Just expired, in grace period
 845          assert!(attestation.is_expired(expiry));
 846          assert!(attestation.is_in_grace_period(expiry));
 847          assert!(!attestation.is_failed(expiry));
 848  
 849          // Still in grace period
 850          let mid_grace = expiry + CLP_GRACE_PERIOD_SECS / 2;
 851          assert!(attestation.is_in_grace_period(mid_grace));
 852          assert!(!attestation.is_failed(mid_grace));
 853  
 854          // Past grace period - failed
 855          let past_grace = expiry + CLP_GRACE_PERIOD_SECS;
 856          assert!(!attestation.is_in_grace_period(past_grace));
 857          assert!(attestation.is_failed(past_grace));
 858      }
 859  
 860      #[test]
 861      fn test_attestation_tiers() {
 862          assert!(AttestationTier::IntelSgx.is_hardware_tee());
 863          assert!(AttestationTier::AmdSevSnp.is_hardware_tee());
 864          assert!(!AttestationTier::Tpm2.is_hardware_tee());
 865          assert!(!AttestationTier::SoftwareOnly.is_hardware_tee());
 866  
 867          assert_eq!(AttestationTier::IntelSgx.tier_number(), 1);
 868          assert_eq!(AttestationTier::AmdSevSnp.tier_number(), 2);
 869          assert_eq!(AttestationTier::Tpm2.tier_number(), 3);
 870          assert_eq!(AttestationTier::SoftwareOnly.tier_number(), 4);
 871      }
 872  
 873      #[test]
 874      fn test_validator_clp_record() {
 875          let mut record = ValidatorClpRecord::<CurrentNetwork>::new(Field::from_u64(1));
 876  
 877          assert_eq!(record.status(), AttestationStatus::Missing);
 878          assert_eq!(record.attestation_count(), 0);
 879  
 880          // Submit attestation
 881          let attestation = ClpAttestation::new(
 882              Field::from_u64(1),
 883              AttestationTier::Tpm2,
 884              Field::from_u64(12345),
 885              vec![1, 2, 3],
 886              vec![4, 5, 6],
 887              1000,
 888          );
 889  
 890          record.submit_attestation(attestation);
 891          assert_eq!(record.status(), AttestationStatus::Valid);
 892          assert_eq!(record.attestation_count(), 1);
 893          assert_eq!(record.consecutive_success(), 1);
 894          assert_eq!(record.current_tier(), Some(AttestationTier::Tpm2));
 895      }
 896  
 897      #[test]
 898      fn test_clp_failure_disqualification() {
 899          let mut record = ValidatorClpRecord::<CurrentNetwork>::new(Field::from_u64(1));
 900  
 901          // Record failure at epoch 10
 902          record.record_failure(1000, 10);
 903  
 904          assert_eq!(record.status(), AttestationStatus::Disqualified);
 905          assert_eq!(record.disqualified_until_epoch(), Some(10 + CLP_DISQUALIFICATION_EPOCHS));
 906          assert!(record.is_disqualified(10));
 907          assert!(record.is_disqualified(14)); // Still disqualified
 908          assert!(!record.is_disqualified(15)); // Disqualification ended
 909      }
 910  
 911      #[test]
 912      fn test_verifier_software_only() {
 913          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
 914          let verifier = ClpVerifier::new(software_hash);
 915  
 916          let attestation = ClpAttestation::new(
 917              Field::from_u64(1),
 918              AttestationTier::SoftwareOnly,
 919              software_hash,
 920              vec![],
 921              vec![1, 2, 3], // Non-empty signature
 922              1000,
 923          );
 924  
 925          let result = verifier.verify(&attestation, 1000);
 926          assert!(result.valid);
 927          assert_eq!(result.verified_tier, Some(AttestationTier::SoftwareOnly));
 928      }
 929  
 930      #[test]
 931      fn test_verifier_wrong_software_hash() {
 932          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
 933          let verifier = ClpVerifier::new(software_hash);
 934  
 935          let attestation = ClpAttestation::new(
 936              Field::from_u64(1),
 937              AttestationTier::SoftwareOnly,
 938              Field::from_u64(99999), // Wrong hash
 939              vec![],
 940              vec![1, 2, 3],
 941              1000,
 942          );
 943  
 944          let result = verifier.verify(&attestation, 1000);
 945          assert!(!result.valid);
 946          assert!(result.error.unwrap().contains("hash mismatch"));
 947      }
 948  
 949      #[test]
 950      fn test_verifier_expired_attestation() {
 951          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
 952          let verifier = ClpVerifier::new(software_hash);
 953  
 954          let attestation = ClpAttestation::new(
 955              Field::from_u64(1),
 956              AttestationTier::SoftwareOnly,
 957              software_hash,
 958              vec![],
 959              vec![1, 2, 3],
 960              1000,
 961          );
 962  
 963          // Verify after expiry
 964          let result = verifier.verify(&attestation, 1000 + CLP_VALIDITY_PERIOD_SECS + 1);
 965          assert!(!result.valid);
 966          assert!(result.error.unwrap().contains("expired"));
 967      }
 968  
 969      #[test]
 970      fn test_registry_basic_operations() {
 971          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
 972          let mut registry = ClpRegistry::<CurrentNetwork>::new(software_hash, 1000, 0);
 973  
 974          // Register validators
 975          registry.register_validator(Field::from_u64(1));
 976          registry.register_validator(Field::from_u64(2));
 977  
 978          assert_eq!(registry.validator_count(), 2);
 979  
 980          // Submit attestation
 981          let attestation = ClpAttestation::new(
 982              Field::from_u64(1),
 983              AttestationTier::SoftwareOnly,
 984              software_hash,
 985              vec![],
 986              vec![1, 2, 3],
 987              1000,
 988          );
 989  
 990          let result = registry.submit_attestation(attestation).unwrap();
 991          assert!(result.valid);
 992  
 993          // Check record updated
 994          let record = registry.get_record(&Field::from_u64(1)).unwrap();
 995          assert_eq!(record.status(), AttestationStatus::Valid);
 996      }
 997  
 998      #[test]
 999      fn test_registry_failure_processing() {
1000          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
1001          let mut registry = ClpRegistry::<CurrentNetwork>::new(software_hash, 1000, 0);
1002  
1003          // Register validator but don't submit attestation
1004          registry.register_validator(Field::from_u64(1));
1005  
1006          // Advance time past grace period
1007          let past_grace = 1000 + CLP_VALIDITY_PERIOD_SECS + CLP_GRACE_PERIOD_SECS + 1;
1008          registry.update_time(past_grace, 5);
1009  
1010          // Process failures
1011          let penalties = registry.process_failures();
1012          assert_eq!(penalties.len(), 1);
1013          assert_eq!(penalties[0].0, Field::from_u64(1));
1014          assert!(penalties[0].1); // Should slash
1015  
1016          // Validator should now be disqualified
1017          let record = registry.get_record(&Field::from_u64(1)).unwrap();
1018          assert!(record.is_disqualified(5));
1019          assert_eq!(record.disqualified_until_epoch(), Some(5 + CLP_DISQUALIFICATION_EPOCHS));
1020      }
1021  
1022      #[test]
1023      fn test_registry_statistics() {
1024          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
1025          let mut registry = ClpRegistry::<CurrentNetwork>::new(software_hash, 1000, 0);
1026  
1027          // Add trusted roots for hardware tiers
1028          registry.verifier_mut().add_trusted_root(TrustedRoot::new(
1029              Field::from_u64(100),
1030              AttestationTier::IntelSgx,
1031              vec![1, 2, 3],
1032              "Intel".to_string(),
1033              1000,
1034          ));
1035          registry.verifier_mut().add_trusted_root(TrustedRoot::new(
1036              Field::from_u64(101),
1037              AttestationTier::AmdSevSnp,
1038              vec![4, 5, 6],
1039              "AMD".to_string(),
1040              1000,
1041          ));
1042          registry.verifier_mut().add_trusted_root(TrustedRoot::new(
1043              Field::from_u64(102),
1044              AttestationTier::Tpm2,
1045              vec![7, 8, 9],
1046              "TPM Manufacturer".to_string(),
1047              1000,
1048          ));
1049  
1050          // Register validators
1051          for i in 1..=10 {
1052              registry.register_validator(Field::from_u64(i));
1053          }
1054  
1055          // Submit attestations for some (with different tiers)
1056          for i in 1..=5 {
1057              let tier = match i {
1058                  1 | 2 => AttestationTier::IntelSgx,
1059                  3 => AttestationTier::AmdSevSnp,
1060                  4 => AttestationTier::Tpm2,
1061                  _ => AttestationTier::SoftwareOnly,
1062              };
1063  
1064              let attestation = ClpAttestation::new(
1065                  Field::from_u64(i),
1066                  tier,
1067                  software_hash,
1068                  vec![1, 2, 3], // Quote data
1069                  vec![4, 5, 6], // Signature
1070                  1000,
1071              );
1072              let result = registry.submit_attestation(attestation).unwrap();
1073              assert!(result.valid, "Attestation for validator {} should be valid", i);
1074          }
1075  
1076          let stats = registry.get_statistics();
1077          assert_eq!(stats.total_validators, 10);
1078          assert_eq!(stats.valid_attestations, 5);
1079          assert_eq!(stats.missing_attestations, 5);
1080          assert_eq!(stats.intel_sgx_count, 2);
1081          assert_eq!(stats.amd_sev_count, 1);
1082          assert_eq!(stats.tpm2_count, 1);
1083          assert_eq!(stats.software_only_count, 1);
1084      }
1085  
1086      #[test]
1087      fn test_trusted_root_management() {
1088          let software_hash = Field::<CurrentNetwork>::from_u64(12345);
1089          let mut verifier = ClpVerifier::<CurrentNetwork>::new(software_hash);
1090  
1091          // Add trusted root for Intel SGX
1092          let root = TrustedRoot::new(
1093              Field::from_u64(1),
1094              AttestationTier::IntelSgx,
1095              vec![1, 2, 3, 4],
1096              "Intel".to_string(),
1097              1000,
1098          );
1099          verifier.add_trusted_root(root);
1100  
1101          // Now SGX attestations can be verified (basic check)
1102          let attestation = ClpAttestation::new(
1103              Field::from_u64(1),
1104              AttestationTier::IntelSgx,
1105              software_hash,
1106              vec![1, 2, 3], // Quote data
1107              vec![4, 5, 6], // Signature
1108              1000,
1109          );
1110  
1111          let result = verifier.verify(&attestation, 1000);
1112          assert!(result.valid);
1113          assert_eq!(result.verified_tier, Some(AttestationTier::IntelSgx));
1114      }
1115  
1116      #[test]
1117      fn test_renewal_detection() {
1118          let mut record = ValidatorClpRecord::<CurrentNetwork>::new(Field::from_u64(1));
1119  
1120          // No attestation - needs renewal
1121          assert!(record.needs_renewal(1000));
1122  
1123          // Submit attestation
1124          let attestation = ClpAttestation::new(
1125              Field::from_u64(1),
1126              AttestationTier::SoftwareOnly,
1127              Field::from_u64(12345),
1128              vec![],
1129              vec![1, 2, 3],
1130              1000,
1131          );
1132          record.submit_attestation(attestation);
1133  
1134          // Just after submission - doesn't need renewal
1135          assert!(!record.needs_renewal(1000));
1136  
1137          // 20 hours later (4 hours before expiry) - needs renewal
1138          let renewal_time = 1000 + CLP_VALIDITY_PERIOD_SECS - (4 * 60 * 60);
1139          assert!(record.needs_renewal(renewal_time));
1140      }
1141  }