PeerId rework: PeerId is now unique and enforces b32-encoded destination

This commit is contained in:
Philip (a-0) 2024-03-23 18:16:46 +01:00
parent 636aff64b9
commit a68c08292f
5 changed files with 57 additions and 67 deletions

View file

@ -10,6 +10,7 @@ axum = { version = "0.7.2", features = [ "macros" ] }
chrono = "0.4.31"
itertools = "0.12.0"
jsonwebtoken = "9.2.0"
regex = "1.10.4"
reqwest = "0.11.23"
serde = { version = "1.0.166", features = [ "derive" ] }
serde_json = "1.0.99"

View file

@ -1,89 +1,78 @@
use std::hash::Hash;
use anyhow::{anyhow, bail};
use anyhow::{anyhow, bail, Error};
use i2p::net::{I2pAddr, I2pSocketAddr, ToI2pSocketAddrs};
use regex::Regex;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// Uniquely identifies a peer. The I2pAddr inside `addr` MUST be the peer's b32 address.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PeerId {
i2p_dest: I2pSocketAddr,
i2p_b32: Option<I2pAddr>,
addr: I2pSocketAddr,
}
impl PeerId {
pub fn addr(&self) -> I2pSocketAddr {
self.i2p_dest.to_owned()
}
pub fn addr_ref(&self) -> &I2pSocketAddr {
&self.i2p_dest
}
pub fn b32_addr(&mut self) -> anyhow::Result<I2pAddr> {
let result = I2pAddr::from_b64(&self.i2p_dest.dest().string());
if let Ok(addr) = &result {
self.i2p_b32 = Some(addr.to_owned());
pub fn try_from_b32(addr: &str, port: Option<u16>) -> anyhow::Result<Self> {
let b32_regex = Regex::new(r"[abcdefghijklmnopqrstuvwxyz234567]{52}.b32.i2p").unwrap();
if b32_regex.is_match(addr) {
Ok(PeerId {
addr: I2pSocketAddr::new(I2pAddr::new(addr), port.unwrap_or(0)),
})
} else {
Err(Error::msg("The supplied address was not b32-formatted."))
}
result.map_err(|e| anyhow!(e))
}
pub fn b32_addr_nocache(&self) -> anyhow::Result<I2pAddr> {
I2pAddr::from_b64(&self.i2p_dest.dest().string()).map_err(|e| anyhow!(e))
pub fn try_from_dest(dest: &str, port: Option<u16>) -> anyhow::Result<Self> {
let b32 = I2pAddr::from_b64(dest).map_err(|e| anyhow!(e))?;
Ok(PeerId {
addr: I2pSocketAddr::new(b32, port.unwrap_or(0)),
})
}
}
// The identity of the PeerId only depends on the i2p_dest (which is unique),
// and not on whether the b32 address has been computed before
impl Hash for PeerId {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.i2p_dest.hash(state);
pub fn addr(&self) -> I2pSocketAddr {
self.addr.to_owned()
}
pub fn addr_ref(&self) -> &I2pSocketAddr {
&self.addr
}
}
impl ToString for PeerId {
fn to_string(&self) -> String {
self.i2p_dest.to_string()
}
}
impl TryFrom<&str> for PeerId {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, anyhow::Error> {
match ToI2pSocketAddrs::to_socket_addrs(&value) {
Ok(addr_iter) => {
for addr in addr_iter {
return Ok(PeerId { i2p_dest: addr, i2p_b32: None });
}
return Err(anyhow::Error::msg("No valid I2P address found"));
}
Err(e) => bail!(e),
}
}
}
impl TryFrom<String> for PeerId {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_from(value.as_str())
}
}
impl From<I2pSocketAddr> for PeerId {
fn from(value: I2pSocketAddr) -> Self {
PeerId { i2p_dest: value, i2p_b32: None }
self.addr.to_string()
}
}
impl From<PeerId> for I2pSocketAddr {
fn from(value: PeerId) -> Self {
value.i2p_dest
value.addr
}
}
impl From<I2pSocketAddr> for PeerId {
fn from(value: I2pSocketAddr) -> Self {
PeerId { addr: value }
}
}
impl Default for PeerId {
fn default() -> Self {
PeerId {
i2p_dest: I2pSocketAddr::new(I2pAddr::new(""), 0),
i2p_b32: None
addr: I2pSocketAddr::new(I2pAddr::new(""), 0),
}
}
}
#[cfg(test)]
mod tests {
use super::PeerId;
#[test]
fn from_b32() {
let addr = "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrst.b32.i2p";
let peer_id = PeerId::try_from_b32(addr, None);
assert!(peer_id.is_ok())
}
}