use serde::{Deserialize, Serialize};
use std::sync::{Arc, OnceLock};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AggregatorStartedInfo {
pub listen_address: String,
pub upstream_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockUpdate {
pub height: u64,
pub base_target: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionInfo {
pub height: u64,
pub account_id: String,
pub machine_id: Option<String>,
pub generation_signature: String,
pub seed: String,
pub nonce: u64,
pub compression: u8,
pub raw_quality: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AcceptedInfo {
pub height: u64,
pub account_id: String,
pub machine_id: Option<String>,
pub generation_signature: String,
pub seed: String,
pub nonce: u64,
pub compression: u8,
pub raw_quality: u64,
pub poc_time: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RejectedInfo {
pub height: u64,
pub account_id: String,
pub machine_id: Option<String>,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ForwardedInfo {
pub account_id: String,
pub raw_quality: u64,
pub pool_name: String,
}
pub trait AggregatorCallback: Send + Sync {
fn on_started(&self, _info: &AggregatorStartedInfo) {}
fn on_new_block(&self, _block: &BlockUpdate) {}
fn on_submission_received(&self, _info: &SubmissionInfo) {}
fn on_submission_forwarded(&self, _info: &ForwardedInfo) {}
fn on_submission_accepted(&self, _info: &AcceptedInfo) {}
fn on_submission_rejected(&self, _info: &RejectedInfo) {}
fn on_miner_connected(&self, _account_id: &str, _machine_id: &str) {}
fn on_stats_updated(&self, _snapshot: &crate::stats::StatsSnapshot) {}
fn on_error(&self, _error: &str) {}
fn on_stopped(&self) {}
}
static AGGREGATOR_CALLBACK: OnceLock<Arc<dyn AggregatorCallback>> = OnceLock::new();
pub fn set_aggregator_callback(
callback: Arc<dyn AggregatorCallback>,
) -> Result<(), Arc<dyn AggregatorCallback>> {
AGGREGATOR_CALLBACK.set(callback)
}
pub fn get_aggregator_callback() -> Option<Arc<dyn AggregatorCallback>> {
AGGREGATOR_CALLBACK.get().cloned()
}
#[inline]
pub fn with_callback<F>(f: F)
where
F: FnOnce(&dyn AggregatorCallback),
{
if let Some(cb) = AGGREGATOR_CALLBACK.get() {
f(cb.as_ref());
}
}
pub struct NoOpCallback;
impl AggregatorCallback for NoOpCallback {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
struct TestCallback {
started_count: AtomicU32,
block_count: AtomicU32,
}
impl AggregatorCallback for TestCallback {
fn on_started(&self, _info: &AggregatorStartedInfo) {
self.started_count.fetch_add(1, Ordering::SeqCst);
}
fn on_new_block(&self, _block: &BlockUpdate) {
self.block_count.fetch_add(1, Ordering::SeqCst);
}
}
#[test]
fn test_callback_invocation() {
let cb = TestCallback {
started_count: AtomicU32::new(0),
block_count: AtomicU32::new(0),
};
let info = AggregatorStartedInfo {
listen_address: "0.0.0.0:8080".to_string(),
upstream_name: "test-pool".to_string(),
};
cb.on_started(&info);
assert_eq!(cb.started_count.load(Ordering::SeqCst), 1);
let block = BlockUpdate {
height: 100,
base_target: 5000,
};
cb.on_new_block(&block);
assert_eq!(cb.block_count.load(Ordering::SeqCst), 1);
}
}