use candid::{CandidType, Principal};
use serde::{Deserialize, Serialize};
mod bytes;
mod cbor;
mod xid;
pub use bytes::*;
pub use cbor::*;
pub use xid::*;
#[derive(CandidType, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct Delegation {
#[serde(alias = "p")]
pub pubkey: ByteBufB64,
#[serde(alias = "e")]
pub expiration: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(alias = "t")]
pub targets: Option<Vec<Principal>>,
}
#[derive(CandidType, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct SignedDelegation {
#[serde(alias = "d")]
pub delegation: Delegation,
#[serde(alias = "s")]
pub signature: ByteBufB64,
}
#[derive(CandidType, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct SignInResponse {
pub expiration: u64,
pub user_key: ByteBufB64,
pub seed: ByteBufB64,
}
#[derive(CandidType, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct DelegationCompact {
#[serde(rename = "p", alias = "pubkey")]
pub pubkey: ByteBufB64,
#[serde(rename = "e", alias = "expiration")]
pub expiration: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "t", alias = "targets")]
pub targets: Option<Vec<Principal>>,
}
#[derive(CandidType, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct SignedDelegationCompact {
#[serde(rename = "d", alias = "delegation")]
pub delegation: DelegationCompact,
#[serde(rename = "s", alias = "signature")]
pub signature: ByteBufB64,
}
impl From<DelegationCompact> for Delegation {
fn from(d: DelegationCompact) -> Self {
Self {
pubkey: d.pubkey,
expiration: d.expiration,
targets: d.targets,
}
}
}
impl From<Delegation> for DelegationCompact {
fn from(d: Delegation) -> Self {
Self {
pubkey: d.pubkey,
expiration: d.expiration,
targets: d.targets,
}
}
}
impl From<SignedDelegationCompact> for SignedDelegation {
fn from(d: SignedDelegationCompact) -> Self {
Self {
delegation: d.delegation.into(),
signature: d.signature,
}
}
}
impl From<SignedDelegation> for SignedDelegationCompact {
fn from(d: SignedDelegation) -> Self {
Self {
delegation: d.delegation.into(),
signature: d.signature,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delegation_format() {
let d = Delegation {
pubkey: ByteBufB64(vec![1, 2, 3, 4]),
expiration: 99,
targets: Some(vec![Principal::management_canister()]),
};
let data = serde_json::to_string(&d).unwrap();
println!("{data}");
assert_eq!(
data,
r#"{"pubkey":"AQIDBA==","expiration":99,"targets":["aaaaa-aa"]}"#
);
let d1: Delegation = serde_json::from_str(&data).unwrap();
assert_eq!(d, d1);
let mut data = Vec::new();
cbor2::to_writer(&d, &mut data).unwrap();
println!("{}", hex::encode(&data));
assert_eq!(
data,
hex::decode("a3667075626b657944010203046a65787069726174696f6e186367746172676574738140")
.unwrap()
);
let d1: Delegation = cbor_from_slice(&data[..]).unwrap();
assert_eq!(d, d1);
}
#[test]
fn test_compact_conversion_and_candid_roundtrips() {
let delegation = Delegation {
pubkey: ByteBufB64(vec![1, 2, 3, 4]),
expiration: 99,
targets: Some(vec![Principal::management_canister()]),
};
let compact: DelegationCompact = delegation.clone().into();
assert_eq!(compact.pubkey, delegation.pubkey);
assert_eq!(compact.expiration, delegation.expiration);
assert_eq!(compact.targets, delegation.targets);
let expanded: Delegation = compact.clone().into();
assert_eq!(expanded, delegation);
let signed = SignedDelegation {
delegation,
signature: ByteBufB64(vec![5, 6, 7, 8]),
};
let compact_signed: SignedDelegationCompact = signed.clone().into();
assert_eq!(compact_signed.delegation, compact);
assert_eq!(compact_signed.signature, signed.signature);
let expanded_signed: SignedDelegation = compact_signed.into();
assert_eq!(expanded_signed, signed);
let sign_in = SignInResponse {
expiration: 123,
user_key: ByteBufB64(vec![9, 10]),
seed: ByteBufB64(vec![11, 12]),
};
let encoded = candid::encode_one(sign_in.clone()).unwrap();
let decoded: SignInResponse = candid::decode_one(&encoded).unwrap();
assert_eq!(decoded, sign_in);
let encoded = candid::encode_one(signed.clone()).unwrap();
let decoded: SignedDelegation = candid::decode_one(&encoded).unwrap();
assert_eq!(decoded, signed);
}
#[test]
fn test_candid_type_metadata_is_available() {
let types = [
Delegation::ty(),
SignedDelegation::ty(),
SignInResponse::ty(),
DelegationCompact::ty(),
SignedDelegationCompact::ty(),
];
assert!(types.iter().all(|ty| !format!("{ty:?}").is_empty()));
}
}