diff options
| author | gabrix73 <gabriel1@frozenstar.info> | 2026-06-01 18:41:36 +0200 |
|---|---|---|
| committer | gabrix73 <gabriel1@frozenstar.info> | 2026-06-01 18:41:36 +0200 |
| commit | 9f5d864d533ce86459e654f5d78212933c0269ea (patch) | |
| tree | 4b71e8c7ab4e78da93e5f3164803da4de68c7031 /timing/src/delay.rs | |
| download | onioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.tar.gz onioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.tar.xz onioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.zip | |
OnionCoin is a privacy cryptocurrency that rewards Tor relay operators
through a unique Proof-of-Contribution consensus mechanism.
Core Features:
- Proof-of-Relay: 30% of block rewards go to Tor operators
- Native .onion node identity (no IP exposure)
- Temporal obfuscation protocols
- Dandelion++ over Tor propagation
- Native inheritance system with dead man's switch
Technical Stack:
- Rust workspace with 8 crates
- Ed25519/X25519 cryptography
- arti (Rust Tor client) integration planned
- 10 minute block time, 5-10 TPS design
Status: Prototype
- Consensus logic complete with passing tests (30/33)
- Network layer conceptual design complete
- Tor integration pending
- Testnet launch planned Q3 2026
License: MIT
Author: Gabriele Salati (virebent)
Contact: g48rix@gmail.com
Website: https://www.gabrielesalati.eu
Repository: https://git.virebent.art/virebent/onioncoin
Diffstat (limited to 'timing/src/delay.rs')
| -rw-r--r-- | timing/src/delay.rs | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/timing/src/delay.rs b/timing/src/delay.rs new file mode 100644 index 0000000..b264381 --- /dev/null +++ b/timing/src/delay.rs @@ -0,0 +1,259 @@ +use rand::Rng; +use std::time::Duration; +use serde::{Deserialize, Serialize}; + +/// Strategy for introducing random delays in transaction propagation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum DelayStrategy { + /// Fixed delay + Fixed(Duration), + + /// Random delay within a range + Random { min: Duration, max: Duration }, + + /// Exponential backoff with jitter + Exponential { + base: Duration, + max: Duration, + attempt: u32, + }, + + /// Delay based on observed Tor latency + TorAdaptive { + observed_latency: Duration, + multiplier: f64, + }, +} + +impl DelayStrategy { + /// Wallet broadcast delay: 5-60 minutes + pub fn wallet_broadcast() -> Self { + Self::Random { + min: Duration::from_secs(5 * 60), + max: Duration::from_secs(60 * 60), + } + } + + /// Node rebroadcast delay: 10s - 2 minutes + pub fn node_rebroadcast() -> Self { + Self::Random { + min: Duration::from_secs(10), + max: Duration::from_secs(120), + } + } + + /// Mixing pool batch delay: 10-30 minutes + pub fn mixing_pool() -> Self { + Self::Random { + min: Duration::from_secs(10 * 60), + max: Duration::from_secs(30 * 60), + } + } + + /// Validation delay before processing: 5s - 60s + pub fn validation() -> Self { + Self::Random { + min: Duration::from_secs(5), + max: Duration::from_secs(60), + } + } + + /// Dandelion STEM hop delay: 30s - 5 minutes + pub fn dandelion_stem() -> Self { + Self::Random { + min: Duration::from_secs(30), + max: Duration::from_secs(5 * 60), + } + } + + /// Create adaptive delay based on observed Tor latency + pub fn from_tor_latency(observed: Duration) -> Self { + Self::TorAdaptive { + observed_latency: observed, + multiplier: 2.0, // Amplify variability + } + } + + /// Calculate the actual delay to use + pub fn calculate(&self) -> Duration { + let mut rng = rand::thread_rng(); + + match self { + Self::Fixed(d) => *d, + + Self::Random { min, max } => { + let min_ms = min.as_millis() as u64; + let max_ms = max.as_millis() as u64; + let delay_ms = rng.gen_range(min_ms..=max_ms); + Duration::from_millis(delay_ms) + } + + Self::Exponential { base, max, attempt } => { + let base_ms = base.as_millis() as u64; + let max_ms = max.as_millis() as u64; + + // Exponential: base * 2^attempt with jitter + let exp_delay = base_ms.saturating_mul(2u64.saturating_pow(*attempt)); + let capped = exp_delay.min(max_ms); + + // Add ±25% jitter + let jitter_range = (capped as f64 * 0.25) as u64; + let jitter = rng.gen_range(0..=jitter_range); + let with_jitter = if rng.gen_bool(0.5) { + capped.saturating_add(jitter) + } else { + capped.saturating_sub(jitter) + }; + + Duration::from_millis(with_jitter) + } + + Self::TorAdaptive { observed_latency, multiplier } => { + let base_ms = observed_latency.as_millis() as u64; + let amplified = (base_ms as f64 * multiplier) as u64; + + // Random delay up to amplified latency + let delay_ms = rng.gen_range(0..=amplified); + Duration::from_millis(delay_ms) + } + } + } + + /// Async sleep with this delay strategy + pub async fn sleep(&self) { + let delay = self.calculate(); + tokio::time::sleep(delay).await; + } +} + +/// Helper for managing multiple delays in sequence +#[derive(Debug)] +pub struct DelaySequence { + strategies: Vec<DelayStrategy>, + current: usize, +} + +impl DelaySequence { + pub fn new(strategies: Vec<DelayStrategy>) -> Self { + Self { + strategies, + current: 0, + } + } + + /// Create a Dandelion++ STEM sequence (1-4 random hops) + pub fn dandelion_stem() -> Self { + let mut rng = rand::thread_rng(); + let hops = rng.gen_range(1..=4); + + let strategies = (0..hops) + .map(|_| DelayStrategy::dandelion_stem()) + .collect(); + + Self::new(strategies) + } + + /// Get next delay, returns None when sequence is exhausted + pub fn next(&mut self) -> Option<&DelayStrategy> { + if self.current < self.strategies.len() { + let strategy = &self.strategies[self.current]; + self.current += 1; + Some(strategy) + } else { + None + } + } + + /// Reset sequence to beginning + pub fn reset(&mut self) { + self.current = 0; + } + + /// Total number of delays in sequence + pub fn len(&self) -> usize { + self.strategies.len() + } + + pub fn is_empty(&self) -> bool { + self.strategies.is_empty() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fixed_delay() { + let strategy = DelayStrategy::Fixed(Duration::from_secs(10)); + let delay = strategy.calculate(); + assert_eq!(delay, Duration::from_secs(10)); + } + + #[test] + fn test_random_delay() { + let strategy = DelayStrategy::Random { + min: Duration::from_secs(5), + max: Duration::from_secs(10), + }; + + for _ in 0..100 { + let delay = strategy.calculate(); + assert!(delay >= Duration::from_secs(5)); + assert!(delay <= Duration::from_secs(10)); + } + } + + #[test] + fn test_exponential_delay() { + let base = Duration::from_secs(1); + let max = Duration::from_secs(100); + + for attempt in 0..5 { + let strategy = DelayStrategy::Exponential { + base, + max, + attempt, + }; + let delay = strategy.calculate(); + assert!(delay <= max); + } + } + + #[test] + fn test_tor_adaptive_delay() { + let observed = Duration::from_secs(2); + let strategy = DelayStrategy::from_tor_latency(observed); + + for _ in 0..100 { + let delay = strategy.calculate(); + // Should be between 0 and 2x observed latency + assert!(delay <= Duration::from_secs(4)); + } + } + + #[test] + fn test_delay_sequence() { + let mut sequence = DelaySequence::dandelion_stem(); + let len = sequence.len(); + + assert!(len >= 1 && len <= 4); + + let mut count = 0; + while sequence.next().is_some() { + count += 1; + } + + assert_eq!(count, len); + } + + #[tokio::test] + async fn test_async_sleep() { + let strategy = DelayStrategy::Fixed(Duration::from_millis(10)); + let start = std::time::Instant::now(); + strategy.sleep().await; + let elapsed = start.elapsed(); + + assert!(elapsed >= Duration::from_millis(10)); + } +} |
