mina_producer_dashboard/evaluator/
mod.rs

1use std::str::FromStr;
2
3use mina_node_account::AccountSecretKey;
4use mina_p2p_messages::v2::EpochSeed;
5use tokio::{sync::mpsc::UnboundedReceiver, task::JoinHandle};
6use vrf::{VrfEvaluationInput, VrfEvaluationOutput};
7
8use crate::{
9    evaluator::epoch::SlotData,
10    node::{calc_slot_timestamp, epoch_ledgers::Ledger},
11    storage::db_sled::Database,
12};
13
14pub mod epoch;
15
16pub struct Evaluator {
17    key: AccountSecretKey,
18    db: Database,
19    receiver: UnboundedReceiver<EpochInit>,
20}
21
22impl Evaluator {
23    pub fn spawn_new(
24        key: AccountSecretKey,
25        db: Database,
26        receiver: UnboundedReceiver<EpochInit>,
27    ) -> JoinHandle<()> {
28        tokio::spawn(async move { Self { key, db, receiver }.run().await })
29    }
30
31    // TODO(adonagy): can be paralellized on slots
32    async fn run(&mut self) {
33        if let Some(init) = self.receiver.recv().await {
34            let (start, end) = init.bounds;
35            println!("Evaluating slots: {start} - {end}");
36            let total_currency = init.ledger.total_currency();
37
38            let pub_key = self.key.public_key();
39
40            let delegates = init
41                .ledger
42                .gather_producer_and_delegates(&self.key.public_key().to_string());
43
44            let epoch_seed = EpochSeed::from_str(&init.seed).unwrap();
45
46            for global_slot in start..=end {
47                // initially set to lost, the winning will overwrite it
48                let timestamp = calc_slot_timestamp(init.genesis_timestamp, global_slot);
49                let _ = self
50                    .db
51                    .store_slot(global_slot, &SlotData::new_lost(global_slot, timestamp));
52                for (index, delegate) in &delegates {
53                    let vrf_input = VrfEvaluationInput {
54                        producer_key: self.key.clone().into(),
55                        global_slot,
56                        epoch_seed: epoch_seed.clone(),
57                        account_pub_key: pub_key.clone(),
58                        delegator_index: (*index).into(),
59                        delegated_stake: delegate.balance.clone().into(),
60                        total_currency: total_currency.clone(),
61                    };
62
63                    if let Ok(VrfEvaluationOutput::SlotWon(_)) = vrf::evaluate_vrf(vrf_input) {
64                        println!("Won slot: {global_slot}");
65
66                        // TODO(adonagy): handle error
67                        let _ = self
68                            .db
69                            .store_slot(global_slot, &SlotData::new(global_slot, timestamp, None));
70                        break;
71                    }
72                }
73            }
74        }
75    }
76}
77
78#[derive(Debug, Clone)]
79pub struct EpochInit {
80    _epoch_number: u32,
81    ledger: Ledger,
82    seed: String,
83    bounds: (u32, u32),
84    genesis_timestamp: i64,
85}
86
87impl EpochInit {
88    pub fn new(
89        epoch_number: u32,
90        ledger: Ledger,
91        seed: String,
92        bounds: (u32, u32),
93        genesis_timestamp: i64,
94    ) -> Self {
95        Self {
96            _epoch_number: epoch_number,
97            ledger,
98            seed,
99            bounds,
100            genesis_timestamp,
101        }
102    }
103}