This commit is contained in:
John Smith 2023-06-19 22:35:49 -04:00
parent cd9a3414cf
commit f17c2f62cb
12 changed files with 222 additions and 137 deletions

View File

@ -29,6 +29,13 @@ pub enum Destination {
} }
impl Destination { impl Destination {
pub fn target(&self) -> Option<NodeRef> {
match self {
Destination::Direct { target, safety_selection: _ } => Some(target.clone()),
Destination::Relay { relay:_, target, safety_selection: _ } => Some(target.clone()),
Destination::PrivateRoute { private_route:_, safety_selection:_ } => None,
}
}
pub fn direct(target: NodeRef) -> Self { pub fn direct(target: NodeRef) -> Self {
let sequencing = target.sequencing(); let sequencing = target.sequencing();
Self::Direct { Self::Direct {

View File

@ -88,6 +88,7 @@ where
for cn in &ctx.closest_nodes { for cn in &ctx.closest_nodes {
if cn.same_entry(&nn) { if cn.same_entry(&nn) {
dup = true; dup = true;
break;
} }
} }
if !dup { if !dup {
@ -125,6 +126,7 @@ where
// New fanout call candidate found // New fanout call candidate found
next_node = Some(cn.clone()); next_node = Some(cn.clone());
ctx.called_nodes.add(key); ctx.called_nodes.add(key);
break;
} }
} }
} }

View File

@ -395,7 +395,7 @@ impl RPCProcessor {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
/// Determine if a SignedNodeInfo can be placed into the specified routing domain /// Determine if a SignedNodeInfo can be placed into the specified routing domain
fn filter_node_info( fn verify_node_info(
&self, &self,
routing_domain: RoutingDomain, routing_domain: RoutingDomain,
signed_node_info: &SignedNodeInfo, signed_node_info: &SignedNodeInfo,
@ -404,6 +404,37 @@ impl RPCProcessor {
routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info) routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info)
} }
/// Determine if set of peers is closer to key_near than key_far
fn verify_peers_closer(
&self,
vcrypto: CryptoSystemVersion,
key_far: TypedKey,
key_near: TypedKey,
peers: &[PeerInfo],
) -> Result<bool, RPCError> {
let kind = vcrypto.kind();
if key_far.kind != kind || key_near.kind != kind {
return Err(RPCError::internal("keys all need the same cryptosystem"));
}
let mut closer = true;
for peer in peers {
let Some(key_peer) = peer.node_ids().get(kind) else {
return Err(RPCError::invalid_format(
"peers need to have a key with the same cryptosystem",
));
};
let d_near = vcrypto.distance(&key_near.value, &key_peer.value);
let d_far = vcrypto.distance(&key_far.value, &key_peer.value);
if d_far < d_near {
closer = false;
}
}
Ok(closer)
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
/// Search the DHT for a single node closest to a key and add it to the routing table and return the node reference /// Search the DHT for a single node closest to a key and add it to the routing table and return the node reference
@ -1348,7 +1379,7 @@ impl RPCProcessor {
// Ensure the sender peer info is for the actual sender specified in the envelope // Ensure the sender peer info is for the actual sender specified in the envelope
// Sender PeerInfo was specified, update our routing table with it // Sender PeerInfo was specified, update our routing table with it
if !self.filter_node_info(routing_domain, sender_peer_info.signed_node_info()) { if !self.verify_node_info(routing_domain, sender_peer_info.signed_node_info()) {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"sender peerinfo has invalid peer scope", "sender peerinfo has invalid peer scope",
)); ));

View File

@ -29,9 +29,9 @@ impl RPCProcessor {
let app_call_a = match kind { let app_call_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::AppCallA(a) => a, RPCAnswerDetail::AppCallA(a) => a,
_ => return Err(RPCError::invalid_format("not an appcall answer")), _ => return Ok(NetworkResult::invalid_message("not an appcall answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let a_message = app_call_a.destructure(); let a_message = app_call_a.destructure();

View File

@ -46,17 +46,17 @@ impl RPCProcessor {
let find_node_a = match kind { let find_node_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::FindNodeA(a) => a, RPCAnswerDetail::FindNodeA(a) => a,
_ => return Err(RPCError::invalid_format("not a find_node answer")), _ => return Ok(NetworkResult::invalid_message("not a find_node answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
// Verify peers are in the correct peer scope // Verify peers are in the correct peer scope
let peers = find_node_a.destructure(); let peers = find_node_a.destructure();
for peer_info in &peers { for peer_info in &peers {
if !self.filter_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) { if !self.verify_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) {
return Err(RPCError::invalid_format( return Ok(NetworkResult::invalid_message(
"find_node response has invalid peer scope", "find_node response has invalid peer scope",
)); ));
} }

View File

@ -24,32 +24,32 @@ impl RPCProcessor {
last_descriptor: Option<SignedValueDescriptor>, last_descriptor: Option<SignedValueDescriptor>,
) -> Result<NetworkResult<Answer<GetValueAnswer>>, RPCError> { ) -> Result<NetworkResult<Answer<GetValueAnswer>>, RPCError> {
// Ensure destination never has a private route // Ensure destination never has a private route
if matches!( // and get the target noderef so we can validate the response
dest, let Some(target) = dest.target() else {
Destination::PrivateRoute {
private_route: _,
safety_selection: _
}
) {
return Err(RPCError::internal( return Err(RPCError::internal(
"Never send get value requests over private routes", "Never send set value requests over private routes",
)); ));
} };
// Get the target node id
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
let Some(target_node_id) = target.node_ids().get(key.kind) else {
return Err(RPCError::internal("No node id for crypto kind"));
};
// Send the getvalue question
let get_value_q = RPCOperationGetValueQ::new(key, subkey, last_descriptor.is_none()); let get_value_q = RPCOperationGetValueQ::new(key, subkey, last_descriptor.is_none());
let question = RPCQuestion::new( let question = RPCQuestion::new(
network_result_try!(self.get_destination_respond_to(&dest)?), network_result_try!(self.get_destination_respond_to(&dest)?),
RPCQuestionDetail::GetValueQ(get_value_q), RPCQuestionDetail::GetValueQ(get_value_q),
); );
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
// Send the getvalue question
let question_context = QuestionContext::GetValue(ValidateGetValueContext { let question_context = QuestionContext::GetValue(ValidateGetValueContext {
last_descriptor, last_descriptor,
subkey, subkey,
vcrypto, vcrypto: vcrypto.clone(),
}); });
let waitable_reply = network_result_try!( let waitable_reply = network_result_try!(
@ -68,13 +68,29 @@ impl RPCProcessor {
let get_value_a = match kind { let get_value_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::GetValueA(a) => a, RPCAnswerDetail::GetValueA(a) => a,
_ => return Err(RPCError::invalid_format("not a getvalue answer")), _ => return Ok(NetworkResult::invalid_message("not a getvalue answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (value, peers, descriptor) = get_value_a.destructure(); let (value, peers, descriptor) = get_value_a.destructure();
// Validate peers returned are, in fact, closer to the key than the node we sent this to
let valid = match self.verify_peers_closer(vcrypto, target_node_id, key, &peers) {
Ok(v) => v,
Err(e) => {
if matches!(e, RPCError::Internal(_)) {
return Err(e);
}
return Ok(NetworkResult::invalid_message(
"missing cryptosystem in peers node ids",
));
}
};
if !valid {
return Ok(NetworkResult::invalid_message("non-closer peers returned"));
}
Ok(NetworkResult::value(Answer::new( Ok(NetworkResult::value(Answer::new(
latency, latency,
GetValueAnswer { GetValueAnswer {

View File

@ -62,7 +62,7 @@ impl RPCProcessor {
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Make sure hop count makes sense // Make sure hop count makes sense
if next_private_route.hop_count as usize > self.unlocked_inner.max_route_hop_count { if next_private_route.hop_count as usize > self.unlocked_inner.max_route_hop_count {
return Err(RPCError::protocol( return Ok(NetworkResult::invalid_message(
"Private route hop count too high to process", "Private route hop count too high to process",
)); ));
} }
@ -110,9 +110,9 @@ impl RPCProcessor {
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
// xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes? // xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes?
let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind); let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind);
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &node_id_secret) else {
.cached_dh(&remote_sr_pubkey.value, &node_id_secret) return Ok(NetworkResult::invalid_message("dh failed for remote safety route for safety routed operation"));
.map_err(RPCError::protocol)?; };
let body = match vcrypto.decrypt_aead( let body = match vcrypto.decrypt_aead(
routed_operation.data(), routed_operation.data(),
routed_operation.nonce(), routed_operation.nonce(),
@ -183,19 +183,18 @@ impl RPCProcessor {
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
// xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes?
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &secret_key) else {
.cached_dh(&remote_sr_pubkey.value, &secret_key) return Ok(NetworkResult::invalid_message("dh failed for remote safety route for private routed operation"));
.map_err(RPCError::protocol)?; };
let body = vcrypto let Ok(body) = vcrypto
.decrypt_aead( .decrypt_aead(
routed_operation.data(), routed_operation.data(),
routed_operation.nonce(), routed_operation.nonce(),
&dh_secret, &dh_secret,
None, None,
) ) else {
.map_err(RPCError::map_internal( return Ok(NetworkResult::invalid_message("decryption of routed operation failed"));
"decryption of routed operation failed", };
))?;
// Pass message to RPC system // Pass message to RPC system
self.enqueue_private_routed_message( self.enqueue_private_routed_message(
@ -401,37 +400,48 @@ impl RPCProcessor {
SafetyRouteHops::Data(ref route_hop_data) => { SafetyRouteHops::Data(ref route_hop_data) => {
// Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret)
let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind);
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto
.cached_dh(&safety_route.public_key.value, &node_id_secret) .cached_dh(&safety_route.public_key.value, &node_id_secret) else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("dh failed for safety route hop"));
let mut dec_blob_data = vcrypto };
let Ok(mut dec_blob_data) = vcrypto
.decrypt_aead( .decrypt_aead(
&route_hop_data.blob, &route_hop_data.blob,
&route_hop_data.nonce, &route_hop_data.nonce,
&dh_secret, &dh_secret,
None, None,
) )
.map_err(RPCError::protocol)?; else {
return Ok(NetworkResult::invalid_message("failed to decrypt route hop data for safety route hop"));
};
// See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop // See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop
let Some(dec_blob_tag) = dec_blob_data.pop() else { let Some(dec_blob_tag) = dec_blob_data.pop() else {
return Ok(NetworkResult::invalid_message("no bytes in blob")); return Ok(NetworkResult::invalid_message("no bytes in blob"));
}; };
let dec_blob_reader = RPCMessageData::new(dec_blob_data).get_reader()?; let Ok(dec_blob_reader) = RPCMessageData::new(dec_blob_data).get_reader() else {
return Ok(NetworkResult::invalid_message("Failed to decode RPCMessageData from blob"));
};
// Decode the blob appropriately // Decode the blob appropriately
if dec_blob_tag == 1 { if dec_blob_tag == 1 {
// PrivateRoute // PrivateRoute
let private_route = { let private_route = {
let pr_reader = dec_blob_reader let Ok(pr_reader) = dec_blob_reader
.get_root::<veilid_capnp::private_route::Reader>() .get_root::<veilid_capnp::private_route::Reader>() else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("failed to get private route reader for blob"));
decode_private_route(&pr_reader)? };
let Ok(private_route) = decode_private_route(&pr_reader) else {
return Ok(NetworkResult::invalid_message("failed to decode private route"));
};
private_route
}; };
// Validate the private route // Validate the private route
private_route.validate(self.crypto.clone()).map_err(RPCError::protocol)?; if let Err(_) = private_route.validate(self.crypto.clone()) {
return Ok(NetworkResult::invalid_message("failed to validate private route"));
}
// Switching from full safety route to private route first hop // Switching from full safety route to private route first hop
network_result_try!( network_result_try!(
@ -445,14 +455,20 @@ impl RPCProcessor {
} else if dec_blob_tag == 0 { } else if dec_blob_tag == 0 {
// RouteHop // RouteHop
let route_hop = { let route_hop = {
let rh_reader = dec_blob_reader let Ok(rh_reader) = dec_blob_reader
.get_root::<veilid_capnp::route_hop::Reader>() .get_root::<veilid_capnp::route_hop::Reader>() else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("failed to get route hop reader for blob"));
decode_route_hop(&rh_reader)? };
let Ok(route_hop) = decode_route_hop(&rh_reader) else {
return Ok(NetworkResult::invalid_message("failed to decode route hop"));
};
route_hop
}; };
// Validate the route hop // Validate the route hop
route_hop.validate(self.crypto.clone()).map_err(RPCError::protocol)?; if let Err(_) = route_hop.validate(self.crypto.clone()) {
return Ok(NetworkResult::invalid_message("failed to validate route hop"));
}
// Continue the full safety route with another hop // Continue the full safety route with another hop
network_result_try!( network_result_try!(

View File

@ -14,7 +14,6 @@ impl RPCProcessor {
/// Because this leaks information about the identity of the node itself, /// Because this leaks information about the identity of the node itself,
/// replying to this request received over a private route will leak /// replying to this request received over a private route will leak
/// the identity of the node and defeat the private route. /// the identity of the node and defeat the private route.
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn rpc_call_set_value( pub async fn rpc_call_set_value(
self, self,
dest: Destination, dest: Destination,
@ -25,18 +24,22 @@ impl RPCProcessor {
send_descriptor: bool, send_descriptor: bool,
) -> Result<NetworkResult<Answer<SetValueAnswer>>, RPCError> { ) -> Result<NetworkResult<Answer<SetValueAnswer>>, RPCError> {
// Ensure destination never has a private route // Ensure destination never has a private route
if matches!( // and get the target noderef so we can validate the response
dest, let Some(target) = dest.target() else {
Destination::PrivateRoute {
private_route: _,
safety_selection: _
}
) {
return Err(RPCError::internal( return Err(RPCError::internal(
"Never send set value requests over private routes", "Never send set value requests over private routes",
)); ));
} };
// Get the target node id
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
let Some(target_node_id) = target.node_ids().get(key.kind) else {
return Err(RPCError::internal("No node id for crypto kind"));
};
// Send the setvalue question
let set_value_q = RPCOperationSetValueQ::new( let set_value_q = RPCOperationSetValueQ::new(
key, key,
subkey, subkey,
@ -51,17 +54,11 @@ impl RPCProcessor {
network_result_try!(self.get_destination_respond_to(&dest)?), network_result_try!(self.get_destination_respond_to(&dest)?),
RPCQuestionDetail::SetValueQ(set_value_q), RPCQuestionDetail::SetValueQ(set_value_q),
); );
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
// Send the setvalue question
let question_context = QuestionContext::SetValue(ValidateSetValueContext { let question_context = QuestionContext::SetValue(ValidateSetValueContext {
descriptor, descriptor,
subkey, subkey,
vcrypto, vcrypto: vcrypto.clone(),
}); });
let waitable_reply = network_result_try!( let waitable_reply = network_result_try!(
self.question(dest, question, Some(question_context)) self.question(dest, question, Some(question_context))
.await? .await?
@ -78,13 +75,29 @@ impl RPCProcessor {
let set_value_a = match kind { let set_value_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::SetValueA(a) => a, RPCAnswerDetail::SetValueA(a) => a,
_ => return Err(RPCError::invalid_format("not a setvalue answer")), _ => return Ok(NetworkResult::invalid_message("not a setvalue answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (set, value, peers) = set_value_a.destructure(); let (set, value, peers) = set_value_a.destructure();
// Validate peers returned are, in fact, closer to the key than the node we sent this to
let valid = match self.verify_peers_closer(vcrypto, target_node_id, key, &peers) {
Ok(v) => v,
Err(e) => {
if matches!(e, RPCError::Internal(_)) {
return Err(e);
}
return Ok(NetworkResult::invalid_message(
"missing cryptosystem in peers node ids",
));
}
};
if !valid {
return Ok(NetworkResult::invalid_message("non-closer peers returned"));
}
Ok(NetworkResult::value(Answer::new( Ok(NetworkResult::value(Answer::new(
latency, latency,
SetValueAnswer { set, value, peers }, SetValueAnswer { set, value, peers },

View File

@ -119,9 +119,9 @@ impl RPCProcessor {
let status_a = match kind { let status_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::StatusA(a) => a, RPCAnswerDetail::StatusA(a) => a,
_ => return Err(RPCError::invalid_format("not a status answer")), _ => return Ok(NetworkResult::invalid_message("not a status answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (a_node_status, sender_info) = status_a.destructure(); let (a_node_status, sender_info) = status_a.destructure();

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
/// The context of the do_get_value operation /// The context of the outbound_get_value operation
struct DoGetValueContext { struct OutboundGetValueContext {
/// The latest value of the subkey, may be the value passed in /// The latest value of the subkey, may be the value passed in
pub value: Option<SignedValueData>, pub value: Option<SignedValueData>,
/// The consensus count for the value we have received /// The consensus count for the value we have received
@ -42,7 +42,7 @@ impl StorageManager {
} else { } else {
None None
}; };
let context = Arc::new(Mutex::new(DoGetValueContext { let context = Arc::new(Mutex::new(OutboundGetValueContext {
value: last_subkey_result.value, value: last_subkey_result.value,
value_count: 0, value_count: 0,
descriptor: last_subkey_result.descriptor.clone(), descriptor: last_subkey_result.descriptor.clone(),

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
/// The context of the do_get_value operation /// The context of the outbound_set_value operation
struct DoSetValueContext { struct OutboundSetValueContext {
/// The latest value of the subkey, may be the value passed in /// The latest value of the subkey, may be the value passed in
pub value: SignedValueData, pub value: SignedValueData,
/// The consensus count for the value we have received /// The consensus count for the value we have received
@ -37,7 +37,7 @@ impl StorageManager {
// Make do-set-value answer context // Make do-set-value answer context
let schema = descriptor.schema()?; let schema = descriptor.schema()?;
let context = Arc::new(Mutex::new(DoSetValueContext { let context = Arc::new(Mutex::new(OutboundSetValueContext {
value, value,
value_count: 0, value_count: 0,
schema, schema,

View File

@ -1,74 +1,74 @@
# # Routing context veilid tests # Routing context veilid tests
# import veilid import veilid
# import pytest import pytest
# import asyncio import asyncio
# import json import json
# from . import * from . import *
# ################################################################## ##################################################################
# BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' ')) BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' '))
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI): async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False) out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI): async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# out = await rc.open_dht_record(BOGUS_KEY, None) out = await rc.open_dht_record(BOGUS_KEY, None)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# await rc.close_dht_record(BOGUS_KEY) await rc.close_dht_record(BOGUS_KEY)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# await rc.delete_dht_record(BOGUS_KEY) await rc.delete_dht_record(BOGUS_KEY)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI): async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
# await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI): async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# assert await rc.get_dht_value(rec.key, 0, False) == None assert await rc.get_dht_value(rec.key, 0, False) == None
# await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
# await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_set_get_dht_value(api_connection: veilid.VeilidAPI): async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH") vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH")
# assert vd != None assert vd != None
# vd2 = await rc.get_dht_value(rec.key, 0, False) vd2 = await rc.get_dht_value(rec.key, 0, False)
# assert vd2 != None assert vd2 != None
# assert vd == vd2 assert vd == vd2
# await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
# await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)