summaryrefslogtreecommitdiffstats
path: root/network/src/propagation.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 /network/src/propagation.rs
downloadonioncoin-0.1.0.tar.gz
onioncoin-0.1.0.tar.xz
onioncoin-0.1.0.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 'network/src/propagation.rs')
-rw-r--r--network/src/propagation.rs238
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);
+ }
+}