use core::convert::{TryFrom, TryInto};
use core::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "serde")]
use serde_with::{As, DisplayFromStr};
use std::net::IpAddr;
#[cfg(feature = "zmq")]
use std::net::SocketAddr;
use std::str::FromStr;
#[cfg(feature = "url")]
use url::Url;
use bitcoin::secp256k1;
use inet2_addr::{InetAddr, InetSocketAddr};
use crate::transport::{LocalSocketAddr, RemoteSocketAddr};
#[cfg(feature = "zmq")]
use crate::zmqsocket::{ZmqSocketAddr, ZmqType};
use crate::{AddrError, UrlString};
#[cfg(not(feature = "zmq"))]
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct ZmqType;
#[cfg_attr(
feature = "serde",
serde_as,
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
Display,
StrictEncode,
StrictDecode,
)]
#[display(inner)]
pub enum NodeAddr {
Local(
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
LocalSocketAddr,
),
Remote(
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
RemoteNodeAddr,
),
}
impl FromStr for NodeAddr {
type Err = AddrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PartialNodeAddr::from_str(s)?.into())
}
}
impl UrlString for NodeAddr {
fn url_scheme(&self) -> &'static str {
match self {
NodeAddr::Local(local) => local.url_scheme(),
NodeAddr::Remote(remote) => remote.url_scheme(),
}
}
fn to_url_string(&self) -> String {
match self {
NodeAddr::Local(local) => local.to_url_string(),
NodeAddr::Remote(remote) => remote.to_url_string(),
}
}
}
#[cfg(feature = "url")]
impl From<NodeAddr> for Url {
fn from(addr: NodeAddr) -> Self {
Url::from(&addr)
}
}
#[cfg(feature = "url")]
impl From<&NodeAddr> for Url {
fn from(addr: &NodeAddr) -> Self {
Url::parse(&addr.to_url_string())
.expect("Parsing URL string must not fail")
}
}
#[cfg(feature = "url")]
impl TryFrom<Url> for NodeAddr {
type Error = AddrError;
fn try_from(url: Url) -> Result<Self, Self::Error> {
Ok(PartialNodeAddr::try_from(url)?.into())
}
}
impl From<RemoteNodeAddr> for NodeAddr {
fn from(addr: RemoteNodeAddr) -> Self {
NodeAddr::Remote(addr)
}
}
impl From<LocalSocketAddr> for NodeAddr {
fn from(addr: LocalSocketAddr) -> Self {
NodeAddr::Local(addr)
}
}
#[cfg(feature = "zmq")]
impl TryFrom<NodeAddr> for ZmqSocketAddr {
type Error = AddrError;
fn try_from(value: NodeAddr) -> Result<Self, Self::Error> {
Ok(match value {
NodeAddr::Local(LocalSocketAddr::Zmq(locator)) => locator,
NodeAddr::Remote(RemoteNodeAddr {
node_id: _,
remote_addr: RemoteSocketAddr::Zmq(addr),
}) => ZmqSocketAddr::Tcp(addr),
_ => Err(AddrError::Unsupported("ZMQ socket"))?,
})
}
}
#[cfg_attr(
feature = "serde",
serde_as,
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
StrictEncode,
StrictDecode,
)]
pub struct RemoteNodeAddr {
pub node_id: secp256k1::PublicKey,
#[cfg_attr(feature = "serde", serde(with = "As::<DisplayFromStr>"))]
pub remote_addr: RemoteSocketAddr,
}
impl_try_from_stringly_standard!(NodeAddr);
impl_into_stringly_standard!(NodeAddr);
impl fmt::Display for RemoteNodeAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(
f,
"{}{}@{}",
self.remote_addr.url_scheme(),
self.node_id,
self.remote_addr
)
} else {
write!(f, "{}@{}", self.node_id, self.remote_addr)
}
}
}
impl FromStr for RemoteNodeAddr {
type Err = AddrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
PartialNodeAddr::from_str(s)?.try_into()
}
}
impl UrlString for RemoteNodeAddr {
fn url_scheme(&self) -> &'static str {
self.remote_addr.url_scheme()
}
fn to_url_string(&self) -> String {
format!("{:#}", self)
}
}
#[cfg(feature = "url")]
impl From<RemoteNodeAddr> for Url {
fn from(addr: RemoteNodeAddr) -> Self {
Url::from(&addr)
}
}
#[cfg(feature = "url")]
impl From<&RemoteNodeAddr> for Url {
fn from(addr: &RemoteNodeAddr) -> Self {
Url::parse(&addr.to_url_string())
.expect("Parsing URL string must not fail")
}
}
#[cfg(feature = "url")]
impl TryFrom<Url> for RemoteNodeAddr {
type Error = AddrError;
fn try_from(url: Url) -> Result<Self, Self::Error> {
PartialNodeAddr::try_from(url)?.try_into()
}
}
impl From<RemoteNodeAddr> for RemoteSocketAddr {
fn from(addr: RemoteNodeAddr) -> RemoteSocketAddr {
addr.remote_addr
}
}
pub trait ToNodeAddr {
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr>;
}
impl ToNodeAddr for NodeAddr {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
Some(self.clone())
}
}
impl ToNodeAddr for RemoteNodeAddr {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
Some(self.clone().into())
}
}
impl ToNodeAddr for LocalSocketAddr {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
Some(self.clone().into())
}
}
impl ToNodeAddr for PartialNodeAddr {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
self.with_port(default_port).try_into().ok()
}
}
impl ToNodeAddr for String {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
self.as_str().to_node_addr(default_port)
}
}
impl ToNodeAddr for &str {
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
NodeAddr::try_from(PartialNodeAddr::from_str(&self).ok()?).ok()
}
}
impl<T> ToNodeAddr for &T
where
T: ToNodeAddr,
{
#[inline]
fn to_node_addr(&self, default_port: u16) -> Option<NodeAddr> {
(*self).to_node_addr(default_port)
}
}
pub trait ToRemoteNodeAddr {
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr>;
}
impl ToRemoteNodeAddr for RemoteNodeAddr {
#[inline]
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr> {
Some(self.clone())
}
}
impl ToRemoteNodeAddr for PartialNodeAddr {
#[inline]
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr> {
self.with_port(default_port).try_into().ok()
}
}
impl ToRemoteNodeAddr for String {
#[inline]
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr> {
self.as_str().to_remote_node_addr(default_port)
}
}
impl ToRemoteNodeAddr for &str {
#[inline]
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr> {
RemoteNodeAddr::from_str(&self)
.or_else(|_| {
RemoteNodeAddr::from_str(&format!("{}:{}", self, default_port))
})
.ok()
}
}
impl<T> ToRemoteNodeAddr for &T
where
T: ToRemoteNodeAddr,
{
#[inline]
fn to_remote_node_addr(&self, default_port: u16) -> Option<RemoteNodeAddr> {
(*self).to_remote_node_addr(default_port)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum PartialNodeAddr {
Native(secp256k1::PublicKey, InetAddr, Option<u16>),
Udp(secp256k1::PublicKey, IpAddr, Option<u16>),
Posix(String),
#[cfg(feature = "zmq")]
ZmqIpc(String, ZmqType),
#[cfg(feature = "zmq")]
ZmqInproc(String, ZmqType),
#[cfg(feature = "zmq")]
ZmqTcpEncrypted(secp256k1::PublicKey, ZmqType, IpAddr, Option<u16>),
#[cfg(feature = "zmq")]
ZmqTcpUnencrypted(ZmqType, IpAddr, Option<u16>),
Http(secp256k1::PublicKey, InetAddr, Option<u16>),
#[cfg(feature = "websockets")]
Websocket(secp256k1::PublicKey, InetAddr, Option<u16>),
Text(secp256k1::PublicKey),
}
impl PartialNodeAddr {
pub fn with_port(&self, port: u16) -> Self {
match self.clone() {
PartialNodeAddr::Native(a, b, _) => {
PartialNodeAddr::Native(a, b, Some(port))
}
PartialNodeAddr::Udp(a, b, _) => {
PartialNodeAddr::Udp(a, b, Some(port))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(a, b, c, _) => {
PartialNodeAddr::ZmqTcpEncrypted(a, b, c, Some(port))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpUnencrypted(a, b, _) => {
PartialNodeAddr::ZmqTcpUnencrypted(a, b, Some(port))
}
PartialNodeAddr::Http(a, b, _) => {
PartialNodeAddr::Http(a, b, Some(port))
}
#[cfg(feature = "websockets")]
PartialNodeAddr::Websocket(a, b, _) => {
PartialNodeAddr::Websocket(a, b, Some(port))
}
me => me,
}
}
pub fn components(
&self,
) -> (
Option<secp256k1::PublicKey>,
Option<InetAddr>,
Option<u16>,
Option<String>,
Option<ZmqType>,
) {
match self {
PartialNodeAddr::Native(pubkey, inet, port) => {
(Some(*pubkey), Some(*inet), *port, None, None)
}
PartialNodeAddr::Udp(pubkey, ip, port) => {
(Some(*pubkey), Some(InetAddr::from(*ip)), *port, None, None)
}
PartialNodeAddr::Posix(path) => {
(None, None, None, Some(path.clone()), None)
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqIpc(path, api) => {
(None, None, None, Some(path.clone()), Some(*api))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqInproc(name, api) => {
(None, None, None, Some(name.clone()), Some(*api))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(pubkey, api, ip, port) => (
Some(*pubkey),
Some(InetAddr::from(*ip)),
*port,
None,
Some(*api),
),
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpUnencrypted(api, ip, port) => {
(None, Some(InetAddr::from(*ip)), *port, None, Some(*api))
}
PartialNodeAddr::Http(pubkey, inet, port) => {
(Some(*pubkey), Some(*inet), *port, None, None)
}
#[cfg(feature = "websockets")]
PartialNodeAddr::Websocket(pubkey, inet, port) => {
(Some(*pubkey), Some(*inet), *port, None, None)
}
PartialNodeAddr::Text(pubkey) => {
(Some(*pubkey), None, None, None, None)
}
}
}
#[inline]
pub fn node_id(&self) -> Option<secp256k1::PublicKey> {
self.components().0
}
#[inline]
pub fn inet_addr(&self) -> Option<InetAddr> {
self.components().1
}
#[inline]
pub fn port(&self) -> Option<u16> {
self.components().2
}
#[inline]
pub fn socket_name(&self) -> Option<String> {
self.components().3
}
#[inline]
pub fn api_type(&self) -> Option<ZmqType> {
self.components().4
}
}
impl Display for PartialNodeAddr {
fn fmt(&self, f: &mut Formatter<'_>) -> ::core::fmt::Result {
if !f.alternate() {
self.node_id()
.map(|id| write!(f, "{}", id))
.unwrap_or(Ok(()))?;
if let Some(addr) = self.inet_addr() {
write!(f, "@{}", addr)?;
self.port()
.map(|port| write!(f, ":{}", port))
.unwrap_or(Ok(()))?;
} else {
f.write_str(&self.socket_name().expect(
"Socket name is always known if internet address is given",
))?;
}
Ok(())
} else {
#[cfg(feature = "url")]
{
write!(f, "{}", Url::from(self))
}
#[cfg(not(feature = "url"))]
{
f.write_str(&self.to_url_string())
}
}
}
}
#[cfg(feature = "url")]
impl FromStr for PartialNodeAddr {
type Err = AddrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut s = s.to_string();
if vec!["lnp:", "lnpu:", "lnpz:", "lnpws:", "lnpt:", "lnph:"]
.into_iter()
.find(|p| s.starts_with(*p))
.is_none()
{
s = format!("lnp://{}", s);
}
Url::from_str(&s)?.try_into()
}
}
#[cfg(not(feature = "url"))]
impl FromStr for PartialNodeAddr {
type Err = AddrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
panic!("Parsing PartialNodeAddr from string requires url feature")
}
}
impl UrlString for PartialNodeAddr {
fn url_scheme(&self) -> &'static str {
match self {
PartialNodeAddr::Native(..) => "lnp",
PartialNodeAddr::Udp(..) => "lnpu",
PartialNodeAddr::Posix(..) => "lnp",
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqIpc(..) | PartialNodeAddr::ZmqInproc(..) => {
"lnpz"
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(..)
| PartialNodeAddr::ZmqTcpUnencrypted(..) => "lnpz",
PartialNodeAddr::Http(..) => "lnph",
#[cfg(feature = "websockets")]
PartialNodeAddr::Websocket(..) => "lnpws",
PartialNodeAddr::Text(..) => "lnpt",
}
}
fn to_url_string(&self) -> String {
match self {
PartialNodeAddr::Native(pubkey, inet, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!("{}://{}@{}{}", self.url_scheme(), pubkey, inet, p)
}
PartialNodeAddr::Udp(pubkey, ip, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!("{}://{}@{}{}", self.url_scheme(), pubkey, ip, p)
}
PartialNodeAddr::Posix(path) => {
format!("{}:{}", self.url_scheme(), path)
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqIpc(path, zmq_type) => format!(
"{}:{}?api={}",
self.url_scheme(),
path,
zmq_type.api_name()
),
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqInproc(name, zmq_type) => format!(
"{}:?api={}#{}",
self.url_scheme(),
zmq_type.api_name(),
name
),
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(pubkey, zmq_type, ip, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!(
"{}://{}@{}{}/?api={}",
self.url_scheme(),
pubkey,
ip,
p,
zmq_type.api_name()
)
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpUnencrypted(zmq_type, ip, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!(
"{}://{}{}/?api={}",
self.url_scheme(),
ip,
p,
zmq_type.api_name()
)
}
PartialNodeAddr::Http(pubkey, inet, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!("{}://{}@{}{}", self.url_scheme(), pubkey, inet, p)
}
#[cfg(feature = "websockets")]
PartialNodeAddr::Websocket(pubkey, inet, port) => {
let p = port.map(|x| format!(":{}", x)).unwrap_or_default();
format!("{}://{}@{}{}", self.url_scheme(), pubkey, inet, p)
}
PartialNodeAddr::Text(pubkey) => {
format!("{}://{}", self.url_scheme(), pubkey)
}
}
}
}
#[cfg(feature = "url")]
impl From<PartialNodeAddr> for Url {
fn from(addr: PartialNodeAddr) -> Self {
Url::from(&addr)
}
}
#[cfg(feature = "url")]
impl From<&PartialNodeAddr> for Url {
fn from(addr: &PartialNodeAddr) -> Self {
Url::parse(&addr.to_url_string())
.expect("Parsing URL string must not fail")
}
}
#[cfg(feature = "url")]
impl TryFrom<Url> for PartialNodeAddr {
type Error = AddrError;
fn try_from(url: Url) -> Result<Self, Self::Error> {
let pubkey = secp256k1::PublicKey::from_str(url.username());
let host = url.host_str();
let ip = host
.map(IpAddr::from_str)
.transpose()
.map_err(AddrError::from)
.and_then(|o| o.ok_or(AddrError::HostRequired));
let port = url.port();
match url.scheme() {
"lnp" => Ok(PartialNodeAddr::Native(
pubkey?,
host.ok_or(AddrError::HostRequired)?.parse::<InetAddr>()?,
port,
)),
"lnpu" => Ok(PartialNodeAddr::Udp(pubkey?, ip?, port)),
"lnph" => Ok(PartialNodeAddr::Http(
pubkey?,
host.ok_or(AddrError::HostRequired)?.parse::<InetAddr>()?,
port,
)),
#[cfg(feature = "websockets")]
"lnpws" => Ok(PartialNodeAddr::Websocket(
pubkey?,
host.ok_or(AddrError::HostRequired)?.parse::<InetAddr>()?,
port,
)),
#[cfg(feature = "zmq")]
"lnpz" => {
let zmq_type = match url
.query_pairs()
.find_map(
|(key, val)| {
if key == "api" {
Some(val)
} else {
None
}
},
)
.ok_or(AddrError::ZmqTypeRequired)?
.to_ascii_lowercase()
.as_str()
{
"p2p" => Ok(ZmqType::Push),
"rpc" => Ok(ZmqType::Req),
"sub" => Ok(ZmqType::Sub),
"esb" => Ok(ZmqType::RouterBind),
unknown => {
Err(AddrError::InvalidZmqType(unknown.to_string()))
}
}?;
Ok(match (ip, pubkey) {
(_, Err(_)) if !url.username().is_empty() => {
Err(AddrError::InvalidPubkey)?
}
(Ok(ip), Ok(pubkey)) => PartialNodeAddr::ZmqTcpEncrypted(
pubkey, zmq_type, ip, port,
),
(Ok(ip), _) => {
PartialNodeAddr::ZmqTcpUnencrypted(zmq_type, ip, port)
}
(Err(_), _) => {
if url.path().is_empty() {
Err(AddrError::ZmqContextRequired)?
}
PartialNodeAddr::ZmqIpc(
url.path().to_string(),
zmq_type,
)
}
})
}
"lnpt" => {
if let Ok(_) = pubkey {
Err(AddrError::UnexpectedHost)?
}
if let Some(_) = port {
Err(AddrError::UnexpectedPort)?
}
if let Some(host) = host {
Ok(PartialNodeAddr::Text(secp256k1::PublicKey::from_str(
host,
)?))
} else {
Err(AddrError::InvalidPubkey)?
}
}
unknown => Err(AddrError::UnknownUrlScheme(unknown.to_string())),
}
}
}
impl From<PartialNodeAddr> for NodeAddr {
fn from(locator: PartialNodeAddr) -> Self {
RemoteNodeAddr::try_from(locator.clone())
.map(|addr| NodeAddr::Remote(addr))
.unwrap_or_else(|_| {
NodeAddr::Local(LocalSocketAddr::try_from(locator).expect(
"PartialNodeAddr must convert to either NodeAddr or LocalAddr",
))
})
}
}
impl TryFrom<PartialNodeAddr> for LocalSocketAddr {
type Error = AddrError;
fn try_from(value: PartialNodeAddr) -> Result<Self, Self::Error> {
Ok(match value {
PartialNodeAddr::Posix(path) => LocalSocketAddr::Posix(path),
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqIpc(path, ..) => {
LocalSocketAddr::Zmq(ZmqSocketAddr::Ipc(path))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqInproc(name, ..) => {
LocalSocketAddr::Zmq(ZmqSocketAddr::Inproc(name))
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpUnencrypted(_, ip, Some(port)) => {
LocalSocketAddr::Zmq(ZmqSocketAddr::Tcp(SocketAddr::new(
ip, port,
)))
}
_ => Err(AddrError::Unsupported("local socket address"))?,
})
}
}
impl TryFrom<PartialNodeAddr> for RemoteNodeAddr {
type Error = AddrError;
fn try_from(locator: PartialNodeAddr) -> Result<Self, Self::Error> {
match locator {
PartialNodeAddr::Native(.., None) => Err(AddrError::PortRequired),
#[cfg(feature = "websocket")]
PartialNodeAddr::Websocket(.., None) => {
Err(AddrError::HostRequired)
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(.., None) => {
Err(AddrError::PortRequired)
}
PartialNodeAddr::Native(pubkey, address, Some(port)) => {
Ok(RemoteNodeAddr {
node_id: pubkey,
remote_addr: RemoteSocketAddr::Ftcp(InetSocketAddr {
address,
port,
}),
})
}
#[cfg(feature = "zmq")]
PartialNodeAddr::ZmqTcpEncrypted(pubkey, _, ip, Some(port)) => {
Ok(RemoteNodeAddr {
node_id: pubkey,
remote_addr: RemoteSocketAddr::Zmq(SocketAddr::new(
ip, port,
)),
})
}
#[cfg(feature = "websocket")]
PartialNodeAddr::Websocket(pubkey, addr, Some(port)) => {
Ok(RemoteNodeAddr {
node_id: pubkey,
remote_addr: RemoteSocketAddr::Websocket(
InetSocketAddr::new(addr, port),
),
})
}
_ => Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr")),
}
}
}
impl From<RemoteNodeAddr> for PartialNodeAddr {
fn from(node_addr: RemoteNodeAddr) -> PartialNodeAddr {
match node_addr.remote_addr {
RemoteSocketAddr::Ftcp(addr) => PartialNodeAddr::Native(
node_addr.node_id,
addr.address,
Some(addr.port),
),
#[cfg(feature = "zmq")]
RemoteSocketAddr::Zmq(addr) => PartialNodeAddr::ZmqTcpEncrypted(
node_addr.node_id,
ZmqType::Rep,
InetSocketAddr::from(addr)
.address
.try_into()
.expect("Conversion from just generated type can't fail"),
Some(addr.port()),
),
RemoteSocketAddr::Http(addr) => PartialNodeAddr::Http(
node_addr.node_id,
addr.address,
Some(addr.port),
),
#[cfg(feature = "websocket")]
RemoteSocketAddr::Websocket(addr) => PartialNodeAddr::Websocket(
node_addr.node_id,
addr.address,
Some(addr.port),
),
RemoteSocketAddr::Smtp(_) => {
PartialNodeAddr::Text(node_addr.node_id)
}
}
}
}
#[cfg(feature = "zmq")]
impl TryFrom<PartialNodeAddr> for ZmqSocketAddr {
type Error = AddrError;
fn try_from(socket_addr: PartialNodeAddr) -> Result<Self, Self::Error> {
Ok(match socket_addr {
PartialNodeAddr::ZmqIpc(path, _) => {
ZmqSocketAddr::Ipc(path)
}
PartialNodeAddr::ZmqTcpUnencrypted(_, ip, Some(port)) |
PartialNodeAddr::ZmqTcpEncrypted(_, _, ip, Some(port)) => {
ZmqSocketAddr::Tcp(SocketAddr::new(ip, port))
}
_ => Err(AddrError::Unsupported(
"Provided partial address can't be converted into a valid ZMQ socket"
))?
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_native() {
let pubkey1 = secp256k1::PublicKey::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let pubkey2 = secp256k1::PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let inet1 = InetAddr::from_str("127.0.0.1").unwrap();
let inet2 = InetAddr::from_str("127.0.0.2").unwrap();
let locator1 = PartialNodeAddr::Native(pubkey1, inet1, None);
let locator2 = PartialNodeAddr::Native(pubkey2, inet2, None);
assert_ne!(locator1, locator2);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnp");
assert_eq!(locator1.node_id(), Some(pubkey1));
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), None);
assert_eq!(locator1.inet_addr(), Some(inet1));
assert_eq!(locator1.socket_name(), None);
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), Some(24));
let socket_addr = InetSocketAddr {
address: inet1,
port: 24,
};
let node_addr = RemoteNodeAddr {
node_id: pubkey1,
remote_addr: RemoteSocketAddr::Ftcp(socket_addr),
};
let l = PartialNodeAddr::from(node_addr.clone());
assert_eq!(l, locator_with_port);
assert_ne!(l, locator1);
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::PortRequired)
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Ok(node_addr)
);
assert_eq!(
locator1.to_url_string(),
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
);
assert_eq!(
locator_with_port.to_url_string(),
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
);
assert_eq!(
l.to_url_string(),
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str(
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
).unwrap(),
locator1
);
assert_eq!(
PartialNodeAddr::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
).unwrap(),
locator_with_port
);
#[cfg(feature = "tor")]
{
use torut::onion::{OnionAddressV2, OnionAddressV3};
assert_eq!(
PartialNodeAddr::from_str(
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af\
@32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfeid"
).unwrap().inet_addr().unwrap().to_onion().unwrap(),
OnionAddressV3::from_str(
"32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfeid"
).unwrap()
);
assert_eq!(
PartialNodeAddr::from_str(
"lnp://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af\
@6zdgh5a5e6zpchdz"
).unwrap().inet_addr().unwrap().to_onion_v2().unwrap(),
OnionAddressV2::from_str(
"6zdgh5a5e6zpchdz"
).unwrap()
);
}
}
}
#[test]
fn test_udp() {
let pubkey1 = secp256k1::PublicKey::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let pubkey2 = secp256k1::PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let inet1 = IpAddr::from_str("127.0.0.1").unwrap();
let inet2 = IpAddr::from_str("127.0.0.2").unwrap();
let locator1 = PartialNodeAddr::Udp(pubkey1, inet1, None);
let locator2 = PartialNodeAddr::Udp(pubkey2, inet2, None);
assert_ne!(locator1, locator2);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpu");
assert_eq!(locator1.node_id(), Some(pubkey1));
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), None);
assert_eq!(locator1.inet_addr(), Some(InetAddr::from(inet1)));
assert_eq!(locator1.socket_name(), None);
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), Some(24));
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
locator1.to_url_string(),
"lnpu://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
);
assert_eq!(
locator_with_port.to_url_string(),
"lnpu://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str(
"lnpu://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
).unwrap(),
locator1
);
assert_eq!(
PartialNodeAddr::from_str(
"lnpu://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
).unwrap(),
locator_with_port
);
}
}
#[cfg(feature = "websockets")]
#[test]
fn test_websocket() {
let pubkey1 = secp256k1::PublicKey::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let pubkey2 = secp256k1::PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let inet1 = InetAddr::from_str("127.0.0.1").unwrap();
let inet2 = InetAddr::from_str("127.0.0.2").unwrap();
let locator1 = PartialNodeAddr::Websocket(pubkey1, inet1, None);
let locator2 = PartialNodeAddr::Websocket(pubkey2, inet2, None);
assert_ne!(locator1, locator2);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpws");
assert_eq!(locator1.node_id(), Some(pubkey1));
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), None);
assert_eq!(locator1.inet_addr(), Some(InetAddr::from(inet1)));
assert_eq!(locator1.socket_name(), None);
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), Some(24));
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
locator1.to_url_string(),
"lnpws://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
);
assert_eq!(
locator_with_port.to_url_string(),
"lnpws://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str(
"lnpws://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1"
).unwrap(),
locator1
);
assert_eq!(
PartialNodeAddr::from_str(
"lnpws://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24"
).unwrap(),
locator_with_port
);
}
}
#[cfg(feature = "zmq")]
#[test]
fn test_zmq_encrypted() {
let pubkey1 = secp256k1::PublicKey::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let pubkey2 = secp256k1::PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let inet1 = IpAddr::from_str("127.0.0.1").unwrap();
let inet2 = IpAddr::from_str("127.0.0.2").unwrap();
let locator1 = PartialNodeAddr::ZmqTcpEncrypted(
pubkey1,
ZmqType::Push,
inet1,
None,
);
let locator2 = PartialNodeAddr::ZmqTcpEncrypted(
pubkey2,
ZmqType::Req,
inet2,
None,
);
let locator3 = PartialNodeAddr::ZmqTcpEncrypted(
pubkey1,
ZmqType::Pull,
inet1,
None,
);
let locator4 = PartialNodeAddr::ZmqTcpEncrypted(
pubkey2,
ZmqType::Rep,
inet2,
None,
);
assert_ne!(locator1, locator2);
assert_ne!(locator2, locator4);
assert_ne!(locator1, locator3);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpz");
assert_eq!(locator1.node_id(), Some(pubkey1));
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), Some(ZmqType::Push));
assert_eq!(locator1.inet_addr(), Some(InetAddr::from(inet1)));
assert_eq!(locator1.socket_name(), None);
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), Some(24));
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::PortRequired)
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Ok(RemoteNodeAddr {
node_id: pubkey1,
remote_addr: RemoteSocketAddr::Zmq(SocketAddr::new(inet1, 24))
})
);
assert_eq!(
locator1.to_url_string(),
"lnpz://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1/?api=p2p"
);
assert_eq!(
locator2.to_url_string(),
"lnpz://032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.2/?api=rpc"
);
assert_eq!(
locator_with_port.to_url_string(),
"lnpz://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24/?api=p2p"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str(
"lnpz://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1/?api=p2p"
).unwrap(),
locator1
);
assert_eq!(
PartialNodeAddr::from_str(
"lnpz://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.0.1:24/?api=p2p"
).unwrap(),
locator1.with_port(24)
);
}
}
#[cfg(feature = "zmq")]
#[test]
fn test_zmq_unencrypted() {
let inet1 = IpAddr::from_str("127.0.0.1").unwrap();
let inet2 = IpAddr::from_str("127.0.0.2").unwrap();
let locator1 =
PartialNodeAddr::ZmqTcpUnencrypted(ZmqType::Push, inet1, None);
let locator2 =
PartialNodeAddr::ZmqTcpUnencrypted(ZmqType::Req, inet2, None);
let locator3 =
PartialNodeAddr::ZmqTcpUnencrypted(ZmqType::Pull, inet1, None);
let locator4 =
PartialNodeAddr::ZmqTcpUnencrypted(ZmqType::Rep, inet2, None);
assert_ne!(locator1, locator2);
assert_ne!(locator2, locator4);
assert_ne!(locator1, locator3);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpz");
assert_eq!(locator1.node_id(), None);
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), Some(ZmqType::Push));
assert_eq!(locator1.inet_addr(), Some(InetAddr::from(inet1)));
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), Some(24));
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(locator1.to_url_string(), "lnpz://127.0.0.1/?api=p2p");
assert_eq!(locator2.to_url_string(), "lnpz://127.0.0.2/?api=rpc");
assert_eq!(
locator_with_port.to_url_string(),
"lnpz://127.0.0.1:24/?api=p2p"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str("lnpz://127.0.0.1/?api=p2p").unwrap(),
locator1
);
}
}
#[cfg(feature = "zmq")]
#[test]
fn test_zmq_inproc() {
let locator1 = PartialNodeAddr::ZmqInproc(s!("socket1"), ZmqType::Push);
let locator1_1 =
PartialNodeAddr::ZmqInproc(s!("socket1"), ZmqType::Push);
let locator2 = PartialNodeAddr::ZmqInproc(s!("socket2"), ZmqType::Req);
let locator3 = PartialNodeAddr::ZmqInproc(s!("socket1"), ZmqType::Pull);
let locator4 = PartialNodeAddr::ZmqInproc(s!("socket2"), ZmqType::Rep);
assert_eq!(locator1, locator1_1);
assert_ne!(locator1, locator2);
assert_ne!(locator2, locator4);
assert_ne!(locator1, locator3);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpz");
assert_eq!(locator1.node_id(), None);
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), Some(ZmqType::Push));
assert_eq!(locator1.inet_addr(), None);
assert_eq!(locator1.socket_name(), Some(s!("socket1")));
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), None);
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(locator1.to_url_string(), "lnpz:?api=p2p#socket1");
assert_eq!(locator2.to_url_string(), "lnpz:?api=rpc#socket2");
assert_eq!(locator_with_port.to_url_string(), "lnpz:?api=p2p#socket1");
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str("lnpz:?api=p2p#socket1").unwrap_err(),
AddrError::ZmqContextRequired
);
}
}
#[cfg(feature = "zmq")]
#[test]
fn test_zmq_ipc() {
let locator1 = PartialNodeAddr::ZmqIpc(s!("./socket1"), ZmqType::Push);
let locator2 = PartialNodeAddr::ZmqIpc(s!("./socket2"), ZmqType::Req);
let locator3 = PartialNodeAddr::ZmqIpc(s!("./socket1"), ZmqType::Pull);
let locator4 = PartialNodeAddr::ZmqIpc(s!("./socket2"), ZmqType::Rep);
assert_ne!(locator1, locator2);
assert_ne!(locator2, locator4);
assert_ne!(locator1, locator3);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpz");
assert_eq!(locator1.node_id(), None);
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), Some(ZmqType::Push));
assert_eq!(locator1.inet_addr(), None);
assert_eq!(locator1.socket_name(), Some(s!("./socket1")));
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), None);
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(locator1.to_url_string(), "lnpz:./socket1?api=p2p");
assert_eq!(locator2.to_url_string(), "lnpz:./socket2?api=rpc");
assert_eq!(locator_with_port.to_url_string(), "lnpz:./socket1?api=p2p");
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str("lnpz:./socket1?api=p2p").unwrap(),
locator1
);
}
}
#[test]
fn test_text() {
let pubkey1 = secp256k1::PublicKey::from_str(
"022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let pubkey2 = secp256k1::PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap();
let locator1 = PartialNodeAddr::Text(pubkey1);
let locator2 = PartialNodeAddr::Text(pubkey2);
assert_ne!(locator1, locator2);
assert_eq!(locator1, locator1.clone());
assert_eq!(locator2, locator2.clone());
assert_eq!(locator1.url_scheme(), "lnpt");
assert_eq!(locator1.node_id(), Some(pubkey1));
assert_eq!(locator1.port(), None);
assert_eq!(locator1.api_type(), None);
assert_eq!(locator1.inet_addr(), None);
assert_eq!(locator1.socket_name(), None);
let locator_with_port = locator1.with_port(24);
assert_eq!(locator_with_port.port(), None);
assert_eq!(
RemoteNodeAddr::try_from(locator1.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
RemoteNodeAddr::try_from(locator_with_port.clone()),
Err(AddrError::Unsupported("Given PartialNodeAddr type can't be converted into RemoteNodeAddr"))
);
assert_eq!(
locator1.to_url_string(),
"lnpt://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
);
assert_eq!(
locator_with_port.to_url_string(),
"lnpt://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
);
#[cfg(feature = "url")]
{
assert_eq!(
PartialNodeAddr::from_str(
"lnpt://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
).unwrap(),
locator1
);
assert_eq!(
PartialNodeAddr::from_str(
"lnpt://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af@127.0.01"
).unwrap_err(),
AddrError::UnexpectedHost
);
assert_eq!(
PartialNodeAddr::from_str(
"lnpt://022e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af:1323"
).unwrap_err(),
AddrError::UnexpectedPort
);
}
}
}