use crate::macros::entity;
use crate::macros::pattern;
use ed25519::signature::Signer;
use ed25519::Signature;
use ed25519_dalek::SignatureError;
use ed25519_dalek::SigningKey;
use ed25519_dalek::Verifier;
use ed25519_dalek::VerifyingKey;
use hifitime::prelude::*;
use itertools::Itertools;
use crate::blob::schemas::longstring::LongString;
use crate::blob::Blob;
use crate::find;
use crate::id::Id;
use crate::metadata;
use crate::prelude::blobschemas::SimpleArchive;
use crate::trible::TribleSet;
use crate::value::schemas::hash::{Blake3, Handle};
use crate::value::schemas::time::NsTAIInterval;
use crate::value::TryToValue;
use crate::value::Value;
fn now_updated_at() -> Value<NsTAIInterval> {
let now = Epoch::now().unwrap_or_else(|_| Epoch::from_gregorian_utc(1970, 1, 1, 0, 0, 0, 0));
(now, now).try_to_value().expect("same epoch is a valid point interval")
}
pub fn branch_metadata(
signing_key: &SigningKey,
branch_id: Id,
name: Value<Handle<Blake3, LongString>>,
commit_head: Option<Blob<SimpleArchive>>,
) -> TribleSet {
let (head_handle, signed_by, signature) = match commit_head.as_ref() {
Some(blob) => (
Some(blob.get_handle::<Blake3>()),
Some(signing_key.verifying_key()),
Some(signing_key.sign(&blob.bytes)),
),
None => (None, None, None),
};
let updated_at = now_updated_at();
let fragment = entity! {
super::branch: branch_id,
super::head?: head_handle,
super::signed_by?: signed_by,
super::signature_r?: signature,
super::signature_s?: signature,
metadata::name: name,
metadata::updated_at: updated_at,
};
fragment.into()
}
pub fn branch_unsigned(
branch_id: Id,
name: Value<Handle<Blake3, LongString>>,
commit_head: Option<Blob<SimpleArchive>>,
) -> TribleSet {
let head_handle = commit_head
.as_ref()
.map(|blob| blob.get_handle::<Blake3>());
let updated_at = now_updated_at();
let fragment = entity! {
super::branch: branch_id,
super::head?: head_handle,
metadata::name: name,
metadata::updated_at: updated_at,
};
fragment.into()
}
pub enum ValidationError {
AmbiguousSignature,
MissingSignature,
FailedValidation,
}
impl From<SignatureError> for ValidationError {
fn from(_: SignatureError) -> Self {
ValidationError::FailedValidation
}
}
pub fn verify(
commit_head: Blob<SimpleArchive>,
metadata: TribleSet,
) -> Result<(), ValidationError> {
let handle = commit_head.get_handle();
let (pubkey, r, s) = match find!(
(pubkey: Value<_>, r, s),
pattern!(&metadata, [
{
super::head: handle,
super::signed_by: ?pubkey,
super::signature_r: ?r,
super::signature_s: ?s,
}]))
.at_most_one()
{
Ok(Some(result)) => result,
Ok(None) => return Err(ValidationError::MissingSignature),
Err(_) => return Err(ValidationError::AmbiguousSignature),
};
let Ok(pubkey): Result<VerifyingKey, _> = pubkey.try_from_value() else {
return Err(ValidationError::FailedValidation);
};
let signature = Signature::from_components(r, s);
pubkey.verify(&commit_head.bytes, &signature)?;
Ok(())
}