Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 111 additions & 2 deletions crates/bitcell-node/src/monitoring/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct MetricsRegistry {
peer_count: Arc<AtomicUsize>,
bytes_sent: Arc<AtomicU64>,
bytes_received: Arc<AtomicU64>,
messages_sent: Arc<AtomicU64>,
messages_received: Arc<AtomicU64>,

// Transaction pool metrics
pending_txs: Arc<AtomicUsize>,
Expand All @@ -33,8 +35,8 @@ pub struct MetricsRegistry {
// EBSL metrics
active_miners: Arc<AtomicUsize>,
banned_miners: Arc<AtomicUsize>,
#[allow(dead_code)]
avg_trust_score: Arc<AtomicU64>, // Stored as fixed-point * 1000
slashing_events: Arc<AtomicU64>,

// DHT metrics
dht_peer_count: Arc<AtomicUsize>,
Expand All @@ -48,6 +50,8 @@ impl MetricsRegistry {
peer_count: Arc::new(AtomicUsize::new(0)),
bytes_sent: Arc::new(AtomicU64::new(0)),
bytes_received: Arc::new(AtomicU64::new(0)),
messages_sent: Arc::new(AtomicU64::new(0)),
messages_received: Arc::new(AtomicU64::new(0)),
pending_txs: Arc::new(AtomicUsize::new(0)),
total_txs_processed: Arc::new(AtomicU64::new(0)),
proofs_generated: Arc::new(AtomicU64::new(0)),
Expand All @@ -57,6 +61,7 @@ impl MetricsRegistry {
active_miners: Arc::new(AtomicUsize::new(0)),
banned_miners: Arc::new(AtomicUsize::new(0)),
avg_trust_score: Arc::new(AtomicU64::new(0)),
slashing_events: Arc::new(AtomicU64::new(0)),
dht_peer_count: Arc::new(AtomicUsize::new(0)),
}
}
Expand Down Expand Up @@ -103,6 +108,22 @@ impl MetricsRegistry {
self.bytes_received.load(Ordering::Relaxed)
}

pub fn add_message_sent(&self) {
self.messages_sent.fetch_add(1, Ordering::Relaxed);
}

pub fn add_message_received(&self) {
self.messages_received.fetch_add(1, Ordering::Relaxed);
}

pub fn get_messages_sent(&self) -> u64 {
self.messages_sent.load(Ordering::Relaxed)
}

pub fn get_messages_received(&self) -> u64 {
self.messages_received.load(Ordering::Relaxed)
}

// Transaction pool metrics
pub fn set_pending_txs(&self, count: usize) {
self.pending_txs.store(count, Ordering::Relaxed);
Expand Down Expand Up @@ -162,6 +183,28 @@ impl MetricsRegistry {
self.banned_miners.load(Ordering::Relaxed)
}

pub fn set_average_trust_score(&self, score: f64) {
// Store as fixed-point * 1000 for atomic operations
// Trust scores are typically in range [0.0, 1.0], so this provides
// 3 decimal places of precision without overflow risk
let clamped_score = score.clamp(0.0, 1.0);
let fixed_point = (clamped_score * 1000.0) as u64;
self.avg_trust_score.store(fixed_point, Ordering::Relaxed);
}

pub fn get_average_trust_score(&self) -> f64 {
let fixed_point = self.avg_trust_score.load(Ordering::Relaxed);
fixed_point as f64 / 1000.0
}

pub fn inc_slashing_events(&self) {
self.slashing_events.fetch_add(1, Ordering::Relaxed);
}

pub fn get_slashing_events(&self) -> u64 {
self.slashing_events.load(Ordering::Relaxed)
}

// DHT metrics
pub fn set_dht_peer_count(&self, count: usize) {
self.dht_peer_count.store(count, Ordering::Relaxed);
Expand Down Expand Up @@ -198,6 +241,14 @@ impl MetricsRegistry {
# TYPE bitcell_bytes_received_total counter\n\
bitcell_bytes_received_total {}\n\
\n\
# HELP bitcell_messages_sent_total Total messages sent\n\
# TYPE bitcell_messages_sent_total counter\n\
bitcell_messages_sent_total {}\n\
\n\
# HELP bitcell_messages_received_total Total messages received\n\
# TYPE bitcell_messages_received_total counter\n\
bitcell_messages_received_total {}\n\
\n\
# HELP bitcell_pending_txs Number of pending transactions\n\
# TYPE bitcell_pending_txs gauge\n\
bitcell_pending_txs {}\n\
Expand All @@ -220,19 +271,31 @@ impl MetricsRegistry {
\n\
# HELP bitcell_banned_miners Number of banned miners\n\
# TYPE bitcell_banned_miners gauge\n\
bitcell_banned_miners {}\n",
bitcell_banned_miners {}\n\
\n\
# HELP bitcell_average_trust_score Average trust score of miners\n\
# TYPE bitcell_average_trust_score gauge\n\
bitcell_average_trust_score {}\n\
\n\
# HELP bitcell_slashing_events_total Total slashing events\n\
# TYPE bitcell_slashing_events_total counter\n\
bitcell_slashing_events_total {}\n",
self.get_chain_height(),
self.get_sync_progress(),
self.get_peer_count(),
self.get_dht_peer_count(),
self.get_bytes_sent(),
self.get_bytes_received(),
self.get_messages_sent(),
self.get_messages_received(),
self.get_pending_txs(),
self.get_total_txs_processed(),
self.get_proofs_generated(),
self.get_proofs_verified(),
self.get_active_miners(),
self.get_banned_miners(),
self.get_average_trust_score(),
self.get_slashing_events(),
)
}
}
Expand Down Expand Up @@ -270,4 +333,50 @@ mod tests {
assert!(export.contains("bitcell_chain_height 42"));
assert!(export.contains("bitcell_peer_count 3"));
}

#[test]
fn test_new_metrics() {
let metrics = MetricsRegistry::new();

// Test message counters
metrics.add_message_sent();
metrics.add_message_sent();
metrics.add_message_sent();
assert_eq!(metrics.get_messages_sent(), 3);

metrics.add_message_received();
assert_eq!(metrics.get_messages_received(), 1);

// Test trust score
metrics.set_average_trust_score(0.85);
assert!((metrics.get_average_trust_score() - 0.85).abs() < 0.001);

metrics.set_average_trust_score(0.923);
assert!((metrics.get_average_trust_score() - 0.923).abs() < 0.001);

// Test slashing events
metrics.inc_slashing_events();
metrics.inc_slashing_events();
assert_eq!(metrics.get_slashing_events(), 2);
}

#[test]
fn test_new_metrics_in_prometheus_export() {
let metrics = MetricsRegistry::new();

// Set new metrics
metrics.add_message_sent();
metrics.add_message_sent();
metrics.add_message_received();
metrics.set_average_trust_score(0.875);
metrics.inc_slashing_events();

let export = metrics.export_prometheus();

// Verify new metrics are in export
assert!(export.contains("bitcell_messages_sent_total 2"));
assert!(export.contains("bitcell_messages_received_total 1"));
assert!(export.contains("bitcell_average_trust_score 0.875"));
assert!(export.contains("bitcell_slashing_events_total 1"));
}
}
10 changes: 10 additions & 0 deletions crates/bitcell-node/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,10 @@ impl NetworkManager {
}

self.metrics.add_bytes_sent(block_size * peer_ids.len() as u64);
// Update message counter for each peer we sent to
for _ in &peer_ids {
self.metrics.add_message_sent();
}

// Broadcast via Gossipsub
let dht_opt = {
Expand Down Expand Up @@ -618,6 +622,10 @@ impl NetworkManager {
}

self.metrics.add_bytes_sent(tx_size * peer_ids.len() as u64);
// Update message counter for each peer we sent to
for _ in &peer_ids {
self.metrics.add_message_sent();
}

// Broadcast via Gossipsub
let dht_opt = {
Expand Down Expand Up @@ -648,6 +656,7 @@ impl NetworkManager {
pub async fn handle_incoming_block(&self, block: Block) -> Result<()> {
let block_size = bincode::serialize(&block).unwrap_or_default().len() as u64;
self.metrics.add_bytes_received(block_size);
self.metrics.add_message_received();

// Forward to block processing channel
let tx_opt = {
Expand All @@ -665,6 +674,7 @@ impl NetworkManager {
pub async fn handle_incoming_transaction(&self, tx: Transaction) -> Result<()> {
let tx_size = bincode::serialize(&tx).unwrap_or_default().len() as u64;
self.metrics.add_bytes_received(tx_size);
self.metrics.add_message_received();

// Forward to transaction processing channel
let sender_opt = {
Expand Down
25 changes: 24 additions & 1 deletion crates/bitcell-node/src/tournament.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const COMMIT_PHASE_SECS: u64 = 5;
const REVEAL_PHASE_SECS: u64 = 5;
const BATTLE_PHASE_SECS: u64 = 5;

/// Default trust score for new miners or when no miners exist
const DEFAULT_TRUST_SCORE: f64 = 0.85;

/// Tournament manager
pub struct TournamentManager {
/// Current tournament
Expand Down Expand Up @@ -164,6 +167,11 @@ impl TournamentManager {
// Add evidence with current block height
let height = *self.current_height.read().unwrap();
counters.add_evidence(bitcell_ebsl::Evidence::new(evidence_type, 0, height));

// Track slashing events (negative evidence)
if evidence_type.is_negative() {
self.metrics.inc_slashing_events();
}
} // Drop write lock here

// Update metrics (acquires read lock)
Expand Down Expand Up @@ -208,19 +216,34 @@ impl TournamentManager {

let mut active_count = 0;
let mut banned_count = 0;
let mut total_trust_score = 0.0;
let mut miner_count = 0;

for (_miner, counters) in evidence_map.iter() {
let trust = TrustScore::from_evidence(counters, &self.ebsl_params);
let trust_value = trust.value();

total_trust_score += trust_value;
miner_count += 1;

if trust.is_eligible(&self.ebsl_params) {
active_count += 1;
} else if trust.value() < self.ebsl_params.t_kill {
} else if trust_value < self.ebsl_params.t_kill {
banned_count += 1;
}
}

self.metrics.set_active_miners(active_count);
self.metrics.set_banned_miners(banned_count);

// Calculate average trust score
if miner_count > 0 {
let avg_trust = total_trust_score / miner_count as f64;
self.metrics.set_average_trust_score(avg_trust);
} else {
// Use default trust score when no miners
self.metrics.set_average_trust_score(DEFAULT_TRUST_SCORE);
}
}
}

Expand Down