1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{fmt, str::FromStr};
mod words;
#[derive(Debug, PartialEq)]
pub struct AnimalName {
adjective: &'static str,
color: &'static str,
animal: &'static str,
}
impl fmt::Display for AnimalName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_fmt(format_args!(
"{}-{}-{}",
self.adjective, self.color, self.animal
))
}
}
impl PartialEq<&str> for AnimalName {
fn eq(&self, other: &&str) -> bool {
let mut split = (*other).split('-');
for entry in &[self.adjective, self.color, self.animal] {
if split.next().map_or_else(|| true, |s| &s != entry) {
return false;
}
}
true
}
}
fn str_to_animal_name(s: &str) -> AnimalName {
let digest = hex_digest(s);
AnimalName {
adjective: words::ADJECTIVES[digest[0]],
color: words::COLORS[digest[1]],
animal: words::ANIMALS[digest[2]],
}
}
impl FromStr for AnimalName {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(str_to_animal_name(s))
}
}
#[cfg(feature = "helium_crypto")]
impl From<helium_crypto::PublicKey> for AnimalName {
fn from(pubkey: helium_crypto::PublicKey) -> Self {
str_to_animal_name(&pubkey.to_string())
}
}
fn hex_digest(s: &str) -> [usize; 3] {
let digest = md5::compute(s);
let mut result = [0usize; 3];
compress(digest.0.len() / 3, &digest.0, &mut result);
result
}
fn compress(size: usize, bytes: &[u8], dest: &mut [usize]) {
if bytes.len() >= (2 * size) {
dest[0] = bytes[0..size].iter().fold(0u8, |acc, b| acc ^ b) as usize;
compress(size, &bytes[size..], &mut dest[1..])
} else {
dest[0] = bytes.iter().fold(0u8, |acc, b| acc ^ b) as usize;
}
}
#[cfg(test)]
mod test {
use super::AnimalName;
#[test]
fn basic() {
let known = "112CuoXo7WCcp6GGwDNBo6H5nKXGH45UNJ39iEefdv2mwmnwdFt8";
let animal_name = known.parse::<AnimalName>().expect("animal name");
assert_eq!(animal_name, "feisty-glass-dalmatian")
}
#[test]
#[cfg(feature = "helium-crypto")]
fn from_public_key() {
use std::str::FromStr;
let known = helium_crypto::PublicKey::from_str(
"112CuoXo7WCcp6GGwDNBo6H5nKXGH45UNJ39iEefdv2mwmnwdFt8",
)
.expect("public key");
let animal_name: AnimalName = known.into();
assert_eq!(animal_name, "feisty-glass-dalmatian")
}
}