├── images └── dump.png ├── Cargo.toml ├── .gitignore ├── src ├── node.rs ├── key.rs ├── lib.rs ├── main.rs ├── utils.rs ├── network.rs ├── routing.rs └── protocol.rs ├── LICENSE └── README.md /images/dump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f0lg0/kademlia-dht/HEAD/images/dump.png -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kademlia_dht" 3 | version = "0.1.0" 4 | authors = ["f0lg0 "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | sha2 = "0.9.5" 11 | hex = "0.4.3" 12 | 13 | serde = { version = "1.0.117", features = ["derive"] } 14 | serde_json = "1.0.59" 15 | crossbeam-channel = "0.5.1" 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | # 15 | # already existing elements were commented out 16 | 17 | /target 18 | #Cargo.lock 19 | 20 | 21 | # custom 22 | dumps 23 | -------------------------------------------------------------------------------- /src/node.rs: -------------------------------------------------------------------------------- 1 | use super::key::Key; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] 5 | pub struct Node { 6 | pub ip: String, 7 | pub port: u16, 8 | pub id: Key, 9 | } 10 | 11 | impl Node { 12 | pub fn new(ip: String, port: u16) -> Self { 13 | let addr = format!("{}:{}", ip, port); 14 | let id = Key::new(addr); 15 | 16 | Node { ip, port, id } 17 | } 18 | pub fn get_info(&self) -> String { 19 | let mut parsed_id = hex::encode(self.id.0); 20 | parsed_id = parsed_id.to_ascii_uppercase(); 21 | 22 | format!("{}:{}:{}", self.ip, self.port, parsed_id) 23 | } 24 | 25 | pub fn get_addr(&self) -> String { 26 | format!("{}:{}", self.ip, self.port) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Leonardo Folgoni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/key.rs: -------------------------------------------------------------------------------- 1 | use super::KEY_LEN; 2 | use serde::{Deserialize, Serialize}; 3 | use sha2::{Digest, Sha256}; 4 | use std::fmt::{Binary, Debug, Error, Formatter}; 5 | 6 | #[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 7 | pub struct Key(pub [u8; KEY_LEN]); 8 | 9 | impl Key { 10 | pub fn new(input: String) -> Self { 11 | let mut hasher = Sha256::new(); 12 | hasher.update(input.as_bytes()); 13 | 14 | // we know that the hash output is going to be 256 bits = 32 bytes 15 | let result = hasher.finalize(); 16 | let mut hash = [0; KEY_LEN]; 17 | 18 | for i in 0..result.len() { 19 | hash[i] = result[i]; 20 | } 21 | 22 | Self(hash) 23 | } 24 | } 25 | 26 | impl Debug for Key { 27 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 28 | for x in &self.0 { 29 | write!(f, "{:X}", x).expect("[FAILED] Key::Debug --> Failed to format contents of Key"); 30 | } 31 | Ok(()) 32 | } 33 | } 34 | 35 | #[derive(Clone, Serialize, Deserialize, Hash, Ord, PartialOrd, Eq, PartialEq, Copy)] 36 | pub struct Distance(pub [u8; KEY_LEN]); 37 | 38 | impl Distance { 39 | pub fn new(k1: &Key, k2: &Key) -> Distance { 40 | let mut ret = [0; KEY_LEN]; 41 | for i in 0..KEY_LEN { 42 | ret[i] = k1.0[i] ^ k2.0[i]; 43 | } 44 | 45 | Self(ret) 46 | } 47 | } 48 | 49 | impl Debug for Distance { 50 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 51 | for x in &self.0 { 52 | write!(f, "{:X}", x) 53 | .expect("[FAILED] Distance::Debug --> Failed to format contents of Key"); 54 | } 55 | Ok(()) 56 | } 57 | } 58 | 59 | impl Binary for Distance { 60 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 61 | for x in &self.0 { 62 | write!(f, "{:b}", x) 63 | .expect("[FAILED] Key::Binary --> Failed to format contents of Distance"); 64 | } 65 | Ok(()) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod key; 2 | pub mod network; 3 | pub mod node; 4 | pub mod protocol; 5 | pub mod routing; 6 | pub mod utils; 7 | 8 | // 256 bits --> 32 bytes 9 | const KEY_LEN: usize = 32; 10 | 11 | // a list for each bit of the node ID 12 | // 32*8 --> 256 13 | const N_BUCKETS: usize = KEY_LEN * 8; 14 | 15 | // number entries in a list 16 | const K_PARAM: usize = 20; 17 | 18 | // buffer size used for streaming UDP 19 | const BUF_SIZE: usize = 4096 * 2; 20 | 21 | // response timeout 5000ms 22 | const TIMEOUT: u64 = 5000; 23 | 24 | // number of concurrent lookups in node lookup 25 | const ALPHA: usize = 3; 26 | 27 | const VERBOSE: bool = false; 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::key::Distance; 32 | use super::node::Node; 33 | use super::protocol::Protocol; 34 | use super::routing::NodeAndDistance; 35 | use super::utils; 36 | 37 | #[test] 38 | fn compare_distance() { 39 | let node0 = Node::new(utils::get_local_ip().unwrap(), 1335); 40 | let node1 = Node::new(utils::get_local_ip().unwrap(), 1336); 41 | 42 | let dist = Distance::new(&node0.id, &node1.id); 43 | let nd0 = NodeAndDistance(node0.clone(), dist.clone()); 44 | let nd1 = NodeAndDistance(node1.clone(), dist.clone()); 45 | 46 | assert_eq!(nd0, nd1); 47 | } 48 | 49 | #[test] 50 | fn main_test() { 51 | let node0 = Node::new(utils::get_local_ip().unwrap(), 1337); 52 | let node1 = Node::new(utils::get_local_ip().unwrap(), 1338); 53 | let node2 = Node::new(utils::get_local_ip().unwrap(), 1339); 54 | 55 | let interface0 = Protocol::new(node0.ip.clone(), node0.port.clone(), None); 56 | let interface1 = Protocol::new(node1.ip.clone(), node1.port.clone(), Some(node0.clone())); 57 | let interface2 = Protocol::new(node2.ip.clone(), node2.port.clone(), Some(node0.clone())); 58 | 59 | interface0.put("some_key".to_owned(), "some_value".to_owned()); 60 | let get_res_1 = interface1.get("some_key".to_owned()); 61 | let get_res_2 = interface2.get("some_key".to_owned()); 62 | 63 | assert_eq!("some_value", get_res_1.clone().unwrap()); 64 | assert_eq!(get_res_1.unwrap(), get_res_2.unwrap()); 65 | } 66 | 67 | #[test] 68 | fn dump_interface() { 69 | let interface = Protocol::new(utils::get_local_ip().unwrap(), 1400, None); 70 | utils::dump_interface_state(&interface, "dumps/interface.json"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate kademlia_dht; 2 | use kademlia_dht::node::Node; 3 | use kademlia_dht::protocol::Protocol; 4 | use kademlia_dht::utils; 5 | 6 | const BIG_TEST: bool = true; 7 | 8 | // be careful with the net size, for example my computer can't spawn too many threads 9 | // messages may also exceed the buffer size used for streaming (see issue #1) 10 | const NET_SIZE: usize = 10; 11 | 12 | fn test_big_net() { 13 | let mut interfaces: Vec = Vec::with_capacity(NET_SIZE); 14 | let mut base_port = 8000; 15 | 16 | let root = Node::new(utils::get_local_ip().unwrap(), 7999); 17 | let root_interface = Protocol::new(root.ip.clone(), root.port.clone(), None); 18 | root_interface.put("MAIN_KEY".to_owned(), "MAIN_VALUE".to_owned()); 19 | 20 | for i in 0..(NET_SIZE - 1) { 21 | let node = Node::new(utils::get_local_ip().unwrap(), base_port); 22 | 23 | interfaces.push(Protocol::new(node.ip, node.port, Some(root.clone()))); 24 | println!( 25 | "[+] Created interface for index: {} on port: {}", 26 | i, base_port 27 | ); 28 | 29 | base_port += 1; 30 | } 31 | 32 | for (index, interface) in interfaces.iter().enumerate() { 33 | println!("[+] Putting pair for index: {}", index); 34 | interface.put(format!("key_{}", index), format!("value_{}", index)); 35 | } 36 | 37 | for (index, interface) in interfaces.iter().enumerate() { 38 | let res = interface.get(format!("key_{}", index)); 39 | println!("[*] Looking for key_{}, got {}", index, res.unwrap()); 40 | } 41 | } 42 | 43 | fn main() { 44 | if BIG_TEST { 45 | test_big_net(); 46 | } else { 47 | let node0 = Node::new(utils::get_local_ip().unwrap(), 1337); 48 | println!("[+] Created node0: {:?}", node0); 49 | 50 | let node1 = Node::new(utils::get_local_ip().unwrap(), 1338); 51 | println!("[+] Created node1: {:?}", node1); 52 | 53 | let node2 = Node::new(utils::get_local_ip().unwrap(), 1339); 54 | println!("[+] Created node2: {:?}", node2); 55 | 56 | let interface0 = Protocol::new(node0.ip.clone(), node0.port.clone(), None); 57 | println!("[+] Initialized Kademlia Protocol for node0 (interface0)"); 58 | 59 | let interface1 = Protocol::new(node1.ip.clone(), node1.port.clone(), Some(node0.clone())); 60 | println!("[+] Initialized Kademlia Protocol for node1 (interface1)"); 61 | 62 | let interface2 = Protocol::new(node2.ip.clone(), node2.port.clone(), Some(node0.clone())); 63 | println!("[+] Initialized Kademlia Protocol for node2 (interface2)"); 64 | 65 | println!("\n--------------------------------------"); 66 | println!("Calling Kademlia API"); 67 | 68 | interface0.put("some_key".to_owned(), "some_value".to_owned()); 69 | println!("\t[*] node0 > called PUT for key: 'some_key' and value: 'some_value'"); 70 | 71 | let get_res = interface2.get("some_key".to_owned()); 72 | println!("\t[*] node2 > called GET on key: 'some_key'"); 73 | println!("\t\t[+] Extracted: {:?}", get_res); 74 | println!("--------------------------------------\n"); 75 | 76 | utils::dump_interface_state(&interface0, "dumps/interface0.json"); 77 | utils::dump_interface_state(&interface1, "dumps/interface1.json"); 78 | utils::dump_interface_state(&interface2, "dumps/interface2.json"); 79 | println!("[*] Dumped protocol states for node0, node1 and node2. Check out the 'dumps' folder for a complete tracelog"); 80 | println!("Exiting..."); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use super::node::Node; 2 | use super::protocol::Protocol; 3 | 4 | use std::fs::create_dir_all; 5 | use std::io::Write; 6 | use std::net::UdpSocket; 7 | 8 | use super::network; 9 | use super::routing::{KBucket, NodeAndDistance}; 10 | 11 | #[derive(Debug)] 12 | pub enum ChannelPayload { 13 | Request((network::Request, Node)), 14 | Response(network::Response), 15 | NoData, 16 | } 17 | 18 | pub fn get_local_ip() -> Option { 19 | let socket = match UdpSocket::bind("0.0.0.0:0") { 20 | Ok(s) => s, 21 | Err(_) => return None, 22 | }; 23 | 24 | match socket.connect("8.8.8.8:80") { 25 | Ok(()) => (), 26 | Err(_) => return None, 27 | }; 28 | 29 | match socket.local_addr() { 30 | Ok(addr) => return Some(addr.ip().to_string()), 31 | Err(_) => return None, 32 | }; 33 | } 34 | 35 | pub fn make_req_get_res( 36 | rpc: &network::Rpc, 37 | req: network::Request, 38 | dst: Node, 39 | ) -> Option { 40 | rpc.make_request(req, dst) 41 | .recv() 42 | .expect("[FAILED] Utils::make_req_get_res --> Failed to receive response through channel") 43 | } 44 | 45 | pub fn dump_interface_state(interface: &Protocol, path: &str) { 46 | create_dir_all("dumps") 47 | .expect("[FAILED] Utils::dump_interface_state --> Unable to create dumps dir"); 48 | 49 | let rt = interface 50 | .routes 51 | .lock() 52 | .expect("[FAILED] Utils::dump_interface_state --> Failed to acquire mutex on Routes"); 53 | let st = interface 54 | .store 55 | .lock() 56 | .expect("[FAILED] Utils::dump_interface_state --> Failed to acquire mutex on Store"); 57 | 58 | let flattened: Vec<&KBucket> = rt.kbuckets.iter().collect(); 59 | 60 | let mut parsed_buckets = vec![]; 61 | for kb in flattened { 62 | for n in &kb.nodes { 63 | let kbucket = serde_json::json!({ 64 | "nodes": { 65 | "ip": n.ip, 66 | "port": n.port, 67 | "id": format!("{:?}", n.id), 68 | }, 69 | "size": kb.size, 70 | }); 71 | parsed_buckets.push(kbucket); 72 | } 73 | } 74 | 75 | let mut parsed_store = vec![]; 76 | // parse store 77 | for (k, v) in &*st { 78 | let obj = serde_json::json!({ k: v }); 79 | parsed_store.push(obj); 80 | } 81 | 82 | let json = serde_json::json!({ 83 | "node": { 84 | "ip": interface.node.ip, 85 | "port": interface.node.port, 86 | "id": format!("{:?}", interface.node.id), 87 | }, 88 | "routes": { 89 | "node": { 90 | "ip": rt.node.ip, 91 | "port": rt.node.port, 92 | "id": format!("{:?}", interface.node.id), 93 | }, 94 | "kbuckets": parsed_buckets, 95 | }, 96 | "store": parsed_store, 97 | "rpc": { 98 | "socket": format!("{:?}", interface.rpc.socket), 99 | "pending": format!("{:?}", interface.rpc.pending.lock().unwrap()), 100 | "node": { 101 | "ip": interface.rpc.node.ip, 102 | "port": interface.rpc.node.port, 103 | "id": format!("{:?}", interface.rpc.node.id), 104 | }, 105 | } 106 | }); 107 | 108 | // write to json file 109 | let mut file = std::fs::File::create(path) 110 | .expect("[FAILED] Utils::dump_interface_state --> Unable to create dump file"); 111 | file.write_all(&json.to_string().as_bytes()) 112 | .expect("[FAILED] Utils::dump_interface_state --> Unable to write to dump file"); 113 | 114 | // write also to a .plantuml file 115 | let mut diagram = std::fs::File::create(format!("{}.plantuml", path)) 116 | .expect("[FAILED] Utils::dump_interface_state --> Unable to create dump file"); 117 | diagram 118 | .write_all("@startjson\n".to_string().as_bytes()) 119 | .expect("[FAILED] Utils::dump_interface_state --> Unable to write to dump file"); 120 | 121 | diagram 122 | .write_all(&json.to_string().as_bytes()) 123 | .expect("[FAILED] Utils::dump_interface_state --> Unable to write to dump file"); 124 | 125 | diagram 126 | .write_all("\n@endjson".to_string().as_bytes()) 127 | .expect("[FAILED] Utils::dump_interface_state --> Unable to write to dump file"); 128 | } 129 | 130 | pub fn dump_node_and_distance( 131 | entries: &Vec, 132 | target: &super::key::Key, 133 | path: &str, 134 | ) { 135 | let mut parsed = vec![]; 136 | 137 | for e in entries { 138 | parsed.push(serde_json::json!({ 139 | "node": { 140 | "ip": e.0.ip, 141 | "port": e.0.port, 142 | "id": format!("{:?}", e.0.id), 143 | }, 144 | "distance": format!("{:?}", e.1), 145 | })) 146 | } 147 | 148 | let json = serde_json::json!({ 149 | "target": format!("{:?}", target), 150 | "found": parsed, 151 | }); 152 | 153 | let mut file = std::fs::File::create(path) 154 | .expect("[FAILED] Utils::dump_node_and_distance --> Unable to create dump file"); 155 | file.write_all(&json.to_string().as_bytes()) 156 | .expect("[FAILED] Utils::dump_node_and_distance --> Unable to write to dump file"); 157 | } 158 | -------------------------------------------------------------------------------- /src/network.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::key::Key; 4 | use super::node::*; 5 | use super::routing::FindValueResult; 6 | use super::routing::NodeAndDistance; 7 | use super::BUF_SIZE; 8 | use super::TIMEOUT; 9 | 10 | use std::collections::HashMap; 11 | use std::net::UdpSocket; 12 | use std::str; 13 | use std::sync::mpsc; 14 | use std::sync::{Arc, Mutex}; 15 | use std::thread; 16 | 17 | #[derive(Serialize, Deserialize, Debug)] 18 | pub enum Request { 19 | Ping, 20 | Store(String, String), 21 | FindNode(Key), 22 | FindValue(String), 23 | } 24 | 25 | #[derive(Serialize, Deserialize, Debug)] 26 | pub enum Response { 27 | Ping, 28 | FindNode(Vec), 29 | FindValue(FindValueResult), 30 | } 31 | 32 | #[derive(Serialize, Deserialize, Debug)] 33 | pub enum Message { 34 | Abort, 35 | Request(Request), 36 | Response(Response), 37 | } 38 | 39 | #[derive(Serialize, Deserialize, Debug)] 40 | pub struct RpcMessage { 41 | pub token: Key, 42 | pub src: String, 43 | pub dst: String, 44 | pub msg: Message, 45 | } 46 | 47 | #[derive(Debug)] 48 | pub struct ReqWrapper { 49 | pub token: Key, 50 | pub src: String, 51 | pub payload: Request, 52 | } 53 | 54 | #[derive(Clone, Debug)] 55 | pub struct Rpc { 56 | pub socket: Arc, 57 | pub pending: Arc>>>>, 58 | pub node: Node, 59 | } 60 | 61 | impl Rpc { 62 | pub fn new(node: Node) -> Self { 63 | let socket = UdpSocket::bind(node.get_addr()) 64 | .expect("[FAILED] Rpc::new --> Error while binding UdpSocket to specified addr"); 65 | 66 | Self { 67 | socket: Arc::new(socket), 68 | pending: Arc::new(Mutex::new(HashMap::new())), 69 | node, 70 | } 71 | } 72 | pub fn open(rpc: Rpc, sender: mpsc::Sender) { 73 | thread::spawn(move || { 74 | let mut buf = [0u8; BUF_SIZE]; 75 | 76 | loop { 77 | let (len, src_addr) = rpc 78 | .socket 79 | .recv_from(&mut buf) 80 | .expect("[FAILED] Rpc::open --> Failed to receive data from peer"); 81 | 82 | let payload = 83 | String::from(str::from_utf8(&buf[..len]).expect( 84 | "[FAILED] Rpc::open --> Unable to parse string from received bytes", 85 | )); 86 | 87 | let mut decoded: RpcMessage = serde_json::from_str(&payload) 88 | .expect("[FAILED] Rpc::open, serde_json --> Unable to decode string payload"); 89 | 90 | decoded.src = src_addr.to_string(); 91 | 92 | if super::VERBOSE { 93 | println!( 94 | "----------\n[+] Received message: {:?}\n\ttoken: {:?}\n\tsrc: {}\n\tdst: {}\n\tmsg: {:?}\n----------", 95 | &decoded.msg, &decoded.token, &decoded.src, &decoded.dst, &decoded.msg 96 | ); 97 | } 98 | 99 | if decoded.dst != rpc.node.get_addr() { 100 | eprintln!("[WARNING] Rpc::open --> Destination address doesn't match node address, ignoring"); 101 | continue; 102 | } 103 | 104 | match decoded.msg { 105 | Message::Abort => { 106 | break; 107 | } 108 | Message::Request(req) => { 109 | let wrapped_req = ReqWrapper { 110 | token: decoded.token, 111 | src: decoded.src, 112 | payload: req, 113 | }; 114 | 115 | if let Err(_) = sender.send(wrapped_req) { 116 | eprintln!("[FAILED] Rpc::open, Request --> Receiver is dead, closing channel."); 117 | break; 118 | } 119 | } 120 | Message::Response(res) => { 121 | rpc.clone().handle_response(decoded.token, res); 122 | } 123 | } 124 | } 125 | }); 126 | } 127 | 128 | pub fn send_msg(&self, msg: &RpcMessage) { 129 | let encoded = serde_json::to_string(msg) 130 | .expect("[FAILED] Rpc::send_msg --> Unable to serialize message"); 131 | self.socket 132 | .send_to(&encoded.as_bytes(), &msg.dst) 133 | .expect("[FAILED] Rpc::send_msg --> Error while sending message to specified address"); 134 | } 135 | 136 | pub fn handle_response(self, token: Key, res: Response) { 137 | thread::spawn(move || { 138 | let mut pending = self 139 | .pending 140 | .lock() 141 | .expect("[FAILED] Rpc::handle_response --> Failed to acquire lock on Pending"); 142 | 143 | let tmp = match pending.get(&token) { 144 | Some(sender) => sender.send(Some(res)), 145 | None => { 146 | eprintln!( 147 | "[WARNING] Rpc::handle_response --> Unsolicited response received, ignoring..." 148 | ); 149 | return; 150 | } 151 | }; 152 | 153 | if let Ok(_) = tmp { 154 | pending.remove(&token); 155 | } 156 | }); 157 | } 158 | 159 | pub fn make_request(&self, req: Request, dst: Node) -> mpsc::Receiver> { 160 | let (sender, receiver) = mpsc::channel(); 161 | let mut pending = self 162 | .pending 163 | .lock() 164 | .expect("[FAILED] Rpc::make_request --> Failed to acquire mutex on Pending"); 165 | 166 | let token = Key::new(format!( 167 | "{}:{}:{:?}", 168 | self.node.get_info(), 169 | dst.get_info(), 170 | std::time::SystemTime::now() 171 | )); 172 | pending.insert(token.clone(), sender.clone()); 173 | 174 | let msg = RpcMessage { 175 | token: token.clone(), 176 | src: self.node.get_addr(), 177 | dst: dst.get_addr(), 178 | msg: Message::Request(req), 179 | }; 180 | 181 | self.send_msg(&msg); 182 | 183 | let rpc = self.clone(); 184 | thread::spawn(move || { 185 | thread::sleep(std::time::Duration::from_millis(TIMEOUT)); 186 | if let Ok(_) = sender.send(None) { 187 | let mut pending = rpc 188 | .pending 189 | .lock() 190 | .expect("[FAILED] Rpc::make_request --> Failed to acquire mutex on Pending"); 191 | pending.remove(&token); 192 | } 193 | }); 194 | 195 | receiver 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/routing.rs: -------------------------------------------------------------------------------- 1 | use super::key::{Distance, Key}; 2 | use super::network; 3 | use super::node::Node; 4 | use super::utils::ChannelPayload; 5 | use super::K_PARAM; 6 | use super::N_BUCKETS; 7 | 8 | use crossbeam_channel; 9 | use serde::{Deserialize, Serialize}; 10 | 11 | #[derive(Debug, Serialize, Deserialize, Eq, Hash, Clone)] 12 | pub struct NodeAndDistance(pub Node, pub Distance); 13 | 14 | #[derive(Debug, Serialize, Deserialize)] 15 | pub enum FindValueResult { 16 | Nodes(Vec), 17 | Value(String), 18 | } 19 | 20 | #[derive(Debug)] 21 | pub struct KBucket { 22 | pub nodes: Vec, 23 | pub size: usize, 24 | } 25 | 26 | #[derive(Debug)] 27 | pub struct RoutingTable { 28 | pub node: Node, 29 | pub kbuckets: Vec, 30 | pub sender: crossbeam_channel::Sender, 31 | pub receiver: crossbeam_channel::Receiver, 32 | } 33 | 34 | impl PartialEq for NodeAndDistance { 35 | fn eq(&self, other: &NodeAndDistance) -> bool { 36 | let mut equal = true; 37 | let mut i = 0; 38 | while equal && i < 32 { 39 | if self.1 .0[i] != other.1 .0[i] { 40 | equal = false; 41 | } 42 | 43 | i += 1; 44 | } 45 | 46 | equal 47 | } 48 | } 49 | 50 | impl PartialOrd for NodeAndDistance { 51 | fn partial_cmp(&self, other: &NodeAndDistance) -> Option { 52 | Some(other.1.cmp(&self.1)) 53 | } 54 | } 55 | 56 | impl Ord for NodeAndDistance { 57 | fn cmp(&self, other: &NodeAndDistance) -> std::cmp::Ordering { 58 | other.1.cmp(&self.1) 59 | } 60 | } 61 | 62 | // A k-bucket with index i stores contacts whose ids 63 | // have a distance between 2^i and 2^i+1 to the own id 64 | impl KBucket { 65 | pub fn new() -> Self { 66 | Self { 67 | nodes: Vec::new(), 68 | size: K_PARAM, 69 | } 70 | } 71 | } 72 | 73 | impl RoutingTable { 74 | pub fn new( 75 | node: Node, 76 | bootstrap: Option, 77 | sender: crossbeam_channel::Sender, 78 | receiver: crossbeam_channel::Receiver, 79 | ) -> Self { 80 | let mut kbuckets: Vec = Vec::new(); 81 | for _ in 0..N_BUCKETS { 82 | kbuckets.push(KBucket::new()); 83 | } 84 | 85 | let mut ret = Self { 86 | node: node.clone(), 87 | kbuckets, 88 | sender, 89 | receiver, 90 | }; 91 | 92 | ret.update(node); 93 | 94 | if let Some(bootstrap) = bootstrap { 95 | ret.update(bootstrap); 96 | } 97 | 98 | ret 99 | } 100 | 101 | fn get_lookup_bucket_index(&self, key: &Key) -> usize { 102 | // https://stackoverflow.com/questions/2656642/easiest-way-to-find-the-correct-kademlia-bucket 103 | 104 | // given a bucket j, we are guaranteed that 105 | // 2^j <= distance(node, contact) < 2^(j+1) 106 | // a node with distance d will be put in the k-bucket with index i=⌊logd⌋ 107 | 108 | // this is a method I found online but I don't remember the source. 109 | // another solution would be computing the log base 10 of the distance 110 | // or we could cmp::min the leading zeros of the Distance with the buckets.len() - 1 111 | let d = Distance::new(&self.node.id, key); 112 | for i in 0..super::KEY_LEN { 113 | for j in (0..8).rev() { 114 | if (d.0[i] >> (7 - j)) & 0x1 != 0 { 115 | return i * 8 + j; 116 | } 117 | } 118 | } 119 | 120 | super::KEY_LEN * 8 - 1 121 | } 122 | 123 | fn contact_via_rpc(&self, dst: Node) -> bool { 124 | if let Err(_) = self 125 | .sender 126 | .send(ChannelPayload::Request((network::Request::Ping, dst))) 127 | { 128 | println!( 129 | "[FAILED] RoutingTable::contact_via_rpc --> Receiver is dead, closing channel" 130 | ); 131 | return false; 132 | } 133 | 134 | true 135 | } 136 | 137 | pub fn update(&mut self, node: Node) { 138 | let bucket_idx = self.get_lookup_bucket_index(&node.id); 139 | 140 | // TODO(testing): fill buckets with dummy nodes so we can reach the else statement 141 | if self.kbuckets[bucket_idx].nodes.len() < K_PARAM { 142 | let node_idx = self.kbuckets[bucket_idx] 143 | .nodes 144 | .iter() 145 | .position(|x| x.id == node.id); 146 | match node_idx { 147 | Some(i) => { 148 | self.kbuckets[bucket_idx].nodes.remove(i); 149 | self.kbuckets[bucket_idx].nodes.push(node); 150 | } 151 | None => { 152 | self.kbuckets[bucket_idx].nodes.push(node); 153 | } 154 | } 155 | } else { 156 | let _success = self.contact_via_rpc(self.kbuckets[bucket_idx].nodes[0].clone()); 157 | let receiver = self.receiver.clone(); 158 | 159 | // We must make it blocking (no threads) because we still want to use the mutable reference to the buckets 160 | // we don't want to clone and move 161 | let res = receiver 162 | .recv() 163 | .expect("[FAILED] Routing::update --> Failed to receive data from channel"); 164 | match res { 165 | ChannelPayload::Response(_) => { 166 | let to_re_add = self.kbuckets[bucket_idx].nodes.remove(0); 167 | self.kbuckets[bucket_idx].nodes.push(to_re_add); 168 | } 169 | ChannelPayload::Request(_) => { 170 | eprintln!("[FAILED] Routing::update --> Unexpectedly got a Request instead of a Response"); 171 | } 172 | ChannelPayload::NoData => { 173 | self.kbuckets[bucket_idx].nodes.remove(0); 174 | self.kbuckets[bucket_idx].nodes.push(node); 175 | } 176 | }; 177 | } 178 | } 179 | 180 | pub fn remove(&mut self, node: &Node) { 181 | let bucket_idx = self.get_lookup_bucket_index(&node.id); 182 | 183 | if let Some(i) = self.kbuckets[bucket_idx] 184 | .nodes 185 | .iter() 186 | .position(|x| x.id == node.id) 187 | { 188 | self.kbuckets[bucket_idx].nodes.remove(i); 189 | } else { 190 | eprintln!("[WARN] Routing::remove --> Tried to remove non-existing entry"); 191 | } 192 | } 193 | 194 | pub fn get_closest_nodes(&self, key: &Key, count: usize) -> Vec { 195 | /* 196 | Notes: 197 | 198 | Kademlia seems not to specify a nice way to search for K closest nodes 199 | 200 | # Method 1 (currently in use) 201 | 1) look at which bucket index the key falls into 202 | 2) check buckets higher up that index 203 | 3) check buckets lower down that index 204 | (of course stop when you reach the desired count) 205 | 206 | # Method 2 (check issue #2 for a better explaination) 207 | 1) calculate the distance between two IDs 208 | 2) visit every kbucket by looking at the XOR result: 209 | - if a bit is set then take its position as an index and visit that kbucket 210 | - bits that are not set are used only after the set ones are used 211 | */ 212 | 213 | let mut ret = Vec::with_capacity(count); 214 | 215 | if count == 0 { 216 | return ret; 217 | } 218 | 219 | let mut bucket_index = self.get_lookup_bucket_index(key); 220 | let mut bucket_index_copy = bucket_index; 221 | 222 | for node in &self.kbuckets[bucket_index].nodes { 223 | ret.push(NodeAndDistance(node.clone(), Distance::new(&node.id, key))); 224 | } 225 | 226 | while ret.len() < count && bucket_index < self.kbuckets.len() - 1 { 227 | bucket_index += 1; 228 | 229 | for node in &self.kbuckets[bucket_index].nodes { 230 | ret.push(NodeAndDistance(node.clone(), Distance::new(&node.id, key))); 231 | } 232 | } 233 | 234 | while ret.len() < count && bucket_index_copy > 0 { 235 | bucket_index_copy -= 1; 236 | 237 | for node in &self.kbuckets[bucket_index_copy].nodes { 238 | ret.push(NodeAndDistance(node.clone(), Distance::new(&node.id, key))); 239 | } 240 | } 241 | 242 | ret.sort_by(|a, b| a.1.cmp(&b.1)); 243 | ret.truncate(count); 244 | ret 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/protocol.rs: -------------------------------------------------------------------------------- 1 | use super::network; 2 | use super::node::Node; 3 | use super::routing; 4 | use super::utils; 5 | 6 | use crossbeam_channel; 7 | use std::collections::{BinaryHeap, HashMap, HashSet}; 8 | use std::sync::mpsc; 9 | use std::sync::{Arc, Mutex}; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct Protocol { 13 | pub routes: Arc>, 14 | pub store: Arc>>, 15 | pub rpc: Arc, 16 | pub node: Node, 17 | } 18 | 19 | impl Protocol { 20 | pub fn new(ip: String, port: u16, bootstrap: Option) -> Self { 21 | let node = Node::new(ip, port); 22 | 23 | // channel used for a 2-way communication with the Routing Table module 24 | let (rt_channel_sender, rt_channel_receiver) = crossbeam_channel::unbounded(); 25 | 26 | let routes = routing::RoutingTable::new( 27 | node.clone(), 28 | bootstrap, 29 | rt_channel_sender.clone(), 30 | rt_channel_receiver.clone(), 31 | ); 32 | 33 | // 1-way channel to communicate with the Network module 34 | let (rpc_channel_sender, rpc_channel_receiver) = mpsc::channel(); 35 | 36 | let rpc = network::Rpc::new(node.clone()); 37 | network::Rpc::open(rpc.clone(), rpc_channel_sender); 38 | 39 | let protocol = Self { 40 | routes: Arc::new(Mutex::new(routes)), 41 | store: Arc::new(Mutex::new(HashMap::new())), 42 | rpc: Arc::new(rpc), 43 | node: node.clone(), 44 | }; 45 | 46 | protocol.clone().requests_handler(rpc_channel_receiver); 47 | protocol 48 | .clone() 49 | .rt_forwarder(rt_channel_sender, rt_channel_receiver); 50 | 51 | // performing node lookup on ourselves 52 | protocol.nodes_lookup(&node.id); 53 | 54 | // republishing pairs every hour 55 | let protocol_clone = protocol.clone(); 56 | std::thread::spawn(move || { 57 | std::thread::sleep(std::time::Duration::from_secs(60 * 60)); 58 | protocol_clone.republish(); 59 | }); 60 | 61 | protocol 62 | } 63 | 64 | fn republish(&self) { 65 | let st = self 66 | .store 67 | .lock() 68 | .expect("[FAILED] Protocol::republish --> Failed to acquire mutex on Store"); 69 | for (key, value) in &*st { 70 | self.put(key.to_string(), value.to_string()); 71 | } 72 | } 73 | 74 | // forwards upcoming requests (only Pings at the moment) from the Routing table 75 | fn rt_forwarder( 76 | self, 77 | sender: crossbeam_channel::Sender, 78 | receiver: crossbeam_channel::Receiver, 79 | ) { 80 | std::thread::spawn(move || { 81 | for req in receiver.iter() { 82 | let protocol = self.clone(); 83 | let sender_clone = sender.clone(); 84 | 85 | std::thread::spawn(move || match req { 86 | utils::ChannelPayload::Request(payload) => match payload.0 { 87 | network::Request::Ping => { 88 | let success = protocol.ping(payload.1); 89 | if success { 90 | if let Err(_) = sender_clone 91 | .send(utils::ChannelPayload::Response(network::Response::Ping)) 92 | { 93 | eprintln!("[FAILED] Protocol::rt_forwared --> Receiver is dead, closing channel"); 94 | } 95 | } else { 96 | if let Err(_) = sender_clone.send(utils::ChannelPayload::NoData) { 97 | eprintln!("[FAILED] Protocol::rt_forwared --> Receiver is dead, closing channel"); 98 | } 99 | } 100 | } 101 | _ => { 102 | unimplemented!(); 103 | } 104 | }, 105 | utils::ChannelPayload::Response(_) => { 106 | eprintln!("[FAILED] Protocol::rt_forwarder --> Received a Response instead of a Request") 107 | } 108 | utils::ChannelPayload::NoData => { 109 | eprintln!("[FAILED] Protocol::rt_forwarder --> Received a NoData instead of a Request") 110 | } 111 | }); 112 | } 113 | }); 114 | } 115 | 116 | // handles requests by crafting responses and sending them 117 | fn requests_handler(self, receiver: mpsc::Receiver) { 118 | std::thread::spawn(move || { 119 | for req in receiver.iter() { 120 | let protocol = self.clone(); 121 | 122 | std::thread::spawn(move || { 123 | let res = protocol.craft_res(req); 124 | protocol.reply(res); 125 | }); 126 | } 127 | }); 128 | } 129 | 130 | fn craft_res(&self, req: network::ReqWrapper) -> (network::Response, network::ReqWrapper) { 131 | let mut routes = self 132 | .routes 133 | .lock() 134 | .expect("[FAILED] Protocol::craft_res --> Failed to acquire mutex on Routes"); 135 | 136 | // must craft node object because ReqWrapper contains only the src string addr 137 | let split = req.src.split(":"); 138 | let parsed: Vec<&str> = split.collect(); 139 | 140 | let src_node = Node::new( 141 | parsed[0].to_string(), 142 | parsed[1] 143 | .parse::() 144 | .expect("[FAILED] Protocol::craft_res --> Failed to parse Node port from address"), 145 | ); 146 | routes.update(src_node); 147 | drop(routes); 148 | 149 | match req.payload { 150 | network::Request::Ping => (network::Response::Ping, req), 151 | network::Request::Store(ref k, ref v) => { 152 | // ref is used to borrow k and v, which are the contents of req 153 | 154 | let mut store = self 155 | .store 156 | .lock() 157 | .expect("[FAILED] Protocol::craft_res --> Failed to acquire mutex on Store"); 158 | store.insert(k.to_string(), v.to_string()); 159 | 160 | (network::Response::Ping, req) 161 | } 162 | network::Request::FindNode(ref id) => { 163 | let routes = self 164 | .routes 165 | .lock() 166 | .expect("[FAILED] Protocol::craft_res --> Failed to acquire mutex on Routes"); 167 | 168 | let result = routes.get_closest_nodes(id, super::K_PARAM); 169 | 170 | (network::Response::FindNode(result), req) 171 | } 172 | network::Request::FindValue(ref k) => { 173 | let key = super::key::Key::new(k.to_string()); 174 | let store = self 175 | .store 176 | .lock() 177 | .expect("[FAILED] Protocol::craft_res --> Failed to acquire mutex on Store"); 178 | 179 | let val = store.get(k); 180 | 181 | match val { 182 | Some(v) => ( 183 | network::Response::FindValue(routing::FindValueResult::Value( 184 | v.to_string(), 185 | )), 186 | req, 187 | ), 188 | None => { 189 | let routes = self.routes.lock().expect( 190 | "[FAILED] Protocol::craft_res --> Failed to acquire mutex on Routes", 191 | ); 192 | ( 193 | network::Response::FindValue(routing::FindValueResult::Nodes( 194 | routes.get_closest_nodes(&key, super::K_PARAM), 195 | )), 196 | req, 197 | ) 198 | } 199 | } 200 | } 201 | } 202 | } 203 | 204 | fn reply(&self, packet_details: (network::Response, network::ReqWrapper)) { 205 | let msg = network::RpcMessage { 206 | token: packet_details.1.token, 207 | src: self.node.get_addr(), 208 | dst: packet_details.1.src, 209 | msg: network::Message::Response(packet_details.0), 210 | }; 211 | 212 | self.rpc.send_msg(&msg); 213 | } 214 | 215 | pub fn ping(&self, dst: Node) -> bool { 216 | let res = utils::make_req_get_res(&self.rpc, network::Request::Ping, dst.clone()); 217 | 218 | let mut routes = self 219 | .routes 220 | .lock() 221 | .expect("[FAILED] Protocol::ping --> Failed to acquire lock on Routes"); 222 | 223 | if let Some(network::Response::Ping) = res { 224 | routes.update(dst); 225 | true 226 | } else { 227 | eprintln!( 228 | "[WARNING] Protocol::Ping --> No response, removing contact from routing table" 229 | ); 230 | routes.remove(&dst); 231 | false 232 | } 233 | } 234 | 235 | pub fn store(&self, dst: Node, key: String, val: String) -> bool { 236 | let res = 237 | utils::make_req_get_res(&self.rpc, network::Request::Store(key, val), dst.clone()); 238 | 239 | // since we get a ping, update our routing table 240 | let mut routes = self 241 | .routes 242 | .lock() 243 | .expect("[FAILED] Protocol::store --> Failed to acquire mutex on Routes"); 244 | if let Some(network::Response::Ping) = res { 245 | routes.update(dst); 246 | true 247 | } else { 248 | routes.remove(&dst); 249 | false 250 | } 251 | } 252 | 253 | pub fn find_node( 254 | &self, 255 | dst: Node, 256 | id: super::key::Key, 257 | ) -> Option> { 258 | let res = utils::make_req_get_res(&self.rpc, network::Request::FindNode(id), dst.clone()); 259 | 260 | let mut routes = self 261 | .routes 262 | .lock() 263 | .expect("[FAILED] Protocol::find_node --> Failed to acquire mutex on Routes"); 264 | if let Some(network::Response::FindNode(entries)) = res { 265 | routes.update(dst); 266 | Some(entries) 267 | } else { 268 | routes.remove(&dst); 269 | None 270 | } 271 | } 272 | 273 | pub fn find_value(&self, dst: Node, k: String) -> Option { 274 | let res = utils::make_req_get_res(&self.rpc, network::Request::FindValue(k), dst.clone()); 275 | 276 | let mut routes = self 277 | .routes 278 | .lock() 279 | .expect("[FAILED] Protocol::find_value --> Failed to acquire mutex on Routes"); 280 | 281 | if let Some(network::Response::FindValue(val)) = res { 282 | routes.update(dst); 283 | Some(val) 284 | } else { 285 | routes.remove(&dst); 286 | None 287 | } 288 | } 289 | 290 | pub fn nodes_lookup(&self, id: &super::key::Key) -> Vec { 291 | let mut ret: Vec = Vec::new(); 292 | 293 | // nodes visited 294 | let mut queried = HashSet::new(); 295 | let routes = self 296 | .routes 297 | .lock() 298 | .expect("[FAILED] Protocol::nodes_lookup --> Failed to acquire mutex on Routes"); 299 | 300 | // nodes to visit 301 | let mut to_query = BinaryHeap::from(routes.get_closest_nodes(id, super::K_PARAM)); 302 | drop(routes); 303 | 304 | for entry in &to_query { 305 | queried.insert(entry.clone()); 306 | } 307 | 308 | while !to_query.is_empty() { 309 | // threads joins 310 | let mut joins: Vec>>> = 311 | Vec::new(); 312 | // outgoing queries 313 | let mut queries: Vec = Vec::new(); 314 | let mut results: Vec>> = Vec::new(); 315 | 316 | for _ in 0..super::ALPHA { 317 | match to_query.pop() { 318 | Some(entry) => { 319 | queries.push(entry); 320 | } 321 | None => { 322 | break; 323 | } 324 | } 325 | } 326 | 327 | for &routing::NodeAndDistance(ref node, _) in &queries { 328 | let n = node.clone(); 329 | let id_clone = id.clone(); 330 | let protocol_clone = self.clone(); 331 | 332 | joins.push(std::thread::spawn(move || { 333 | protocol_clone.find_node(n, id_clone) 334 | })); 335 | } 336 | 337 | for j in joins { 338 | results.push(j.join().expect( 339 | "[FAILED] Protocol::nodes_lookup --> Failed to join thread while visiting nodes", 340 | )); 341 | } 342 | 343 | for (result, query) in results.into_iter().zip(queries) { 344 | if let Some(entries) = result { 345 | ret.push(query); 346 | 347 | for entry in entries { 348 | if queried.insert(entry.clone()) { 349 | to_query.push(entry); 350 | } 351 | } 352 | } 353 | } 354 | } 355 | 356 | ret.sort_by(|a, b| a.1.cmp(&b.1)); 357 | ret.truncate(super::K_PARAM); 358 | 359 | ret 360 | } 361 | 362 | pub fn value_lookup(&self, k: String) -> (Option, Vec) { 363 | // NOTE: k and key are two different things, one is a string used to search for the corresponding value while the other is a key::Key 364 | 365 | let mut ret: Vec = Vec::new(); 366 | let key = super::key::Key::new(k.clone()); 367 | let mut queried = HashSet::new(); 368 | 369 | let routes = self 370 | .routes 371 | .lock() 372 | .expect("[FAILED] Protocol::value_lookup --> Failed to acquire mutex on Routes"); 373 | let mut to_query = BinaryHeap::from(routes.get_closest_nodes(&key, super::K_PARAM)); 374 | drop(routes); 375 | 376 | for entry in &to_query { 377 | queried.insert(entry.clone()); 378 | } 379 | 380 | while !to_query.is_empty() { 381 | let mut joins: Vec>> = 382 | Vec::new(); 383 | let mut queries: Vec = Vec::new(); 384 | let mut results: Vec> = Vec::new(); 385 | 386 | for _ in 0..super::ALPHA { 387 | match to_query.pop() { 388 | Some(entry) => { 389 | queries.push(entry); 390 | } 391 | None => { 392 | break; 393 | } 394 | } 395 | } 396 | 397 | for &routing::NodeAndDistance(ref n, _) in &queries { 398 | let k_clone = k.clone(); 399 | let node = n.clone(); 400 | let protocol = self.clone(); 401 | 402 | joins.push(std::thread::spawn(move || { 403 | protocol.find_value(node, k_clone) 404 | })); 405 | } 406 | 407 | for j in joins { 408 | results.push(j.join().expect("[FAILED] Protocol::value_lookup --> Failed to join thread while searching for value")); 409 | } 410 | 411 | for (result, query) in results.into_iter().zip(queries) { 412 | if let Some(find_value_result) = result { 413 | match find_value_result { 414 | routing::FindValueResult::Nodes(entries) => { 415 | // we didn't get the value we looked for 416 | ret.push(query); 417 | for entry in entries { 418 | if queried.insert(entry.clone()) { 419 | to_query.push(entry); 420 | } 421 | } 422 | } 423 | 424 | routing::FindValueResult::Value(val) => { 425 | ret.sort_by(|a, b| a.1.cmp(&b.1)); 426 | ret.truncate(super::K_PARAM); 427 | 428 | return (Some(val), ret); 429 | } 430 | } 431 | } 432 | } 433 | } 434 | ret.sort_by(|a, b| a.1.cmp(&b.1)); 435 | ret.truncate(super::K_PARAM); 436 | (None, ret) 437 | } 438 | 439 | pub fn put(&self, k: String, v: String) { 440 | let candidates = self.nodes_lookup(&super::key::Key::new(k.clone())); 441 | 442 | for routing::NodeAndDistance(node, _) in candidates { 443 | let protocol_clone = self.clone(); 444 | let k_clone = k.clone(); 445 | let v_clone = v.clone(); 446 | 447 | std::thread::spawn(move || { 448 | protocol_clone.store(node, k_clone, v_clone); 449 | }); 450 | } 451 | } 452 | 453 | pub fn get(&self, k: String) -> Option { 454 | let (val, mut nodes) = self.value_lookup(k.clone()); 455 | 456 | val.map(|v| { 457 | if let Some(routing::NodeAndDistance(target, _)) = nodes.pop() { 458 | self.store(target, k, v.clone()); 459 | } else { 460 | self.store(self.node.clone(), k, v.clone()); 461 | } 462 | 463 | v 464 | }) 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kademlia-dht 2 | 3 | Simple implementation of the Kademlia DHT protocol in Rust with state dumping features for **educational purposes** (not production-ready). 4 | 5 | ## Table of contents 6 | 7 | - [kademlia-dht](#kademlia-dht) 8 | - [Lib structure](#lib-structure) 9 | - [Usage](#usage) 10 | - [Interface creation](#interface-creation) 11 | - [Main operations](#main-operations) 12 | - [PUT](#put) 13 | - [GET](#get) 14 | - [Example program](#example-program) 15 | - [Documentation](#documentation) 16 | - [Kademlia node](#kademlia-node) 17 | - [Node::new](#nodenew) 18 | - [get_info](#get_info) 19 | - [get_addr](#get_addr) 20 | - [256bits Key and Distance](#256bits-key-and-distance) 21 | - [Key::new](#keynew) 22 | - [Distance::new](#distancenew) 23 | - [Routing Table](#routing-table) 24 | - [Routing::new](#routingnew) 25 | - [get_lookup_bucket_index](#get_lookup_bucket_index) 26 | - [contact_via_rpc](#contact_via_rpc) 27 | - [update](#update) 28 | - [remove](#remove) 29 | - [get_closest_nodes](#get_closest_nodes) 30 | - [Network](#network) 31 | - [Request](#request) 32 | - [Response](#response) 33 | - [Message](#message) 34 | - [RpcMessage](#rpcmessage) 35 | - [Rpc::new](#rpcnew) 36 | - [Rpc::open](#rpcopen) 37 | - [send_msg](#send_msg) 38 | - [handle_response](#handle_response) 39 | - [make_request](#make_request) 40 | - [Kademlia interface creation](#kademlia-interface-creation) 41 | - [Protocol::new](#protocolnew) 42 | - [rt_forwarder](#rt_forwarder) 43 | - [request_handler](#request_handler) 44 | - [craft_res](#craft_res) 45 | - [reply](#reply) 46 | - [Kademlia API](#kademlia-api) 47 | - [nodes_lookup](#nodes_lookup) 48 | - [value_lookup](#value_lookup) 49 | - [put](#put) 50 | - [get](#get) 51 | - [State dumping](#state-dumping) 52 | - [Implemented features](#implemented-features) 53 | - [Missing features](#missing-features) 54 | - [Enhancements](#enhancements) 55 | - [References](#references) 56 | - [Thanks](#thanks) 57 | 58 | ## Lib structure 59 | 60 | ``` 61 | src/ 62 | key.res ---> Implementation of the 256bits unique ID 63 | node.rs ---> Node struct definition 64 | network.rs ---> Network module used to issue RPCs 65 | routing.rs ---> Routing Table implementation using vectors 66 | protocol.rs ---> Main library API 67 | utils.rs ---> General utilities functions 68 | main.rs ---> Example program 69 | lib.rs ---> Main lib file 70 | ``` 71 | 72 | ## Usage 73 | 74 | ### Interface creation 75 | 76 | In order to join the network you must create an interface with the `Protocol::new` method: 77 | 78 | ```rust 79 | // BRAND NEW NETWORK 80 | // if you want you can explicitely create a node first 81 | let root = Node::new(utils::get_local_ip().unwrap(), 8080); 82 | 83 | // it needs an IP, a PORT and an Option (bootstrap node) 84 | let root_interface = Protocol::new(root.ip.clone(), root.port.clone(), None); 85 | ``` 86 | 87 | If you want to join a network and you already know a peer you can provide it as a `bootstrap` node: 88 | 89 | ```rust 90 | // this is the contact we already know 91 | let root = Node::new(utils::get_local_ip().unwrap(), 8080); 92 | 93 | let our_node = Node::new(utils::get_local_ip().unwrap(), 8081); 94 | let our_interface = Protocol::new(our_node.ip, our_node.port, Some(root.clone()))); 95 | ``` 96 | 97 | ### Main operations 98 | 99 | These are the main operations, there are more methods you can use but these are the ones you probably need (see [Docs](##Documentation) for more). 100 | 101 | #### PUT 102 | 103 | Store a `` pair in the network: 104 | 105 | ```rust 106 | // interface is already defined 107 | interface.put("some_key", "some_value"); 108 | ``` 109 | 110 | #### GET 111 | 112 | Retreive a value from the network given its key: 113 | 114 | ```rust 115 | // interface is already defined 116 | let value = interface.get("some_key"); // some_value 117 | ``` 118 | 119 | ## Example program 120 | 121 | I've written an example program to test the lib out. In order to run it issue the following command: 122 | 123 | ``` 124 | cargo run 125 | ``` 126 | 127 | It will spin up 10 nodes and it will test the `PUT` and the `GET` method. 128 | 129 | If you want to run tests, issue: 130 | 131 | ``` 132 | cargo test 133 | ``` 134 | 135 | ## Documentation 136 | 137 | Very brief and not detailed explaination of the library. I left some comments in the code to help people understand it better. If this project will be useful for some people I will expand this section. 138 | 139 | ### Kademlia node 140 | 141 | A node is a struct containing an IP, a PORT and a unique ID of type `Key` (see `key.rs`). 142 | 143 | The `node.rs` module exposes the following methods: 144 | 145 | #### Node::new 146 | 147 | Creates a node on a given address and port 148 | 149 | ```rust 150 | let node = Node::new("192.168.1.10", 8080); 151 | ``` 152 | 153 | We can also use this `utils.rs` method to automatically grab the local address: 154 | 155 | ```rust 156 | use kademlia_dht::utils; 157 | 158 | let node = Node::new(utils::get_local_ip().unwrap(), 8080); 159 | ``` 160 | 161 | #### get_info 162 | 163 | Returns a string containing the IP, PORT and ID of the given node: 164 | 165 | ```rust 166 | let node = Node::new(utils::get_local_ip().unwrap(), 8080); 167 | 168 | printl!("node: {}", node.get_info()); // 192.168.1.10:8080: 169 | ``` 170 | 171 | #### get_addr 172 | 173 | Returns a string containing the IP and the PORT of the given node. See [get_info](####get_info) for similar behavior. 174 | 175 | ### 256bits Key and Distance 176 | 177 | Both a `Key` and a `Distance` are struct wrappers around a `[u8]` slice of `KEY_LEN` length (32 bytes, 256bits). 178 | 179 | The `key.rs` module provides methods to create a 256bits `unique ID` and to calculate the distance (**XOR**) between two IDs. 180 | 181 | #### Key::new 182 | 183 | ```rust 184 | let key = Key::new("some string".to_string()); 185 | ``` 186 | 187 | #### Distance::new 188 | 189 | Caluclates the distance between two `Key`s. 190 | 191 | ```rust 192 | // for example, to calculate the distance between 2 nodes 193 | 194 | let node0 = Node::new(utils::get_local_ip().unwrap(), 1335); 195 | let node1 = Node::new(utils::get_local_ip().unwrap(), 1336); 196 | 197 | let dist = Distance::new(&node0.id, &node1.id); // as we know, the id field is of type Key 198 | ``` 199 | 200 | ### Routing Table 201 | 202 | The routing table is a struct containing a `node` field, representing the current node instance, a `kbuckets` field which is a `Vec` of `KBucket` (a struct containing a `Vec` of nodes and a size field) and a _crossbeam_channel_ `sender` and `receiver`(external crate used to communicate with the protocol module). 203 | 204 | The routing table communicates with the `protocol.rs` module for some actions such as _pinging_ nodes that must be checked. The following struct (coming from `utils.rs`) is used in the _crossbeam_channel_: 205 | 206 | ```rust 207 | pub enum ChannelPayload { 208 | Request((network::Request, Node)), 209 | Response(network::Response), 210 | NoData, 211 | } 212 | ``` 213 | 214 | Through the channel we can see a `Request`, a `Response` or `NoData` if the contacted peer doesn't reply to the messages coming from the routing table. 215 | 216 | For more information about `Request` and `Response` see the [Network module](###Network). 217 | 218 | #### Routing::new 219 | 220 | Creates a new routing table: 221 | 222 | ```rust 223 | pub fn new( 224 | node: Node, // current node 225 | bootstrap: Option, // bootstrap node 226 | sender: crossbeam_channel::Sender, // sender of type ChannelPayload 227 | receiver: crossbeam_channel::Receiver, // receiver of type ChannelPayload 228 | ) -> Self 229 | ``` 230 | 231 | #### get_lookup_bucket_index 232 | 233 | Computes the corresponding bucket index for a given node ID with bitwise operations: 234 | 235 | ```rust 236 | fn get_lookup_bucket_index(&self, key: &Key) -> usize 237 | ``` 238 | 239 | #### contact_via_rpc 240 | 241 | Method used to communicate internally with the `protocol.rs` module. Used to send `Ping`s to other nodes: 242 | 243 | ```rust 244 | fn contact_via_rpc(&self, dst: Node) -> bool 245 | ``` 246 | 247 | Here we use the _crossbream_channel_. 248 | 249 | #### update 250 | 251 | Inserts a given node into the routing table. If there's space for it the node gets pushed to the vector, otherwise the necessary checks are performed on the other nodes (see official paper for more details). 252 | 253 | ```rust 254 | pub fn update(&mut self, node: Node) 255 | ``` 256 | 257 | #### remove 258 | 259 | Removes a given node from the routing table: 260 | 261 | ```rust 262 | pub fn remove(&mut self, node: &Node) 263 | ``` 264 | 265 | #### get_closest_nodes 266 | 267 | In this method the `NodeAndDistance` struct is used, which is a tuple of a `Node` and a `Distance`. 268 | 269 | Returns a Vector of `NodeAndDistance` for a given `Key` target: 270 | 271 | ```rust 272 | pub fn get_closest_nodes(&self, key: &Key, count: usize) -> Vec 273 | ``` 274 | 275 | ### Network 276 | 277 | The `network.rs` module provides methods to communicate to other network nodes. Here we issue `RPCs` (Remote Procedure Calls) through the `Rpc` struct. 278 | 279 | The `Rpc` contains a _socket_ field which is an `Arc` to a `std::net::UdpSocket`, a _pending_ field which is an `Arc` `Mutex` around a `HashMap` of `Key`s and `mpsc::Sender>` and a _node_ field representing the current node. 280 | 281 | ```rust 282 | pub struct Rpc { 283 | pub socket: Arc, 284 | pub pending: Arc>>>>, 285 | pub node: Node, 286 | } 287 | ``` 288 | 289 | #### Request 290 | 291 | Enum around Kademlia RPCs. 292 | 293 | ```rust 294 | pub enum Request { 295 | Ping, 296 | Store(String, String), 297 | FindNode(Key), 298 | FindValue(String), 299 | } 300 | ``` 301 | 302 | #### Response 303 | 304 | ```rust 305 | pub enum Response { 306 | Ping, 307 | FindNode(Vec), 308 | FindValue(FindValueResult), 309 | } 310 | ``` 311 | 312 | Where `FindValueResult` comes from `routing.rs` and it wraps either a vector of `NodeAndDistance` or the `String` value that we had looked for. 313 | 314 | #### Message 315 | 316 | ```rust 317 | pub enum Message { 318 | Abort, 319 | Request(Request), 320 | Response(Response), 321 | } 322 | ``` 323 | 324 | #### RpcMessage 325 | 326 | This is what gets sent to other network nodes. 327 | 328 | ```rust 329 | pub struct RpcMessage { 330 | pub token: Key, // token of the message, crafted from source addr and timestamp 331 | pub src: String, 332 | pub dst: String, 333 | pub msg: Message, 334 | } 335 | ``` 336 | 337 | #### Rpc::new 338 | 339 | Creates a new RPC around a node without starting communications: 340 | 341 | ```rust 342 | pub fn new(node: Node) -> Self 343 | ``` 344 | 345 | #### Rpc::open 346 | 347 | Starts listening and sending modes: 348 | 349 | ```rust 350 | pub fn open(rpc: Rpc, sender: mpsc::Sender) { 351 | ``` 352 | 353 | Where `ReqWrapper` is a wrapper around the `Request` enum, used to keep track of metadata about the request (who sent it): 354 | 355 | ```rust 356 | pub struct ReqWrapper { 357 | pub token: Key, 358 | pub src: String, 359 | pub payload: Request, 360 | } 361 | ``` 362 | 363 | In this method, as soon as we receive a request we send that through the channel to the `protocol.rs` module, which handles it. 364 | 365 | #### send_msg 366 | 367 | Forwards a `RpcMessage` to another node using the `UdpSocket`: 368 | 369 | ```rust 370 | pub fn send_msg(&self, msg: &RpcMessage) 371 | ``` 372 | 373 | #### handle_response 374 | 375 | Method used to handle incoming responses from other nodes: 376 | 377 | ```rust 378 | pub fn handle_response(self, token: Key, res: Response) 379 | ``` 380 | 381 | Here we keep track of the `pending` HashMap. 382 | 383 | #### make_request 384 | 385 | Makes a `Request` to a `dst` node that is then forwared to the `protocol.rs` module, also waits for the corresponding `Response` from the contacted node. It also handles the `pending` HashMap 386 | 387 | ```rust 388 | pub fn make_request(&self, req: Request, dst: Node) -> mpsc::Receiver> 389 | ``` 390 | 391 | ### Kademlia interface creation 392 | 393 | The interface has the following structure: 394 | 395 | ```rust 396 | pub struct Protocol { 397 | pub routes: Arc>, 398 | pub store: Arc>>, 399 | pub rpc: Arc, 400 | pub node: Node, 401 | } 402 | ``` 403 | 404 | It includes the routing table, the store (HashMap used to store `` pairs), the rpc coming from the network module and the current active node. 405 | 406 | The `protocol.rs` module exposes the following methods: 407 | 408 | #### Protocol::new 409 | 410 | ```rust 411 | pub fn new(ip: String, port: u16, bootstrap: Option) -> Self 412 | ``` 413 | 414 | Creates an interface to use Kademlia on a given address and port. A `bootstrap` node is a node that we already know in the network. 415 | 416 | **With bootstrap node**: 417 | 418 | ```rust 419 | // some already existing node 420 | let root = Node::new("192.168.1.10", 8080); 421 | 422 | // cloning the node it's not mandatory 423 | let root_interface = Protocol::new("192.168.1.10".to_string(), 8081, Some(root.clone())); 424 | ``` 425 | 426 | **Without bootstrap node**: 427 | 428 | ```rust 429 | let interface = Protocol::new("192.168.1.10", 8080, None); 430 | ``` 431 | 432 | In this method we also establish communications with the `routing.rs` module and the `network.rs` one by using channels, after of course creating them. 433 | 434 | #### rt_forwarder 435 | 436 | Used internally to forward requests issued by the Routing table: 437 | 438 | ```rust 439 | fn rt_forwarder( 440 | self, 441 | sender: crossbeam_channel::Sender, 442 | receiver: crossbeam_channel::Receiver, 443 | ) { 444 | ``` 445 | 446 | #### request_handler 447 | 448 | Used to handle incoming requests thorugh the `mpsc` channel. Here we send (see [reply](####reply)) responses to the requests. 449 | 450 | ```rust 451 | fn requests_handler(self, receiver: mpsc::Receiver) { 452 | ``` 453 | 454 | #### craft_res 455 | 456 | Simply crafts responses for requests and executes `RPCs` coming from those requests (this means that we mutate the routing table and the store). 457 | 458 | ```rust 459 | fn craft_res(&self, req: network::ReqWrapper) -> (network::Response, network::ReqWrapper) { 460 | ``` 461 | 462 | #### reply 463 | 464 | Used to reply to requests. Calls `send_msg`. 465 | 466 | ```rust 467 | fn reply(&self, packet_details: (network::Response, network::ReqWrapper)) { 468 | ``` 469 | 470 | #### Kademlia API 471 | 472 | Here there are the implementations for the needed API calls: 473 | 474 | ```rust 475 | pub fn ping(&self, dst: Node) -> bool // pings a node, returns true in case of response 476 | 477 | pub fn store(&self, dst: Node, key: String, val: String) -> bool // rpc to store a pair on a given destination. Returns true in case of response 478 | 479 | 480 | pub fn find_node( 481 | &self, 482 | dst: Node, 483 | id: super::key::Key, 484 | ) -> Option> // finds a node given the current node id. Returns a NodeAndDistance struct for that node or None in case it doesnt get a response 485 | 486 | 487 | pub fn find_value(&self, dst: Node, k: String) -> Option // finds a given value using the provided key on a given node. Returns a FindValueResult or None in case it doesnt get a response 488 | ``` 489 | 490 | #### nodes_lookup 491 | 492 | Method used to lookup nodes given a starting ID. 493 | 494 | ```rust 495 | pub fn nodes_lookup(&self, id: &super::key::Key) -> Vec { 496 | ``` 497 | 498 | #### value_lookup 499 | 500 | Method used to lookup a value given a `String` key: 501 | 502 | ```rust 503 | pub fn value_lookup(&self, k: String) -> (Option, Vec) { 504 | ``` 505 | 506 | #### put 507 | 508 | Method used to `put` a `` pair into the network. It calls `nodes_lookup` and `store`. 509 | 510 | ```rust 511 | pub fn put(&self, k: String, v: String) 512 | ``` 513 | 514 | #### get 515 | 516 | Method used to extract a value from the network given a key. It calls `value_lookup` but also `store`. 517 | 518 | ```rust 519 | pub fn get(&self, k: String) -> Option 520 | ``` 521 | 522 | ## State dumping 523 | 524 | There are two `utils.rs` methods used to dump the internal state of a Kademlia node: 525 | 526 | ```rust 527 | pub fn dump_interface_state(interface: &Protocol, path: &str) 528 | ``` 529 | 530 | Dumps the `Protocol` object to a given file path (**must be** `dumps/.json`, where you choose `name`). It dumps it as `json` and as `plantuml`. 531 | 532 | Here's an example of the rendered dump using PlantUML: 533 | 534 | ![example](./images/dump.png) 535 | 536 | ```rust 537 | pub fn dump_node_and_distance( 538 | entries: &Vec, 539 | target: &super::key::Key, 540 | path: &str, 541 | ) { 542 | ``` 543 | 544 | Dumps a vector of `NodeAndDistance`s in `json` format. Example: 545 | 546 | ```json 547 | { 548 | "found": [ 549 | { 550 | "distance": "00000000000000000000000000000000", 551 | "node": { 552 | "id": "9278733FBB7F4C6914839C98A54912F4F18B3F15EAED15178663AA5FC63", 553 | "ip": "192.168.1.10", 554 | "port": 1339 555 | } 556 | }, 557 | { 558 | "distance": "3D0C24670ACCA14C1DEE576D7AF2D85486F125E4E0BFD664CCDABA9E532ED2", 559 | "node": { 560 | "id": "342BA354F17B558A8CA66EA4F0A6497BC9E99615BE11735CBD2DCA4F6D2B1", 561 | "ip": "192.168.1.10", 562 | "port": 1338 563 | } 564 | }, 565 | { 566 | "distance": "B8F339EA9FB5DECF23F9C7D754476AD9C6698AC5787281EF371456C766F96C88", 567 | "node": { 568 | "id": "B1F14199A0EA18325688FEE9DCD3E48E9269276892C2F3E66135EA15C5C90EB", 569 | "ip": "192.168.1.10", 570 | "port": 1337 571 | } 572 | } 573 | ], 574 | "target": "9278733FBB7F4C6914839C98A54912F4F18B3F15EAED15178663AA5FC63" 575 | } 576 | ``` 577 | 578 | ## Implemented features 579 | 580 | Features specified in the paper that are implemented in this lib 581 | 582 | - [x] Keys 583 | - [x] XOR Distance between Keys 584 | - [x] KBuckets 585 | 586 | - represented as a `Vec` of `Vec`s. A max of 256 kbuckets is set, each of them containing up to 20 elements 587 | 588 | - [x] PING 589 | - [x] STORE 590 | - [x] FIND_NODE 591 | - [x] FIND_VALUE 592 | - [x] Node lookup 593 | - [x] Republishing of `` pairs every hour 594 | 595 | - [ ] technically, the original publisher should republish ever 24 hours 596 | 597 | - [x] ability to dump a node internal state to `JSON` and `plantuml` 598 | - [x] ability to dump distances between nodes to `JSON` 599 | 600 | ## Missing features 601 | 602 | - [ ] expiration date on `` pairs 603 | 604 | - this isn't wanted when kademlia is used in a decentralized storage system 605 | 606 | - [ ] replicate closest `` pairs when a node joins the network 607 | 608 | - [ ] if no lookup has been performed for an hour in a `kbucket`, that bucket must be refreshed 609 | 610 | ## Enhancements 611 | 612 | - [ ] better nodes lookup algorithm, as described in the paper 613 | 614 | ## References 615 | 616 | - Kademlia: A Peer-to-peer Information System 617 | Based on the XOR Metric by Petar Maymounkov and David Mazières [PDF](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.18.6160&rep=rep1&type=pdf) 618 | 619 | - Implementation of the Kademlia Distributed Hash Table by Bruno Spori [PDF](https://pub.tik.ee.ethz.ch/students/2006-So/SA-2006-19.pdf) 620 | 621 | - Kademlia: A Design Specification by XLattice project [PDF](http://xlattice.sourceforge.net/components/protocol/kademlia/specs.pdf) 622 | 623 | - TinyTorrent: Implementing a Kademlia Based DHT 624 | for File Sharing by Sierra Kaplan-Nelson, Jestin Ma, Jake Rachleff [PDF](https://www.scs.stanford.edu/17au-cs244b/labs/projects/kaplan-nelson_ma_rachleff.pdf) 625 | 626 | ## Thanks 627 | 628 | Thanks for taking the time to check out my library. If you found this good enough to be on `crates.io` please let me know! 629 | 630 | I will also make a small article about Kademlia in general. Check out my [blog](https://f0lg0.vercel.app/blog)! 631 | --------------------------------------------------------------------------------