aboutsummaryrefslogtreecommitdiffstats
path: root/examples/two_nodes.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/two_nodes.rs')
-rw-r--r--examples/two_nodes.rs133
1 files changed, 133 insertions, 0 deletions
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::<Vec<_>>()
+ );
+ println!(
+ "Node 2 get('hello'): {:?}",
+ vals2
+ .iter()
+ .map(|v| String::from_utf8_lossy(v).to_string())
+ .collect::<Vec<_>>()
+ );
+
+ // ── 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::<Vec<_>>()
+ );
+
+ // ── 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}");
+}