aboutsummaryrefslogtreecommitdiffstats
path: root/examples/remote_get.rs
blob: 60add190e7258b9033e5679704cf076973974258 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Remote get example: FIND_VALUE across the network.
//!
//! Node 1 stores a value locally. Node 3 retrieves it
//! via iterative FIND_VALUE, even though it never
//! received a STORE.
//!
//! Usage:
//!   cargo run --example remote_get

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 3 nodes
    let mut node1 = Node::bind(0).expect("bind");
    node1.set_nat_state(NatState::Global);
    let port1 = node1.local_addr().unwrap().port();

    let mut node2 = Node::bind(0).expect("bind");
    node2.set_nat_state(NatState::Global);
    node2.join("127.0.0.1", port1).expect("join");

    let mut node3 = Node::bind(0).expect("bind");
    node3.set_nat_state(NatState::Global);
    node3.join("127.0.0.1", port1).expect("join");

    println!("Node 1: {} (has the value)", &node1.id_hex()[..8]);
    println!("Node 2: {} (relay)", &node2.id_hex()[..8]);
    println!("Node 3: {} (will search)", &node3.id_hex()[..8]);

    // Let them discover each other
    for _ in 0..10 {
        node1.poll().ok();
        node2.poll().ok();
        node3.poll().ok();
        std::thread::sleep(Duration::from_millis(20));
    }

    println!(
        "\nRouting tables: N1={} N2={} N3={}",
        node1.routing_table_size(),
        node2.routing_table_size(),
        node3.routing_table_size(),
    );

    // Node 1 stores a value locally only (no STORE sent)
    println!("\n--- Node 1 stores 'secret-key' ---");
    node1.put(b"secret-key", b"secret-value", 300, false);

    // Verify: Node 3 does NOT have it
    assert!(
        node3.get(b"secret-key").is_empty(),
        "Node 3 should not have the value yet"
    );
    println!("Node 3 get('secret-key'): [] (not found)");

    // Node 3 does get() — triggers FIND_VALUE
    println!("\n--- Node 3 searches via FIND_VALUE ---");
    let _ = node3.get(b"secret-key"); // starts query

    // Poll to let FIND_VALUE propagate
    for _ in 0..15 {
        node1.poll().ok();
        node2.poll().ok();
        node3.poll().ok();
        std::thread::sleep(Duration::from_millis(30));
    }

    // Now Node 3 should have cached the value
    let result = node3.get(b"secret-key");
    println!(
        "Node 3 get('secret-key'): {:?}",
        result
            .iter()
            .map(|v| String::from_utf8_lossy(v).to_string())
            .collect::<Vec<_>>()
    );

    if result.is_empty() {
        println!("\n(Value not found — may need more poll rounds)");
    } else {
        println!("\nRemote get successful!");
    }

    // Storage summary
    println!("\n--- Storage ---");
    println!("Node 1: {} values", node1.storage_count());
    println!("Node 2: {} values", node2.storage_count());
    println!("Node 3: {} values", node3.storage_count());
}