summaryrefslogtreecommitdiffstats
path: root/consensus/src/relay_proof.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 /consensus/src/relay_proof.rs
downloadonioncoin-9f5d864d533ce86459e654f5d78212933c0269ea.tar.gz
onioncoin-9f5d864d533ce86459e654f5d78212933c0269ea.tar.xz
onioncoin-9f5d864d533ce86459e654f5d78212933c0269ea.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 'consensus/src/relay_proof.rs')
-rw-r--r--consensus/src/relay_proof.rs392
1 files changed, 392 insertions, 0 deletions
diff --git a/consensus/src/relay_proof.rs b/consensus/src/relay_proof.rs
new file mode 100644
index 0000000..0d55af3
--- /dev/null
+++ b/consensus/src/relay_proof.rs
@@ -0,0 +1,392 @@
+use serde::{Deserialize, Serialize};
+use blake3::Hasher;
+use std::collections::VecDeque;
+
+/// Proof that a validator relayed traffic through Tor for OnionCoin network
+/// This is OnionCoin's UNIQUE feature: earn rewards by being a Tor relay!
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct RelayProof {
+ /// Validator's public key
+ pub validator_pubkey: [u8; 32],
+
+ /// Time period this proof covers (Unix timestamp range)
+ pub period_start: i64,
+ pub period_end: i64,
+
+ /// Metrics for this period
+ pub metrics: RelayMetrics,
+
+ /// Cryptographic proof of relay work
+ /// In production: ZK-SNARK proof of bandwidth relay
+ /// For prototype: hash chain of relayed packet hashes
+ pub proof_data: Vec<u8>,
+
+ /// Signature from validator
+ #[serde(with = "serde_bytes")]
+ pub signature: [u8; 64],
+}
+
+impl RelayProof {
+ /// Standard proof period (1 hour)
+ pub const PROOF_PERIOD_SECONDS: i64 = 3600;
+
+ /// Create a new relay proof
+ pub fn new(
+ validator_pubkey: [u8; 32],
+ period_start: i64,
+ period_end: i64,
+ metrics: RelayMetrics,
+ ) -> Self {
+ // Generate proof data (simplified)
+ let proof_data = Self::generate_proof_data(&metrics);
+
+ Self {
+ validator_pubkey,
+ period_start,
+ period_end,
+ metrics,
+ proof_data,
+ signature: [0u8; 64], // Placeholder
+ }
+ }
+
+ /// Verify relay proof
+ pub fn verify(&self) -> bool {
+ // Check time period is valid
+ if self.period_end <= self.period_start {
+ return false;
+ }
+
+ // Check period is not too long
+ if self.period_end - self.period_start > Self::PROOF_PERIOD_SECONDS * 24 {
+ return false;
+ }
+
+ // Verify metrics are reasonable
+ if !self.metrics.is_valid() {
+ return false;
+ }
+
+ // Verify proof data
+ let expected_proof = Self::generate_proof_data(&self.metrics);
+ if self.proof_data != expected_proof {
+ return false;
+ }
+
+ true
+ }
+
+ /// Generate proof data from metrics
+ fn generate_proof_data(metrics: &RelayMetrics) -> Vec<u8> {
+ let mut hasher = Hasher::new();
+ hasher.update(&metrics.bytes_relayed.to_le_bytes());
+ hasher.update(&metrics.packets_relayed.to_le_bytes());
+ hasher.update(&metrics.unique_circuits.to_le_bytes());
+ hasher.finalize().as_bytes().to_vec()
+ }
+
+ /// Calculate reward for this relay proof
+ pub fn calculate_reward(&self) -> u64 {
+ // Base reward per GB relayed
+ const REWARD_PER_GB: u64 = 1; // 1 ONC per GB
+
+ let gb_relayed = self.metrics.bytes_relayed / (1024 * 1024 * 1024);
+
+ // Bonus for high circuit diversity
+ let circuit_bonus = if self.metrics.unique_circuits > 100 {
+ 1.2
+ } else if self.metrics.unique_circuits > 50 {
+ 1.1
+ } else {
+ 1.0
+ };
+
+ ((gb_relayed * REWARD_PER_GB) as f64 * circuit_bonus) as u64
+ }
+}
+
+/// Metrics about relay activity
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct RelayMetrics {
+ /// Total bytes relayed in this period
+ pub bytes_relayed: u64,
+
+ /// Total packets relayed
+ pub packets_relayed: u64,
+
+ /// Number of unique circuits served
+ pub unique_circuits: u32,
+
+ /// Average latency (milliseconds)
+ pub avg_latency_ms: u32,
+
+ /// Number of failed relay attempts
+ pub failed_relays: u32,
+
+ /// Uptime during this period (seconds)
+ pub uptime_seconds: u64,
+}
+
+impl RelayMetrics {
+ pub fn new() -> Self {
+ Self {
+ bytes_relayed: 0,
+ packets_relayed: 0,
+ unique_circuits: 0,
+ avg_latency_ms: 0,
+ failed_relays: 0,
+ uptime_seconds: 0,
+ }
+ }
+
+ /// Check if metrics are valid (sanity checks)
+ pub fn is_valid(&self) -> bool {
+ // Reasonable bounds
+ if self.bytes_relayed > 1_000_000_000_000 {
+ // > 1 TB
+ return false;
+ }
+
+ if self.packets_relayed > 1_000_000_000 {
+ // > 1B packets
+ return false;
+ }
+
+ if self.avg_latency_ms > 60_000 {
+ // > 1 minute
+ return false;
+ }
+
+ // Packets should roughly match bytes
+ if self.packets_relayed > 0 {
+ let avg_packet_size = self.bytes_relayed / self.packets_relayed;
+ if avg_packet_size < 20 || avg_packet_size > 1_000_000 {
+ // Unrealistic packet size
+ return false;
+ }
+ }
+
+ true
+ }
+
+ /// Calculate quality score (0.0 - 1.0)
+ pub fn quality_score(&self) -> f64 {
+ let mut score = 1.0;
+
+ // Penalty for high latency
+ if self.avg_latency_ms > 5000 {
+ score *= 0.5;
+ } else if self.avg_latency_ms > 2000 {
+ score *= 0.8;
+ }
+
+ // Penalty for failed relays
+ if self.packets_relayed > 0 {
+ let failure_rate = self.failed_relays as f64 / self.packets_relayed as f64;
+ score *= (1.0 - failure_rate).max(0.0);
+ }
+
+ score.clamp(0.0, 1.0)
+ }
+}
+
+impl Default for RelayMetrics {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Tracks relay activity for a validator over time
+#[derive(Debug)]
+pub struct RelayTracker {
+ /// Validator public key
+ validator_pubkey: [u8; 32],
+
+ /// Current period metrics
+ current_metrics: RelayMetrics,
+
+ /// Period start time
+ period_start: i64,
+
+ /// Historical proofs (last 24 hours)
+ history: VecDeque<RelayProof>,
+}
+
+impl RelayTracker {
+ /// Maximum history to keep (24 hours = 24 proofs)
+ const MAX_HISTORY: usize = 24;
+
+ pub fn new(validator_pubkey: [u8; 32], current_time: i64) -> Self {
+ Self {
+ validator_pubkey,
+ current_metrics: RelayMetrics::new(),
+ period_start: current_time,
+ history: VecDeque::new(),
+ }
+ }
+
+ /// Record relayed bytes
+ pub fn record_relay(&mut self, bytes: u64, latency_ms: u32, success: bool) {
+ self.current_metrics.bytes_relayed += bytes;
+ self.current_metrics.packets_relayed += 1;
+
+ // Update average latency (simple moving average)
+ let total = self.current_metrics.packets_relayed;
+ self.current_metrics.avg_latency_ms =
+ ((self.current_metrics.avg_latency_ms as u64 * (total - 1) + latency_ms as u64)
+ / total) as u32;
+
+ if !success {
+ self.current_metrics.failed_relays += 1;
+ }
+ }
+
+ /// Record a unique circuit
+ pub fn record_circuit(&mut self) {
+ self.current_metrics.unique_circuits += 1;
+ }
+
+ /// Record uptime
+ pub fn record_uptime(&mut self, seconds: u64) {
+ self.current_metrics.uptime_seconds += seconds;
+ }
+
+ /// Finalize current period and generate proof
+ pub fn finalize_period(&mut self, current_time: i64) -> RelayProof {
+ let proof = RelayProof::new(
+ self.validator_pubkey,
+ self.period_start,
+ current_time,
+ self.current_metrics.clone(),
+ );
+
+ // Add to history
+ self.history.push_back(proof.clone());
+
+ // Trim history
+ if self.history.len() > Self::MAX_HISTORY {
+ self.history.pop_front();
+ }
+
+ // Reset for next period
+ self.current_metrics = RelayMetrics::new();
+ self.period_start = current_time;
+
+ proof
+ }
+
+ /// Get total bytes relayed in last 24 hours
+ pub fn get_24h_bytes(&self) -> u64 {
+ self.history
+ .iter()
+ .map(|p| p.metrics.bytes_relayed)
+ .sum::<u64>()
+ + self.current_metrics.bytes_relayed
+ }
+
+ /// Get average quality score over last 24 hours
+ pub fn get_avg_quality(&self) -> f64 {
+ if self.history.is_empty() {
+ return self.current_metrics.quality_score();
+ }
+
+ let total: f64 = self
+ .history
+ .iter()
+ .map(|p| p.metrics.quality_score())
+ .sum();
+
+ total / self.history.len() as f64
+ }
+
+ /// Get current metrics
+ pub fn current_metrics(&self) -> &RelayMetrics {
+ &self.current_metrics
+ }
+
+ /// Get proof history
+ pub fn history(&self) -> &VecDeque<RelayProof> {
+ &self.history
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_relay_metrics_validation() {
+ let mut metrics = RelayMetrics::new();
+ metrics.bytes_relayed = 1_000_000; // 1 MB
+ metrics.packets_relayed = 1000;
+ metrics.avg_latency_ms = 100;
+
+ assert!(metrics.is_valid());
+
+ // Invalid: too many bytes
+ metrics.bytes_relayed = 2_000_000_000_000;
+ assert!(!metrics.is_valid());
+ }
+
+ #[test]
+ fn test_relay_proof_creation() {
+ let pubkey = [1u8; 32];
+ let mut metrics = RelayMetrics::new();
+ metrics.bytes_relayed = 1_000_000_000; // 1 GB
+ metrics.packets_relayed = 10_000;
+ metrics.unique_circuits = 50;
+
+ let proof = RelayProof::new(pubkey, 0, 3600, metrics);
+
+ assert!(proof.verify());
+ assert_eq!(proof.calculate_reward(), 1); // 1 ONC for 1 GB
+ }
+
+ #[test]
+ fn test_relay_tracker() {
+ let pubkey = [1u8; 32];
+ let mut tracker = RelayTracker::new(pubkey, 0);
+
+ // Record some relay activity
+ tracker.record_relay(1000, 100, true);
+ tracker.record_relay(2000, 150, true);
+ tracker.record_circuit();
+
+ assert_eq!(tracker.current_metrics().bytes_relayed, 3000);
+ assert_eq!(tracker.current_metrics().packets_relayed, 2);
+ assert_eq!(tracker.current_metrics().unique_circuits, 1);
+
+ // Finalize period
+ let proof = tracker.finalize_period(3600);
+ assert!(proof.verify());
+ assert_eq!(tracker.history().len(), 1);
+ }
+
+ #[test]
+ fn test_quality_score() {
+ let mut metrics = RelayMetrics::new();
+ metrics.packets_relayed = 100;
+ metrics.failed_relays = 10; // 10% failure rate
+ metrics.avg_latency_ms = 1000;
+
+ let score = metrics.quality_score();
+ assert!(score < 1.0);
+ assert!(score > 0.0);
+ }
+
+ #[test]
+ fn test_reward_calculation() {
+ let pubkey = [1u8; 32];
+
+ // 5 GB relayed with good circuit diversity
+ let mut metrics = RelayMetrics::new();
+ metrics.bytes_relayed = 5 * 1024 * 1024 * 1024;
+ metrics.unique_circuits = 150; // High diversity = bonus
+
+ let proof = RelayProof::new(pubkey, 0, 3600, metrics);
+ let reward = proof.calculate_reward();
+
+ assert!(reward >= 5); // At least 5 ONC base
+ assert!(reward > 5); // Should have bonus
+ }
+}