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