use crate::{kbucket::Key, rpc::RequestBody, Enr}; use enr::NodeId; use sha2::digest::generic_array::GenericArray; use smallvec::SmallVec; use tokio::sync::oneshot; /// Information about a query. #[derive(Debug)] pub struct QueryInfo { /// What we are querying and why. pub query_type: QueryType, /// Temporary ENRs used when trying to reach nodes. pub untrusted_enrs: SmallVec<[Enr; 16]>, /// A callback channel for the service that requested the query. pub callback: oneshot::Sender>, /// The number of distances we request for each peer. pub distances_to_request: usize, } /// Additional information about the query. #[derive(Debug, Clone, PartialEq, Eq)] pub enum QueryType { /// The user requested a `FIND_NODE` query to be performed. It should be reported when finished. FindNode(NodeId), } impl QueryInfo { /// Builds an RPC Request, given the QueryInfo pub(crate) fn rpc_request(&self, peer: NodeId) -> Result { let request = match self.query_type { QueryType::FindNode(node_id) => { let distances = findnode_log2distance(node_id, peer, self.distances_to_request) .ok_or("Requested a node find itself")?; RequestBody::FindNode { distances } } }; Ok(request) } } impl crate::query_pool::TargetKey for QueryInfo { fn key(&self) -> Key { match self.query_type { QueryType::FindNode(ref node_id) => { Key::new_raw(*node_id, *GenericArray::from_slice(&node_id.raw())) } } } } /// Calculates the log2 distance for a destination peer given a target and the size (number of /// distances to request). /// /// As the iteration increases, FINDNODE requests adjacent distances from the exact peer distance. /// /// As an example, if the target has a distance of 12 from the remote peer, the sequence of distances that are sent for increasing iterations would be [12, 13, 11, 14, 10, .. ]. fn findnode_log2distance(target: NodeId, peer: NodeId, size: usize) -> Option> { if size > 127 { // invoke and endless loop - coding error panic!("Iterations cannot be greater than 127"); } let dst_key: Key = peer.into(); let distance = dst_key.log2_distance(&target.into())?; let mut result_list = vec![distance]; let mut difference = 1; while result_list.len() < size { if distance + difference <= 256 { result_list.push(distance + difference); } if result_list.len() < size { if let Some(d) = distance.checked_sub(difference) { result_list.push(d); } } difference += 1; } Some(result_list[..size].to_vec()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_log2distance() { let target = NodeId::new(&[0u8; 32]); let mut destination = [0u8; 32]; destination[10] = 1; // gives a log2 distance of 169 let destination = NodeId::new(&destination); let expected_distances = vec![169, 170, 168, 171, 167, 172, 166, 173, 165]; assert_eq!( findnode_log2distance(target, destination, expected_distances.len()).unwrap(), expected_distances ); } #[test] fn test_log2distance_lower() { let target = NodeId::new(&[0u8; 32]); let mut destination = [0u8; 32]; destination[31] = 8; // gives a log2 distance of 5 let destination = NodeId::new(&destination); let expected_distances = vec![4, 5, 3, 6, 2, 7, 1, 8, 0, 9, 10]; assert_eq!( findnode_log2distance(target, destination, expected_distances.len()).unwrap(), expected_distances ); } #[test] fn test_log2distance_upper() { let target = NodeId::new(&[0u8; 32]); let mut destination = [0u8; 32]; destination[0] = 8; // gives a log2 distance of 252 let destination = NodeId::new(&destination); let expected_distances = vec![252, 253, 251, 254, 250, 255, 249, 256, 248, 247, 246]; assert_eq!( findnode_log2distance(target, destination, expected_distances.len()).unwrap(), expected_distances ); } }