diff --git a/data-model/src/entry.rs b/data-model/src/entry.rs index 11c0334..f30a084 100644 --- a/data-model/src/entry.rs +++ b/data-model/src/entry.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + #[cfg(feature = "dev")] use arbitrary::{size_hint::and_all, Arbitrary}; @@ -13,7 +15,7 @@ pub type Timestamp = u64; /// The metadata associated with each Payload. /// [Definition](https://willowprotocol.org/specs/data-model/index.html#Entry). -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct Entry where N: NamespaceId, @@ -28,10 +30,10 @@ where path: Path, /// The claimed creation time of the [`Entry`]. timestamp: Timestamp, - /// The length of the Payload in bytes. - payload_length: u64, /// The result of applying hash_payload to the Payload. payload_digest: PD, + /// The length of the Payload in bytes. + payload_length: u64, } impl Entry @@ -101,6 +103,52 @@ where } } +/// Returns an ordering between `self` and `other`. +/// +/// See the implementation of [`Ord`] on [`Entry`]. +impl PartialOrd + for Entry +where + N: NamespaceId + Ord, + S: SubspaceId, + PD: PayloadDigest, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Returns an ordering between `self` and `other`. +/// +/// The ordering is the ordering defined in the [sideloading protocol]: Entries are first compared +/// by namespace id, then by subspace id, then by path. If those are all equal, the entries are +/// sorted by their newer relation (see [`Self::is_newer_than`]). +/// +/// [sideloading protocol]: https://willowprotocol.org/specs/sideloading/index.html#sideload_protocol +impl Ord + for Entry +where + N: NamespaceId + Ord, + S: SubspaceId, + PD: PayloadDigest, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.namespace_id + .cmp(&other.namespace_id) + .then_with(|| self.subspace_id.cmp(&other.subspace_id)) + .then_with(|| self.path.cmp(&other.path)) + .then_with(|| { + if self.is_newer_than(other) { + Ordering::Greater + } else if other.is_newer_than(self) { + Ordering::Less + } else { + Ordering::Equal + } + }) + } +} + use syncify::syncify; use syncify::syncify_replace; @@ -230,8 +278,8 @@ impl std::error::Error for UnauthorisedWriteError {} /// [Definition](https://willowprotocol.org/specs/data-model/index.html#AuthorisedEntry). #[derive(Debug, PartialEq, Eq, Clone)] pub struct AuthorisedEntry( - pub Entry, - pub AT, + Entry, + AT, ) where N: NamespaceId, @@ -260,6 +308,32 @@ where Err(UnauthorisedWriteError) } + + /// Construct an [`AuthorisedEntry`] without checking if the token permits the writing of this + /// entry. + /// + /// Should only be used if the token was created by ourselves or previously validated. + pub fn new_unchecked(entry: Entry, token: AT) -> Self + where + AT: AuthorisationToken, + { + Self(entry, token) + } + + /// Split into [`Entry`] and [`AuthorisationToken`] halves. + pub fn into_parts(self) -> (Entry, AT) { + (self.0, self.1) + } + + /// Get a reference to the [`Entry`]. + pub fn entry(&self) -> &Entry { + &self.0 + } + + /// Get a reference to the [`AuthorisationToken`]. + pub fn token(&self) -> &AT { + &self.1 + } } #[cfg(test)] diff --git a/data-model/src/grouping/area_of_interest.rs b/data-model/src/grouping/area_of_interest.rs index 4b73327..1f06655 100644 --- a/data-model/src/grouping/area_of_interest.rs +++ b/data-model/src/grouping/area_of_interest.rs @@ -16,11 +16,20 @@ pub struct AreaOfInterest AreaOfInterest { + /// Creates a new [`AreaOfInterest`]. + pub fn new(area: Area, max_count: u64, max_size: u64) -> Self { + Self { + area, + max_count, + max_size, + } + } + /// Return the intersection of this [`AreaOfInterest`] with another. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#aoi_intersection). pub fn intersection( &self, - other: AreaOfInterest, + other: &AreaOfInterest, ) -> Option> { match self.area.intersection(&other.area) { None => None, diff --git a/data-model/src/grouping/range.rs b/data-model/src/grouping/range.rs index 4555230..16f9e7c 100644 --- a/data-model/src/grouping/range.rs +++ b/data-model/src/grouping/range.rs @@ -6,7 +6,7 @@ use arbitrary::{Arbitrary, Error as ArbitraryError}; use crate::path::Path; -#[derive(Debug, Hash, PartialEq, Eq)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] /// Determines whether a [`Range`] is _closed_ or _open_. pub enum RangeEnd { /// A [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) consists of a [start value](https://willowprotocol.org/specs/grouping-entries/index.html#start_value) and an [end_value](https://willowprotocol.org/specs/grouping-entries/index.html#end_value). @@ -119,18 +119,6 @@ impl PartialOrd Clone for RangeEnd -where - T: Ord + Clone, -{ - fn clone(&self) -> Self { - match self { - RangeEnd::Closed(val) => RangeEnd::Closed(val.clone()), - RangeEnd::Open => RangeEnd::Open, - } - } -} - #[cfg(feature = "dev")] impl<'a, T> Arbitrary<'a> for RangeEnd where @@ -152,7 +140,7 @@ where /// One-dimensional grouping over a type of value. /// /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#range) -#[derive(Debug, Hash, PartialEq, Eq)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] pub struct Range { /// A range [includes](https://willowprotocol.org/specs/grouping-entries/index.html#range_include) all values greater than or equal to its [start value](https://willowprotocol.org/specs/grouping-entries/index.html#start_value) **and** less than its [end_value](https://willowprotocol.org/specs/grouping-entries/index.html#end_value) pub start: T, @@ -164,6 +152,11 @@ impl Range where T: Ord + Clone, { + /// Construct a range. + pub fn new(start: T, end: RangeEnd) -> Self { + Self { start, end } + } + /// Construct a new [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range) from a [start value](https://willowprotocol.org/specs/grouping-entries/index.html#start_value). pub fn new_open(start: T) -> Self { Self { @@ -248,18 +241,6 @@ impl PartialOrd for Range { } } -impl Clone for Range -where - T: Ord + Clone, -{ - fn clone(&self) -> Self { - Self { - start: self.start.clone(), - end: self.end.clone(), - } - } -} - #[cfg(feature = "dev")] impl<'a, T> Arbitrary<'a> for Range where diff --git a/data-model/src/grouping/range_3d.rs b/data-model/src/grouping/range_3d.rs index 7ee1f0c..59929fa 100644 --- a/data-model/src/grouping/range_3d.rs +++ b/data-model/src/grouping/range_3d.rs @@ -39,6 +39,15 @@ impl } } + /// Create a new [`Range3d`] that covers everything. + pub fn new_full() -> Self { + Self::new( + Default::default(), + Range::new_open(Path::new_empty()), + Default::default(), + ) + } + /// Return a reference to the range of [`SubspaceId`]s. /// [Definition](https://willowprotocol.org/specs/grouping-entries/index.html#SubspaceRange). pub fn subspaces(&self) -> &Range { diff --git a/fuzz/fuzz_targets/mc_is_authorised_write.rs b/fuzz/fuzz_targets/mc_is_authorised_write.rs index b9197d7..d6b2faa 100644 --- a/fuzz/fuzz_targets/mc_is_authorised_write.rs +++ b/fuzz/fuzz_targets/mc_is_authorised_write.rs @@ -19,7 +19,7 @@ fuzz_target!(|data: ( let (entry, token) = data; let is_within_granted_area = token.capability.granted_area().includes_entry(&entry); - let is_write_cap = token.capability.access_mode() == &AccessMode::Write; + let is_write_cap = token.capability.access_mode() == AccessMode::Write; let mut consumer = IntoVec::::new(); entry.encode(&mut consumer).unwrap(); diff --git a/meadowcap/src/communal_capability.rs b/meadowcap/src/communal_capability.rs index 00f18c7..38b9a98 100644 --- a/meadowcap/src/communal_capability.rs +++ b/meadowcap/src/communal_capability.rs @@ -26,7 +26,7 @@ impl std::error::Error /// A capability that implements [communal namespaces](https://willowprotocol.org/specs/meadowcap/index.html#communal_namespace). /// /// [Definition](https://willowprotocol.org/specs/meadowcap/index.html#communal_capabilities). -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct CommunalCapability< const MCL: usize, const MCC: usize, @@ -157,8 +157,8 @@ where /// The kind of access this capability grants. /// /// [Definition](https://willowprotocol.org/specs/meadowcap/index.html#communal_cap_mode) - pub fn access_mode(&self) -> &AccessMode { - &self.access_mode + pub fn access_mode(&self) -> AccessMode { + self.access_mode } /// The user to whom this capability grants access. diff --git a/meadowcap/src/lib.rs b/meadowcap/src/lib.rs index 0e0c680..007c06a 100644 --- a/meadowcap/src/lib.rs +++ b/meadowcap/src/lib.rs @@ -28,7 +28,7 @@ pub trait IsCommunal { } /// A delegation of access rights to a user for a given [area](https://willowprotocol.org/specs/grouping-entries/index.html#areas). -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Delegation< const MCL: usize, const MCC: usize, @@ -100,7 +100,7 @@ where } /// A mode granting read or write access to some [`Area`]. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AccessMode { Read, Write, diff --git a/meadowcap/src/mc_authorisation_token.rs b/meadowcap/src/mc_authorisation_token.rs index d6ea03c..527b6b6 100644 --- a/meadowcap/src/mc_authorisation_token.rs +++ b/meadowcap/src/mc_authorisation_token.rs @@ -37,6 +37,49 @@ pub struct McAuthorisationToken< pub signature: UserSignature, } +impl< + const MCL: usize, + const MCC: usize, + const MPL: usize, + NamespacePublicKey, + NamespaceSignature, + UserPublicKey, + UserSignature, + > + McAuthorisationToken< + MCL, + MCC, + MPL, + NamespacePublicKey, + NamespaceSignature, + UserPublicKey, + UserSignature, + > +where + NamespacePublicKey: NamespaceId + Encodable + Verifier + IsCommunal, + UserPublicKey: SubspaceId + Encodable + Verifier, + NamespaceSignature: Encodable + Clone, + UserSignature: Encodable + Clone, +{ + pub fn new( + capability: McCapability< + MCL, + MCC, + MPL, + NamespacePublicKey, + NamespaceSignature, + UserPublicKey, + UserSignature, + >, + signature: UserSignature, + ) -> Self { + Self { + capability, + signature, + } + } +} + impl< const MCL: usize, const MCC: usize, diff --git a/meadowcap/src/mc_capability.rs b/meadowcap/src/mc_capability.rs index 54a83ae..64afddb 100644 --- a/meadowcap/src/mc_capability.rs +++ b/meadowcap/src/mc_capability.rs @@ -31,7 +31,7 @@ impl std::error::Error for NotAWriteCapabilityError {} /// A Meadowcap capability. /// /// [Definition](https://willowprotocol.org/specs/meadowcap/index.html#Capability) -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "dev", derive(Arbitrary))] pub enum McCapability< const MCL: usize, @@ -96,9 +96,9 @@ where } /// Create a new owned capability granting access to the [full area](https://willowprotocol.org/specs/grouping-entries/index.html#full_area) of the [namespace](https://willowprotocol.org/specs/data-model/index.html#namespace) to the given `UserPublicKey`. - pub async fn new_owned( + pub fn new_owned( namespace_key: NamespacePublicKey, - namespace_secret: NamespaceSecret, + namespace_secret: &NamespaceSecret, user_key: UserPublicKey, access_mode: AccessMode, ) -> Result> @@ -111,7 +111,7 @@ where } /// The kind of access this capability grants. - pub fn access_mode(&self) -> &AccessMode { + pub fn access_mode(&self) -> AccessMode { match self { Self::Communal(cap) => cap.access_mode(), Self::Owned(cap) => cap.access_mode(), @@ -251,7 +251,7 @@ where /// Return a new [`McAuthorisationToken`], or an error if the resulting signature was not for the capability's receiver. pub fn authorisation_token( &self, - entry: Entry, + entry: &Entry, secret: UserSecret, ) -> Result< McAuthorisationToken< @@ -354,12 +354,12 @@ pub(super) mod encoding { match self { McCapability::Communal(_) => { - if self.access_mode() == &AccessMode::Write { + if self.access_mode() == AccessMode::Write { header |= 0b0100_0000; } } McCapability::Owned(_) => { - if self.access_mode() == &AccessMode::Read { + if self.access_mode() == AccessMode::Read { header |= 0b1000_0000; } else { header |= 0b1100_0000; diff --git a/meadowcap/src/mc_subspace_capability.rs b/meadowcap/src/mc_subspace_capability.rs index 06b2ad5..2c093f1 100644 --- a/meadowcap/src/mc_subspace_capability.rs +++ b/meadowcap/src/mc_subspace_capability.rs @@ -10,7 +10,7 @@ use crate::IsCommunal; use arbitrary::{Arbitrary, Error as ArbitraryError}; /// A [delegation](https://willowprotocol.org/specs/pai/index.html#subspace_cap_delegations) of read access for arbitrary `SubspaceId`s to a `UserPublicKey`. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "dev", derive(Arbitrary))] pub struct SubspaceDelegation where @@ -42,7 +42,7 @@ where /// A capability that certifies read access to arbitrary [SubspaceIds](https://willowprotocol.org/specs/data-model/index.html#SubspaceId) at some unspecified non-empty [`willow_data_model::Path`]. /// /// [Definition](https://willowprotocol.org/specs/pai/index.html#subspace_capability) -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct McSubspaceCapability< NamespacePublicKey, NamespaceSignature, @@ -69,9 +69,9 @@ where UserSignature: Encodable + Clone, { /// Generate a new [`McSubspaceCapability`] for a given user, or return an error if the given namespace secret is incorrect. - pub fn new( + pub fn new( namespace_key: NamespacePublicKey, - namespace_secret: NamespaceSecret, + namespace_secret: &NamespaceSecret, user_key: UserPublicKey, ) -> Result< McSubspaceCapability, diff --git a/meadowcap/src/owned_capability.rs b/meadowcap/src/owned_capability.rs index c636764..ad47c4e 100644 --- a/meadowcap/src/owned_capability.rs +++ b/meadowcap/src/owned_capability.rs @@ -40,7 +40,7 @@ impl std::error::Error /// A capability that implements [owned namespaces](https://willowprotocol.org/specs/meadowcap/index.html#owned_namespace). /// /// [Definition](https://willowprotocol.org/specs/meadowcap/index.html#owned_capabilities). -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct OwnedCapability< const MCL: usize, const MCC: usize, @@ -89,7 +89,7 @@ where /// Create a new owned capability granting access to the [full area](https://willowprotocol.org/specs/grouping-entries/index.html#full_area) of the [namespace](https://willowprotocol.org/specs/data-model/index.html#namespace) to the given `UserPublicKey`. pub fn new( namespace_key: NamespacePublicKey, - namespace_secret: NamespaceSecret, + namespace_secret: &NamespaceSecret, user_key: UserPublicKey, access_mode: AccessMode, ) -> Result> @@ -263,8 +263,8 @@ where /// The kind of access this capability grants. /// /// [Definition](https://willowprotocol.org/specs/meadowcap/index.html#owned_cap_mode) - pub fn access_mode(&self) -> &AccessMode { - &self.access_mode + pub fn access_mode(&self) -> AccessMode { + self.access_mode } /// The user to whom this capability grants access.