diff --git a/ucan/src/builder.rs b/ucan/src/builder.rs index bbb8cf80..d259a3f1 100644 --- a/ucan/src/builder.rs +++ b/ucan/src/builder.rs @@ -62,6 +62,18 @@ where false => None, }; + let facts = if self.facts.is_empty() { + None + } else { + Some(self.facts.clone()) + }; + + let proofs = if self.proofs.is_empty() { + None + } else { + Some(self.proofs.clone()) + }; + Ok(UcanPayload { aud: self.audience.clone(), iss: self.issuer.get_did().await?, @@ -69,8 +81,8 @@ where nbf: self.not_before, nnc: nonce, att: self.capabilities.clone(), - fct: self.facts.clone(), - prf: self.proofs.clone(), + fct: facts, + prf: proofs, }) } diff --git a/ucan/src/chain.rs b/ucan/src/chain.rs index afe48d65..dab6c458 100644 --- a/ucan/src/chain.rs +++ b/ucan/src/chain.rs @@ -62,13 +62,15 @@ impl ProofChain { let mut proofs: Vec = Vec::new(); - for cid_string in ucan.proofs().iter() { - let cid = Cid::try_from(cid_string.as_str())?; - let ucan_token = store.require_token(&cid).await?; - let proof_chain = - Self::try_from_token_string(&ucan_token, now_time, did_parser, store).await?; - proof_chain.validate_link_to(&ucan)?; - proofs.push(proof_chain); + if let Some(ucan_proofs) = ucan.proofs() { + for cid_string in ucan_proofs.iter() { + let cid = Cid::try_from(cid_string.as_str())?; + let ucan_token = store.require_token(&cid).await?; + let proof_chain = + Self::try_from_token_string(&ucan_token, now_time, did_parser, store).await?; + proof_chain.validate_link_to(&ucan)?; + proofs.push(proof_chain); + } } let mut redelegations = BTreeSet::::new(); diff --git a/ucan/src/ipld/ucan.rs b/ucan/src/ipld/ucan.rs index 26d2c40b..2750284e 100644 --- a/ucan/src/ipld/ucan.rs +++ b/ucan/src/ipld/ucan.rs @@ -19,9 +19,9 @@ pub struct UcanIpld { pub s: Signature, pub att: Vec, - pub prf: Vec, + pub prf: Option>, pub exp: u64, - pub fct: Vec, + pub fct: Option>, pub nnc: Option, pub nbf: Option, @@ -31,10 +31,19 @@ impl TryFrom<&Ucan> for UcanIpld { type Error = anyhow::Error; fn try_from(ucan: &Ucan) -> Result { - let mut prf = Vec::new(); - for cid_string in ucan.proofs() { - prf.push(Cid::try_from(cid_string.as_str())?); - } + let prf = if let Some(proofs) = ucan.proofs() { + let mut prf = Vec::new(); + for cid_string in proofs { + prf.push(Cid::try_from(cid_string.as_str())?); + } + if prf.is_empty() { + None + } else { + Some(prf) + } + } else { + None + }; Ok(UcanIpld { v: ucan.version().to_string(), @@ -74,7 +83,10 @@ impl TryFrom<&UcanIpld> for Ucan { nnc: value.nnc.clone(), att: value.att.clone(), fct: value.fct.clone(), - prf: value.prf.iter().map(|cid| cid.to_string()).collect(), + prf: value + .prf + .clone() + .map(|prf| prf.iter().map(|cid| cid.to_string()).collect()), }; let signed_data = format!( diff --git a/ucan/src/tests/builder.rs b/ucan/src/tests/builder.rs index 10c4aedb..13d5a5e1 100644 --- a/ucan/src/tests/builder.rs +++ b/ucan/src/tests/builder.rs @@ -61,7 +61,7 @@ async fn it_builds_with_a_simple_example() { assert_eq!(ucan.expires_at(), &expiration); assert!(ucan.not_before().is_some()); assert_eq!(ucan.not_before().unwrap(), not_before); - assert_eq!(ucan.facts(), &vec![fact_1, fact_2]); + assert_eq!(ucan.facts(), &Some(vec![fact_1, fact_2])); let expected_attenuations = Vec::from([CapabilityIpld::from(&cap_1), CapabilityIpld::from(&cap_2)]); @@ -132,6 +132,6 @@ async fn it_prevents_duplicate_proofs() { assert_eq!( next_ucan.proofs(), - &vec![Cid::try_from(ucan).unwrap().to_string()] + &Some(vec![Cid::try_from(ucan).unwrap().to_string()]) ) } diff --git a/ucan/src/tests/ucan.rs b/ucan/src/tests/ucan.rs index 668cc28c..f41f0def 100644 --- a/ucan/src/tests/ucan.rs +++ b/ucan/src/tests/ucan.rs @@ -103,8 +103,6 @@ mod validate { "exp": ucan.expires_at(), "nbf": ucan.not_before(), "att": [], - "fct": [], - "prf": [] }, "signed_data": ucan.signed_data(), "signature": ucan.signature() diff --git a/ucan/src/ucan.rs b/ucan/src/ucan.rs index d5447c23..a9dc9aa3 100644 --- a/ucan/src/ucan.rs +++ b/ucan/src/ucan.rs @@ -34,8 +34,10 @@ pub struct UcanPayload { #[serde(skip_serializing_if = "Option::is_none")] pub nnc: Option, pub att: Vec, - pub fct: Vec, - pub prf: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub fct: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub prf: Option>, } #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] @@ -153,7 +155,7 @@ impl Ucan { &self.payload.aud } - pub fn proofs(&self) -> &Vec { + pub fn proofs(&self) -> &Option> { &self.payload.prf } @@ -173,7 +175,7 @@ impl Ucan { &self.payload.att } - pub fn facts(&self) -> &Vec { + pub fn facts(&self) -> &Option> { &self.payload.fct }