Make sure endpoint sorting is stable forever, and some Locator stuff.

This commit is contained in:
Adam Ierymenko 2021-08-04 13:21:10 -04:00
parent 653a6dcf3c
commit faecf04dd5
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
2 changed files with 159 additions and 2 deletions

View file

@ -3,6 +3,7 @@ use std::hash::{Hash, Hasher};
use crate::vl1::{Address, MAC};
use crate::vl1::inetaddress::InetAddress;
use crate::vl1::buffer::Buffer;
use std::cmp::Ordering;
const TYPE_NIL: u8 = 0;
const TYPE_ZEROTIER: u8 = 1;
@ -15,6 +16,7 @@ const TYPE_IPTCP: u8 = 7;
const TYPE_HTTP: u8 = 8;
const TYPE_WEBRTC: u8 = 9;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Type {
Nil = TYPE_NIL,
@ -29,7 +31,7 @@ pub enum Type {
WebRTC = TYPE_WEBRTC,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq)]
pub enum Endpoint {
Nil,
ZeroTier(Address),
@ -206,6 +208,119 @@ impl Hash for Endpoint {
}
}
impl PartialOrd for Endpoint {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Endpoint {
fn cmp(&self, other: &Self) -> Ordering {
// This ordering is done explicitly instead of using derive() so it will be certain
// to be consistent with the integer order in the Type enum. Make sure it stays this
// way if new types are added in future revisions.
match self {
Endpoint::Nil => {
match other {
Endpoint::Nil => Ordering::Equal,
_ => Ordering::Greater,
}
}
Endpoint::ZeroTier(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::Ethernet(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::WifiDirect(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::Bluetooth(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(_) => Ordering::Less,
Endpoint::Bluetooth(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::Ip(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(_) => Ordering::Less,
Endpoint::Bluetooth(_) => Ordering::Less,
Endpoint::Ip(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::IpUdp(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(_) => Ordering::Less,
Endpoint::Bluetooth(_) => Ordering::Less,
Endpoint::Ip(_) => Ordering::Less,
Endpoint::IpUdp(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::IpTcp(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(_) => Ordering::Less,
Endpoint::Bluetooth(_) => Ordering::Less,
Endpoint::Ip(_) => Ordering::Less,
Endpoint::IpUdp(_) => Ordering::Less,
Endpoint::IpTcp(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::Http(a) => {
match other {
Endpoint::Nil => Ordering::Less,
Endpoint::ZeroTier(_) => Ordering::Less,
Endpoint::Ethernet(_) => Ordering::Less,
Endpoint::WifiDirect(_) => Ordering::Less,
Endpoint::Bluetooth(_) => Ordering::Less,
Endpoint::Ip(_) => Ordering::Less,
Endpoint::IpUdp(_) => Ordering::Less,
Endpoint::IpTcp(_) => Ordering::Less,
Endpoint::Http(b) => a.cmp(b),
_ => Ordering::Greater,
}
}
Endpoint::WebRTC(a) => {
match other {
Endpoint::WebRTC(b) => a.cmp(b),
_ => Ordering::Less,
}
}
}
}
}
impl ToString for Endpoint {
fn to_string(&self) -> String {
match self {

View file

@ -1 +1,43 @@
pub struct Locator;
use crate::vl1::{Endpoint, Address, Identity};
use std::hash::{Hash, Hasher};
/// A signed object generated by nodes to inform the network where they may be found.
///
/// By default this will just enumerate the roots used by this node, but nodes with
/// static IPs can also list physical IP/port addresses where they can be reached with
/// no involvement from a root at all.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Locator {
pub(crate) signer: Address,
pub(crate) timestamp: i64,
pub(crate) endpoints: Vec<Endpoint>,
pub(crate) signature: Vec<u8>,
}
impl Locator {
pub fn create(id: &Identity, ts: i64, endpoints: &[Endpoint]) -> Option<Locator> {
let mut loc = Locator {
signer: id.address(),
timestamp: ts,
endpoints: endpoints.to_vec(),
signature: Vec::new()
};
loc.endpoints.sort_unstable();
loc.endpoints.dedup();
Some(loc)
}
}
impl Hash for Locator {
fn hash<H: Hasher>(&self, state: &mut H) {
if !self.signature.is_empty() {
state.write(self.signature.as_slice());
} else {
state.write_u64(self.signer.to_u64());
state.write_i64(self.timestamp);
for e in self.endpoints.iter() {
e.hash(state);
}
}
}
}