Skip to main content

chaincraft_rust/examples/
randomness_beacon.rs

1use crate::{
2    crypto::{
3        ecdsa::{ECDSASigner, ECDSAVerifier},
4        KeyType, PrivateKey, PublicKey, Signature,
5    },
6    error::{ChaincraftError, Result},
7    shared::{MessageType, SharedMessage, SharedObjectId},
8    shared_object::ApplicationObject,
9};
10use async_trait::async_trait;
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use sha2::{Digest, Sha256};
14use std::collections::{HashMap, HashSet};
15
16/// Randomness beacon message types
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub enum BeaconMessageType {
19    /// VRF proof submission
20    VrfProof {
21        round: u64,
22        input: String,
23        proof: String,
24        output: String,
25        validator: String,
26        signature: String,
27        timestamp: DateTime<Utc>,
28    },
29    /// Partial signature for threshold signature
30    PartialSignature {
31        round: u64,
32        validator: String,
33        partial_sig: String,
34        signature: String,
35        timestamp: DateTime<Utc>,
36    },
37    /// Finalized randomness for a round
38    FinalizedBeacon {
39        round: u64,
40        randomness: String,
41        vrf_proofs: Vec<String>,
42        threshold_sig: String,
43        participants: Vec<String>,
44        timestamp: DateTime<Utc>,
45    },
46    /// Validator registration for beacon participation
47    ValidatorRegistration {
48        validator: String,
49        public_key: String,
50        vrf_key: String,
51        stake: u64,
52        signature: String,
53    },
54    /// Challenge for bias resistance
55    BiasChallenge {
56        round: u64,
57        challenger: String,
58        target_validator: String,
59        challenge_data: String,
60        signature: String,
61    },
62}
63
64/// VRF (Verifiable Random Function) proof
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct VrfProof {
67    pub validator: String,
68    pub input: String,
69    pub proof: String,
70    pub output: String,
71    pub signature: String,
72    pub timestamp: DateTime<Utc>,
73}
74
75/// Beacon validator information
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct BeaconValidator {
78    pub address: String,
79    pub public_key: String,
80    pub vrf_key: String,
81    pub stake: u64,
82    pub active: bool,
83    pub last_participation: Option<u64>,
84}
85
86/// Finalized beacon round
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct BeaconRound {
89    pub round: u64,
90    pub randomness: String,
91    pub participants: Vec<String>,
92    pub vrf_proofs: Vec<VrfProof>,
93    pub threshold_signature: String,
94    pub finalized_at: DateTime<Utc>,
95    pub bias_challenges: Vec<String>,
96}
97
98/// Randomness beacon implementation
99#[derive(Debug)]
100pub struct RandomnessBeaconObject {
101    pub id: SharedObjectId,
102    pub validators: HashMap<String, BeaconValidator>,
103    pub rounds: HashMap<u64, BeaconRound>,
104    pub current_round: u64,
105    pub round_duration_secs: u64,
106    pub last_round_time: DateTime<Utc>,
107    pub pending_vrf_proofs: HashMap<u64, Vec<VrfProof>>,
108    pub pending_partial_sigs: HashMap<u64, HashMap<String, String>>,
109    pub threshold: u64, // Minimum number of participants needed
110    pub my_validator_address: String,
111    pub signer: ECDSASigner,
112    pub verifier: ECDSAVerifier,
113    pub messages: Vec<BeaconMessageType>,
114    pub bias_resistance_enabled: bool,
115    pub challenges: HashMap<u64, Vec<BeaconMessageType>>,
116}
117
118impl RandomnessBeaconObject {
119    pub fn new(round_duration_secs: u64, threshold: u64) -> Result<Self> {
120        let signer = ECDSASigner::new()?;
121        let my_validator_address = signer.get_public_key_pem()?;
122
123        Ok(Self {
124            id: SharedObjectId::new(),
125            validators: HashMap::new(),
126            rounds: HashMap::new(),
127            current_round: 1,
128            round_duration_secs,
129            last_round_time: Utc::now(),
130            pending_vrf_proofs: HashMap::new(),
131            pending_partial_sigs: HashMap::new(),
132            threshold,
133            my_validator_address,
134            signer,
135            verifier: ECDSAVerifier::new(),
136            messages: Vec::new(),
137            bias_resistance_enabled: true,
138            challenges: HashMap::new(),
139        })
140    }
141
142    /// Register a validator for beacon participation
143    pub fn register_validator(&mut self, validator: BeaconValidator) -> Result<()> {
144        // Verify validator signature (simplified)
145        self.validators.insert(validator.address.clone(), validator);
146        Ok(())
147    }
148
149    /// Check if enough time has passed to advance to next round
150    pub fn should_advance_round(&self) -> bool {
151        let elapsed = Utc::now().signed_duration_since(self.last_round_time);
152        elapsed.num_seconds() >= self.round_duration_secs as i64
153    }
154
155    /// Generate VRF proof for current round
156    pub fn generate_vrf_proof(&self, input: &str) -> Result<VrfProof> {
157        // Simplified VRF implementation
158        let vrf_input = format!("round_{}_{}", self.current_round, input);
159        let mut hasher = Sha256::new();
160        hasher.update(vrf_input.as_bytes());
161        hasher.update(self.my_validator_address.as_bytes());
162        let hash = hasher.finalize();
163
164        let proof = hex::encode(&hash[0..16]); // First 16 bytes as proof
165        let output = hex::encode(&hash[16..32]); // Last 16 bytes as output
166
167        let signature_data = format!("vrf:{}:{}:{}:{}", self.current_round, input, proof, output);
168        let signature = self.signer.sign(signature_data.as_bytes())?;
169
170        Ok(VrfProof {
171            validator: self.my_validator_address.clone(),
172            input: input.to_string(),
173            proof,
174            output,
175            signature: hex::encode(signature.to_bytes()),
176            timestamp: Utc::now(),
177        })
178    }
179
180    /// Process VRF proof submission
181    pub fn process_vrf_proof(&mut self, msg: BeaconMessageType) -> Result<bool> {
182        if let BeaconMessageType::VrfProof {
183            round,
184            input,
185            proof,
186            output,
187            validator,
188            signature,
189            timestamp,
190        } = msg
191        {
192            if round != self.current_round {
193                return Ok(false);
194            }
195
196            // Verify validator is registered
197            if !self.validators.contains_key(&validator) {
198                return Ok(false);
199            }
200
201            let vrf_proof = VrfProof {
202                validator: validator.clone(),
203                input,
204                proof,
205                output,
206                signature,
207                timestamp,
208            };
209
210            self.pending_vrf_proofs
211                .entry(round)
212                .or_default()
213                .push(vrf_proof.clone());
214
215            self.messages.push(BeaconMessageType::VrfProof {
216                round,
217                input: vrf_proof.input,
218                proof: vrf_proof.proof,
219                output: vrf_proof.output,
220                validator,
221                signature: vrf_proof.signature,
222                timestamp,
223            });
224
225            return Ok(true);
226        }
227        Ok(false)
228    }
229
230    /// Process partial signature
231    pub fn process_partial_signature(&mut self, msg: BeaconMessageType) -> Result<bool> {
232        if let BeaconMessageType::PartialSignature {
233            round,
234            validator,
235            partial_sig,
236            signature,
237            timestamp,
238        } = msg
239        {
240            if round != self.current_round {
241                return Ok(false);
242            }
243
244            if !self.validators.contains_key(&validator) {
245                return Ok(false);
246            }
247
248            self.pending_partial_sigs
249                .entry(round)
250                .or_default()
251                .insert(validator.clone(), partial_sig.clone());
252
253            self.messages.push(BeaconMessageType::PartialSignature {
254                round,
255                validator,
256                partial_sig,
257                signature,
258                timestamp,
259            });
260
261            return Ok(true);
262        }
263        Ok(false)
264    }
265
266    /// Check if we can finalize the current round
267    pub fn can_finalize_round(&self) -> bool {
268        let vrf_count = self
269            .pending_vrf_proofs
270            .get(&self.current_round)
271            .map(|proofs| proofs.len())
272            .unwrap_or(0);
273
274        let partial_sig_count = self
275            .pending_partial_sigs
276            .get(&self.current_round)
277            .map(|sigs| sigs.len())
278            .unwrap_or(0);
279
280        vrf_count >= self.threshold as usize && partial_sig_count >= self.threshold as usize
281    }
282
283    /// Finalize the current round and generate randomness
284    pub fn finalize_round(&mut self) -> Result<String> {
285        if !self.can_finalize_round() {
286            return Err(ChaincraftError::validation(
287                "Insufficient proofs/signatures for finalization",
288            ));
289        }
290
291        let vrf_proofs = self
292            .pending_vrf_proofs
293            .get(&self.current_round)
294            .cloned()
295            .unwrap_or_default();
296
297        let partial_sigs = self
298            .pending_partial_sigs
299            .get(&self.current_round)
300            .cloned()
301            .unwrap_or_default();
302
303        // Combine VRF outputs to create final randomness
304        let mut combined_randomness = String::new();
305        for proof in &vrf_proofs {
306            combined_randomness.push_str(&proof.output);
307        }
308
309        let mut hasher = Sha256::new();
310        hasher.update(combined_randomness.as_bytes());
311        hasher.update(self.current_round.to_string().as_bytes());
312        let final_randomness = hex::encode(hasher.finalize());
313
314        // Create threshold signature (simplified)
315        let mut threshold_sig = String::new();
316        for (validator, sig) in &partial_sigs {
317            threshold_sig.push_str(&format!("{}:{};", validator, sig));
318        }
319
320        let participants: Vec<String> = vrf_proofs.iter().map(|p| p.validator.clone()).collect();
321
322        let beacon_round = BeaconRound {
323            round: self.current_round,
324            randomness: final_randomness.clone(),
325            participants: participants.clone(),
326            vrf_proofs,
327            threshold_signature: threshold_sig,
328            finalized_at: Utc::now(),
329            bias_challenges: self
330                .challenges
331                .get(&self.current_round)
332                .map(|challenges| challenges.iter().map(|c| format!("{:?}", c)).collect())
333                .unwrap_or_default(),
334        };
335
336        self.rounds.insert(self.current_round, beacon_round);
337
338        // Clean up and advance to next round
339        self.pending_vrf_proofs.remove(&self.current_round);
340        self.pending_partial_sigs.remove(&self.current_round);
341        self.challenges.remove(&self.current_round);
342
343        self.current_round += 1;
344        self.last_round_time = Utc::now();
345
346        Ok(final_randomness)
347    }
348
349    /// Process bias challenge
350    pub fn process_bias_challenge(&mut self, msg: BeaconMessageType) -> Result<bool> {
351        if !self.bias_resistance_enabled {
352            return Ok(false);
353        }
354
355        if let BeaconMessageType::BiasChallenge { round, .. } = &msg {
356            self.challenges.entry(*round).or_default().push(msg.clone());
357
358            self.messages.push(msg);
359            return Ok(true);
360        }
361        Ok(false)
362    }
363
364    /// Get beacon statistics
365    pub fn get_beacon_stats(&self) -> serde_json::Value {
366        let current_vrf_count = self
367            .pending_vrf_proofs
368            .get(&self.current_round)
369            .map(|proofs| proofs.len())
370            .unwrap_or(0);
371
372        let current_partial_sigs = self
373            .pending_partial_sigs
374            .get(&self.current_round)
375            .map(|sigs| sigs.len())
376            .unwrap_or(0);
377
378        serde_json::json!({
379            "current_round": self.current_round,
380            "total_rounds": self.rounds.len(),
381            "active_validators": self.validators.values().filter(|v| v.active).count(),
382            "threshold": self.threshold,
383            "current_vrf_proofs": current_vrf_count,
384            "current_partial_signatures": current_partial_sigs,
385            "can_finalize": self.can_finalize_round(),
386            "should_advance": self.should_advance_round(),
387            "bias_resistance": self.bias_resistance_enabled,
388            "total_challenges": self.challenges.values().map(|c| c.len()).sum::<usize>()
389        })
390    }
391
392    /// Get latest randomness
393    pub fn get_latest_randomness(&self) -> Option<String> {
394        if self.current_round > 1 {
395            self.rounds
396                .get(&(self.current_round - 1))
397                .map(|round| round.randomness.clone())
398        } else {
399            None
400        }
401    }
402
403    /// Get randomness history
404    pub fn get_randomness_history(&self, count: usize) -> Vec<(u64, String)> {
405        let mut history: Vec<_> = self
406            .rounds
407            .iter()
408            .map(|(round, data)| (*round, data.randomness.clone()))
409            .collect();
410        history.sort_by_key(|(round, _)| *round);
411        history.into_iter().rev().take(count).collect()
412    }
413}
414
415#[async_trait]
416impl ApplicationObject for RandomnessBeaconObject {
417    fn id(&self) -> &SharedObjectId {
418        &self.id
419    }
420
421    fn type_name(&self) -> &'static str {
422        "RandomnessBeacon"
423    }
424
425    async fn is_valid(&self, message: &SharedMessage) -> Result<bool> {
426        let msg_result: std::result::Result<BeaconMessageType, _> =
427            serde_json::from_value(message.data.clone());
428        Ok(msg_result.is_ok())
429    }
430
431    async fn add_message(&mut self, message: SharedMessage) -> Result<()> {
432        let beacon_msg: BeaconMessageType =
433            serde_json::from_value(message.data.clone()).map_err(|e| {
434                ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
435            })?;
436
437        let processed = match &beacon_msg {
438            BeaconMessageType::VrfProof { .. } => self.process_vrf_proof(beacon_msg.clone())?,
439            BeaconMessageType::PartialSignature { .. } => {
440                self.process_partial_signature(beacon_msg.clone())?
441            },
442            BeaconMessageType::ValidatorRegistration {
443                validator,
444                public_key,
445                vrf_key,
446                stake,
447                ..
448            } => {
449                let beacon_validator = BeaconValidator {
450                    address: validator.clone(),
451                    public_key: public_key.clone(),
452                    vrf_key: vrf_key.clone(),
453                    stake: *stake,
454                    active: true,
455                    last_participation: None,
456                };
457                self.register_validator(beacon_validator)?;
458                true
459            },
460            BeaconMessageType::BiasChallenge { .. } => {
461                self.process_bias_challenge(beacon_msg.clone())?
462            },
463            BeaconMessageType::FinalizedBeacon { .. } => {
464                // Already finalized beacon rounds are informational
465                self.messages.push(beacon_msg.clone());
466                true
467            },
468        };
469
470        if processed {
471            tracing::debug!("Successfully processed beacon message: {:?}", beacon_msg);
472
473            // Check if we can finalize the current round
474            if self.can_finalize_round() {
475                if let Ok(randomness) = self.finalize_round() {
476                    tracing::info!(
477                        "Finalized beacon round {} with randomness: {}",
478                        self.current_round - 1,
479                        randomness
480                    );
481                }
482            }
483        }
484
485        Ok(())
486    }
487
488    fn is_merkleized(&self) -> bool {
489        false
490    }
491
492    async fn get_latest_digest(&self) -> Result<String> {
493        Ok(format!("beacon_round:{}", self.current_round))
494    }
495
496    async fn has_digest(&self, digest: &str) -> Result<bool> {
497        let current_digest = format!("beacon_round:{}", self.current_round);
498        Ok(digest == current_digest)
499    }
500
501    async fn is_valid_digest(&self, _digest: &str) -> Result<bool> {
502        Ok(true)
503    }
504
505    async fn add_digest(&mut self, _digest: String) -> Result<bool> {
506        Ok(true)
507    }
508
509    async fn gossip_messages(&self, _digest: Option<&str>) -> Result<Vec<SharedMessage>> {
510        Ok(Vec::new())
511    }
512
513    async fn get_messages_since_digest(&self, _digest: &str) -> Result<Vec<SharedMessage>> {
514        Ok(Vec::new())
515    }
516
517    async fn get_state(&self) -> Result<serde_json::Value> {
518        Ok(serde_json::json!({
519            "type": "RandomnessBeacon",
520            "current_round": self.current_round,
521            "validators": self.validators.len(),
522            "finalized_rounds": self.rounds.len(),
523            "messages": self.messages.len(),
524            "latest_randomness": self.get_latest_randomness(),
525            "beacon_stats": self.get_beacon_stats(),
526            "randomness_history": self.get_randomness_history(10)
527        }))
528    }
529
530    async fn reset(&mut self) -> Result<()> {
531        self.rounds.clear();
532        self.current_round = 1;
533        self.last_round_time = Utc::now();
534        self.pending_vrf_proofs.clear();
535        self.pending_partial_sigs.clear();
536        self.challenges.clear();
537        self.messages.clear();
538        Ok(())
539    }
540
541    fn clone_box(&self) -> Box<dyn ApplicationObject> {
542        // Create a new instance with same configuration
543        let new_obj = RandomnessBeaconObject::new(self.round_duration_secs, self.threshold)
544            .unwrap_or_else(|_| {
545                // Fallback if creation fails
546                let signer = ECDSASigner::new().unwrap();
547                let my_validator_address = signer.get_public_key_pem().unwrap();
548                RandomnessBeaconObject {
549                    id: SharedObjectId::new(),
550                    validators: HashMap::new(),
551                    rounds: HashMap::new(),
552                    current_round: 1,
553                    round_duration_secs: 60,
554                    last_round_time: Utc::now(),
555                    pending_vrf_proofs: HashMap::new(),
556                    pending_partial_sigs: HashMap::new(),
557                    threshold: 3,
558                    my_validator_address,
559                    signer,
560                    verifier: ECDSAVerifier::new(),
561                    messages: Vec::new(),
562                    bias_resistance_enabled: true,
563                    challenges: HashMap::new(),
564                }
565            });
566        Box::new(new_obj)
567    }
568
569    fn as_any(&self) -> &dyn std::any::Any {
570        self
571    }
572
573    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
574        self
575    }
576}
577
578/// Helper functions for creating beacon messages
579pub mod helpers {
580    use super::*;
581
582    pub fn create_validator_registration(
583        validator: String,
584        public_key: String,
585        vrf_key: String,
586        stake: u64,
587        signer: &ECDSASigner,
588    ) -> Result<serde_json::Value> {
589        let signature_data = format!("register:{}:{}:{}:{}", validator, public_key, vrf_key, stake);
590        let signature = signer.sign(signature_data.as_bytes())?;
591
592        let registration = BeaconMessageType::ValidatorRegistration {
593            validator,
594            public_key,
595            vrf_key,
596            stake,
597            signature: hex::encode(signature.to_bytes()),
598        };
599
600        serde_json::to_value(registration)
601            .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
602    }
603
604    pub fn create_vrf_proof_message(
605        round: u64,
606        input: String,
607        proof: String,
608        output: String,
609        validator: String,
610        signer: &ECDSASigner,
611    ) -> Result<serde_json::Value> {
612        let signature_data = format!("vrf:{}:{}:{}:{}", round, input, proof, output);
613        let signature = signer.sign(signature_data.as_bytes())?;
614
615        let vrf_msg = BeaconMessageType::VrfProof {
616            round,
617            input,
618            proof,
619            output,
620            validator,
621            signature: hex::encode(signature.to_bytes()),
622            timestamp: Utc::now(),
623        };
624
625        serde_json::to_value(vrf_msg)
626            .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
627    }
628
629    pub fn create_partial_signature_message(
630        round: u64,
631        validator: String,
632        partial_sig: String,
633        signer: &ECDSASigner,
634    ) -> Result<serde_json::Value> {
635        let signature_data = format!("partial_sig:{}:{}:{}", round, validator, partial_sig);
636        let signature = signer.sign(signature_data.as_bytes())?;
637
638        let partial_sig_msg = BeaconMessageType::PartialSignature {
639            round,
640            validator,
641            partial_sig,
642            signature: hex::encode(signature.to_bytes()),
643            timestamp: Utc::now(),
644        };
645
646        serde_json::to_value(partial_sig_msg)
647            .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
648    }
649
650    pub fn create_bias_challenge(
651        round: u64,
652        challenger: String,
653        target_validator: String,
654        challenge_data: String,
655        signer: &ECDSASigner,
656    ) -> Result<serde_json::Value> {
657        let signature_data =
658            format!("challenge:{}:{}:{}:{}", round, challenger, target_validator, challenge_data);
659        let signature = signer.sign(signature_data.as_bytes())?;
660
661        let challenge = BeaconMessageType::BiasChallenge {
662            round,
663            challenger,
664            target_validator,
665            challenge_data,
666            signature: hex::encode(signature.to_bytes()),
667        };
668
669        serde_json::to_value(challenge)
670            .map_err(|e| ChaincraftError::Serialization(crate::error::SerializationError::Json(e)))
671    }
672}