From fc8bbe3a71cf1c44a9c219c491c6b1208e23c5e8 Mon Sep 17 00:00:00 2001 From: D'Angelo Rodriguez <70290504+dangelo352@users.noreply.github.com> Date: Sun, 21 Jun 2026 21:12:54 -0400 Subject: [PATCH] perf: cache attestation health metrics incrementally --- ...ation_engine_incremental_health_metrics.md | 34 ++++ contracts/attestation_engine/src/lib.rs | 184 ++++++++++++++---- contracts/attestation_engine/src/tests.rs | 51 +++++ 3 files changed, 233 insertions(+), 36 deletions(-) create mode 100644 benchmarks/results/attestation_engine_incremental_health_metrics.md diff --git a/benchmarks/results/attestation_engine_incremental_health_metrics.md b/benchmarks/results/attestation_engine_incremental_health_metrics.md new file mode 100644 index 00000000..6a4b7ee9 --- /dev/null +++ b/benchmarks/results/attestation_engine_incremental_health_metrics.md @@ -0,0 +1,34 @@ +# attestation_engine incremental health metrics cache + +## Scope + +`get_health_metrics` and `calculate_compliance_score` previously depended on full +`Attestations(commitment_id)` history recomputation whenever no usable cache existed. +`update_health_metrics` also reloaded the full attestation vector after each write. + +This change keeps the existing `HealthMetrics(commitment_id)` cache, marks entries +written by the incremental updater, and updates fee totals, latest drawdown, +volatility exposure, last attestation time, and compliance score as each attestation +is written. + +## Cost shape + +Before: + +- Write path: append attestation, then reload and reduce the full commitment history. +- Cached read path: `calculate_compliance_score` could return a stored score, but + `get_health_metrics` recomputed aggregate fields from full history. +- Complexity: `O(attestations_for_commitment)` on affected writes and cold aggregate reads. + +After: + +- Write path: append attestation and update cached aggregate fields from the new + attestation only. +- Cached read path: `get_health_metrics` reads the updater-marked cache for aggregate + fields while still cross-reading `commitment_core` for canonical current/initial values. +- Legacy/manual cache path: unmarked caches fall back to the previous full recomputation. +- Complexity: `O(1)` for normal cached writes and aggregate reads. + +The regression test `test_incremental_health_metrics_match_full_recomputation` +compares cached fields against a full history reduction for mixed fee and drawdown +attestations. diff --git a/contracts/attestation_engine/src/lib.rs b/contracts/attestation_engine/src/lib.rs index 1f1bacf7..58d353d5 100644 --- a/contracts/attestation_engine/src/lib.rs +++ b/contracts/attestation_engine/src/lib.rs @@ -69,6 +69,10 @@ pub enum DataKey { Attestations(String), /// Health metrics for a commitment (commitment_id -> HealthMetrics) HealthMetrics(String), + /// Marks HealthMetrics entries written by the incremental aggregate updater. + HealthMetricsCacheInitialized(String), + /// Whether cached health metrics have seen at least one drawdown attestation. + HealthMetricsHasDrawdown(String), /// Attestation counter for a commitment (commitment_id -> u64) AttestationCounter(String), /// Reentrancy guard @@ -641,12 +645,15 @@ impl AttestationEngineContract { // Health Metrics Update // ======================================================================== - /// Update cached health metrics after an attestation. + /// Update cached health metrics from the newly written attestation. /// - /// Recomputes aggregate fee and volatility fields from the stored - /// attestation history so cached metrics stay aligned with read-time - /// aggregation. - fn update_health_metrics(e: &Env, commitment_id: &String, attestation: &Attestation) { + /// Maintains the per-commitment aggregate incrementally so cached reads do + /// not need to reduce the full attestation history. + fn update_health_metrics( + e: &Env, + commitment_id: &String, + attestation: &Attestation, + ) -> Result<(), AttestationError> { // Get or create health metrics let key = DataKey::HealthMetrics(commitment_id.clone()); let mut metrics: HealthMetrics = @@ -664,35 +671,52 @@ impl AttestationEngineContract { compliance_score: 100, }); - let attestation_key = DataKey::Attestations(commitment_id.clone()); - let attestations: Vec = e - .storage() - .persistent() - .get(&attestation_key) - .unwrap_or_else(|| Vec::new(e)); - let aggregates = Self::aggregate_attestation_metrics(e, &attestations); - - metrics.last_attestation = aggregates.last_attestation; - metrics.fees_generated = aggregates.fees_generated; - metrics.volatility_exposure = aggregates.volatility_exposure; - if let Some(drawdown_percent) = aggregates.latest_drawdown_percent { - metrics.drawdown_percent = drawdown_percent; - } - // Update type-specific metrics that depend on the latest attestation. let fee_generation = String::from_str(e, "fee_generation"); + let drawdown = String::from_str(e, "drawdown"); let violation = String::from_str(e, "violation"); + let has_drawdown_key = DataKey::HealthMetricsHasDrawdown(commitment_id.clone()); + let had_previous_drawdown = e + .storage() + .persistent() + .get::(&has_drawdown_key) + .unwrap_or(false); if attestation.attestation_type == fee_generation { let fee_amount_key = String::from_str(e, "fee_amount"); if let Some(fee_str) = attestation.data.get(fee_amount_key) { if let Some(fee_amount) = Self::parse_i128_from_string(e, &fee_str) { + metrics.fees_generated = metrics + .fees_generated + .checked_add(fee_amount) + .ok_or(AttestationError::StorageError)?; + let total_fees: i128 = e.storage().instance().get(&DataKey::TotalFees).unwrap_or(0); - let new_total = total_fees.checked_add(fee_amount).unwrap_or(total_fees); + let new_total = total_fees + .checked_add(fee_amount) + .ok_or(AttestationError::StorageError)?; e.storage().instance().set(&DataKey::TotalFees, &new_total); } } + } else if attestation.attestation_type == drawdown { + let drawdown_percent_key = String::from_str(e, "drawdown_percent"); + if let Some(drawdown_str) = attestation.data.get(drawdown_percent_key) { + if let Some(drawdown_percent) = Self::parse_i128_from_string(e, &drawdown_str) { + if had_previous_drawdown { + if let Some(delta) = + Self::absolute_difference(drawdown_percent, metrics.drawdown_percent) + { + metrics.volatility_exposure = metrics + .volatility_exposure + .checked_add(delta) + .ok_or(AttestationError::StorageError)?; + } + } + metrics.drawdown_percent = drawdown_percent; + e.storage().persistent().set(&has_drawdown_key, &true); + } + } } else if attestation.attestation_type == violation { // Decrease compliance score for violations let severity_key = String::from_str(e, "severity"); @@ -720,8 +744,17 @@ impl AttestationEngineContract { core::cmp::min(100, metrics.compliance_score.saturating_add(1)); } + if attestation.timestamp > metrics.last_attestation { + metrics.last_attestation = attestation.timestamp; + } + // Store updated metrics e.storage().persistent().set(&key, &metrics); + e.storage().persistent().set( + &DataKey::HealthMetricsCacheInitialized(commitment_id.clone()), + &true, + ); + Ok(()) } fn aggregate_attestation_metrics( @@ -979,7 +1012,7 @@ impl AttestationEngineContract { e.storage().persistent().set(&key, &attestations); // 10. Update health metrics - Self::update_health_metrics(e, &commitment_id, &attestation); + Self::update_health_metrics(e, &commitment_id, &attestation)?; // 11. Increment attestation counter let counter_key = DataKey::AttestationCounter(commitment_id.clone()); @@ -1305,21 +1338,73 @@ impl AttestationEngineContract { 0 }; - let attestations = Self::load_attestations_from_storage(&e, &commitment_id); - let aggregates = Self::aggregate_attestation_metrics(&e, &attestations); + let cached_metrics = e + .storage() + .persistent() + .get::(&DataKey::HealthMetrics(commitment_id.clone())); + let cache_initialized = e + .storage() + .persistent() + .get::(&DataKey::HealthMetricsCacheInitialized( + commitment_id.clone(), + )) + .unwrap_or(false); - let compliance_score = Self::calculate_compliance_score(e.clone(), commitment_id.clone()); + let (fees_generated, volatility_exposure, last_attestation, metric_drawdown, compliance_score) = + if cache_initialized { + let metrics = cached_metrics.clone().unwrap_or_else(|| HealthMetrics { + commitment_id: commitment_id.clone(), + current_value: 0, + initial_value: 0, + drawdown_percent: 0, + fees_generated: 0, + volatility_exposure: 0, + last_attestation: 0, + compliance_score: 100, + }); + let has_drawdown = e + .storage() + .persistent() + .get::(&DataKey::HealthMetricsHasDrawdown( + commitment_id.clone(), + )) + .unwrap_or(false); + ( + metrics.fees_generated, + metrics.volatility_exposure, + metrics.last_attestation, + if has_drawdown { + Some(metrics.drawdown_percent) + } else { + None + }, + metrics.compliance_score, + ) + } else { + let fallback_attestations = Self::load_attestations_from_storage(&e, &commitment_id); + let fallback_aggregates = + Self::aggregate_attestation_metrics(&e, &fallback_attestations); + ( + fallback_aggregates.fees_generated, + fallback_aggregates.volatility_exposure, + fallback_aggregates.last_attestation, + fallback_aggregates.latest_drawdown_percent, + cached_metrics + .map(|metrics| metrics.compliance_score) + .unwrap_or_else(|| { + Self::calculate_compliance_score(e.clone(), commitment_id.clone()) + }), + ) + }; HealthMetrics { commitment_id, current_value, initial_value, - drawdown_percent: aggregates - .latest_drawdown_percent - .unwrap_or(drawdown_percent), - fees_generated: aggregates.fees_generated, - volatility_exposure: aggregates.volatility_exposure, - last_attestation: aggregates.last_attestation, + drawdown_percent: metric_drawdown.unwrap_or(drawdown_percent), + fees_generated, + volatility_exposure, + last_attestation, compliance_score, } } @@ -1564,12 +1649,21 @@ impl AttestationEngineContract { pub fn calculate_compliance_score(e: Env, commitment_id: String) -> u32 { // First check if we have stored metrics with a compliance score let metrics_key = DataKey::HealthMetrics(commitment_id.clone()); - if let Some(stored_metrics) = e + let cache_initialized = e .storage() .persistent() - .get::(&metrics_key) - { - return stored_metrics.compliance_score; + .get::(&DataKey::HealthMetricsCacheInitialized( + commitment_id.clone(), + )) + .unwrap_or(false); + if cache_initialized { + if let Some(stored_metrics) = + e.storage() + .persistent() + .get::(&metrics_key) + { + return stored_metrics.compliance_score; + } } // Get commitment from core contract @@ -2021,7 +2115,25 @@ impl AttestationEngineContract { e.storage().persistent().set(&key, &attestations); // Update health metrics - Self::update_health_metrics(&e, ¶ms.commitment_id, &attestation); + if let Err(err) = Self::update_health_metrics(&e, ¶ms.commitment_id, &attestation) + { + if mode == BatchMode::Atomic { + e.storage().instance().remove(&DataKey::ReentrancyGuard); + errors.push_back(BatchError { + index: i, + error_code: err as u32, + context: String::from_str(&e, "health_metrics_update"), + }); + return BatchResultVoid::failure(&e, errors); + } else { + errors.push_back(BatchError { + index: i, + error_code: err as u32, + context: String::from_str(&e, "health_metrics_update"), + }); + continue; + } + } // Increment attestation counter let counter_key = DataKey::AttestationCounter(params.commitment_id.clone()); diff --git a/contracts/attestation_engine/src/tests.rs b/contracts/attestation_engine/src/tests.rs index bd27b78c..0f1de5b4 100644 --- a/contracts/attestation_engine/src/tests.rs +++ b/contracts/attestation_engine/src/tests.rs @@ -626,6 +626,57 @@ fn test_record_drawdown_within_max_loss_records_drawdown() { assert_eq!(metrics.drawdown_percent, 5); } +#[test] +fn test_incremental_health_metrics_match_full_recomputation() { + let e = Env::default(); + e.mock_all_auths(); + let attestation_id = e.register_contract(None, AttestationEngineContract); + let core_id = e.register_contract(None, commitment_core::CommitmentCoreContract); + let client = AttestationEngineContractClient::new(&e, &attestation_id); + + let admin = Address::generate(&e); + let commitment_id = String::from_str(&e, "commitment_incremental_metrics"); + + client.initialize(&admin, &core_id); + client.add_verifier(&admin, &admin); + + let commitment = create_mock_commitment_with_status_internal( + &e, + "commitment_incremental_metrics", + "active", + 1_000, + 1_000, + 10, + ); + e.as_contract(&core_id, || { + e.storage().instance().set( + &commitment_core::DataKey::Commitment(commitment_id.clone()), + &commitment, + ); + }); + + client.record_fees(&admin, &commitment_id, &100); + client.record_drawdown(&admin, &commitment_id, &2); + client.record_drawdown(&admin, &commitment_id, &7); + client.record_fees(&admin, &commitment_id, &40); + + let cached = client.get_stored_health_metrics(&commitment_id).unwrap(); + let attestations = client.get_attestations(&commitment_id); + let full = e.as_contract(&attestation_id, || { + AttestationEngineContract::aggregate_attestation_metrics(&e, &attestations) + }); + let public_metrics = client.get_health_metrics(&commitment_id); + + assert_eq!(cached.fees_generated, full.fees_generated); + assert_eq!(cached.drawdown_percent, full.latest_drawdown_percent.unwrap()); + assert_eq!(cached.volatility_exposure, full.volatility_exposure); + assert_eq!(cached.last_attestation, full.last_attestation); + assert_eq!(public_metrics.fees_generated, cached.fees_generated); + assert_eq!(public_metrics.drawdown_percent, cached.drawdown_percent); + assert_eq!(public_metrics.volatility_exposure, cached.volatility_exposure); + assert_eq!(client.calculate_compliance_score(&commitment_id), cached.compliance_score); +} + #[test] fn test_get_attestations_page_logic() { let e = Env::default();