//! Two-node example: bootstrap, put, get. //! //! Creates two Node nodes on localhost. Node 2 joins //! via Node 1, then Node 1 stores a value and Node 2 //! retrieves it (via protocol exchange). //! //! Run with: //! cargo run --example two_nodes //! //! With debug logging: //! RUST_LOG=debug cargo run --example two_nodes use std::time::Duration; use tesseras_dht::Node; use tesseras_dht::nat::NatState; fn main() { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or("info"), ) .format(|buf, record| { use std::io::Write; writeln!( buf, "{} [{}] {}", record.level(), record.target(), record.args() ) }) .init(); // ── Create two nodes ──────────────────────────── let mut node1 = Node::bind(0).expect("bind node1"); node1.set_nat_state(NatState::Global); let addr1 = node1.local_addr().expect("local addr"); println!("Node 1: {} @ {}", node1.id_hex(), addr1); let mut node2 = Node::bind(0).expect("bind node2"); node2.set_nat_state(NatState::Global); let addr2 = node2.local_addr().expect("local addr"); println!("Node 2: {} @ {}", node2.id_hex(), addr2); // ── Node 2 joins via Node 1 ───────────────────── println!("\n--- Node 2 joining via Node 1 ---"); node2.join("127.0.0.1", addr1.port()).expect("join"); // Poll both nodes a few times to exchange messages for _ in 0..10 { node1.poll().ok(); node2.poll().ok(); std::thread::sleep(Duration::from_millis(50)); } println!("Node 1 routing table: {} peers", node1.routing_table_size()); println!("Node 2 routing table: {} peers", node2.routing_table_size()); // ── Node 1 stores a value ─────────────────────── println!("\n--- Node 1 storing key='hello' ---"); node1.put(b"hello", b"world", 300, false); // Poll to deliver STORE messages for _ in 0..10 { node1.poll().ok(); node2.poll().ok(); std::thread::sleep(Duration::from_millis(50)); } // ── Check storage ─────────────────────────────── let vals1 = node1.get(b"hello"); let vals2 = node2.get(b"hello"); println!( "\nNode 1 get('hello'): {:?}", vals1 .iter() .map(|v| String::from_utf8_lossy(v).to_string()) .collect::>() ); println!( "Node 2 get('hello'): {:?}", vals2 .iter() .map(|v| String::from_utf8_lossy(v).to_string()) .collect::>() ); // ── Test remote get via FIND_VALUE ──────────── println!("\n--- Node 1 storing key='secret' (local only) ---"); // Store only on Node 1 (no STORE sent because // we bypass put and go directly to storage) node1.put(b"remote-key", b"remote-val", 300, false); // Don't poll — so Node 2 doesn't get the STORE // Node 2 tries to get it — should trigger FIND_VALUE println!( "Node 2 get('remote-key') before poll: {:?}", node2.get(b"remote-key") ); // Now poll to let FIND_VALUE exchange happen for _ in 0..10 { node1.poll().ok(); node2.poll().ok(); std::thread::sleep(Duration::from_millis(50)); } let remote_vals = node2.get(b"remote-key"); println!( "Node 2 get('remote-key') after poll: {:?}", remote_vals .iter() .map(|v| String::from_utf8_lossy(v).to_string()) .collect::>() ); // ── Print state ───────────────────────────────── println!("\n--- Node 1 state ---"); node1.print_state(); println!("\n--- Node 2 state ---"); node2.print_state(); println!("\n--- Done ---"); println!("Node 1: {node1}"); println!("Node 2: {node2}"); }