summaryrefslogtreecommitdiffstats
path: root/consensus/src/proof_of_contribution.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/proof_of_contribution.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 'consensus/src/proof_of_contribution.rs')
-rw-r--r--consensus/src/proof_of_contribution.rs340
1 files changed, 340 insertions, 0 deletions
diff --git a/consensus/src/proof_of_contribution.rs b/consensus/src/proof_of_contribution.rs
new file mode 100644
index 0000000..f13fe56
--- /dev/null
+++ b/consensus/src/proof_of_contribution.rs
@@ -0,0 +1,340 @@
+use crate::{Validator, RelayProof, BandwidthMetrics, UptimeMetrics, StorageMetrics};
+use serde::{Deserialize, Serialize};
+
+/// Proof-of-Contribution: OnionCoin's unique consensus mechanism
+/// Combines stake + relay work + bandwidth + uptime + storage
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct ProofOfContribution {
+ /// Validator public key
+ pub validator_pubkey: [u8; 32],
+
+ /// Contribution score breakdown
+ pub score: ContributionScore,
+
+ /// Timestamp
+ pub timestamp: i64,
+}
+
+impl ProofOfContribution {
+ /// Calculate contribution score for a validator
+ pub fn calculate(
+ validator: &Validator,
+ relay_proof: Option<&RelayProof>,
+ bandwidth: &BandwidthMetrics,
+ uptime: &UptimeMetrics,
+ storage: &StorageMetrics,
+ ) -> Self {
+ let score = ContributionScore::calculate(
+ validator,
+ relay_proof,
+ bandwidth,
+ uptime,
+ storage,
+ );
+
+ Self {
+ validator_pubkey: validator.pubkey,
+ score,
+ timestamp: chrono::Utc::now().timestamp(),
+ }
+ }
+
+ /// Get total weighted score
+ pub fn total_score(&self) -> f64 {
+ self.score.weighted_total()
+ }
+}
+
+/// Breakdown of contribution score components
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct ContributionScore {
+ /// Stake component score (0.0 - 1.0)
+ pub stake_score: f64,
+
+ /// Relay work score (0.0 - 1.0)
+ pub relay_score: f64,
+
+ /// Bandwidth score (0.0 - 1.0)
+ pub bandwidth_score: f64,
+
+ /// Uptime score (0.0 - 1.0)
+ pub uptime_score: f64,
+
+ /// Storage score (0.0 - 1.0)
+ pub storage_score: f64,
+
+ /// Weights for each component
+ pub weights: ScoreWeights,
+}
+
+/// Weights for different contribution components
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct ScoreWeights {
+ pub stake: f64,
+ pub relay: f64,
+ pub bandwidth: f64,
+ pub uptime: f64,
+ pub storage: f64,
+}
+
+impl Default for ScoreWeights {
+ fn default() -> Self {
+ Self {
+ stake: 0.40, // 40% - Still important but not dominant
+ relay: 0.30, // 30% - UNIQUE: Tor relay work
+ bandwidth: 0.15, // 15% - Network contribution
+ uptime: 0.10, // 10% - Reliability
+ storage: 0.05, // 5% - Data availability
+ }
+ }
+}
+
+impl ScoreWeights {
+ /// Verify weights sum to 1.0
+ pub fn is_valid(&self) -> bool {
+ let sum = self.stake + self.relay + self.bandwidth + self.uptime + self.storage;
+ (sum - 1.0).abs() < 0.001
+ }
+}
+
+impl ContributionScore {
+ /// Calculate contribution score from validator metrics
+ pub fn calculate(
+ validator: &Validator,
+ relay_proof: Option<&RelayProof>,
+ bandwidth: &BandwidthMetrics,
+ uptime: &UptimeMetrics,
+ storage: &StorageMetrics,
+ ) -> Self {
+ let weights = ScoreWeights::default();
+
+ // 1. Stake score (logarithmic to reduce whale advantage)
+ let stake_score = Self::calculate_stake_score(validator);
+
+ // 2. Relay score (from Proof-of-Relay)
+ let relay_score = Self::calculate_relay_score(relay_proof);
+
+ // 3. Bandwidth score
+ let bandwidth_score = bandwidth.score();
+
+ // 4. Uptime score
+ let uptime_score = uptime.score();
+
+ // 5. Storage score
+ let storage_score = storage.score();
+
+ Self {
+ stake_score,
+ relay_score,
+ bandwidth_score,
+ uptime_score,
+ storage_score,
+ weights,
+ }
+ }
+
+ /// Calculate stake score (logarithmic)
+ /// This prevents whales from dominating
+ fn calculate_stake_score(validator: &Validator) -> f64 {
+ if validator.stake == 0 {
+ return 0.0;
+ }
+
+ // Use log scale to reduce advantage of large stakes
+ // 10 ONC = 0.1, 100 ONC = 0.2, 1000 ONC = 0.3, etc.
+ let log_stake = (validator.stake as f64).log10();
+ let max_log_stake = 5.0; // 100,000 ONC = max score
+
+ // Apply tier multiplier
+ let tier_multiplier = validator.tier.stake_multiplier();
+
+ ((log_stake / max_log_stake) * tier_multiplier).clamp(0.0, 1.0)
+ }
+
+ /// Calculate relay score from relay proof
+ fn calculate_relay_score(relay_proof: Option<&RelayProof>) -> f64 {
+ match relay_proof {
+ Some(proof) => {
+ // Base score from bytes relayed
+ let gb_relayed = proof.metrics.bytes_relayed as f64 / (1024.0 * 1024.0 * 1024.0);
+ let base_score = (gb_relayed / 100.0).clamp(0.0, 0.8); // Max 0.8 from volume
+
+ // Quality multiplier
+ let quality = proof.metrics.quality_score();
+
+ // Circuit diversity bonus
+ let diversity_bonus = if proof.metrics.unique_circuits > 100 {
+ 0.2
+ } else if proof.metrics.unique_circuits > 50 {
+ 0.1
+ } else {
+ 0.0
+ };
+
+ (base_score * quality + diversity_bonus).clamp(0.0, 1.0)
+ }
+ None => 0.0,
+ }
+ }
+
+ /// Calculate weighted total score
+ pub fn weighted_total(&self) -> f64 {
+ self.stake_score * self.weights.stake
+ + self.relay_score * self.weights.relay
+ + self.bandwidth_score * self.weights.bandwidth
+ + self.uptime_score * self.weights.uptime
+ + self.storage_score * self.weights.storage
+ }
+
+ /// Get human-readable breakdown
+ pub fn breakdown(&self) -> String {
+ format!(
+ "Stake: {:.2}×{:.0}% = {:.3}\n\
+ Relay: {:.2}×{:.0}% = {:.3}\n\
+ Bandwidth: {:.2}×{:.0}% = {:.3}\n\
+ Uptime: {:.2}×{:.0}% = {:.3}\n\
+ Storage: {:.2}×{:.0}% = {:.3}\n\
+ ───────────────────────\n\
+ TOTAL: {:.3}",
+ self.stake_score,
+ self.weights.stake * 100.0,
+ self.stake_score * self.weights.stake,
+ self.relay_score,
+ self.weights.relay * 100.0,
+ self.relay_score * self.weights.relay,
+ self.bandwidth_score,
+ self.weights.bandwidth * 100.0,
+ self.bandwidth_score * self.weights.bandwidth,
+ self.uptime_score,
+ self.weights.uptime * 100.0,
+ self.uptime_score * self.weights.uptime,
+ self.storage_score,
+ self.weights.storage * 100.0,
+ self.storage_score * self.weights.storage,
+ self.weighted_total()
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::RelayMetrics;
+
+ #[test]
+ fn test_score_weights_valid() {
+ let weights = ScoreWeights::default();
+ assert!(weights.is_valid());
+ }
+
+ #[test]
+ fn test_stake_score_logarithmic() {
+ let validator_small = create_test_validator(100); // 100 ONC
+ let validator_large = create_test_validator(10000); // 10,000 ONC
+
+ let score_small = ContributionScore::calculate_stake_score(&validator_small);
+ let score_large = ContributionScore::calculate_stake_score(&validator_large);
+
+ // Large stake should have higher score but not 100x higher
+ assert!(score_large > score_small);
+ assert!(score_large < score_small * 2.0); // Logarithmic dampening
+ }
+
+ #[test]
+ fn test_relay_score() {
+ let mut metrics = RelayMetrics::new();
+ metrics.bytes_relayed = 50 * 1024 * 1024 * 1024; // 50 GB
+ metrics.unique_circuits = 150;
+ metrics.packets_relayed = 1_000_000;
+
+ let proof = RelayProof::new([1u8; 32], 0, 3600, metrics);
+ let score = ContributionScore::calculate_relay_score(Some(&proof));
+
+ assert!(score > 0.0);
+ assert!(score <= 1.0);
+ }
+
+ #[test]
+ fn test_contribution_score_calculation() {
+ let validator = create_test_validator(1000);
+
+ let mut relay_metrics = RelayMetrics::new();
+ relay_metrics.bytes_relayed = 10 * 1024 * 1024 * 1024; // 10 GB
+ relay_metrics.unique_circuits = 50;
+ relay_metrics.packets_relayed = 100_000;
+
+ let relay_proof = RelayProof::new([1u8; 32], 0, 3600, relay_metrics);
+
+ let mut bandwidth = BandwidthMetrics::new();
+ bandwidth.update(1_000_000, 1_000_000, 100);
+
+ let uptime = UptimeMetrics::new(0);
+
+ let mut storage = StorageMetrics::new();
+ storage.update(20 * 1024 * 1024 * 1024, 10 * 1024 * 1024 * 1024, 1000, 10000);
+
+ let score = ContributionScore::calculate(
+ &validator,
+ Some(&relay_proof),
+ &bandwidth,
+ &uptime,
+ &storage,
+ );
+
+ let total = score.weighted_total();
+ assert!(total > 0.0);
+ assert!(total <= 1.0);
+
+ println!("{}", score.breakdown());
+ }
+
+ #[test]
+ fn test_micro_validator_can_compete() {
+ // Micro validator with great relay work
+ let micro = create_test_validator(10); // Only 10 ONC
+ let mut relay_metrics = RelayMetrics::new();
+ relay_metrics.bytes_relayed = 100 * 1024 * 1024 * 1024; // 100 GB relay!
+ relay_metrics.unique_circuits = 200;
+ relay_metrics.packets_relayed = 1_000_000;
+
+ let relay_proof = RelayProof::new([1u8; 32], 0, 3600, relay_metrics);
+
+ let mut bandwidth = BandwidthMetrics::new();
+ bandwidth.update(10_000_000, 10_000_000, 100); // Good bandwidth
+
+ let mut uptime = UptimeMetrics::new(0);
+ for i in 1..=1000 {
+ uptime.record_heartbeat(i * 60); // Great uptime
+ }
+
+ let storage = StorageMetrics::new();
+
+ let micro_score = ContributionScore::calculate(
+ &micro,
+ Some(&relay_proof),
+ &bandwidth,
+ &uptime,
+ &storage,
+ );
+
+ // Power validator with just stake
+ let power = create_test_validator(10000); // 10,000 ONC
+ let power_score = ContributionScore::calculate(
+ &power,
+ None, // No relay work
+ &BandwidthMetrics::new(),
+ &UptimeMetrics::new(0),
+ &StorageMetrics::new(),
+ );
+
+ println!("Micro validator (10 ONC + relay work):\n{}", micro_score.breakdown());
+ println!("\nPower validator (10,000 ONC, no work):\n{}", power_score.breakdown());
+
+ // Micro validator should be competitive!
+ assert!(micro_score.weighted_total() > power_score.weighted_total() * 0.5);
+ }
+
+ fn create_test_validator(stake: u64) -> Validator {
+ Validator::new([1u8; 32], stake, "test.onion".to_string(), 0).unwrap()
+ }
+}