From 9821aabf0b50d2487b07502d3d2cd89e7d62bdbe Mon Sep 17 00:00:00 2001 From: murilo ijanc Date: Tue, 24 Mar 2026 15:04:03 -0300 Subject: Initial commit NAT-aware Kademlia DHT library for peer-to-peer networks. Features: - Distributed key-value storage (iterative FIND_NODE, FIND_VALUE, STORE) - NAT traversal via DTUN hole-punching and proxy relay - Reliable Datagram Protocol (RDP) with 7-state connection machine - Datagram transport with automatic fragmentation/reassembly - Ed25519 packet authentication - 256-bit node IDs (Ed25519 public keys) - Rate limiting, ban list, and eclipse attack mitigation - Persistence and metrics - OpenBSD and Linux support --- examples/two_nodes.rs | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 examples/two_nodes.rs (limited to 'examples/two_nodes.rs') diff --git a/examples/two_nodes.rs b/examples/two_nodes.rs new file mode 100644 index 0000000..13565c6 --- /dev/null +++ b/examples/two_nodes.rs @@ -0,0 +1,133 @@ +//! 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}"); +} -- cgit v1.2.3