Files
bft-crdt-experiment/crates/oracle-demo/src/oracle/network_crdt.rs
2025-06-12 15:50:42 -04:00

95 lines
2.9 KiB
Rust

use std::collections::HashMap;
use crate::{utils, AssetPair, OracleId, PriceAttestation};
#[derive(Clone)]
pub(crate) struct NetworkCRDT {
pub(crate) attestations: HashMap<String, PriceAttestation>,
pub(crate) oracle_scores: HashMap<OracleId, f64>,
}
impl NetworkCRDT {
pub(crate) fn new() -> Self {
Self {
attestations: HashMap::new(),
oracle_scores: HashMap::new(),
}
}
pub(crate) fn submit_attestation(&mut self, attestation: PriceAttestation) {
self.attestations
.insert(attestation.id.clone(), attestation.clone());
// Update oracle reputation
let score = self
.oracle_scores
.entry(attestation.oracle_id.clone())
.or_insert(0.5);
*score = (*score * 0.95) + 0.05;
}
pub(crate) fn merge(&mut self, other: &Self) {
for (id, attestation) in &other.attestations {
if !self.attestations.contains_key(id) {
self.attestations.insert(id.clone(), attestation.clone());
}
}
for (oracle_id, score) in &other.oracle_scores {
self.oracle_scores.insert(oracle_id.clone(), *score);
}
}
pub(crate) fn get_aggregate_price(
&self,
asset_pair: &AssetPair,
max_age: u64,
) -> Option<(f64, u8, usize)> {
let now = utils::timestamp();
let min_time = now.saturating_sub(max_age);
let mut prices = Vec::new();
for attestation in self.attestations.values() {
if attestation.asset_pair == *asset_pair && attestation.timestamp >= min_time {
let weight = self
.oracle_scores
.get(&attestation.oracle_id)
.unwrap_or(&0.5);
prices.push((attestation.price, attestation.confidence, *weight));
}
}
if prices.is_empty() {
return None;
}
// Remove outliers
prices.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
if prices.len() > 4 {
let q1 = prices[prices.len() / 4].0;
let q3 = prices[3 * prices.len() / 4].0;
let iqr = q3 - q1;
let lower = q1 - iqr * 1.5;
let upper = q3 + iqr * 1.5;
prices.retain(|(price, _, _)| *price >= lower && *price <= upper);
}
// Calculate weighted average
let mut total_weight = 0.0;
let mut weighted_sum = 0.0;
let mut confidence_sum = 0.0;
for (price, confidence, weight) in &prices {
let w = (*confidence as f64 / 100.0) * weight;
weighted_sum += price * w;
confidence_sum += *confidence as f64 * w;
total_weight += w;
}
let avg_price = weighted_sum / total_weight;
let avg_confidence = (confidence_sum / total_weight) as u8;
Some((avg_price, avg_confidence, prices.len()))
}
}