willow-data-model 0.7.0

The core datatypes of Willow, an eventually consistent data store with improved distributed deletion.
Documentation
use core::convert::Infallible;

use arbitrary::Arbitrary;

use order_theory::*;

use signature::{Keypair, Signer, Verifier};
use ufotofu::codec_prelude::*;

use super::TestDigest;
use crate::prelude::*;

/// A subspace id for testing.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Arbitrary)]
#[allow(missing_docs)]
pub enum TestSubspace {
    Alfie,
    Betty,
    Gemma,
    Dalton,
}

pub use TestSubspace::*;

impl Encodable for TestSubspace {
    async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
    where
        C: BulkConsumer<Item = u8> + ?Sized,
    {
        match self {
            Alfie => consumer.consume_item(0).await,
            Betty => consumer.consume_item(1).await,
            Gemma => consumer.consume_item(2).await,
            Dalton => consumer.consume_item(3).await,
        }
    }
}

impl EncodableKnownLength for TestSubspace {
    fn len_of_encoding(&self) -> usize {
        1
    }
}

impl Decodable for TestSubspace {
    type ErrorReason = Blame;

    async fn decode<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
    where
        P: BulkProducer<Item = u8> + ?Sized,
        Self: Sized,
    {
        let byte = producer.produce_item().await?;

        match byte {
            0 => Ok(Alfie),
            1 => Ok(Betty),
            2 => Ok(Gemma),
            3 => Ok(Dalton),
            _ => Err(DecodeError::Other(Blame::TheirFault)),
        }
    }
}

impl DecodableCanonic for TestSubspace {
    type ErrorCanonic = Blame;

    async fn decode_canonic<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
    where
        P: BulkProducer<Item = u8> + ?Sized,
        Self: Sized,
    {
        Self::decode(producer).await
    }
}

impl Verifier<TestSubspaceSignature> for TestSubspace {
    fn verify(
        &self,
        _msg: &[u8],
        signature: &TestSubspaceSignature,
    ) -> Result<(), signature::Error> {
        match (self, signature) {
            (Alfie, AlfieSignature) => Ok(()),
            (Betty, BettySignature) => Ok(()),
            (Gemma, GemmaSignature) => Ok(()),
            (Dalton, DaltonSignature) => Ok(()),
            _ => Err(signature::Error::new()),
        }
    }
}

impl GreatestElement for TestSubspace {
    fn greatest() -> Self {
        Dalton
    }
}

impl TrySuccessor for TestSubspace {
    fn try_successor(&self) -> Option<Self> {
        match self {
            Alfie => Some(Betty),
            Betty => Some(Gemma),
            Gemma => Some(Dalton),
            Dalton => None,
        }
    }
}

impl SuccessorExceptForGreatest for TestSubspace {}

impl LeastElement for TestSubspace {
    fn least() -> Self {
        Alfie
    }
}

impl TryPredecessor for TestSubspace {
    fn try_predecessor(&self) -> Option<Self> {
        match self {
            Alfie => None,
            Betty => Some(Alfie),
            Gemma => Some(Betty),
            Dalton => Some(Gemma),
        }
    }
}

impl PredecessorExceptForLeast for TestSubspace {}

/// A "private key" corresponding to a subspace, used for issuing [`TestSubspaceSignature`]s.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Arbitrary)]
#[allow(missing_docs)]
pub enum TestSubspaceSecret {
    AlfieSecret,
    BettySecret,
    GemmaSecret,
    DaltonSecret,
}

pub use TestSubspaceSecret::*;

impl Signer<TestSubspaceSignature> for TestSubspaceSecret {
    fn try_sign(&self, _msg: &[u8]) -> Result<TestSubspaceSignature, signature::Error> {
        Ok(match self {
            AlfieSecret => AlfieSignature,
            BettySecret => BettySignature,
            GemmaSecret => GemmaSignature,
            DaltonSecret => DaltonSignature,
        })
    }
}

impl Keypair for TestSubspaceSecret {
    type VerifyingKey = TestSubspace;

    fn verifying_key(&self) -> Self::VerifyingKey {
        match self {
            AlfieSecret => Alfie,
            BettySecret => Betty,
            GemmaSecret => Gemma,
            DaltonSecret => Dalton,
        }
    }
}

/// A "signature" issued by a [`TestSubspaceSecret`]. Implements [`AuthorisationToken`].
///
/// The signing process ignores the actual details of the payload.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Arbitrary)]
#[allow(missing_docs)]
pub enum TestSubspaceSignature {
    AlfieSignature,
    BettySignature,
    GemmaSignature,
    DaltonSignature,
}

impl AuthorisationToken<4, 4, 4, TestNamespace, TestSubspace, TestDigest>
    for TestSubspaceSignature
{
    type Ingredients = TestSubspaceSecret;

    /// Emitted when the secret does not correspond to the subspace of the entry to authorise.
    type CreationError = ();

    fn new_for_entry<E>(
        entry: &E,
        ingredients: &Self::Ingredients,
    ) -> Result<Self, Self::CreationError>
    where
        E: Entrylike<4, 4, 4, TestNamespace, TestSubspace, TestDigest> + ?Sized,
    {
        match (entry.wdm_subspace_id(), ingredients) {
            (Alfie, AlfieSecret) => Ok(AlfieSignature),
            (Betty, BettySecret) => Ok(BettySignature),
            (Gemma, GemmaSecret) => Ok(GemmaSignature),
            (Dalton, DaltonSecret) => Ok(DaltonSignature),
            _ => Err(()),
        }
    }

    /// Determines whether `self` [authorises](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) the given entry.
    fn does_authorise<E>(&self, entry: &E) -> bool
    where
        E: Entrylike<4, 4, 4, TestNamespace, TestSubspace, TestDigest> + ?Sized,
    {
        matches!(
            (entry.wdm_subspace_id(), self),
            (Alfie, AlfieSignature)
                | (Betty, BettySignature)
                | (Gemma, GemmaSignature)
                | (Dalton, DaltonSignature)
        )
    }
}

pub use TestSubspaceSignature::*;

impl Encodable for TestSubspaceSignature {
    async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
    where
        C: BulkConsumer<Item = u8> + ?Sized,
    {
        match self {
            AlfieSignature => consumer.consume_item(0).await,
            BettySignature => consumer.consume_item(1).await,
            GemmaSignature => consumer.consume_item(2).await,
            DaltonSignature => consumer.consume_item(3).await,
        }
    }
}

impl EncodableKnownLength for TestSubspaceSignature {
    fn len_of_encoding(&self) -> usize {
        1
    }
}

impl Decodable for TestSubspaceSignature {
    type ErrorReason = Blame;

    async fn decode<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
    where
        P: BulkProducer<Item = u8> + ?Sized,
        Self: Sized,
    {
        let byte = producer.produce_item().await?;

        match byte {
            0 => Ok(AlfieSignature),
            1 => Ok(BettySignature),
            2 => Ok(GemmaSignature),
            3 => Ok(DaltonSignature),
            _ => Err(DecodeError::Other(Blame::TheirFault)),
        }
    }
}

impl DecodableCanonic for TestSubspaceSignature {
    type ErrorCanonic = Blame;

    async fn decode_canonic<P>(
        producer: &mut P,
    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
    where
        P: BulkProducer<Item = u8> + ?Sized,
        Self: Sized,
    {
        Self::decode(producer).await
    }
}

impl AuthorisationToken<4, 4, 4, TestSubspace, TestSubspace, TestDigest> for TestSubspaceSignature {
    type Ingredients = ();

    type CreationError = Infallible;

    fn new_for_entry<E>(
        entry: &E,
        _ingredients: &Self::Ingredients,
    ) -> Result<Self, Self::CreationError>
    where
        E: Entrylike<4, 4, 4, TestSubspace, TestSubspace, TestDigest> + ?Sized,
    {
        match entry.wdm_subspace_id() {
            TestSubspace::Alfie => Ok(TestSubspaceSignature::AlfieSignature),
            TestSubspace::Betty => Ok(TestSubspaceSignature::BettySignature),
            TestSubspace::Gemma => Ok(TestSubspaceSignature::GemmaSignature),
            TestSubspace::Dalton => Ok(TestSubspaceSignature::DaltonSignature),
        }
    }

    fn does_authorise<E>(&self, entry: &E) -> bool
    where
        E: Entrylike<4, 4, 4, TestSubspace, TestSubspace, TestDigest> + ?Sized,
    {
        match entry.wdm_subspace_id() {
            TestSubspace::Alfie => self == &TestSubspaceSignature::AlfieSignature,
            TestSubspace::Betty => self == &TestSubspaceSignature::BettySignature,
            TestSubspace::Gemma => self == &TestSubspaceSignature::GemmaSignature,
            TestSubspace::Dalton => self == &TestSubspaceSignature::DaltonSignature,
        }
    }
}