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 /network/src/propagation.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 'network/src/propagation.rs')
| -rw-r--r-- | network/src/propagation.rs | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/network/src/propagation.rs b/network/src/propagation.rs new file mode 100644 index 0000000..519f5f4 --- /dev/null +++ b/network/src/propagation.rs @@ -0,0 +1,238 @@ +use crate::dandelion::{DandelionRouter, DandelionPhase, RoutingDecision, PeerId}; +use onioncoin_timing::{TimingObfuscator, obfuscation::ObfuscationConfig, DelayStrategy}; +use onioncoin_core::Transaction; +use async_trait::async_trait; +use std::sync::Arc; +use tokio::sync::Mutex; + +/// Manages transaction propagation with timing obfuscation +pub struct PropagationManager { + /// Dandelion++ router + dandelion: Arc<Mutex<DandelionRouter<Vec<u8>>>>, + + /// Timing obfuscator + obfuscator: Arc<Mutex<TimingObfuscator<Transaction>>>, + + /// Connected peers + peers: Arc<Mutex<Vec<PeerId>>>, +} + +impl PropagationManager { + pub fn new() -> Self { + let config = ObfuscationConfig::default(); + + Self { + dandelion: Arc::new(Mutex::new(DandelionRouter::new(Default::default()))), + obfuscator: Arc::new(Mutex::new(TimingObfuscator::new(config))), + peers: Arc::new(Mutex::new(Vec::new())), + } + } + + /// Receive a new transaction from wallet/user + pub async fn receive_transaction(&self, tx: Transaction) -> PropagationResult { + let tx_id = tx.id(); + + // Register with Dandelion router + { + let mut dandelion = self.dandelion.lock().await; + dandelion.register_transaction(tx_id.to_vec()); + } + + // Add to mixing pool + { + let mut obfuscator = self.obfuscator.lock().await; + obfuscator.add_to_pool(tx); + } + + PropagationResult::Queued + } + + /// Process pending transactions for propagation + pub async fn process_pending(&self) -> Vec<(Transaction, RoutingDecision)> { + let mut results = Vec::new(); + + // Check if should release batch from mixing pool + let should_release = { + let obfuscator = self.obfuscator.lock().await; + obfuscator.should_release_batch() + }; + + if !should_release { + return results; + } + + // Release batch + let batch = { + let mut obfuscator = self.obfuscator.lock().await; + obfuscator.release_batch() + }; + + // Route each transaction + for tx in batch { + let tx_id = tx.id(); + let routing = self.route_transaction(&tx_id).await; + results.push((tx, routing)); + } + + results + } + + /// Route a single transaction + async fn route_transaction(&self, tx_id: &[u8]) -> RoutingDecision { + let mut dandelion = self.dandelion.lock().await; + let phase = dandelion.process_hop(&tx_id.to_vec()); + + match phase { + DandelionPhase::Stem => { + // Select random peer for STEM + let peer = self.select_random_peer().await; + let delay = DelayStrategy::dandelion_stem(); + RoutingDecision::stem(peer, delay) + } + + DandelionPhase::Fluff => { + // Broadcast to random subset of peers + let peers = self.peers.lock().await; + let subset_size = (peers.len() as f64).sqrt() as usize; + RoutingDecision::fluff_random(subset_size.max(1)) + } + } + } + + /// Select random peer for STEM routing + async fn select_random_peer(&self) -> PeerId { + use rand::seq::SliceRandom; + + let peers = self.peers.lock().await; + peers + .choose(&mut rand::thread_rng()) + .cloned() + .unwrap_or_else(|| "default".to_string()) + } + + /// Add peer to connection pool + pub async fn add_peer(&self, peer: PeerId) { + let mut peers = self.peers.lock().await; + if !peers.contains(&peer) { + peers.push(peer); + } + } + + /// Remove peer from connection pool + pub async fn remove_peer(&self, peer: &PeerId) { + let mut peers = self.peers.lock().await; + peers.retain(|p| p != peer); + } + + /// Get number of connected peers + pub async fn peer_count(&self) -> usize { + self.peers.lock().await.len() + } +} + +impl Default for PropagationManager { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PropagationResult { + /// Transaction queued for propagation + Queued, + + /// Transaction propagated immediately + Propagated, + + /// Transaction rejected + Rejected, +} + +/// Strategy for propagating transactions +#[async_trait] +pub trait PropagationStrategy: Send + Sync { + /// Decide how to propagate a transaction + async fn route(&self, tx: &Transaction) -> RoutingDecision; + + /// Apply timing delays + async fn apply_delay(&self, delay: &DelayStrategy); +} + +/// Standard propagation strategy (Dandelion++ with timing obfuscation) +pub struct StandardPropagation { + manager: Arc<PropagationManager>, +} + +impl StandardPropagation { + pub fn new(manager: Arc<PropagationManager>) -> Self { + Self { manager } + } +} + +#[async_trait] +impl PropagationStrategy for StandardPropagation { + async fn route(&self, tx: &Transaction) -> RoutingDecision { + let tx_id = tx.id(); + self.manager.route_transaction(&tx_id).await + } + + async fn apply_delay(&self, delay: &DelayStrategy) { + delay.sleep().await; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use onioncoin_timing::TimingMetadata; + use chrono::Utc; + + fn create_test_tx() -> Transaction { + use onioncoin_core::transaction::*; + + let seed = [42u8; 32]; + let timing = TimingMetadata::new(Utc::now(), &seed).unwrap(); + + Transaction { + version: 1, + inputs: vec![TransactionInput { + previous_output: OutPoint { + txid: [1u8; 32], + vout: 0, + }, + key_image: [2u8; 32], + }], + outputs: vec![TransactionOutput { + encrypted_amount: vec![0u8; 32], + stealth_address: [3u8; 32], + commitment: [4u8; 32], + }], + ring_signatures: vec![RingSignature::new(11)], + range_proof: vec![0u8; 64], + encrypted_fee: 1000, + timing, + } + } + + #[tokio::test] + async fn test_propagation_manager() { + let manager = PropagationManager::new(); + let tx = create_test_tx(); + + let result = manager.receive_transaction(tx).await; + assert_eq!(result, PropagationResult::Queued); + } + + #[tokio::test] + async fn test_peer_management() { + let manager = PropagationManager::new(); + + manager.add_peer("peer1".to_string()).await; + manager.add_peer("peer2".to_string()).await; + + assert_eq!(manager.peer_count().await, 2); + + manager.remove_peer(&"peer1".to_string()).await; + assert_eq!(manager.peer_count().await, 1); + } +} |
