use std::{
cmp::Ordering,
collections::HashSet,
fmt,
hash::{Hash, Hasher},
io::{self, Error, ErrorKind},
str::FromStr,
string::String,
};
use lazy_static::lazy_static;
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
use zerocopy::{AsBytes, FromBytes, Unaligned};
use crate::{formatting, ids::short, key::secp256k1::address};
#[cfg(feature = "cert")]
use crate::key::cert;
pub const ID_LEN: usize = 20;
pub const ID_ENCODE_PREFIX: &str = "NodeID-";
lazy_static! {
static ref EMPTY: Vec<u8> = vec![0; ID_LEN];
}
#[derive(Debug, Deserialize, Copy, Clone, Eq, AsBytes, FromBytes, Unaligned)]
#[repr(transparent)]
pub struct Id([u8; ID_LEN]);
impl Default for Id {
fn default() -> Self {
Self::default()
}
}
impl Id {
pub fn default() -> Self {
Id([0; ID_LEN])
}
pub fn empty() -> Self {
Id([0; ID_LEN])
}
pub fn is_empty(&self) -> bool {
(*self) == Self::empty()
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn from_slice(d: &[u8]) -> Self {
assert_eq!(d.len(), ID_LEN);
let d: [u8; ID_LEN] = d.try_into().unwrap();
Id(d)
}
#[cfg(feature = "cert")]
pub fn from_cert_pem_file(cert_file_path: &str) -> io::Result<Self> {
log::info!("loading node ID from certificate {}", cert_file_path);
let pub_key_der = cert::x509::load_pem_cert_to_der(cert_file_path)?;
Self::from_cert_der_bytes(&pub_key_der)
}
pub fn from_cert_der_bytes<S>(cert_bytes: S) -> io::Result<Self>
where
S: AsRef<[u8]>,
{
let short_address = address::hash_sha256_ripemd160(cert_bytes)?;
let node_id = Self::from_slice(&short_address);
Ok(node_id)
}
pub fn short_id(&self) -> short::Id {
short::Id::from_slice(&self.0)
}
}
impl AsRef<[u8]> for Id {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut node_id = String::from(ID_ENCODE_PREFIX);
let short_id = formatting::encode_cb58_with_checksum_string(&self.0);
node_id.push_str(&short_id);
write!(f, "{}", node_id)
}
}
impl FromStr for Id {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let processed = strip_node_id_prefix(s);
let decoded = formatting::decode_cb58_with_checksum(processed).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed decode_cb58_with_checksum '{}'", e),
)
})?;
Ok(Self::from_slice(&decoded))
}
}
impl Serialize for Id {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
fn fmt_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Id::from_str(&s).map_err(serde::de::Error::custom)
}
pub fn deserialize_id<'de, D>(deserializer: D) -> Result<Option<Id>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
let v = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}
pub fn must_deserialize_id<'de, D>(deserializer: D) -> Result<Id, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(deserialize_with = "fmt_id")] Id);
let v = Option::deserialize(deserializer)?;
match v.map(|Wrapper(a)| a) {
Some(unwrapped) => Ok(unwrapped),
None => Err(serde::de::Error::custom(
"empty node::Id from deserialization",
)),
}
}
pub type Set = HashSet<Id>;
pub fn new_set(size: usize) -> Set {
let set: HashSet<Id> = HashSet::with_capacity(size);
set
}
#[test]
fn test_from_cert_file() {
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Info)
.is_test(true)
.try_init();
let node_id = Id::from_slice(&<Vec<u8>>::from([
0x3d, 0x0a, 0xd1, 0x2b, 0x8e, 0xe8, 0x92, 0x8e, 0xdf, 0x24, 0x8c, 0xa9, 0x1c, 0xa5, 0x56, 0x00, 0xfb, 0x38, 0x3f, 0x07, ]));
assert_eq!(
format!("{}", node_id),
"NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
);
assert_eq!(
node_id.to_string(),
"NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
);
assert_eq!(
node_id.short_id().to_string(),
"6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx"
);
assert_eq!(
node_id,
Id::from_str("6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-6ZmBHXTqjknJoZtXbnJ6x7af863rXDTwx").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/staker1.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
);
assert_eq!(
node_id.to_string(),
"NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
);
assert_eq!(
node_id,
Id::from_str("7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/staker2.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ"
);
assert_eq!(
node_id.to_string(),
"NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ"
);
assert_eq!(
node_id,
Id::from_str("MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/staker3.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN"
);
assert_eq!(
node_id.to_string(),
"NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN"
);
assert_eq!(
node_id,
Id::from_str("NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/staker4.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu"
);
assert_eq!(
node_id.to_string(),
"NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu"
);
assert_eq!(
node_id,
Id::from_str("GWPcbFJZFfZreETSoWjPimr846mXEKCtu").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/staker5.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5"
);
assert_eq!(
node_id.to_string(),
"NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5"
);
assert_eq!(
node_id,
Id::from_str("P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5").unwrap()
);
let node_id = Id::from_cert_pem_file("./artifacts/test.insecure.crt").unwrap();
assert_eq!(
format!("{}", node_id),
"NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG"
);
assert_eq!(
node_id.to_string(),
"NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG"
);
assert_eq!(
node_id,
Id::from_str("29HTAG5cfN2fw79A67Jd5zY9drcT51EBG").unwrap()
);
assert_eq!(
node_id,
Id::from_str("NodeID-29HTAG5cfN2fw79A67Jd5zY9drcT51EBG").unwrap()
);
}
impl Ord for Id {
fn cmp(&self, other: &Id) -> Ordering {
self.0.cmp(&(other.0))
}
}
impl PartialOrd for Id {
fn partial_cmp(&self, other: &Id) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Id {
fn eq(&self, other: &Id) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Hash for Id {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
#[derive(Eq)]
pub struct Ids(Vec<Id>);
impl Ids {
pub fn new(ids: &[Id]) -> Self {
Ids(Vec::from(ids))
}
}
impl Ord for Ids {
fn cmp(&self, other: &Ids) -> Ordering {
let l1 = self.0.len();
let l2 = other.0.len();
l1.cmp(&l2) .then_with(
|| self.0.cmp(&other.0), )
}
}
impl PartialOrd for Ids {
fn partial_cmp(&self, other: &Ids) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Ids {
fn eq(&self, other: &Ids) -> bool {
self.cmp(other) == Ordering::Equal
}
}
#[test]
fn test_sort() {
let id1 = Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
let id2 = Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
assert!(id1 == id2);
let id1 = Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
let id2 = Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
assert!(id1 < id2);
let id1 = Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
let id2 = Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
]));
assert!(id1 > id2);
let ids1 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
let ids2 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
assert!(ids1 == ids2);
let ids1 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
let ids2 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
assert!(ids1 < ids2);
let ids1 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
let ids2 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
assert!(ids1 > ids2);
let ids1 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
let ids2 = Ids(vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
]);
assert!(ids1 < ids2);
let mut ids1 = vec![
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
];
ids1.sort();
let ids2 = vec![
Id::from_slice(&<Vec<u8>>::from([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
Id::from_slice(&<Vec<u8>>::from([
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
])),
];
assert!(ids1 == ids2);
}
pub fn strip_node_id_prefix(addr: &str) -> &str {
let n = ID_ENCODE_PREFIX.len();
if &addr[0..n] == ID_ENCODE_PREFIX {
&addr[n..]
} else {
addr
}
}