use crate::error::Error;
use crate::subnet_id::SubnetID;
use crate::{deserialize_human_readable_str, HumanReadable};
use fvm_shared::address::{Address, Protocol};
use serde::ser::Error as SerializeError;
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
use std::{fmt, str::FromStr};
const IPC_SEPARATOR_ADDR: &str = ":";
#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize_tuple, Deserialize_tuple)]
pub struct IPCAddress {
subnet_id: SubnetID,
raw_address: Address,
}
impl IPCAddress {
pub fn new(sn: &SubnetID, addr: &Address) -> Result<Self, Error> {
Ok(Self {
subnet_id: sn.clone(),
raw_address: *addr,
})
}
pub fn subnet(&self) -> Result<SubnetID, Error> {
Ok(self.subnet_id.clone())
}
pub fn raw_addr(&self) -> Result<Address, Error> {
Ok(self.raw_address)
}
#[cfg(feature = "fil-actor")]
pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
Ok(recall_fil_actors_runtime::cbor::serialize(self, "ipc-address")?.to_vec())
}
#[cfg(feature = "fil-actor")]
pub fn from_bytes(bz: &[u8]) -> Result<Self, Error> {
let i: Self = recall_fil_actors_runtime::cbor::deserialize(
&fvm_ipld_encoding::RawBytes::new(bz.to_vec()),
"ipc-address",
)?;
Ok(i)
}
pub fn to_string(&self) -> Result<String, Error> {
Ok(format!(
"{}{}{}",
self.subnet_id, IPC_SEPARATOR_ADDR, self.raw_address
))
}
pub fn is_valid_contract_address(addr: &Address) -> bool {
matches!(addr.protocol(), Protocol::Delegated | Protocol::Actor)
}
pub fn is_valid_account_address(addr: &Address) -> bool {
matches!(
addr.protocol(),
Protocol::Delegated | Protocol::BLS | Protocol::Secp256k1 | Protocol::ID
)
}
}
impl fmt::Display for IPCAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.subnet_id, IPC_SEPARATOR_ADDR)?;
write!(f, "{}", self.raw_address)
}
}
impl FromStr for IPCAddress {
type Err = Error;
fn from_str(addr: &str) -> Result<Self, Error> {
let r: Vec<&str> = addr.split(IPC_SEPARATOR_ADDR).collect();
if r.len() != 2 {
Err(Error::InvalidIPCAddr)
} else {
Ok(Self {
raw_address: Address::from_str(r[1])?,
subnet_id: SubnetID::from_str(r[0])?,
})
}
}
}
impl serde_with::SerializeAs<IPCAddress> for HumanReadable {
fn serialize_as<S>(address: &IPCAddress, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
address
.to_string()
.map_err(|e| {
S::Error::custom(format!("cannot convert ipc address to string: {e}"))
})?
.serialize(serializer)
} else {
address.serialize(serializer)
}
}
}
deserialize_human_readable_str!(IPCAddress);
#[cfg(test)]
mod tests {
use crate::address::IPCAddress;
use crate::subnet_id::SubnetID;
use fvm_shared::address::Address;
use std::str::FromStr;
use std::vec;
#[test]
fn test_ipc_address() {
let act = Address::new_id(1001);
let sub_id = SubnetID::new(123, vec![act]);
let bls = Address::from_str("f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a").unwrap();
let haddr = IPCAddress::new(&sub_id, &bls).unwrap();
let str = haddr.to_string().unwrap();
let blss = IPCAddress::from_str(&str).unwrap();
assert_eq!(haddr.raw_addr().unwrap(), bls);
assert_eq!(haddr.subnet().unwrap(), sub_id);
assert_eq!(haddr, blss);
}
#[test]
fn test_ipc_from_str() {
let sub_id = SubnetID::new(123, vec![Address::new_id(100)]);
let addr = IPCAddress::new(&sub_id, &Address::new_id(101)).unwrap();
let st = addr.to_string().unwrap();
let addr_out = IPCAddress::from_str(&st).unwrap();
assert_eq!(addr, addr_out);
let addr_out = IPCAddress::from_str(&format!("{}", addr)).unwrap();
assert_eq!(addr, addr_out);
}
#[cfg(feature = "fil-actor")]
#[test]
fn test_ipc_serialization() {
let sub_id = SubnetID::new(123, vec![Address::new_id(100)]);
let addr = IPCAddress::new(&sub_id, &Address::new_id(101)).unwrap();
let st = addr.to_bytes().unwrap();
let addr_out = IPCAddress::from_bytes(&st).unwrap();
assert_eq!(addr, addr_out);
}
}