secret_rotation/
secret_rotation.rs

1use anyhow::Result;
2use iroh::{Endpoint, SecretKey};
3use iroh_gossip::{api::Event, net::Gossip};
4use sha2::Digest;
5
6// Imports from distrubuted-topic-tracker
7use distributed_topic_tracker::{
8    AutoDiscoveryBuilder, AutoDiscoveryGossip, SecretRotation, TopicId,
9};
10
11
12#[derive(Debug, Clone, Copy)]
13struct MySecretRotation;
14
15impl SecretRotation for MySecretRotation {
16    fn get_unix_minute_secret(
17        &self,
18        topic_hash: [u8; 32],
19        unix_minute: u64,
20        initial_secret_hash: [u8; 32],
21    ) -> [u8; 32] {
22        let mut hash = sha2::Sha512::new();
23        hash.update(topic_hash);
24        hash.update(unix_minute.to_be_bytes());
25        hash.update(initial_secret_hash);
26        hash.update(b"as long as you return 32 bytes this is a valid secret rotation function");
27        hash.finalize()[..32].try_into().expect("hashing failed")
28    }
29}
30
31impl Default for MySecretRotation {
32    fn default() -> Self {
33        Self {}
34    }
35}
36
37
38#[tokio::main]
39async fn main() -> Result<()> {
40    // Generate a new random secret key
41    let secret_key = SecretKey::generate(rand::rngs::OsRng);
42
43    // Set up endpoint with discovery enabled
44    let endpoint = Endpoint::builder()
45        .secret_key(secret_key)
46        .discovery_n0()
47        .bind()
48        .await?;
49
50    // Initialize gossip with auto-discovery
51    let gossip = Gossip::builder()
52        .spawn_with_auto_discovery::<MySecretRotation>(endpoint.clone(), None)
53        .await?;
54
55    // Set up protocol router
56    let _router = iroh::protocol::Router::builder(endpoint.clone())
57        .accept(iroh_gossip::ALPN, gossip.gossip.clone())
58        .spawn();
59
60    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
61    let initial_secret = b"my-initial-secret".to_vec();
62
63    // Split into sink (sending) and stream (receiving)
64    let (sink, mut stream) = gossip
65        .subscribe_and_join_with_auto_discovery(topic_id, &initial_secret)
66        .await?
67        .split();
68
69    println!("Joined topic");
70
71    // Spawn listener for incoming messages
72    tokio::spawn(async move {
73        while let Ok(event) = stream.recv().await {
74            if let Event::Received(msg) = event {
75                println!(
76                    "\nMessage from {}: {}",
77                    &msg.delivered_from.to_string()[0..8],
78                    String::from_utf8(msg.content.to_vec()).unwrap()
79                );
80            } else if let Event::NeighborUp(peer) = event {
81                println!("\nJoined by {}", &peer.to_string()[0..8]);
82            }
83        }
84    });
85
86    // Main input loop for sending messages
87    let mut buffer = String::new();
88    let stdin = std::io::stdin();
89    loop {
90        print!("\n> ");
91        stdin.read_line(&mut buffer).unwrap();
92        sink.broadcast(buffer.clone().replace("\n", "").into())
93            .await
94            .unwrap();
95        print!(" - (sent)\n");
96        buffer.clear();
97    }
98}