use alloy::primitives::{Address, B256, U256, keccak256};
#[must_use]
pub fn ownership_message_digest(
node_id: B256,
operator: Address,
chain_id: u64,
registration_nonce: u64,
) -> B256 {
let mut preimage = Vec::with_capacity(92);
preimage.extend_from_slice(node_id.as_slice()); preimage.extend_from_slice(operator.as_slice()); preimage.extend_from_slice(&U256::from(chain_id).to_be_bytes::<32>()); preimage.extend_from_slice(®istration_nonce.to_be_bytes()); keccak256(preimage)
}
pub fn pack_multiaddrs(addrs: &[String]) -> anyhow::Result<Vec<u8>> {
let mut out = Vec::with_capacity(addrs.iter().map(|a| a.len() + 2).sum());
for addr in addrs {
let bytes = addr.as_bytes();
let len = u16::try_from(bytes.len()).map_err(|_| {
anyhow::anyhow!(
"multiaddr is {} bytes, exceeds the uint16 length prefix max of {}: {addr}",
bytes.len(),
u16::MAX,
)
})?;
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(bytes);
}
Ok(out)
}
pub fn unpack_multiaddrs(packed: &[u8]) -> anyhow::Result<Vec<String>> {
let mut out = Vec::new();
let mut rest = packed;
while !rest.is_empty() {
let (prefix, body) = rest.split_at_checked(2).ok_or_else(|| {
anyhow::anyhow!(
"truncated multiaddr length prefix ({} byte(s) left)",
rest.len()
)
})?;
let len =
usize::from(u16::from_be_bytes(prefix.try_into().map_err(|_| {
anyhow::anyhow!("multiaddr length prefix is not 2 bytes")
})?));
let (entry, tail) = body.split_at_checked(len).ok_or_else(|| {
anyhow::anyhow!(
"multiaddr entry claims {len} bytes but only {} remain",
body.len()
)
})?;
out.push(
std::str::from_utf8(entry)
.map_err(|e| anyhow::anyhow!("multiaddr entry is not UTF-8: {e}"))?
.to_string(),
);
rest = tail;
}
Ok(out)
}
#[must_use]
pub fn decode_dial_addrs(packed: &[u8]) -> Vec<std::net::SocketAddr> {
let mut out = Vec::new();
let mut rest = packed;
while let Some((prefix, body)) = rest.split_at_checked(2) {
let Ok(len_bytes) = <[u8; 2]>::try_from(prefix) else {
break;
};
let len = usize::from(u16::from_be_bytes(len_bytes));
let Some((entry, tail)) = body.split_at_checked(len) else {
break;
};
if let Ok(s) = std::str::from_utf8(entry)
&& let Some(sock) = parse_quic_multiaddr(s)
{
out.push(sock);
}
rest = tail;
}
out
}
fn parse_quic_multiaddr(s: &str) -> Option<std::net::SocketAddr> {
let mut segs = s.strip_prefix('/')?.split('/');
let proto = segs.next()?;
if proto != "ip4" && proto != "ip6" {
return None;
}
let ip: std::net::IpAddr = segs.next()?.parse().ok()?;
if proto == "ip4" && !ip.is_ipv4() || proto == "ip6" && !ip.is_ipv6() {
return None;
}
if segs.next()? != "udp" {
return None;
}
let port: u16 = segs.next()?.parse().ok()?;
if segs.next()? != "quic-v1" {
return None;
}
Some(std::net::SocketAddr::new(ip, port))
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
mod tests;