use core::fmt;
#[cfg(feature = "dev")]
use arbitrary::Arbitrary;
#[cfg(feature = "dev")]
use ed25519_dalek::{SecretKey, SigningKey};
use signature::{Keypair, Signer};
use ufotofu::codec_prelude::*;
#[cfg(feature = "dev")]
use crate::authorisation::raw::RawCapability;
use crate::{
authorisation::raw::{
AccessMode, Delegation, Genesis, InvalidCapability, PossiblyValidWriteCapability,
owned_genesis_data_to_sign,
},
groupings::private_context::{PrivateAreaContext, PrivateInterest},
is_bitflagged,
prelude::*,
};
#[derive(Clone, PartialEq, Eq)]
pub struct WriteCapability {
pub(crate) inner: PossiblyValidWriteCapability,
}
impl fmt::Debug for WriteCapability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WriteCapability")
.field("genesis", &self.inner.inner.genesis)
.field("delegations", &self.inner.inner.delegations)
.finish()
}
}
impl WriteCapability {
pub fn receiver(&self) -> &SubspaceId {
self.inner.receiver()
}
pub fn granted_namespace(&self) -> &NamespaceId {
self.inner.granted_namespace()
}
pub fn granted_area_ref(&self) -> Option<&Area> {
self.inner.granted_area_ref()
}
pub fn genesis(&self) -> &Genesis {
self.inner.genesis()
}
pub fn is_owned(&self) -> bool {
self.inner.is_owned()
}
pub fn delegations(&self) -> &[Delegation] {
self.inner.delegations()
}
pub fn new_communal(namespace_key: NamespaceId, user_key: SubspaceId) -> Self {
Self {
inner: PossiblyValidWriteCapability::new_communal(namespace_key, user_key),
}
}
pub fn new_owned<NamespaceKeypair>(keypair: &NamespaceKeypair, user_key: SubspaceId) -> Self
where
NamespaceKeypair: Signer<NamespaceSignature> + Keypair<VerifyingKey = NamespaceId>,
{
let data_to_sign = owned_genesis_data_to_sign(AccessMode::Write, &user_key);
let initial_authorisation = keypair.sign(&data_to_sign[..]);
Self {
inner: PossiblyValidWriteCapability::new_owned(
keypair.verifying_key(),
user_key,
initial_authorisation,
),
}
}
pub fn includes_area(&self, area: &Area) -> bool {
self.inner.includes_area(area)
}
pub fn granted_area(&self) -> Area {
self.inner.granted_area()
}
pub fn includes<T>(&self, t: &T) -> bool
where
T: Namespaced + Coordinatelike + ?Sized,
{
self.inner.includes(t)
}
pub fn try_delegate<UserKeypair>(
&mut self,
keypair: &UserKeypair,
new_area: Area,
new_receiver: SubspaceId,
) -> Result<(), InvalidCapability>
where
UserKeypair: Signer<SubspaceSignature> + Keypair<VerifyingKey = SubspaceId>,
{
if !self.granted_area().includes_grouping(&new_area) {
return Err(InvalidCapability);
}
let handover =
self.inner
.inner
.create_handover(&new_area, &new_receiver, self.delegations().last());
let prev_receiver = self.receiver();
if prev_receiver != &keypair.verifying_key() {
return Err(InvalidCapability);
}
let signature = keypair.sign(&handover);
let delegation = Delegation {
area: new_area,
user: new_receiver,
signature,
};
self.inner.inner.delegations.push(delegation);
Ok(())
}
pub fn delegate<UserKeypair>(
&mut self,
keypair: &UserKeypair,
new_area: Area,
new_receiver: SubspaceId,
) where
UserKeypair: Signer<SubspaceSignature> + Keypair<VerifyingKey = SubspaceId>,
{
self.try_delegate(keypair, new_area, new_receiver).unwrap()
}
}
impl WriteCapability {
pub fn longest_common_delegation_prefix_len(&self, delegations: &[Delegation]) -> usize {
let mut count = 0;
while count < core::cmp::min(self.delegations().len(), delegations.len()) {
if self.delegations()[count] == delegations[count] {
count += 1;
} else {
break;
}
}
count
}
}
impl Encodable for WriteCapability {
async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
where
C: BulkConsumer<Item = u8> + ?Sized,
{
self.inner.encode(consumer).await
}
}
impl EncodableKnownLength for WriteCapability {
fn len_of_encoding(&self) -> usize {
self.inner.len_of_encoding()
}
}
impl Decodable for WriteCapability {
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 decoded: PossiblyValidWriteCapability = producer.produce_decoded().await?;
if decoded.is_valid() {
Ok(Self { inner: decoded })
} else {
Err(DecodeError::Other(Blame::TheirFault))
}
}
}
impl DecodableCanonic for WriteCapability {
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,
{
let decoded: PossiblyValidWriteCapability = producer.produce_decoded_canonic().await?;
if decoded.is_valid() {
Ok(Self { inner: decoded })
} else {
Err(DecodeError::Other(Blame::TheirFault))
}
}
}
#[cfg(feature = "dev")]
impl<'a> Arbitrary<'a> for WriteCapability {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let (genesis, first_receiver) = Genesis::arbitrary_with_receiver(u)?;
if genesis.access_mode() == AccessMode::Read {
return Err(arbitrary::Error::IncorrectFormat);
}
let delegation_count: u16 = Arbitrary::arbitrary(u)?;
let delegation_count = (delegation_count % 260) as usize;
let mut cap = WriteCapability {
inner: PossiblyValidWriteCapability {
inner: RawCapability {
genesis,
delegations: vec![],
},
},
};
debug_assert!(cap.inner.is_valid());
let mut signing_key = first_receiver;
for _ in 0..delegation_count {
let sk: SecretKey = Arbitrary::arbitrary(u)?;
let new_signing_key = SigningKey::from_bytes(&sk);
let area = arbitrary_area_in_area(&cap.granted_area(), u)?;
cap.try_delegate(
&SubspaceSecret::from(signing_key.clone()),
area,
SubspaceId::from_bytes(new_signing_key.verifying_key().as_bytes()),
)
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
signing_key = new_signing_key;
}
Ok(cap)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct PriorCapEntryPair {
prior_cap: WriteCapability,
entry: Entry,
}
impl PriorCapEntryPair {
pub fn new(prior_cap: WriteCapability, entry: Entry) -> Self {
Self { prior_cap, entry }
}
pub fn prior_cap(&self) -> &WriteCapability {
&self.prior_cap
}
pub fn entry(&self) -> &Entry {
&self.entry
}
}
#[cfg(feature = "dev")]
impl<'a> Arbitrary<'a> for PriorCapEntryPair {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let prior_cap: WriteCapability = Arbitrary::arbitrary(u)?;
let entry: Entry = Arbitrary::arbitrary(u)?;
Ok(Self { prior_cap, entry })
}
}
impl RelativeEncodable<PriorCapEntryPair> for WriteCapability {
async fn relative_encode<C>(
&self,
rel: &PriorCapEntryPair,
consumer: &mut C,
) -> Result<(), C::Error>
where
C: BulkConsumer<Item = u8> + ?Sized,
{
let prior_cap = &rel.prior_cap;
let entry = &rel.entry;
let (shared, nice_hack) = match (self.genesis(), prior_cap.genesis()) {
(Genesis::Communal(_), Genesis::Communal(_)) => {
let shared = self.longest_common_delegation_prefix_len(prior_cap.delegations());
(shared, shared + 1)
}
(Genesis::Owned(genesis), Genesis::Owned(prior_genesis)) => {
if genesis.user_key != prior_genesis.user_key
|| genesis.initial_authorisation != prior_genesis.initial_authorisation
{
(0, 0)
} else {
let shared = self.longest_common_delegation_prefix_len(prior_cap.delegations());
(shared, shared + 1)
}
}
_ => (0, 0),
};
let mut header = 0;
if self.is_owned() {
header |= 0b1000_0000;
}
compact_u64::write_tag(&mut header, 3, 1, nice_hack as u64);
compact_u64::write_tag(&mut header, 4, 4, self.delegations().len() as u64);
consumer.consume(Left(header)).await?;
compact_u64::cu64_encode(nice_hack as u64, 3, consumer).await?;
compact_u64::cu64_encode(self.delegations().len() as u64, 4, consumer).await?;
if let Genesis::Owned(owned_genesis) = self.genesis()
&& nice_hack == 0
{
owned_genesis.user_key.encode(consumer).await?;
owned_genesis.initial_authorisation.encode(consumer).await?;
}
let mut prev_area = Area::new_subspace_area(entry.subspace_id().clone());
for (i, del) in self.delegations().iter().enumerate() {
if i < shared {
continue;
}
let private_interest = PrivateInterest::new(
entry.namespace_id().clone(),
Some(entry.subspace_id().clone()),
entry.path().clone(),
);
let ctx = if i == 0 {
PrivateAreaContext::new(
private_interest,
Area::new_subspace_area(entry.subspace_id().clone()),
)
.unwrap()
} else {
PrivateAreaContext::new(private_interest, prev_area).unwrap()
};
prev_area = del.area.clone();
del.area.relative_encode(&ctx, consumer).await?;
del.user.encode(consumer).await?;
del.signature.encode(consumer).await?;
}
Ok(())
}
fn can_be_encoded_relative_to(&self, rel: &PriorCapEntryPair) -> bool {
self.granted_namespace() == rel.entry.namespace_id()
&& self.granted_area().includes(&rel.entry)
}
}
impl RelativeDecodable<PriorCapEntryPair> for WriteCapability {
type ErrorReason = Blame;
async fn relative_decode<P>(
rel: &PriorCapEntryPair,
producer: &mut P,
) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
where
P: BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
let header = producer.produce_item().await?;
let nice_hack = compact_u64::cu64_decode(header, 3, 1, producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let del_len = compact_u64::cu64_decode(header, 4, 4, producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let prior_cap = &rel.prior_cap;
let entry = &rel.entry;
let mut decoded_cap = if is_bitflagged(header, 0) {
let user_key = if nice_hack == 0 {
SubspaceId::decode(producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?
} else {
match prior_cap.genesis() {
Genesis::Communal(_) => {
return Err(DecodeError::Other(Blame::OurFault));
}
Genesis::Owned(owned_genesis) => owned_genesis.user_key.clone(),
}
};
let initial_authorisation = if nice_hack == 0 {
NamespaceSignature::decode(producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))
.map(Some)?
} else {
None
};
let init_auth_to_use = match prior_cap.genesis() {
Genesis::Communal(_) => {
if let Some(init_auth) = initial_authorisation {
Ok(init_auth)
} else {
Err(DecodeError::Other(Blame::OurFault))
}
}
Genesis::Owned(owned_genesis) => {
if let Some(init_auth) = initial_authorisation {
Ok(init_auth)
} else {
Ok(owned_genesis.initial_authorisation.clone())
}
}
}?;
PossiblyValidWriteCapability::new_owned(
entry.namespace_id().clone(),
user_key,
init_auth_to_use,
)
} else {
PossiblyValidWriteCapability::new_communal(
entry.namespace_id().clone(),
entry.subspace_id().clone(),
)
};
if del_len > 0 {
let how_many_shared = nice_hack.saturating_sub(1);
for (i, del) in prior_cap.delegations().iter().enumerate() {
if (i as u64) < how_many_shared {
decoded_cap
.try_append_delegation(del.clone())
.map_err(|_| DecodeError::Other(Blame::TheirFault))?;
}
}
let how_many_to_decode = del_len
.checked_sub(how_many_shared)
.ok_or(DecodeError::Other(Blame::TheirFault))?;
let mut prev_area = Area::new_subspace_area(entry.subspace_id().clone());
for _i in 0..how_many_to_decode {
let private_interest = PrivateInterest::new(
entry.namespace_id().clone(),
Some(entry.subspace_id().clone()),
entry.path().clone(),
);
let ctx_to_use = PrivateAreaContext::new(private_interest, prev_area).unwrap();
let area = Area::relative_decode(&ctx_to_use, producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
prev_area = area.clone();
let user = SubspaceId::decode(producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let signature = SubspaceSignature::decode(producer)
.await
.map_err(|err| err.map_other(|_| Blame::TheirFault))?;
let delegation = Delegation {
area,
user,
signature,
};
decoded_cap
.try_append_delegation(delegation)
.map_err(|_| DecodeError::Other(Blame::TheirFault))?;
}
};
if !decoded_cap.includes(entry) {
return Err(DecodeError::Other(Blame::OurFault));
}
decoded_cap
.into_write_capability()
.map_err(|_| DecodeError::Other(Blame::TheirFault))
}
}