use crate::bin_types::*;
use crate::dht_arc::DhtArc;
use crate::tx2::tx2_utils::TxUrl;
use crate::*;
use agent_info_helper::*;
pub type UrlList = Vec<TxUrl>;
pub type AgentArc = (Arc<KitsuneAgent>, DhtArc);
pub mod agent_info_helper {
use super::*;
#[allow(missing_docs)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct AgentMetaInfoEncode {
pub dht_storage_arc_half_length: u32,
}
#[allow(missing_docs)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct AgentInfoEncode {
pub space: Arc<KitsuneSpace>,
pub agent: Arc<KitsuneAgent>,
pub urls: UrlList,
pub signed_at_ms: u64,
pub expires_after_ms: u64,
#[serde(with = "serde_bytes")]
pub meta_info: Box<[u8]>,
}
#[allow(missing_docs)]
#[derive(Debug, serde::Deserialize)]
pub struct AgentInfoSignedEncode {
pub agent: Arc<KitsuneAgent>,
pub signature: Arc<KitsuneSignature>,
#[serde(with = "serde_bytes")]
pub agent_info: Box<[u8]>,
}
#[allow(missing_docs)]
#[derive(Debug, serde::Serialize)]
pub struct AgentInfoSignedEncodeRef<'lt> {
pub agent: &'lt Arc<KitsuneAgent>,
pub signature: &'lt Arc<KitsuneSignature>,
#[serde(with = "serde_bytes")]
pub agent_info: &'lt [u8],
}
}
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
pub struct AgentInfoInner {
pub space: Arc<KitsuneSpace>,
pub agent: Arc<KitsuneAgent>,
pub storage_arc: DhtArc,
pub url_list: UrlList,
pub signed_at_ms: u64,
pub expires_at_ms: u64,
pub signature: Arc<KitsuneSignature>,
pub encoded_bytes: Box<[u8]>,
}
impl AgentInfoInner {
pub fn is_active(&self) -> bool {
!self.url_list.is_empty()
}
}
impl std::fmt::Debug for AgentInfoInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AgentInfoSigned")
.field("space", &self.space)
.field("agent", &self.agent)
.field("storage_arc", &self.storage_arc)
.field("url_list", &self.url_list)
.field("signed_at_ms", &self.signed_at_ms)
.field("expires_at_ms", &self.expires_at_ms)
.finish()
}
}
impl PartialEq for AgentInfoInner {
fn eq(&self, oth: &Self) -> bool {
self.encoded_bytes.eq(&oth.encoded_bytes)
}
}
impl Eq for AgentInfoInner {}
impl std::hash::Hash for AgentInfoInner {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.encoded_bytes.hash(state);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
pub struct AgentInfoSigned(pub Arc<AgentInfoInner>);
impl std::ops::Deref for AgentInfoSigned {
type Target = AgentInfoInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl serde::Serialize for AgentInfoSigned {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let encode = AgentInfoSignedEncodeRef {
agent: &self.agent,
signature: &self.signature,
agent_info: &self.encoded_bytes,
};
encode.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for AgentInfoSigned {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let AgentInfoSignedEncode {
agent,
signature,
agent_info,
} = AgentInfoSignedEncode::deserialize(deserializer)?;
let mut bytes: &[u8] = &agent_info;
let info: AgentInfoEncode =
crate::codec::rmp_decode(&mut bytes).map_err(serde::de::Error::custom)?;
let mut bytes: &[u8] = &info.meta_info;
let meta: AgentMetaInfoEncode =
crate::codec::rmp_decode(&mut bytes).map_err(serde::de::Error::custom)?;
if agent != info.agent {
return Err(serde::de::Error::custom("agent mismatch"));
}
let start_loc = agent.get_loc();
let storage_arc =
DhtArc::from_start_and_half_len(start_loc, meta.dht_storage_arc_half_length);
let AgentInfoEncode {
space,
agent,
urls,
signed_at_ms,
expires_after_ms,
..
} = info;
let inner = AgentInfoInner {
space,
agent,
storage_arc,
url_list: urls,
signed_at_ms,
expires_at_ms: signed_at_ms + expires_after_ms,
signature,
encoded_bytes: agent_info,
};
Ok(AgentInfoSigned(Arc::new(inner)))
}
}
impl AgentInfoSigned {
pub async fn sign<'a, R, F>(
space: Arc<KitsuneSpace>,
agent: Arc<KitsuneAgent>,
dht_storage_arc_half_length: u32,
url_list: UrlList,
signed_at_ms: u64,
expires_at_ms: u64,
f: F,
) -> KitsuneResult<Self>
where
R: std::future::Future<Output = KitsuneResult<Arc<KitsuneSignature>>>,
F: FnOnce(&[u8]) -> R,
{
let meta = AgentMetaInfoEncode {
dht_storage_arc_half_length,
};
let mut buf = Vec::new();
crate::codec::rmp_encode(&mut buf, meta).map_err(KitsuneError::other)?;
let meta = buf.into_boxed_slice();
let info = AgentInfoEncode {
space: space.clone(),
agent: agent.clone(),
urls: url_list.clone(),
signed_at_ms,
expires_after_ms: expires_at_ms - signed_at_ms,
meta_info: meta,
};
let mut buf = Vec::new();
crate::codec::rmp_encode(&mut buf, info).map_err(KitsuneError::other)?;
let encoded_bytes = buf.into_boxed_slice();
let signature = f(&encoded_bytes).await?;
let start_loc = agent.get_loc();
let inner = AgentInfoInner {
space,
agent,
storage_arc: DhtArc::from_start_and_half_len(start_loc, dht_storage_arc_half_length),
url_list,
signed_at_ms,
expires_at_ms,
signature,
encoded_bytes,
};
Ok(Self(Arc::new(inner)))
}
pub fn decode(b: &[u8]) -> KitsuneResult<Self> {
let mut bytes: &[u8] = b;
crate::codec::rmp_decode(&mut bytes).map_err(KitsuneError::other)
}
pub fn encode(&self) -> KitsuneResult<Box<[u8]>> {
let mut buf = Vec::new();
crate::codec::rmp_encode(&mut buf, self).map_err(KitsuneError::other)?;
Ok(buf.into_boxed_slice())
}
pub fn to_agent_arc(&self) -> AgentArc {
(self.agent.clone(), self.storage_arc)
}
pub fn agent(&self) -> Arc<KitsuneAgent> {
self.agent.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn agent_info() {
let space = Arc::new(KitsuneSpace(vec![0x01; 36]));
let agent = Arc::new(KitsuneAgent(vec![0x02; 36]));
let info = AgentInfoSigned::sign(
space.clone(),
agent.clone(),
42,
vec![],
42,
69,
|_| async move { Ok(Arc::new(vec![0x03; 64].into())) },
)
.await
.unwrap();
assert_eq!(info.space, space);
assert_eq!(info.agent, agent);
let mut enc = Vec::new();
crate::codec::rmp_encode(&mut enc, &info).unwrap();
let mut bytes: &[u8] = &enc;
let info2: AgentInfoSigned = crate::codec::rmp_decode(&mut bytes).unwrap();
assert_eq!(info, info2);
}
}