summaryrefslogtreecommitdiffstats
path: root/timing/src/delay.rs
diff options
context:
space:
mode:
authorgabrix73 <gabriel1@frozenstar.info>2026-06-01 18:41:36 +0200
committergabrix73 <gabriel1@frozenstar.info>2026-06-01 18:41:36 +0200
commit9f5d864d533ce86459e654f5d78212933c0269ea (patch)
tree4b71e8c7ab4e78da93e5f3164803da4de68c7031 /timing/src/delay.rs
downloadonioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.tar.gz
onioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.tar.xz
onioncoin-9e960fa6d453b29aef970ba3cd81e9cbbfd94bcc.zip
Initial commit: OnionCoin prototype with Proof-of-Relay consensusHEADv0.1.0main
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.rs259
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));
+ }
+}