diff --git a/src/delegation/agent.rs b/src/delegation/agent.rs index 89f86a8b..bfee4119 100644 --- a/src/delegation/agent.rs +++ b/src/delegation/agent.rs @@ -1,5 +1,6 @@ use super::{payload::Payload, policy::Predicate, store::Store, Delegation}; use crate::ability::arguments::Named; +use crate::did; use crate::{ crypto::{signature::Envelope, varsig, Nonce}, did::Did, @@ -14,40 +15,38 @@ use std::{collections::BTreeMap, marker::PhantomData}; use thiserror::Error; use web_time::SystemTime; -/// A stateful agent capable of delegatint to others, and being delegated to. +/// A stateful agent capable of delegating to others, and being delegated to. /// /// This is helpful for sessions where more than one delegation will be made. #[derive(Debug)] pub struct Agent< - 'a, - DID: Did, S: Store, - V: varsig::Header, - Enc: Codec + TryFrom + Into, + DID: Did = did::preset::Verifier, + V: varsig::Header + Clone = varsig::header::Preset, + Enc: Codec + Into + TryFrom = varsig::encoding::Preset, > { /// The [`Did`][Did] of the agent. - pub did: &'a DID, + pub did: DID, /// The attached [`deleagtion::Store`][super::store::Store]. - pub store: &'a mut S, + pub store: S, - signer: &'a ::Signer, + signer: ::Signer, _marker: PhantomData<(V, Enc)>, } impl< - 'a, - DID: Did + Clone, S: Store + Clone, + DID: Did + Clone, V: varsig::Header + Clone, Enc: Codec + TryFrom + Into, - > Agent<'a, DID, S, V, Enc> + > Agent where Ipld: Encode, Payload: TryFrom>, Named: From>, { - pub fn new(did: &'a DID, signer: &'a ::Signer, store: &'a mut S) -> Self { + pub fn new(did: DID, signer: ::Signer, store: S) -> Self { Self { did, store, @@ -73,7 +72,7 @@ where let nonce = Nonce::generate_12(&mut salt); if let Some(ref sub) = subject { - if sub == self.did { + if sub == &self.did { let payload: Payload = Payload { issuer: self.did.clone(), audience, @@ -88,19 +87,17 @@ where }; return Ok( - Delegation::try_sign(self.signer, varsig_header, payload).expect("FIXME") + Delegation::try_sign(&self.signer, varsig_header, payload).expect("FIXME") ); } } - let to_delegate = &self + let proofs = &self .store .get_chain(&self.did, &subject, "/".into(), vec![], now) .map_err(DelegateError::StoreError)? - .ok_or(DelegateError::ProofsNotFound)? - .first() - .1 - .payload(); + .ok_or(DelegateError::ProofsNotFound)?; + let to_delegate = proofs.first().1.payload(); let mut policy = to_delegate.policy.clone(); policy.append(&mut new_policy.clone()); @@ -118,11 +115,11 @@ where not_before: not_before.map(Into::into), }; - Ok(Delegation::try_sign(self.signer, varsig_header, payload).expect("FIXME")) + Ok(Delegation::try_sign(&self.signer, varsig_header, payload).expect("FIXME")) } pub fn receive( - &mut self, + &self, cid: Cid, // FIXME remove and generate from the capsule header? delegation: Delegation, ) -> Result<(), ReceiveError> { @@ -130,7 +127,7 @@ where return Ok(()); } - if delegation.audience() != self.did { + if delegation.audience() != &self.did { return Err(ReceiveError::WrongAudience(delegation.audience().clone())); } diff --git a/src/delegation/store/memory.rs b/src/delegation/store/memory.rs index 18d86103..c36a6e2d 100644 --- a/src/delegation/store/memory.rs +++ b/src/delegation/store/memory.rs @@ -10,7 +10,11 @@ use libipld_core::codec::Encode; use libipld_core::ipld::Ipld; use libipld_core::{cid::Cid, codec::Codec}; use nonempty::NonEmpty; -use std::collections::{BTreeMap, BTreeSet}; +use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::{ + collections::{BTreeMap, BTreeSet}, + convert::Infallible, +}; use web_time::SystemTime; #[cfg_attr(doc, aquamarine::aquamarine)] @@ -69,36 +73,77 @@ use web_time::SystemTime; /// linkStyle 6 stroke:orange; /// linkStyle 1 stroke:orange; /// ``` -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct MemoryStore< DID: did::Did + Ord = did::preset::Verifier, V: varsig::Header = varsig::header::Preset, C: Codec + TryFrom + Into = varsig::encoding::Preset, > { - ucans: BTreeMap>, + inner: Arc>>, +} + +#[derive(Debug, Clone, PartialEq)] +struct MemoryStoreInner< + DID: did::Did + Ord = did::preset::Verifier, + V: varsig::Header = varsig::header::Preset, + C: Codec + TryFrom + Into = varsig::encoding::Preset, +> { + ucans: BTreeMap>>, index: BTreeMap, BTreeMap>>, revocations: BTreeSet, } -impl MemoryStore { +impl, C: Codec + TryFrom + Into> + MemoryStore +{ pub fn new() -> Self { Self::default() } pub fn len(&self) -> usize { - self.ucans.len() + self.read().ucans.len() } pub fn is_empty(&self) -> bool { - self.ucans.is_empty() // FIXME acocunt for revocations? + self.read().ucans.is_empty() // FIXME acocunt for revocations? + } + + fn read(&self) -> RwLockReadGuard<'_, MemoryStoreInner> { + match self.inner.read() { + Ok(guard) => guard, + Err(poison) => { + // We ignore lock poisoning for simplicity + poison.into_inner() + } + } + } + + fn write(&self) -> RwLockWriteGuard<'_, MemoryStoreInner> { + match self.inner.write() { + Ok(guard) => guard, + Err(poison) => { + // We ignore lock poisoning for simplicity + poison.into_inner() + } + } } } -impl, C: Codec + TryFrom + Into> Default +impl, C: Codec + TryFrom + Into> Default for MemoryStore { fn default() -> Self { - MemoryStore { + Self { + inner: Default::default(), + } + } +} + +impl, C: Codec + TryFrom + Into> Default + for MemoryStoreInner +{ + fn default() -> Self { + MemoryStoreInner { ucans: BTreeMap::new(), index: BTreeMap::new(), revocations: BTreeSet::new(), @@ -117,34 +162,39 @@ where delegation::Payload: TryFrom>, Delegation: Encode, { - type DelegationStoreError = String; // FIXME misisng + type DelegationStoreError = Infallible; - fn get(&self, cid: &Cid) -> Result<&Delegation, Self::DelegationStoreError> { - self.ucans - .get(cid) - .ok_or(format!("not found in delegation memstore: {:?}", cid).into()) + fn get( + &self, + cid: &Cid, + ) -> Result>>, Self::DelegationStoreError> { + // cheap Arc clone + Ok(self.read().ucans.get(cid).cloned()) // FIXME } fn insert( - &mut self, + &self, cid: Cid, delegation: Delegation, ) -> Result<(), Self::DelegationStoreError> { - self.index + let mut write_tx = self.write(); + + write_tx + .index .entry(delegation.subject().clone()) .or_default() .entry(delegation.audience().clone()) .or_default() .insert(cid); - self.ucans.insert(cid.clone(), delegation); + write_tx.ucans.insert(cid.clone(), Arc::new(delegation)); Ok(()) } - fn revoke(&mut self, cid: Cid) -> Result<(), Self::DelegationStoreError> { - self.revocations.insert(cid); + fn revoke(&self, cid: Cid) -> Result<(), Self::DelegationStoreError> { + self.write().revocations.insert(cid); Ok(()) } @@ -155,12 +205,14 @@ where command: String, policy: Vec, // FIXME now: SystemTime, - ) -> Result)>>, Self::DelegationStoreError> { + ) -> Result>)>>, Self::DelegationStoreError> + { let blank_set = BTreeSet::new(); let blank_map = BTreeMap::new(); + let read_tx = self.read(); - let all_powerlines = self.index.get(&None).unwrap_or(&blank_map); - let all_aud_for_subject = self.index.get(subject).unwrap_or(&blank_map); + let all_powerlines = read_tx.index.get(&None).unwrap_or(&blank_map); + let all_aud_for_subject = read_tx.index.get(subject).unwrap_or(&blank_map); let powerline_candidates = all_powerlines.get(aud).unwrap_or(&blank_set); let sub_candidates = all_aud_for_subject.get(aud).unwrap_or(&blank_set); @@ -185,11 +237,11 @@ where } 'inner: for cid in parent_cid_candidates { - if self.revocations.contains(cid) { + if read_tx.revocations.contains(cid) { continue; } - if let Some(delegation) = self.ucans.get(cid) { + if let Some(delegation) = read_tx.ucans.get(cid) { if delegation.check_time(now).is_err() { continue; } @@ -217,7 +269,7 @@ where } } - hypothesis_chain.push((cid.clone(), delegation)); + hypothesis_chain.push((cid.clone(), Arc::clone(delegation))); let issuer = delegation.issuer().clone(); diff --git a/src/delegation/store/traits.rs b/src/delegation/store/traits.rs index 3bd7b1fb..7413e700 100644 --- a/src/delegation/store/traits.rs +++ b/src/delegation/store/traits.rs @@ -5,16 +5,19 @@ use crate::{ }; use libipld_core::{cid::Cid, codec::Codec}; use nonempty::NonEmpty; -use std::fmt::Debug; +use std::{fmt::Debug, sync::Arc}; use web_time::SystemTime; pub trait Store, Enc: Codec + TryFrom + Into> { type DelegationStoreError: Debug; - fn get(&self, cid: &Cid) -> Result<&Delegation, Self::DelegationStoreError>; + fn get( + &self, + cid: &Cid, + ) -> Result>>, Self::DelegationStoreError>; fn insert( - &mut self, + &self, cid: Cid, delegation: Delegation, ) -> Result<(), Self::DelegationStoreError>; @@ -22,7 +25,7 @@ pub trait Store, Enc: Codec + TryFrom + In // FIXME validate invocation // store invocation // just... move to invocation - fn revoke(&mut self, cid: Cid) -> Result<(), Self::DelegationStoreError>; + fn revoke(&self, cid: Cid) -> Result<(), Self::DelegationStoreError>; fn get_chain( &self, @@ -31,7 +34,7 @@ pub trait Store, Enc: Codec + TryFrom + In command: String, policy: Vec, now: SystemTime, - ) -> Result)>>, Self::DelegationStoreError>; + ) -> Result>)>>, Self::DelegationStoreError>; fn get_chain_cids( &self, @@ -60,32 +63,34 @@ pub trait Store, Enc: Codec + TryFrom + In fn get_many( &self, cids: &[Cid], - ) -> Result>, Self::DelegationStoreError> { - cids.iter().try_fold(vec![], |mut acc, cid| { - acc.push(self.get(cid)?); - Ok(acc) - }) + ) -> Result>>>, Self::DelegationStoreError> { + cids.iter() + .map(|cid| self.get(cid)) + .collect::>() } } impl, DID: Did, V: varsig::Header, C: Codec + TryFrom + Into> - Store for &mut T + Store for &T { type DelegationStoreError = >::DelegationStoreError; - fn get(&self, cid: &Cid) -> Result<&Delegation, Self::DelegationStoreError> { + fn get( + &self, + cid: &Cid, + ) -> Result>>, Self::DelegationStoreError> { (**self).get(cid) } fn insert( - &mut self, + &self, cid: Cid, delegation: Delegation, ) -> Result<(), Self::DelegationStoreError> { (**self).insert(cid, delegation) } - fn revoke(&mut self, cid: Cid) -> Result<(), Self::DelegationStoreError> { + fn revoke(&self, cid: Cid) -> Result<(), Self::DelegationStoreError> { (**self).revoke(cid) } @@ -96,7 +101,8 @@ impl, DID: Did, V: varsig::Header, C: Codec + TryFrom, now: SystemTime, - ) -> Result)>>, Self::DelegationStoreError> { + ) -> Result>)>>, Self::DelegationStoreError> + { (**self).get_chain(audience, subject, command, policy, now) } } diff --git a/src/invocation/agent.rs b/src/invocation/agent.rs index a4e5f7fd..11838bde 100644 --- a/src/invocation/agent.rs +++ b/src/invocation/agent.rs @@ -27,13 +27,13 @@ use std::{ collections::{BTreeMap, BTreeSet}, fmt, marker::PhantomData, + sync::Arc, }; use thiserror::Error; use web_time::SystemTime; #[derive(Debug)] pub struct Agent< - 'a, S: Store, D: delegation::store::Store, T: ToCommand = ability::preset::Preset, @@ -42,7 +42,7 @@ pub struct Agent< C: Codec + Into + TryFrom = varsig::encoding::Preset, > { /// The agent's [`DID`]. - pub did: &'a DID, + pub did: DID, /// A [`delegation::Store`][delegation::store::Store]. pub delegation_store: D, @@ -50,11 +50,11 @@ pub struct Agent< /// A [`Store`][Store] for the agent's [`Invocation`]s. pub invocation_store: S, - signer: &'a ::Signer, + signer: ::Signer, marker: PhantomData<(T, V, C)>, } -impl<'a, T, DID, S, D, V, C> Agent<'a, S, D, T, DID, V, C> +impl Agent where Ipld: Encode, T: ToCommand + Clone + ParseAbility, @@ -70,8 +70,8 @@ where >::DelegationStoreError: fmt::Debug, { pub fn new( - did: &'a DID, - signer: &'a ::Signer, + did: DID, + signer: ::Signer, invocation_store: S, delegation_store: D, ) -> Self { @@ -85,7 +85,7 @@ where } pub fn invoke( - &mut self, + &self, audience: Option, subject: DID, ability: T, @@ -171,7 +171,7 @@ where // } pub fn receive( - &mut self, + &self, invocation: Invocation, ) -> Result>, ReceiveError> where @@ -183,7 +183,7 @@ where } pub fn generic_receive( - &mut self, + &self, invocation: Invocation, now: SystemTime, ) -> Result>, ReceiveError> @@ -204,20 +204,27 @@ where .put(cid.clone(), invocation.clone()) .map_err(ReceiveError::InvocationStoreError)?; - let proof_payloads: Vec<&delegation::Payload> = self + let proofs = &self .delegation_store .get_many(&invocation.proofs()) - .map_err(ReceiveError::DelegationStoreError)? + .map_err(ReceiveError::DelegationStoreError)?; + let proof_payloads: Vec<&delegation::Payload> = proofs .iter() - .map(|d| &d.payload) - .collect(); + .zip(invocation.proofs().iter()) + .map(|(d, cid)| { + Ok(&d + .as_ref() + .ok_or(ReceiveError::MissingDelegation(*cid))? + .payload) + }) + .collect::>>()?; let _ = &invocation .payload .check(proof_payloads, now) .map_err(ReceiveError::ValidationError)?; - Ok(if invocation.normalized_audience() != self.did { + Ok(if invocation.normalized_audience() != &self.did { Recipient::Other(invocation.payload) } else { Recipient::You(invocation.payload) @@ -225,7 +232,7 @@ where } // pub fn revoke( - // &mut self, + // &self, // subject: DID, // cause: Option, // cid: Cid, @@ -290,6 +297,9 @@ pub enum ReceiveError< > where >::InvocationStoreError: fmt::Debug, { + #[error("missing delegation: {0}")] + MissingDelegation(Cid), + #[error("encoding error: {0}")] EncodingError(#[from] libipld_core::error::Error), @@ -397,11 +407,9 @@ mod tests { (verifier, signer) } - fn setup_agent<'a>( - did: &'a crate::did::preset::Verifier, - signer: &'a crate::did::preset::Signer, - ) -> Agent<'a, crate::invocation::store::MemoryStore, crate::delegation::store::MemoryStore> - { + fn setup_agent( + ) -> Agent { + let (did, signer) = gen_did(); let inv_store = crate::invocation::store::MemoryStore::default(); let del_store = crate::delegation::store::MemoryStore::default(); @@ -427,8 +435,7 @@ mod tests { #[test_log::test] fn test_invoker_is_sub_implicit_aud() -> TestResult { let (_nbf, now, exp) = setup_valid_time(); - let (server, server_signer) = gen_did(); - let mut agent = setup_agent(&server, &server_signer); + let mut agent = setup_agent(); let invocation = agent.invoke( None, @@ -456,8 +463,7 @@ mod tests { #[test_log::test] fn test_invoker_is_sub_and_aud() -> TestResult { let (_nbf, now, exp) = setup_valid_time(); - let (server, server_signer) = gen_did(); - let mut agent = setup_agent(&server, &server_signer); + let mut agent = setup_agent(); let invocation = agent.invoke( Some(agent.did.clone()), @@ -486,8 +492,7 @@ mod tests { #[test_log::test] fn test_other_recipient() -> TestResult { let (_nbf, now, exp) = setup_valid_time(); - let (server, server_signer) = gen_did(); - let mut agent = setup_agent(&server, &server_signer); + let mut agent = setup_agent(); let (not_server, _) = gen_did(); @@ -517,8 +522,7 @@ mod tests { #[test_log::test] fn test_expired() -> TestResult { let (past, now, _exp) = setup_valid_time(); - let (server, server_signer) = gen_did(); - let mut agent = setup_agent(&server, &server_signer); + let mut agent = setup_agent(); let invocation = agent.invoke( None, @@ -553,8 +557,8 @@ mod tests { #[test_log::test] fn test_invalid_sig() -> TestResult { let (_past, now, _exp) = setup_valid_time(); - let (server, server_signer) = gen_did(); - let mut agent = setup_agent(&server, &server_signer); + let mut agent = setup_agent(); + let server = &agent.did; let mut invocation = agent.invoke( None, @@ -624,7 +628,7 @@ mod tests { ); let inv_store = crate::invocation::store::MemoryStore::default(); - let mut del_store = crate::delegation::store::MemoryStore::default(); + let del_store = crate::delegation::store::MemoryStore::default(); // Scenario // ======== @@ -739,18 +743,13 @@ mod tests { #[test_log::test] fn test_chain_ok() -> TestResult { - let mut ctx = setup_test_chain()?; - - let mut agent: Agent< - '_, - &mut crate::invocation::store::MemoryStore, - &mut crate::delegation::store::MemoryStore, - AccountManage, - > = Agent::new( - &ctx.server, - &ctx.server_signer, - &mut ctx.inv_store, - &mut ctx.del_store, + let ctx = setup_test_chain()?; + + let mut agent = Agent::new( + ctx.server.clone(), + ctx.server_signer.clone(), + &ctx.inv_store, + &ctx.del_store, ); let observed = agent.receive(ctx.account_invocation.clone()); @@ -760,18 +759,13 @@ mod tests { #[test_log::test] fn test_chain_wrong_sub() -> TestResult { - let mut ctx = setup_test_chain()?; - - let mut agent: Agent< - '_, - &mut crate::invocation::store::MemoryStore, - &mut crate::delegation::store::MemoryStore, - AccountManage, - > = Agent::new( - &ctx.server, - &ctx.server_signer, - &mut ctx.inv_store, - &mut ctx.del_store, + let ctx = setup_test_chain()?; + + let mut agent = Agent::new( + ctx.server.clone(), + ctx.server_signer.clone(), + &ctx.inv_store, + &ctx.del_store, ); let not_account_invocation = crate::Invocation::try_sign( diff --git a/src/invocation/store.rs b/src/invocation/store.rs index 26618909..4c9279fa 100644 --- a/src/invocation/store.rs +++ b/src/invocation/store.rs @@ -4,6 +4,7 @@ use super::Invocation; use crate::ability; use crate::{crypto::varsig, did::Did}; use libipld_core::{cid::Cid, codec::Codec}; +use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{collections::BTreeMap, convert::Infallible}; pub trait Store, C: Codec + Into + TryFrom> { @@ -12,10 +13,10 @@ pub trait Store, C: Codec + Into + TryFro fn get( &self, cid: Cid, - ) -> Result>, Self::InvocationStoreError>; + ) -> Result>>, Self::InvocationStoreError>; fn put( - &mut self, + &self, cid: Cid, invocation: Invocation, ) -> Result<(), Self::InvocationStoreError>; @@ -31,20 +32,22 @@ impl< DID: Did, V: varsig::Header, C: Codec + Into + TryFrom, - > Store for &mut S + > Store for &S { type InvocationStoreError = >::InvocationStoreError; fn get( &self, cid: Cid, - ) -> Result>, >::InvocationStoreError> - { + ) -> Result< + Option>>, + >::InvocationStoreError, + > { (**self).get(cid) } fn put( - &mut self, + &self, cid: Cid, invocation: Invocation, ) -> Result<(), >::InvocationStoreError> { @@ -52,14 +55,48 @@ impl< } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct MemoryStore< T = crate::ability::preset::Preset, DID: crate::did::Did = crate::did::preset::Verifier, V: varsig::Header = varsig::header::Preset, C: Codec + TryFrom + Into = varsig::encoding::Preset, > { - store: BTreeMap>, + inner: Arc>>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct MemoryStoreInner< + T = crate::ability::preset::Preset, + DID: crate::did::Did = crate::did::preset::Verifier, + V: varsig::Header = varsig::header::Preset, + C: Codec + TryFrom + Into = varsig::encoding::Preset, +> { + store: BTreeMap>>, +} + +impl, Enc: Codec + Into + TryFrom> + MemoryStore +{ + fn read(&self) -> RwLockReadGuard<'_, MemoryStoreInner> { + match self.inner.read() { + Ok(guard) => guard, + Err(poison) => { + // There's no logic errors through lock poisoning in our case + poison.into_inner() + } + } + } + + fn write(&self) -> RwLockWriteGuard<'_, MemoryStoreInner> { + match self.inner.write() { + Ok(guard) => guard, + Err(poison) => { + // There's no logic errors through lock poisoning in our case + poison.into_inner() + } + } + } } impl, Enc: Codec + Into + TryFrom> Default @@ -67,7 +104,9 @@ impl, Enc: Codec + Into + TryFrom> { fn default() -> Self { Self { - store: BTreeMap::new(), + inner: Arc::new(RwLock::new(MemoryStoreInner { + store: BTreeMap::new(), + })), } } } @@ -80,16 +119,16 @@ impl, Enc: Codec + Into + TryFrom> fn get( &self, cid: Cid, - ) -> Result>, Self::InvocationStoreError> { - Ok(self.store.get(&cid)) + ) -> Result>>, Self::InvocationStoreError> { + Ok(self.read().store.get(&cid).cloned()) } fn put( - &mut self, + &self, cid: Cid, invocation: Invocation, ) -> Result<(), Self::InvocationStoreError> { - self.store.insert(cid, invocation); + self.write().store.insert(cid, Arc::new(invocation)); Ok(()) } }