1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
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
}
}
|