checkpoint

This commit is contained in:
John Smith 2023-02-22 21:47:00 -05:00
parent 4085af7fc4
commit 8fc38febca
11 changed files with 216 additions and 110 deletions

View File

@ -201,8 +201,18 @@ impl TypedKeySet {
self.remove(*k);
}
}
/// Return preferred typed key of our supported crypto kinds
pub fn best(&self) -> Option<TypedKey> {
self.items.first().copied()
match self.items.first().copied() {
None => None,
Some(k) => {
if !VALID_CRYPTO_KINDS.contains(&k.kind) {
None
} else {
Some(k)
}
}
}
}
pub fn len(&self) -> usize {
self.items.len()
@ -221,6 +231,14 @@ impl TypedKeySet {
}
false
}
pub fn contains_key(&self, key: &PublicKey) -> bool {
for tk in &self.items {
if tk.key == *key {
return true;
}
}
false
}
}
impl core::ops::Deref for TypedKeySet {
@ -264,6 +282,13 @@ impl FromStr for TypedKeySet {
Ok(Self { items })
}
}
impl From<TypedKey> for TypedKeySet {
fn from(x: TypedKey) -> Self {
let mut tks = TypedKeySet::with_capacity(1);
tks.add(x);
tks
}
}
#[derive(
Clone,

View File

@ -145,6 +145,15 @@ impl RoutingTableUnlockedInner {
false
}
pub fn matches_own_node_id_key(&self, node_id_key: &PublicKey) -> bool {
for (ck, v) in &self.node_id_keypairs {
if v.key == *node_id_key {
return true;
}
}
false
}
pub fn calculate_bucket_index(&self, node_id: &TypedKey) -> (CryptoKind, usize) {
let crypto = self.crypto();
let self_node_id = self.node_id_keypairs.get(&node_id.kind).unwrap().key;
@ -587,6 +596,13 @@ impl RoutingTable {
}
}
/// Resolve an existing routing table entry using any crypto kind and return a reference to it
pub fn lookup_any_node_ref(&self, node_id_key: PublicKey) -> Option<NodeRef> {
self.inner
.read()
.lookup_any_node_ref(self.clone(), node_id_key)
}
/// Resolve an existing routing table entry and return a reference to it
pub fn lookup_node_ref(&self, node_id: TypedKey) -> Option<NodeRef> {
self.inner.read().lookup_node_ref(self.clone(), node_id)

View File

@ -1780,7 +1780,7 @@ impl RouteSpecStore {
/// Import a remote private route for compilation
#[instrument(level = "trace", skip(self, blob), ret, err)]
pub fn import_remote_private_route(&self, blob: Vec<u8>) -> EyreResult<TypedKeySet> {
pub fn import_remote_private_route(&self, blob: Vec<u8>) -> EyreResult<TypedKeySet> { xxx continue here, maybe formalize 'private route set' as having its own non-key identifier for both remote and local routes... just a uuid map to typedkeyset?
// decode the pr blob
let private_routes = RouteSpecStore::blob_to_private_routes(blob)?;
@ -2006,7 +2006,7 @@ impl RouteSpecStore {
let inner = &mut *self.inner.lock();
// Check for stub route
if *key == self.unlocked_inner.routing_table.node_id() {
if self.unlocked_inner.routing_table.matches_own_node_id_key(key) {
return None;
}
// Check for local route
@ -2096,7 +2096,7 @@ impl RouteSpecStore {
}
/// Convert binary blob to private route
pub fn blob_to_private_routes(blob: Vec<u8>) -> EyreResult<Vec<PrivateRoute>> {
pub fn blob_to_private_routes(crypto: Crypto, blob: Vec<u8>) -> EyreResult<Vec<PrivateRoute>> {
// Deserialize count
if blob.is_empty() {
@ -2123,7 +2123,7 @@ impl RouteSpecStore {
.get_root::<veilid_capnp::private_route::Reader>()
.map_err(RPCError::internal)
.wrap_err("failed to make reader for private_route")?;
let private_route = decode_private_route(&pr_reader).wrap_err("failed to decode private route")?;
let private_route = decode_private_route(&pr_reader, crypto).wrap_err("failed to decode private route")?;
out.push(private_route);
}
Ok(out)

View File

@ -678,6 +678,17 @@ impl RoutingTableInner {
Some(nr)
}
/// Resolve an existing routing table entry using any crypto kind and return a reference to it
pub fn lookup_any_node_ref(
&self,
outer_self: RoutingTable,
node_id_key: PublicKey,
) -> Option<NodeRef> {
VALID_CRYPTO_KINDS
.iter()
.find_map(|ck| self.lookup_node_ref(outer_self, TypedKey::new(*ck, node_id_key)))
}
/// Resolve an existing routing table entry and return a reference to it
pub fn lookup_node_ref(&self, outer_self: RoutingTable, node_id: TypedKey) -> Option<NodeRef> {
if self.unlocked_inner.matches_own_node_id(&[node_id]) {

View File

@ -92,7 +92,7 @@ impl RPCMessageHeader {
pub fn crypto_kind(&self) -> CryptoKind {
match &self.detail {
RPCMessageHeaderDetail::Direct(d) => d.envelope.get_crypto_kind(),
RPCMessageHeaderDetail::SafetyRouted(s) => todo!(),
RPCMessageHeaderDetail::SafetyRouted(s) => s.remote_safety_route.,
RPCMessageHeaderDetail::PrivateRouted(p) => todo!(),
}
}
@ -1199,7 +1199,7 @@ impl RPCProcessor {
let routing_domain = detail.routing_domain;
// Decode the operation
let sender_node_id = detail.envelope.get_sender_id();
let sender_node_id = TypedKey::new(detail.envelope.get_crypto_kind(), detail.envelope.get_sender_id());
// Decode the RPC message
let operation = {
@ -1208,7 +1208,7 @@ impl RPCProcessor {
.get_root::<veilid_capnp::operation::Reader>()
.map_err(RPCError::protocol)
.map_err(logthru_rpc!())?;
RPCOperation::decode(&op_reader, Some(&sender_node_id))?
RPCOperation::decode(&op_reader, self.crypto.clone())?
};
// Get the sender noderef, incorporating sender's peer info
@ -1217,14 +1217,14 @@ impl RPCProcessor {
// Ensure the sender peer info is for the actual sender specified in the envelope
// Sender PeerInfo was specified, update our routing table with it
if !self.filter_node_info(routing_domain, &sender_peer_info) {
if !self.filter_node_info(routing_domain, &sender_peer_info.signed_node_info) {
return Err(RPCError::invalid_format(
"sender peerinfo has invalid peer scope",
));
}
opt_sender_nr = self.routing_table().register_node_with_peer_info(
routing_domain,
sender_peer_info,
sender_peer_info.clone(),
false,
);
}
@ -1255,7 +1255,7 @@ impl RPCProcessor {
.get_root::<veilid_capnp::operation::Reader>()
.map_err(RPCError::protocol)
.map_err(logthru_rpc!())?;
RPCOperation::decode(&op_reader, None)?
RPCOperation::decode(&op_reader, self.crypto.clone())?
};
// Make the RPC message

View File

@ -89,13 +89,6 @@ impl RPCProcessor {
_ => panic!("not a question"),
};
// Get the crypto kinds the requesting node is capable of
let crypto_kinds = if let Some(sender_nr) = msg.opt_sender_nr {
sender_nr.node_ids().kinds()
} else {
vec![msg.header.crypto_kind()]
};
// add node information for the requesting node to our routing table
let routing_table = self.routing_table();
let Some(own_peer_info) = routing_table.get_own_peer_info(RoutingDomain::PublicInternet) else {
@ -106,12 +99,6 @@ impl RPCProcessor {
// find N nodes closest to the target node in our routing table
let filter = Box::new(
move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
// ensure the returned nodes have at least the crypto kind used to send the findnodeq
if let Some(entry) = opt_entry {
if !entry.with(rti, |_rti, e| e.crypto_kinds().contains(&crypto_kind)) {
return false;
}
}
// Ensure only things that are valid/signed in the PublicInternet domain are returned
rti.filter_has_valid_signed_node_info(
RoutingDomain::PublicInternet,

View File

@ -123,18 +123,18 @@ pub async fn test_store_delete_load(ts: TableStore) {
assert_eq!(db.load(2, b"baz").unwrap(), Some(b"QWERTY".to_vec()));
}
pub async fn test_frozen(ts: TableStore) {
pub async fn test_frozen(vcrypto: CryptoSystemVersion, ts: TableStore) {
trace!("test_frozen");
let _ = ts.delete("test");
let db = ts.open("test", 3).await.expect("should have opened");
let (dht_key, _) = generate_secret();
let (dht_key, _) = vcrypto.generate_keypair();
assert!(db.store_rkyv(0, b"asdf", &dht_key).await.is_ok());
assert_eq!(db.load_rkyv::<TypedKey>(0, b"qwer").unwrap(), None);
assert_eq!(db.load_rkyv::<PublicKey>(0, b"qwer").unwrap(), None);
let d = match db.load_rkyv::<TypedKey>(0, b"asdf") {
let d = match db.load_rkyv::<PublicKey>(0, b"asdf") {
Ok(x) => x,
Err(e) => {
panic!("couldn't decode: {}", e);
@ -155,12 +155,16 @@ pub async fn test_frozen(ts: TableStore) {
pub async fn test_all() {
let api = startup().await;
let crypto = api.crypto().unwrap();
let ts = api.table_store().unwrap();
for ck in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(ck).unwrap();
test_delete_open_delete(ts.clone()).await;
test_store_delete_load(ts.clone()).await;
test_frozen(ts.clone()).await;
test_frozen(vcrypto, ts.clone()).await;
let _ = ts.delete("test").await;
}
shutdown(api).await;
}

View File

@ -192,9 +192,18 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.client_whitelist_timeout_ms" => Ok(Box::new(300_000u32)),
"network.reverse_connection_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.hole_punch_receipt_time_ms" => Ok(Box::new(5_000u32)),
"network.node_id" => Ok(Box::new(Option::<TypedKey>::None)),
"network.node_id_secret" => Ok(Box::new(Option::<SecretKey>::None)),
"network.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.routing_table.node_ids" => {
let mut nids = BTreeMap::<CryptoKind, VeilidConfigNodeId>::new();
nids.insert(
CRYPTO_KIND_VLD0,
VeilidConfigNodeId {
node_id: None,
node_id_secret: None,
},
);
Ok(Box::new(nids))
}
"network.routing_table.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.routing_table.limit_over_attached" => Ok(Box::new(64u32)),
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
"network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)),
@ -315,14 +324,13 @@ pub async fn test_config() {
assert_eq!(inner.network.client_whitelist_timeout_ms, 300_000u32);
assert_eq!(inner.network.reverse_connection_receipt_time_ms, 5_000u32);
assert_eq!(inner.network.hole_punch_receipt_time_ms, 5_000u32);
assert!(inner.network.node_id.is_none());
assert!(inner.network.node_id_secret.is_none());
assert_eq!(inner.network.bootstrap, Vec::<String>::new());
assert_eq!(inner.network.rpc.concurrency, 2u32);
assert_eq!(inner.network.rpc.queue_size, 1024u32);
assert_eq!(inner.network.rpc.timeout_ms, 10_000u32);
assert_eq!(inner.network.rpc.max_route_hop_count, 4u8);
assert_eq!(inner.network.rpc.default_route_hop_count, 1u8);
assert_eq!(inner.network.routing_table.node_ids.len(), 1);
assert_eq!(inner.network.routing_table.bootstrap, Vec::<String>::new());
assert_eq!(inner.network.routing_table.limit_over_attached, 64u32);
assert_eq!(inner.network.routing_table.limit_fully_attached, 32u32);
assert_eq!(inner.network.routing_table.limit_attached_strong, 16u32);

View File

@ -50,65 +50,84 @@ pub async fn test_signed_node_info() {
.await
.expect("startup failed");
let crypto = api.crypto().unwrap();
for ck in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(ck).unwrap();
// Test direct
let node_info = NodeInfo {
network_class: NetworkClass::InboundCapable,
outbound_protocols: ProtocolTypeSet::all(),
address_types: AddressTypeSet::all(),
min_version: 0,
max_version: 0,
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
dial_info_detail_list: vec![DialInfoDetail {
class: DialInfoClass::Mapped,
dial_info: DialInfo::udp(SocketAddress::default()),
}],
};
let (pkey, skey) = generate_secret();
let (pkey, skey) = vcrypto.generate_keypair();
let sni =
SignedDirectNodeInfo::with_secret(NodeId::new(pkey.clone()), node_info.clone(), &skey)
.unwrap();
let _ = SignedDirectNodeInfo::new(
NodeId::new(pkey),
let sni = SignedDirectNodeInfo::make_signatures(
crypto.clone(),
vec![TypedKeyPair::new(ck, pkey, skey)],
node_info.clone(),
sni.timestamp,
sni.signature.unwrap(),
)
.unwrap();
let mut tks: TypedKeySet = TypedKey::new(ck, pkey).into();
let oldtkslen = tks.len();
let _ = SignedDirectNodeInfo::new(
crypto.clone(),
&mut tks,
node_info.clone(),
sni.timestamp,
sni.signatures.clone(),
)
.unwrap();
assert_eq!(tks.len(), oldtkslen);
assert_eq!(tks.len(), sni.signatures.len());
// Test relayed
let node_info2 = NodeInfo {
network_class: NetworkClass::OutboundOnly,
outbound_protocols: ProtocolTypeSet::all(),
address_types: AddressTypeSet::all(),
min_version: 0,
max_version: 0,
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
dial_info_detail_list: vec![DialInfoDetail {
class: DialInfoClass::Blocked,
dial_info: DialInfo::udp(SocketAddress::default()),
}],
};
let (pkey2, skey2) = generate_secret();
let (pkey2, skey2) = vcrypto.generate_keypair();
let mut tks2: TypedKeySet = TypedKey::new(ck, pkey2).into();
let oldtks2len = tks2.len();
let sni2 = SignedRelayedNodeInfo::make_signatures(
NodeId::new(pkey2.clone()),
crypto.clone(),
vec![TypedKeyPair::new(ck, pkey2, skey2)],
node_info2.clone(),
NodeId::new(pkey.clone()),
tks.clone(),
sni.clone(),
&skey2,
)
.unwrap();
let _ = SignedRelayedNodeInfo::new(
NodeId::new(pkey2),
crypto.clone(),
&mut tks2,
node_info2,
NodeId::new(pkey),
tks,
sni,
sni2.timestamp,
sni2.signature,
sni2.signatures.clone(),
)
.unwrap();
assert_eq!(tks2.len(), oldtks2len);
assert_eq!(tks2.len(), sni2.signatures.len());
}
api.shutdown().await;
}

View File

@ -7,7 +7,7 @@ use routing_table::*;
#[derive(Default, Debug)]
struct DebugCache {
imported_routes: Vec<TypedKey>,
imported_routes: Vec<TypedKeySet>,
}
static DEBUG_CACHE: Mutex<DebugCache> = Mutex::new(DebugCache {
@ -30,12 +30,12 @@ fn get_string(text: &str) -> Option<String> {
Some(text.to_owned())
}
fn get_route_id(rss: RouteSpecStore) -> impl Fn(&str) -> Option<TypedKey> {
fn get_route_id(rss: RouteSpecStore) -> impl Fn(&str) -> Option<PublicKey> {
return move |text: &str| {
if text.is_empty() {
return None;
}
match TypedKey::from_str(text).ok() {
match PublicKey::from_str(text).ok() {
Some(key) => {
let routes = rss.list_allocated_routes(|k, _| Some(*k));
if routes.contains(&key) {
@ -128,12 +128,31 @@ fn get_destination(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<D
}
if &text[0..1] == "#" {
// Private route
let text = &text[1..];
let mut text = &text[1..];
let opt_crypto_kind = if text.len() > 5 {
let fcc = &text[0..4];
if &text[4..5] != ":" {
return None;
}
let ck = match FourCC::from_str(fcc) {
Ok(v) => v,
Err(_) => {
return None;
}
};
text = &text[5..];
Some(ck)
} else {
None
};
let n = get_number(text)?;
let mut dc = DEBUG_CACHE.lock();
let pr_pubkey = dc.imported_routes.get(n)?;
let pr_pubkey = match opt_crypto_kind {
Some(ck) => dc.imported_routes.get(n)?.get(ck)?,
None => dc.imported_routes.get(n)?.best()?,
};
let rss = routing_table.route_spec_store();
let Some(private_route) = rss.get_remote_private_route(&pr_pubkey) else {
let Some(private_route) = rss.get_remote_private_route(&pr_pubkey.key) else {
// Remove imported route
dc.imported_routes.remove(n);
info!("removed dead imported route {}", n);
@ -150,15 +169,14 @@ fn get_destination(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<D
.unwrap_or((text, None));
if let Some((first, second)) = text.split_once('@') {
// Relay
let relay_id = get_typed_key(second)?;
let mut relay_nr = routing_table.lookup_node_ref(relay_id)?;
let target_id = get_typed_key(first)?;
let mut relay_nr = get_node_ref(routing_table.clone())(second)?;
let target_nr = get_node_ref(routing_table)(first)?;
if let Some(mods) = mods {
relay_nr = get_node_ref_modifiers(relay_nr)(mods)?;
}
let mut d = Destination::relay(relay_nr, target_id);
let mut d = Destination::relay(relay_nr, target_nr);
if let Some(ss) = ss {
d = d.with_safety(ss)
}
@ -166,8 +184,7 @@ fn get_destination(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<D
Some(d)
} else {
// Direct
let target_id = get_typed_key(text)?;
let mut target_nr = routing_table.lookup_node_ref(target_id)?;
let mut target_nr = get_node_ref(routing_table)(text)?;
if let Some(mods) = mods {
target_nr = get_node_ref_modifiers(target_nr)(mods)?;
@ -190,6 +207,9 @@ fn get_number(text: &str) -> Option<usize> {
fn get_typed_key(text: &str) -> Option<TypedKey> {
TypedKey::from_str(text).ok()
}
fn get_public_key(text: &str) -> Option<PublicKey> {
PublicKey::from_str(text).ok()
}
fn get_node_ref(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<NodeRef> {
move |text| {
@ -198,8 +218,13 @@ fn get_node_ref(routing_table: RoutingTable) -> impl FnOnce(&str) -> Option<Node
.map(|x| (x.0, Some(x.1)))
.unwrap_or((text, None));
let node_id = get_typed_key(text)?;
let mut nr = routing_table.lookup_node_ref(node_id)?;
let mut nr = if let Some(key) = get_public_key(text) {
routing_table.lookup_any_node_ref(key)?
} else if let Some(key) = get_typed_key(text) {
routing_table.lookup_node_ref(key)?
} else {
return None;
};
if let Some(mods) = mods {
nr = get_node_ref_modifiers(nr)(mods)?;
}
@ -607,8 +632,15 @@ impl VeilidAPI {
}
// Allocate route
let out = match rss.allocate_route(stability, sequencing, hop_count, directions, &[]) {
Ok(Some(v)) => format!("{}", v.encode()),
let out = match rss.allocate_route(
&VALID_CRYPTO_KINDS,
stability,
sequencing,
hop_count,
directions,
&[],
) {
Ok(Some(v)) => format!("{}", v),
Ok(None) => format!("<unavailable>"),
Err(e) => {
format!("Route allocation failed: {}", e)
@ -685,7 +717,7 @@ impl VeilidAPI {
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_typed_key)?;
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_public_key)?;
// Unpublish route
let out = if let Err(e) = rss.mark_route_published(&route_id, false) {
@ -701,7 +733,7 @@ impl VeilidAPI {
let routing_table = netman.routing_table();
let rss = routing_table.route_spec_store();
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_typed_key)?;
let route_id = get_debug_argument_at(&args, 1, "debug_route", "route_id", get_public_key)?;
match rss.debug_route(&route_id) {
Some(s) => Ok(s),

View File

@ -1842,7 +1842,7 @@ impl MatchesDialInfoFilter for DialInfo {
//////////////////////////////////////////////////////////////////////////
// Signed NodeInfo that can be passed around amongst peers and verifiable
/// Signed NodeInfo that can be passed around amongst peers and verifiable
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct SignedDirectNodeInfo {
@ -1851,6 +1851,8 @@ pub struct SignedDirectNodeInfo {
pub signatures: Vec<TypedSignature>,
}
impl SignedDirectNodeInfo {
/// Returns a new SignedDirectNodeInfo that has its signatures validated. Will modify the node_ids set to only include node_ids whose signatures validate
/// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures.
pub fn new(
crypto: Crypto,
node_ids: &mut TypedKeySet,
@ -1937,6 +1939,8 @@ pub struct SignedRelayedNodeInfo {
}
impl SignedRelayedNodeInfo {
/// Returns a new SignedRelayedNodeInfo that has its signatures validated. Will modify the node_ids set to only include node_ids whose signatures validate
/// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures.
pub fn new(
crypto: Crypto,
node_ids: &mut TypedKeySet,