Skip to content

Commit

Permalink
Merge pull request #48 from pubky/feat/new-node-example
Browse files Browse the repository at this point in the history
feat: add DHT Node example with better message visualization
  • Loading branch information
Nuhvi authored Feb 9, 2025
2 parents 6d111f6 + bfa3ab2 commit 3136b65
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 23 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ histo = "1.0.0"
rayon = "1.10"
dashmap = "6.1"
flume = "0.11.1"
colored = "2.0.0"

[features]
## Include [Dht] node.
Expand Down
58 changes: 35 additions & 23 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,61 @@
# Examples

## Announce peer
## Core API Examples
These examples demonstrate the main functionality of the Mainline DHT library:

### Basic DHT Operations
```sh
cargo run --example announce_peer <40 bytes hex info_hash>
```
# Bootstrap a DHT node
cargo run --example bootstrap

# Implement a custom request filter
cargo run --example request_filter

## Get peers
# Cache and reuse bootstrap nodes
cargo run --example cache_bootstrap

# Advanced logging configuration
cargo run --example logging
```

### Peer Operations
```sh
# Announce as a peer
cargo run --example announce_peer <40 bytes hex info_hash>

# Find peers
cargo run --example get_peers <40 bytes hex info_hash>
```

## Put Immutable

### Data Storage
```sh
# Store immutable data
cargo run --example put_immutable <string>
```

## Get Immutable

```sh
# Retrieve immutable data
cargo run --example get_immutable <40 bytes hex target from put_immutable>
```

## Put Mutable

```sh
# Store mutable data
cargo run --example put_mutable <64 bytes hex secret_key> <string>
```

## Get Mutable

```sh
# Retrieve mutable data
cargo run --example get_mutable <40 bytes hex target from put_mutable>
```

## Custom Server
---

```sh
cargo run --example custom_server
````
## Analysis & Research Tools
These examples are for DHT network analysis and research purposes:

## Measure the Dht size
> Note: These tools are not part of the main API and are provided for curiosity/research only.
```sh
# Analyze DHT node distribution
cargo run --example count_ips_close_to_key

# Estimate DHT size (Mark-Recapture method)
cargo run --example mark_recapture_dht

# Measure DHT network size
cargo run --example measure_dht
```
185 changes: 185 additions & 0 deletions examples/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use colored::*;
use mainline::Dht;
use std::{thread, time::Duration};
use tracing::{debug, info, Level};
use tracing_subscriber::{self, fmt::format::FmtSpan};

/// Custom logger that formats DHT messages
struct DhtLogger;

impl DhtLogger {
fn info(msg: &str) {
info!("{}", format_dht_message(msg));
}

fn debug(msg: &str) {
debug!("{}", format_dht_message(msg));
}
}

/// Format a DHT message for better readability
fn format_dht_message(msg: &str) -> String {
// Extract message type and content
if msg.contains("Sending") {
format_outgoing_message(msg)
} else if msg.contains("Received") {
format_incoming_message(msg)
} else {
msg.to_string()
}
}

/// Format outgoing messages
fn format_outgoing_message(msg: &str) -> String {
format_message(msg, "OUT".bright_blue())
}

/// Format incoming messages
fn format_incoming_message(msg: &str) -> String {
format_message(msg, "IN".bright_green())
}

/// Common message formatting logic
fn format_message(msg: &str, direction: ColoredString) -> String {
// Extract message type (find_node, get_peers, etc)
let msg_type = if msg.contains("find_node") {
"FIND_NODE".yellow()
} else if msg.contains("get_peers") {
"GET_PEERS".purple()
} else if msg.contains("announce_peer") {
"ANNOUNCE".cyan()
} else if msg.contains("ping") {
"PING".bright_black()
} else {
"OTHER".white()
};

// Format the message in a structured way
format!(
"[{}] {} | {}\n{}",
direction,
msg_type,
"DHT Message".bright_black(),
format_message_details(msg)
)
}

/// Format message details in a structured way
fn format_message_details(msg: &str) -> String {
let mut details = String::new();
// Extract and format common fields
if let Some(id) = extract_field(msg, "id=") {
details.push_str(&format!(" ID: {}\n", id.bright_yellow()));
}
if let Some(addr) = extract_field(msg, "address=") {
details.push_str(&format!(" Address: {}\n", addr.bright_blue()));
}
if let Some(nodes) = extract_field(msg, "nodes=") {
details.push_str(&format!(" Nodes: {}\n", format_nodes(nodes)));
}
if let Some(values) = extract_field(msg, "values=") {
details.push_str(&format!(" Values: {}\n", values.bright_magenta()));
}
if let Some(token) = extract_field(msg, "token=") {
details.push_str(&format!(" Token: {}\n", token.bright_yellow()));
}
details
}

/// Extract a field from the message
fn extract_field<'a>(msg: &'a str, field: &str) -> Option<&'a str> {
msg.split(field)
.nth(1)
.map(|s| s.split_whitespace().next().unwrap_or(""))
}

/// Format nodes list for better readability
fn format_nodes(nodes: &str) -> String {
nodes
.split(", ")
.take(3)
.map(|n| n.bright_cyan().to_string())
.collect::<Vec<_>>()
.join(", ")
+ if nodes.split(", ").count() > 3 {
" ..."
} else {
""
}
}

fn main() {
// Configure logging to see ALL details
tracing_subscriber::fmt()
.with_max_level(Level::TRACE)
.with_thread_ids(true)
.with_target(true)
.with_file(true)
.with_ansi(true)
.with_line_number(true)
.with_thread_names(true)
.with_span_events(FmtSpan::FULL)
.event_format(
tracing_subscriber::fmt::format()
.with_thread_ids(true)
.with_thread_names(true)
.with_file(true)
.with_line_number(true)
.with_target(true)
.compact(), // This will make the output more compact
)
.init();

// Configure and start the DHT node in server mode
let dht = Dht::builder()
.server_mode()
.build()
.expect("Failed to create DHT server");

DhtLogger::info("DHT server node is running! Press Ctrl+C to stop.");
// Wait for bootstrap to complete
DhtLogger::info("Waiting for bootstrap...");
dht.bootstrapped();
DhtLogger::info("Bootstrap complete!");
// Keep the program running and show periodic information
loop {
thread::sleep(Duration::from_secs(30));
let info = dht.info();
// Basic node information
DhtLogger::info("=== DHT Node Status ===");
DhtLogger::info(&format!("Node ID: {}", info.id().to_string().yellow()));
DhtLogger::info(&format!(
"Local address: {}",
info.local_addr().to_string().blue()
));
if let Some(addr) = info.public_address() {
DhtLogger::info(&format!("Public address: {}", addr.to_string().green()));
}
DhtLogger::info(&format!(
"Firewalled: {}",
if info.firewalled() {
"Yes".red()
} else {
"No".green()
}
));
DhtLogger::info(&format!(
"Server mode: {}",
if info.server_mode() {
"Yes".green()
} else {
"No".yellow()
}
));
// Network statistics
let (size_estimate, std_dev) = info.dht_size_estimate();
DhtLogger::info("=== Network Statistics ===");
DhtLogger::info(&format!(
"Estimated nodes in network: {} (±{}%)",
size_estimate.to_string().cyan(),
format!("{:.1}", std_dev * 100.0).yellow()
));
DhtLogger::debug(&format!("Raw DHT Info: {:?}", info));
DhtLogger::info(""); // Blank line to separate updates
}
}

0 comments on commit 3136b65

Please # to comment.