From 9f5d864d533ce86459e654f5d78212933c0269ea Mon Sep 17 00:00:00 2001 From: gabrix73 Date: Mon, 1 Jun 2026 18:41:36 +0200 Subject: Initial commit: OnionCoin prototype with Proof-of-Relay consensus 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 --- timing/src/obfuscation.rs | 243 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 timing/src/obfuscation.rs (limited to 'timing/src/obfuscation.rs') diff --git a/timing/src/obfuscation.rs b/timing/src/obfuscation.rs new file mode 100644 index 0000000..b48ea82 --- /dev/null +++ b/timing/src/obfuscation.rs @@ -0,0 +1,243 @@ +use crate::{TimeRange, TimeRangeProof, DelayStrategy, MixingPool}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +/// Orchestrates all timing obfuscation strategies +#[derive(Debug)] +pub struct TimingObfuscator { + /// Mixing pool for batch releases + mixing_pool: MixingPool, + + /// Strategy for adding delays + delay_strategy: DelayStrategy, + + /// Configuration + config: ObfuscationConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ObfuscationConfig { + /// Enable mixing pool batching + pub use_mixing_pool: bool, + + /// Enable random delays + pub use_delays: bool, + + /// Enable fuzzy timestamps + pub use_fuzzy_timestamps: bool, + + /// Ratio of fake items to inject (0.0 - 1.0) + pub fake_item_ratio: f32, +} + +impl Default for ObfuscationConfig { + fn default() -> Self { + Self { + use_mixing_pool: true, + use_delays: true, + use_fuzzy_timestamps: true, + fake_item_ratio: 0.3, // 30% fake traffic + } + } +} + +impl TimingObfuscator { + pub fn new(config: ObfuscationConfig) -> Self { + Self { + mixing_pool: MixingPool::default(), + delay_strategy: DelayStrategy::node_rebroadcast(), + config, + } + } + + /// Add item to mixing pool + pub fn add_to_pool(&mut self, item: T) { + if self.config.use_mixing_pool { + self.mixing_pool.add(item); + } + } + + /// Check if should release batch + pub fn should_release_batch(&self) -> bool { + if !self.config.use_mixing_pool { + return false; + } + self.mixing_pool.should_release() + } + + /// Release shuffled batch + pub fn release_batch(&mut self) -> Vec { + self.mixing_pool.release_batch() + } + + /// Apply delay strategy + pub async fn apply_delay(&self) { + if self.config.use_delays { + self.delay_strategy.sleep().await; + } + } + + /// Get current pool size + pub fn pool_size(&self) -> usize { + self.mixing_pool.len() + } +} + +/// Transaction timing metadata +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TimingMetadata { + /// Fuzzy time range + pub time_range: TimeRange, + + /// Zero-knowledge proof of creation time + pub time_proof: TimeRangeProof, + + /// Optional: observed network delay (for adaptive strategies) + pub network_delay_hint: Option, +} + +impl TimingMetadata { + /// Create timing metadata for a transaction + pub fn new(real_time: DateTime, seed: &[u8; 32]) -> Result { + let time_range = TimeRange::new_fuzzy(real_time); + let time_proof = TimeRangeProof::generate(seed, real_time, &time_range)?; + + Ok(Self { + time_range, + time_proof, + network_delay_hint: None, + }) + } + + /// Validate timing metadata + pub fn validate(&self, current_time: DateTime) -> bool { + // Check time range is valid + if !self.time_range.is_valid(current_time) { + return false; + } + + // Verify time proof + if !self.time_proof.verify(&self.time_range) { + return false; + } + + true + } + + /// Set network delay hint for adaptive strategies + pub fn with_network_delay(mut self, delay: Duration) -> Self { + self.network_delay_hint = Some(delay); + self + } +} + +/// Strategies for transaction propagation +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum PropagationPhase { + /// Dandelion STEM phase (anonymity) + Stem, + + /// Dandelion FLUFF phase (broadcast) + Fluff, +} + +/// Transaction wrapper with timing information +#[derive(Debug, Clone)] +pub struct TimedItem { + pub item: T, + pub timing: TimingMetadata, + pub phase: PropagationPhase, +} + +impl TimedItem { + pub fn new(item: T, timing: TimingMetadata) -> Self { + Self { + item, + timing, + phase: PropagationPhase::Stem, + } + } + + pub fn transition_to_fluff(mut self) -> Self { + self.phase = PropagationPhase::Fluff; + self + } + + pub fn is_stem(&self) -> bool { + self.phase == PropagationPhase::Stem + } + + pub fn is_fluff(&self) -> bool { + self.phase == PropagationPhase::Fluff + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_timing_metadata_creation() { + let now = Utc::now(); + let seed = [42u8; 32]; + + let metadata = TimingMetadata::new(now, &seed).unwrap(); + assert!(metadata.validate(now)); + } + + #[test] + fn test_timing_metadata_validation() { + let now = Utc::now(); + let seed = [42u8; 32]; + + let metadata = TimingMetadata::new(now, &seed).unwrap(); + + // Should validate at current time + assert!(metadata.validate(now)); + + // Should validate slightly in the future + let future = now + chrono::Duration::hours(1); + assert!(metadata.validate(future)); + } + + #[test] + fn test_obfuscator_pool() { + let config = ObfuscationConfig::default(); + let mut obfuscator = TimingObfuscator::::new(config); + + obfuscator.add_to_pool(1); + obfuscator.add_to_pool(2); + obfuscator.add_to_pool(3); + + assert_eq!(obfuscator.pool_size(), 3); + } + + #[test] + fn test_timed_item_phases() { + let now = Utc::now(); + let seed = [42u8; 32]; + let timing = TimingMetadata::new(now, &seed).unwrap(); + + let item = TimedItem::new(42u32, timing); + assert!(item.is_stem()); + assert!(!item.is_fluff()); + + let item = item.transition_to_fluff(); + assert!(!item.is_stem()); + assert!(item.is_fluff()); + } + + #[tokio::test] + async fn test_apply_delay() { + let config = ObfuscationConfig::default(); + let obfuscator = TimingObfuscator::::new(config); + + let start = std::time::Instant::now(); + obfuscator.apply_delay().await; + let elapsed = start.elapsed(); + + // Should have some delay (at least 10s from node_rebroadcast strategy) + assert!(elapsed >= Duration::from_secs(10)); + } +} -- cgit v1.2.3