dht fixes
This commit is contained in:
		| @@ -31,6 +31,19 @@ fn get_string(text: &str) -> Option<String> { | ||||
|     Some(text.to_owned()) | ||||
| } | ||||
|  | ||||
| fn get_data(text: &str) -> Option<Vec<u8>> { | ||||
|     if text.starts_with("#") { | ||||
|         hex::decode(&text[1..]).ok() | ||||
|     } else if text.starts_with("\"") || text.starts_with("'") { | ||||
|         json::parse(text) | ||||
|             .ok()? | ||||
|             .as_str() | ||||
|             .map(|x| x.to_owned().as_bytes().to_vec()) | ||||
|     } else { | ||||
|         Some(text.as_bytes().to_vec()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn get_subkeys(text: &str) -> Option<ValueSubkeyRangeSet> { | ||||
|     if let Some(n) = get_number(text) { | ||||
|         Some(ValueSubkeyRangeSet::single(n.try_into().ok()?)) | ||||
| @@ -88,44 +101,50 @@ fn get_route_id( | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn get_safety_selection(text: &str, routing_table: RoutingTable) -> Option<SafetySelection> { | ||||
|     let rss = routing_table.route_spec_store(); | ||||
|     let default_route_hop_count = | ||||
|         routing_table.with_config(|c| c.network.rpc.default_route_hop_count as usize); | ||||
| fn get_dht_schema(text: &str) -> Option<DHTSchema> { | ||||
|     deserialize_json::<DHTSchema>(text).ok() | ||||
| } | ||||
|  | ||||
|     if text.len() != 0 && &text[0..1] == "-" { | ||||
|         // Unsafe | ||||
|         let text = &text[1..]; | ||||
|         let seq = get_sequencing(text).unwrap_or_default(); | ||||
|         Some(SafetySelection::Unsafe(seq)) | ||||
|     } else { | ||||
|         // Safe | ||||
|         let mut preferred_route = None; | ||||
|         let mut hop_count = default_route_hop_count; | ||||
|         let mut stability = Stability::default(); | ||||
|         let mut sequencing = Sequencing::default(); | ||||
|         for x in text.split(",") { | ||||
|             let x = x.trim(); | ||||
|             if let Some(pr) = get_route_id(rss.clone(), true, false)(x) { | ||||
|                 preferred_route = Some(pr) | ||||
|             } | ||||
|             if let Some(n) = get_number(x) { | ||||
|                 hop_count = n; | ||||
|             } | ||||
|             if let Some(s) = get_stability(x) { | ||||
|                 stability = s; | ||||
|             } | ||||
|             if let Some(s) = get_sequencing(x) { | ||||
|                 sequencing = s; | ||||
| fn get_safety_selection(routing_table: RoutingTable) -> impl Fn(&str) -> Option<SafetySelection> { | ||||
|     move |text| { | ||||
|         let rss = routing_table.route_spec_store(); | ||||
|         let default_route_hop_count = | ||||
|             routing_table.with_config(|c| c.network.rpc.default_route_hop_count as usize); | ||||
|  | ||||
|         if text.len() != 0 && &text[0..1] == "-" { | ||||
|             // Unsafe | ||||
|             let text = &text[1..]; | ||||
|             let seq = get_sequencing(text).unwrap_or_default(); | ||||
|             Some(SafetySelection::Unsafe(seq)) | ||||
|         } else { | ||||
|             // Safe | ||||
|             let mut preferred_route = None; | ||||
|             let mut hop_count = default_route_hop_count; | ||||
|             let mut stability = Stability::default(); | ||||
|             let mut sequencing = Sequencing::default(); | ||||
|             for x in text.split(",") { | ||||
|                 let x = x.trim(); | ||||
|                 if let Some(pr) = get_route_id(rss.clone(), true, false)(x) { | ||||
|                     preferred_route = Some(pr) | ||||
|                 } | ||||
|                 if let Some(n) = get_number(x) { | ||||
|                     hop_count = n; | ||||
|                 } | ||||
|                 if let Some(s) = get_stability(x) { | ||||
|                     stability = s; | ||||
|                 } | ||||
|                 if let Some(s) = get_sequencing(x) { | ||||
|                     sequencing = s; | ||||
|                 } | ||||
|             } | ||||
|             let ss = SafetySpec { | ||||
|                 preferred_route, | ||||
|                 hop_count, | ||||
|                 stability, | ||||
|                 sequencing, | ||||
|             }; | ||||
|             Some(SafetySelection::Safe(ss)) | ||||
|         } | ||||
|         let ss = SafetySpec { | ||||
|             preferred_route, | ||||
|             hop_count, | ||||
|             stability, | ||||
|             sequencing, | ||||
|         }; | ||||
|         Some(SafetySelection::Safe(ss)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -150,7 +169,7 @@ fn get_destination(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<D | ||||
|     move |text| { | ||||
|         // Safety selection | ||||
|         let (text, ss) = if let Some((first, second)) = text.split_once('+') { | ||||
|             let ss = get_safety_selection(second, routing_table.clone())?; | ||||
|             let ss = get_safety_selection(routing_table.clone())(second)?; | ||||
|             (first, Some(ss)) | ||||
|         } else { | ||||
|             (text, None) | ||||
| @@ -234,6 +253,9 @@ fn get_typed_key(text: &str) -> Option<TypedKey> { | ||||
| fn get_public_key(text: &str) -> Option<PublicKey> { | ||||
|     PublicKey::from_str(text).ok() | ||||
| } | ||||
| fn get_keypair(text: &str) -> Option<KeyPair> { | ||||
|     KeyPair::from_str(text).ok() | ||||
| } | ||||
|  | ||||
| fn get_crypto_system_version(crypto: Crypto) -> impl FnOnce(&str) -> Option<CryptoSystemVersion> { | ||||
|     move |text| { | ||||
| @@ -249,7 +271,7 @@ fn get_dht_key( | ||||
|     move |text| { | ||||
|         // Safety selection | ||||
|         let (text, ss) = if let Some((first, second)) = text.split_once('+') { | ||||
|             let ss = get_safety_selection(second, routing_table.clone())?; | ||||
|             let ss = get_safety_selection(routing_table.clone())(second)?; | ||||
|             (first, Some(ss)) | ||||
|         } else { | ||||
|             (text, None) | ||||
| @@ -389,25 +411,32 @@ fn get_debug_argument_at<T, G: FnOnce(&str) -> Option<T>>( | ||||
|     Ok(val) | ||||
| } | ||||
|  | ||||
| fn print_data_truncated(data: &[u8]) -> String { | ||||
| pub fn print_data(data: &[u8], truncate_len: Option<usize>) -> String { | ||||
|     // check is message body is ascii printable | ||||
|     let mut printable = true; | ||||
|     for c in data { | ||||
|         if *c < 32 || *c > 126 { | ||||
|             printable = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let (data, truncated) = if data.len() > 64 { | ||||
|     let (data, truncated) = if truncate_len.is_some() && data.len() > truncate_len.unwrap() { | ||||
|         (&data[0..64], true) | ||||
|     } else { | ||||
|         (&data[..], false) | ||||
|     }; | ||||
|  | ||||
|     let strdata = if printable { | ||||
|         format!("\"{}\"", String::from_utf8_lossy(&data).to_string()) | ||||
|         format!("{}", String::from_utf8_lossy(&data).to_string()) | ||||
|     } else { | ||||
|         hex::encode(data) | ||||
|         let sw = shell_words::quote(&String::from_utf8_lossy(&data).to_string()).to_string(); | ||||
|         let h = hex::encode(data); | ||||
|         if h.len() < sw.len() { | ||||
|             h | ||||
|         } else { | ||||
|             sw | ||||
|         } | ||||
|     }; | ||||
|     if truncated { | ||||
|         format!("{}...", strdata) | ||||
| @@ -416,14 +445,6 @@ fn print_data_truncated(data: &[u8]) -> String { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn print_value_data(value_data: ValueData) -> String { | ||||
|     format!( | ||||
|         "ValueData {{\n  seq: {},\n  writer: {},\n  data: {}\n}}\n", | ||||
|         value_data.seq(), | ||||
|         value_data.writer(), | ||||
|         print_data_truncated(value_data.data()) | ||||
|     ) | ||||
| } | ||||
| impl VeilidAPI { | ||||
|     async fn debug_buckets(&self, args: String) -> VeilidAPIResult<String> { | ||||
|         let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect(); | ||||
| @@ -1010,6 +1031,62 @@ impl VeilidAPI { | ||||
|         }; | ||||
|         return Ok(out); | ||||
|     } | ||||
|  | ||||
|     async fn debug_record_create(&self, args: Vec<String>) -> VeilidAPIResult<String> { | ||||
|         let netman = self.network_manager()?; | ||||
|         let routing_table = netman.routing_table(); | ||||
|         let crypto = self.crypto()?; | ||||
|  | ||||
|         let csv = get_debug_argument_at( | ||||
|             &args, | ||||
|             1, | ||||
|             "debug_record_create", | ||||
|             "kind", | ||||
|             get_crypto_system_version(crypto.clone()), | ||||
|         ) | ||||
|         .unwrap_or_else(|_| crypto.best()); | ||||
|         let schema = get_debug_argument_at( | ||||
|             &args, | ||||
|             2, | ||||
|             "debug_record_create", | ||||
|             "dht_schema", | ||||
|             get_dht_schema, | ||||
|         ) | ||||
|         .unwrap_or_else(|_| DHTSchema::dflt(1)); | ||||
|         let ss = get_debug_argument_at( | ||||
|             &args, | ||||
|             3, | ||||
|             "debug_record_create", | ||||
|             "safety_selection", | ||||
|             get_safety_selection(routing_table), | ||||
|         ) | ||||
|         .ok(); | ||||
|  | ||||
|         // Get routing context with optional privacy | ||||
|         let rc = self.routing_context(); | ||||
|         let rc = if let Some(ss) = ss { | ||||
|             let rcp = match rc.with_custom_privacy(ss) { | ||||
|                 Err(e) => return Ok(format!("Can't use safety selection: {}", e)), | ||||
|                 Ok(v) => v, | ||||
|             }; | ||||
|             rcp | ||||
|         } else { | ||||
|             rc | ||||
|         }; | ||||
|  | ||||
|         // Do a record get | ||||
|         let record = match rc.create_dht_record(csv.kind(), schema).await { | ||||
|             Err(e) => return Ok(format!("Can't open DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         match rc.close_dht_record(*record.key()).await { | ||||
|             Err(e) => return Ok(format!("Can't close DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         debug!("DHT Record Created:\n{:#?}", record); | ||||
|         return Ok(format!("{:?}", record)); | ||||
|     } | ||||
|  | ||||
|     async fn debug_record_get(&self, args: Vec<String>) -> VeilidAPIResult<String> { | ||||
|         let netman = self.network_manager()?; | ||||
|         let routing_table = netman.routing_table(); | ||||
| @@ -1080,7 +1157,66 @@ impl VeilidAPI { | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         let out = if let Some(value) = value { | ||||
|             print_value_data(value) | ||||
|             format!("{:?}", value) | ||||
|         } else { | ||||
|             "No value data returned".to_owned() | ||||
|         }; | ||||
|         match rc.close_dht_record(key).await { | ||||
|             Err(e) => return Ok(format!("Can't close DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         return Ok(out); | ||||
|     } | ||||
|  | ||||
|     async fn debug_record_set(&self, args: Vec<String>) -> VeilidAPIResult<String> { | ||||
|         let netman = self.network_manager()?; | ||||
|         let routing_table = netman.routing_table(); | ||||
|  | ||||
|         let (key, ss) = get_debug_argument_at( | ||||
|             &args, | ||||
|             1, | ||||
|             "debug_record_set", | ||||
|             "key", | ||||
|             get_dht_key(routing_table), | ||||
|         )?; | ||||
|         let subkey = get_debug_argument_at(&args, 2, "debug_record_set", "subkey", get_number)?; | ||||
|         let writer = get_debug_argument_at(&args, 3, "debug_record_set", "writer", get_keypair)?; | ||||
|         let data = get_debug_argument_at(&args, 4, "debug_record_set", "data", get_data)?; | ||||
|  | ||||
|         // Get routing context with optional privacy | ||||
|         let rc = self.routing_context(); | ||||
|         let rc = if let Some(ss) = ss { | ||||
|             let rcp = match rc.with_custom_privacy(ss) { | ||||
|                 Err(e) => return Ok(format!("Can't use safety selection: {}", e)), | ||||
|                 Ok(v) => v, | ||||
|             }; | ||||
|             rcp | ||||
|         } else { | ||||
|             rc | ||||
|         }; | ||||
|  | ||||
|         // Do a record get | ||||
|         let _record = match rc.open_dht_record(key, Some(writer)).await { | ||||
|             Err(e) => return Ok(format!("Can't open DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         let value = match rc.set_dht_value(key, subkey as ValueSubkey, data).await { | ||||
|             Err(e) => { | ||||
|                 match rc.close_dht_record(key).await { | ||||
|                     Err(e) => { | ||||
|                         return Ok(format!( | ||||
|                             "Can't set DHT value and can't close DHT record: {}", | ||||
|                             e | ||||
|                         )) | ||||
|                     } | ||||
|                     Ok(v) => v, | ||||
|                 }; | ||||
|                 return Ok(format!("Can't set DHT value: {}", e)); | ||||
|             } | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|         let out = if let Some(value) = value { | ||||
|             format!("{:?}", value) | ||||
|         } else { | ||||
|             "No value data returned".to_owned() | ||||
|         }; | ||||
| @@ -1104,46 +1240,35 @@ impl VeilidAPI { | ||||
|     } | ||||
|  | ||||
|     async fn debug_record_info(&self, args: Vec<String>) -> VeilidAPIResult<String> { | ||||
|         let netman = self.network_manager()?; | ||||
|         let routing_table = netman.routing_table(); | ||||
|         let storage_manager = self.storage_manager()?; | ||||
|  | ||||
|         let (key, ss) = get_debug_argument_at( | ||||
|             &args, | ||||
|             1, | ||||
|             "debug_record_info", | ||||
|             "key", | ||||
|             get_dht_key(routing_table), | ||||
|         )?; | ||||
|         let key = get_debug_argument_at(&args, 1, "debug_record_info", "key", get_typed_key)?; | ||||
|  | ||||
|         // Get routing context with optional privacy | ||||
|         let rc = self.routing_context(); | ||||
|         let rc = if let Some(ss) = ss { | ||||
|             let rcp = match rc.with_custom_privacy(ss) { | ||||
|                 Err(e) => return Ok(format!("Can't use safety selection: {}", e)), | ||||
|                 Ok(v) => v, | ||||
|             }; | ||||
|             rcp | ||||
|         let subkey = | ||||
|             get_debug_argument_at(&args, 2, "debug_record_info", "subkey", get_number).ok(); | ||||
|  | ||||
|         let out = if let Some(subkey) = subkey { | ||||
|             let li = storage_manager | ||||
|                 .debug_local_record_subkey_info(key, subkey as ValueSubkey) | ||||
|                 .await; | ||||
|             let ri = storage_manager | ||||
|                 .debug_remote_record_subkey_info(key, subkey as ValueSubkey) | ||||
|                 .await; | ||||
|             format!( | ||||
|                 "Local Subkey Info:\n{}\n\nRemote Subkey Info:\n{}\n", | ||||
|                 li, ri | ||||
|             ) | ||||
|         } else { | ||||
|             rc | ||||
|         }; | ||||
|  | ||||
|         // Do a record get | ||||
|         let record = match rc.open_dht_record(key, None).await { | ||||
|             Err(e) => return Ok(format!("Can't open DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|         }; | ||||
|  | ||||
|         let out = format!("{:#?}", record); | ||||
|  | ||||
|         match rc.close_dht_record(key).await { | ||||
|             Err(e) => return Ok(format!("Can't close DHT record: {}", e)), | ||||
|             Ok(v) => v, | ||||
|             let li = storage_manager.debug_local_record_info(key).await; | ||||
|             let ri = storage_manager.debug_remote_record_info(key).await; | ||||
|             format!("Local Info:\n{}\n\nRemote Info:\n{}\n", li, ri) | ||||
|         }; | ||||
|         return Ok(out); | ||||
|     } | ||||
|  | ||||
|     async fn debug_record(&self, args: String) -> VeilidAPIResult<String> { | ||||
|         let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect(); | ||||
|         let args: Vec<String> = | ||||
|             shell_words::split(&args).map_err(|e| VeilidAPIError::parse_error(e, args))?; | ||||
|  | ||||
|         let command = get_debug_argument_at(&args, 0, "debug_record", "command", get_string)?; | ||||
|  | ||||
| @@ -1151,8 +1276,12 @@ impl VeilidAPI { | ||||
|             self.debug_record_list(args).await | ||||
|         } else if command == "purge" { | ||||
|             self.debug_record_purge(args).await | ||||
|         } else if command == "create" { | ||||
|             self.debug_record_create(args).await | ||||
|         } else if command == "get" { | ||||
|             self.debug_record_get(args).await | ||||
|         } else if command == "set" { | ||||
|             self.debug_record_set(args).await | ||||
|         } else if command == "delete" { | ||||
|             self.debug_record_delete(args).await | ||||
|         } else if command == "info" { | ||||
| @@ -1187,9 +1316,11 @@ route allocate [ord|*ord] [rel] [<count>] [in|out] | ||||
|       test <route> | ||||
| record list <local|remote> | ||||
|        purge <local|remote> [bytes] | ||||
|        create <cryptokind> <dhtschema> <safety> | ||||
|        set <key>[+<safety>] <subkey> <writer> <data>  | ||||
|        get <key>[+<safety>] <subkey> [force] | ||||
|        delete <key> | ||||
|        info <key> | ||||
|        info <key> [subkey] | ||||
| -------------------------------------------------------------------- | ||||
| <key> is: VLD0:GsgXCRPrzSK6oBNgxhNpm-rTYFd02R0ySx6j9vbQBG4 | ||||
|     * also <node>, <relay>, <target>, <route> | ||||
| @@ -1205,10 +1336,16 @@ record list <local|remote> | ||||
| <protocoltype> is: udp|tcp|ws|wss | ||||
| <addresstype> is: ipv4|ipv6 | ||||
| <routingdomain> is: public|local | ||||
| <cryptokind> is: VLD0 | ||||
| <dhtschema> is: a json dht schema, default is '{"kind":"DFLT","o_cnt":1}' | ||||
| <subkey> is: a number: 2 | ||||
| <subkeys> is:  | ||||
|     * a number: 2 | ||||
|     * a comma-separated inclusive range list: 1..=3,5..=8 | ||||
| <data> is: | ||||
|     * a single-word string: foobar | ||||
|     * a shell-quoted string: "foo\nbar\n" | ||||
|     * a '#' followed by hex data: #12AB34CD... | ||||
| "# | ||||
|         .to_owned()) | ||||
|     } | ||||
|   | ||||
| @@ -46,6 +46,9 @@ impl DHTRecordDescriptor { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn key(&self) -> &TypedKey { | ||||
|         &self.key | ||||
|     } | ||||
|     pub fn owner(&self) -> &PublicKey { | ||||
|         &self.owner | ||||
|     } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ use super::*; | ||||
|  | ||||
| #[derive( | ||||
|     Clone, | ||||
|     Debug, | ||||
|     Default, | ||||
|     PartialOrd, | ||||
|     PartialEq, | ||||
| @@ -61,3 +60,13 @@ impl ValueData { | ||||
|         mem::size_of::<Self>() + self.data.len() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for ValueData { | ||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt.debug_struct("ValueData") | ||||
|             .field("seq", &self.seq) | ||||
|             .field("data", &print_data(&self.data, None)) | ||||
|             .field("writer", &self.writer) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user