lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

revision.rs (3883B)


      1 use core::fmt;
      2 
      3 use crate::{
      4     food_availability::inbound::{
      5         RadrootsFoodAvailabilityProjectionError, project_strict_verified_food_availability_event,
      6     },
      7     verification::RadrootsSignatureVerifiedEvent,
      8 };
      9 
     10 #[non_exhaustive]
     11 #[derive(Clone, Debug, PartialEq, Eq)]
     12 pub enum RadrootsFoodAvailabilityRevisionError {
     13     PreviousInvalid(RadrootsFoodAvailabilityProjectionError),
     14     CurrentInvalid(RadrootsFoodAvailabilityProjectionError),
     15     CoordinateChanged,
     16     PublishedAtChanged,
     17     NotNewer,
     18 }
     19 
     20 impl RadrootsFoodAvailabilityRevisionError {
     21     pub const fn code(&self) -> &'static str {
     22         match self {
     23             Self::PreviousInvalid(_) => "food_revision_previous_invalid",
     24             Self::CurrentInvalid(_) => "food_revision_current_invalid",
     25             Self::CoordinateChanged => "food_coordinate_changed",
     26             Self::PublishedAtChanged => "food_published_at_changed",
     27             Self::NotNewer => "food_revision_not_newer",
     28         }
     29     }
     30 }
     31 
     32 impl fmt::Display for RadrootsFoodAvailabilityRevisionError {
     33     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     34         match self {
     35             Self::PreviousInvalid(error) => {
     36                 write!(
     37                     formatter,
     38                     "previous FoodAvailability revision is invalid: {error}"
     39                 )
     40             }
     41             Self::CurrentInvalid(error) => {
     42                 write!(
     43                     formatter,
     44                     "current FoodAvailability revision is invalid: {error}"
     45                 )
     46             }
     47             Self::CoordinateChanged => {
     48                 formatter.write_str("FoodAvailability revision coordinate changed")
     49             }
     50             Self::PublishedAtChanged => {
     51                 formatter.write_str("FoodAvailability revision published_at changed")
     52             }
     53             Self::NotNewer => {
     54                 formatter.write_str("FoodAvailability revision does not win NIP-01 ordering")
     55             }
     56         }
     57     }
     58 }
     59 
     60 #[cfg(feature = "std")]
     61 impl std::error::Error for RadrootsFoodAvailabilityRevisionError {
     62     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     63         match self {
     64             Self::PreviousInvalid(error) | Self::CurrentInvalid(error) => Some(error),
     65             _ => None,
     66         }
     67     }
     68 }
     69 
     70 /// Validates a replacement using strict authored wire semantics on both sides.
     71 ///
     72 /// Both inputs must already have passed NIP-01 id and signature verification.
     73 pub fn validate_food_availability_revision(
     74     previous: &RadrootsSignatureVerifiedEvent,
     75     current: &RadrootsSignatureVerifiedEvent,
     76 ) -> Result<(), RadrootsFoodAvailabilityRevisionError> {
     77     let previous_projection = project_strict_verified_food_availability_event(previous)
     78         .map_err(RadrootsFoodAvailabilityRevisionError::PreviousInvalid)?;
     79     let current_projection = project_strict_verified_food_availability_event(current)
     80         .map_err(RadrootsFoodAvailabilityRevisionError::CurrentInvalid)?;
     81     let previous_event = previous.event();
     82     let current_event = current.event();
     83 
     84     if previous_event.kind_u32() != current_event.kind_u32()
     85         || previous_event.author().to_hex() != current_event.author().to_hex()
     86         || previous_projection.identifier() != current_projection.identifier()
     87     {
     88         return Err(RadrootsFoodAvailabilityRevisionError::CoordinateChanged);
     89     }
     90     if previous_projection.published_at() != current_projection.published_at() {
     91         return Err(RadrootsFoodAvailabilityRevisionError::PublishedAtChanged);
     92     }
     93 
     94     let current_wins = current_event.created_at_u64() > previous_event.created_at_u64()
     95         || (current_event.created_at_u64() == previous_event.created_at_u64()
     96             && current_event.id() < previous_event.id());
     97     if !current_wins {
     98         return Err(RadrootsFoodAvailabilityRevisionError::NotNewer);
     99     }
    100     Ok(())
    101 }