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