|
1 | 1 | //! Epoch tracking for Bittensor subnets |
2 | 2 | //! |
3 | | -//! Tracks epoch boundaries and phases for weight commit-reveal. |
| 3 | +//! Tracks epoch boundaries and commit-reveal timing windows as defined by Subtensor. |
| 4 | +//! |
| 5 | +//! # Bittensor Commit-Reveal Protocol |
| 6 | +//! |
| 7 | +//! Subtensor divides each epoch into timing windows for weight submission: |
| 8 | +//! - **Evaluation** (0% - 75%): Standard operation period |
| 9 | +//! - **CommitWindow** (75% - 87.5%): Validators submit weight commitment hashes |
| 10 | +//! - **RevealWindow** (87.5% - 100%): Validators reveal actual weights |
| 11 | +//! |
| 12 | +//! Reference: `pallets/subtensor/src/subnets/weights.rs` |
4 | 13 |
|
5 | 14 | use crate::chain::BittensorClient; |
6 | 15 | use anyhow::Result; |
7 | 16 |
|
8 | | -/// Epoch phase in Bittensor |
9 | | -#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 17 | +/// Bittensor epoch timing window |
| 18 | +/// |
| 19 | +/// Represents the current position within an epoch as defined by Subtensor's |
| 20 | +/// commit-reveal protocol for mechanism weights. |
| 21 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] |
10 | 22 | pub enum EpochPhase { |
11 | | - /// Normal operation / evaluation phase |
| 23 | + /// Standard operation period (0% - 75% of epoch) |
| 24 | + #[default] |
12 | 25 | Evaluation, |
13 | | - /// Weight commit window is open |
| 26 | + /// Subtensor accepts weight commitment hashes (75% - 87.5% of epoch) |
14 | 27 | CommitWindow, |
15 | | - /// Weight reveal window is open |
| 28 | + /// Subtensor accepts weight reveals (87.5% - 100% of epoch) |
16 | 29 | RevealWindow, |
17 | 30 | } |
18 | 31 |
|
@@ -147,21 +160,19 @@ impl EpochTracker { |
147 | 160 | } |
148 | 161 | } |
149 | 162 |
|
150 | | - /// Determine the current phase based on block position |
| 163 | + /// Determines the current Subtensor timing window based on block position. |
| 164 | + /// |
| 165 | + /// Subtensor commit-reveal windows (percentage of epoch tempo): |
| 166 | + /// - Evaluation: blocks 0 to 75% |
| 167 | + /// - CommitWindow: blocks 75% to 87.5% |
| 168 | + /// - RevealWindow: blocks 87.5% to 100% |
151 | 169 | fn determine_phase(&self, current_block: u64, epoch_start_block: u64) -> EpochPhase { |
152 | | - if !self.commit_reveal_enabled { |
| 170 | + if self.tempo == 0 { |
153 | 171 | return EpochPhase::Evaluation; |
154 | 172 | } |
155 | 173 |
|
156 | 174 | let blocks_into_epoch = current_block.saturating_sub(epoch_start_block); |
157 | 175 |
|
158 | | - // Bittensor commit-reveal windows: |
159 | | - // - Commit window: blocks before reveal window |
160 | | - // - Reveal window: last portion of epoch |
161 | | - // The exact timing depends on subnet parameters |
162 | | - |
163 | | - // Default: last 25% of epoch is commit/reveal |
164 | | - // Last 12.5% is reveal, 12.5% before that is commit |
165 | 176 | let reveal_start = (self.tempo * 7) / 8; // 87.5% |
166 | 177 | let commit_start = (self.tempo * 3) / 4; // 75% |
167 | 178 |
|
@@ -278,4 +289,44 @@ mod tests { |
278 | 289 | // No transition at block 400 (same epoch) |
279 | 290 | assert!(tracker.check_epoch_transition(400).is_none()); |
280 | 291 | } |
| 292 | + |
| 293 | + #[test] |
| 294 | + fn test_timing_windows() { |
| 295 | + // Test with commit_reveal_enabled = false |
| 296 | + let tracker = EpochTracker { |
| 297 | + netuid: 1, |
| 298 | + tempo: 100, |
| 299 | + commit_reveal_enabled: false, |
| 300 | + reveal_period_epochs: 1, |
| 301 | + last_epoch_number: 0, |
| 302 | + }; |
| 303 | + |
| 304 | + // 0-74: Evaluation, 75-87: CommitWindow, 88-99: RevealWindow |
| 305 | + let info = tracker.get_epoch_info(50); |
| 306 | + assert_eq!(info.phase, EpochPhase::Evaluation); |
| 307 | + |
| 308 | + let info = tracker.get_epoch_info(75); |
| 309 | + assert_eq!(info.phase, EpochPhase::CommitWindow); |
| 310 | + |
| 311 | + let info = tracker.get_epoch_info(88); |
| 312 | + assert_eq!(info.phase, EpochPhase::RevealWindow); |
| 313 | + |
| 314 | + // Test with commit_reveal_enabled = true (same behavior) |
| 315 | + let tracker = EpochTracker { |
| 316 | + netuid: 1, |
| 317 | + tempo: 100, |
| 318 | + commit_reveal_enabled: true, |
| 319 | + reveal_period_epochs: 1, |
| 320 | + last_epoch_number: 0, |
| 321 | + }; |
| 322 | + |
| 323 | + let info = tracker.get_epoch_info(50); |
| 324 | + assert_eq!(info.phase, EpochPhase::Evaluation); |
| 325 | + |
| 326 | + let info = tracker.get_epoch_info(75); |
| 327 | + assert_eq!(info.phase, EpochPhase::CommitWindow); |
| 328 | + |
| 329 | + let info = tracker.get_epoch_info(88); |
| 330 | + assert_eq!(info.phase, EpochPhase::RevealWindow); |
| 331 | + } |
281 | 332 | } |
0 commit comments