Skip to main content

pot_o_extensions/
pool_strategy.rs

1//! Pool strategy: solo and proportional/PPLNS pool info and reward distribution.
2
3use pot_o_core::TribeResult;
4use serde::{Deserialize, Serialize};
5
6// ---------------------------------------------------------------------------
7// Types
8// ---------------------------------------------------------------------------
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum PoolType {
12    Solo,
13    Proportional,
14    PPLNS,
15    Custom,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct MinerShare {
20    pub miner_pubkey: String,
21    pub share_pct: f64,
22    pub reward_amount: u64,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct MinerInfo {
27    pub pubkey: String,
28    pub stake: u64,
29    pub proofs_submitted: u64,
30    pub reputation: f64,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ProofRecord {
35    pub miner_pubkey: String,
36    pub challenge_id: String,
37    pub reward: u64,
38    pub timestamp: chrono::DateTime<chrono::Utc>,
39}
40
41#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct PoolInfo {
43    pub pool_type: String,
44    pub total_miners: usize,
45    pub total_stake: u64,
46    pub minimum_stake: u64,
47}
48
49// ---------------------------------------------------------------------------
50// Trait
51// ---------------------------------------------------------------------------
52
53/// How mining rewards are distributed among participants.
54pub trait PoolStrategy: Send + Sync {
55    fn pool_type(&self) -> PoolType;
56    fn calculate_shares(&self, proofs: &[ProofRecord], reward: u64)
57        -> TribeResult<Vec<MinerShare>>;
58    fn minimum_stake(&self) -> u64;
59    fn accept_miner(&self, miner: &MinerInfo) -> TribeResult<bool>;
60    fn pool_info(&self, miners: usize, total_stake: u64) -> PoolInfo;
61}
62
63// ---------------------------------------------------------------------------
64// SoloStrategy (implemented now)
65// ---------------------------------------------------------------------------
66
67pub struct SoloStrategy;
68
69impl PoolStrategy for SoloStrategy {
70    fn pool_type(&self) -> PoolType {
71        PoolType::Solo
72    }
73
74    fn calculate_shares(
75        &self,
76        proofs: &[ProofRecord],
77        reward: u64,
78    ) -> TribeResult<Vec<MinerShare>> {
79        // 100% to the miner who submitted the proof
80        Ok(proofs
81            .last()
82            .map(|p| {
83                vec![MinerShare {
84                    miner_pubkey: p.miner_pubkey.clone(),
85                    share_pct: 100.0,
86                    reward_amount: reward,
87                }]
88            })
89            .unwrap_or_default())
90    }
91
92    fn minimum_stake(&self) -> u64 {
93        0 // No stake required for solo
94    }
95
96    fn accept_miner(&self, _miner: &MinerInfo) -> TribeResult<bool> {
97        Ok(true)
98    }
99
100    fn pool_info(&self, miners: usize, total_stake: u64) -> PoolInfo {
101        PoolInfo {
102            pool_type: "solo".into(),
103            total_miners: miners,
104            total_stake,
105            minimum_stake: 0,
106        }
107    }
108}
109
110// ---------------------------------------------------------------------------
111// ProportionalPool (stubbed)
112// ---------------------------------------------------------------------------
113
114pub struct ProportionalPool {
115    pub min_stake: u64,
116}
117
118impl PoolStrategy for ProportionalPool {
119    fn pool_type(&self) -> PoolType {
120        PoolType::Proportional
121    }
122
123    fn calculate_shares(
124        &self,
125        _proofs: &[ProofRecord],
126        _reward: u64,
127    ) -> TribeResult<Vec<MinerShare>> {
128        todo!("Proportional reward distribution not yet implemented")
129    }
130
131    fn minimum_stake(&self) -> u64 {
132        self.min_stake
133    }
134
135    fn accept_miner(&self, miner: &MinerInfo) -> TribeResult<bool> {
136        Ok(miner.stake >= self.min_stake)
137    }
138
139    fn pool_info(&self, miners: usize, total_stake: u64) -> PoolInfo {
140        PoolInfo {
141            pool_type: "proportional".into(),
142            total_miners: miners,
143            total_stake,
144            minimum_stake: self.min_stake,
145        }
146    }
147}
148
149// ---------------------------------------------------------------------------
150// PPLNSPool (stubbed)
151// ---------------------------------------------------------------------------
152
153pub struct PPLNSPool {
154    pub window_size: usize,
155    pub min_stake: u64,
156}
157
158impl PoolStrategy for PPLNSPool {
159    fn pool_type(&self) -> PoolType {
160        PoolType::PPLNS
161    }
162
163    fn calculate_shares(
164        &self,
165        _proofs: &[ProofRecord],
166        _reward: u64,
167    ) -> TribeResult<Vec<MinerShare>> {
168        todo!("PPLNS reward distribution not yet implemented")
169    }
170
171    fn minimum_stake(&self) -> u64 {
172        self.min_stake
173    }
174
175    fn accept_miner(&self, miner: &MinerInfo) -> TribeResult<bool> {
176        Ok(miner.stake >= self.min_stake)
177    }
178
179    fn pool_info(&self, miners: usize, total_stake: u64) -> PoolInfo {
180        PoolInfo {
181            pool_type: "pplns".into(),
182            total_miners: miners,
183            total_stake,
184            minimum_stake: self.min_stake,
185        }
186    }
187}