triblespace_core/repo/
commit.rs1use crate::macros::entity;
2use crate::macros::pattern;
3use crate::inline::TryToInline;
4use ed25519::Signature;
5use ed25519_dalek::SignatureError;
6use ed25519_dalek::SigningKey;
7use ed25519_dalek::Verifier;
8use ed25519_dalek::VerifyingKey;
9use itertools::Itertools;
10
11use ed25519::signature::Signer;
12
13use crate::blob::encodings::longstring::LongString;
14use crate::blob::encodings::simplearchive::SimpleArchive;
15use crate::blob::Blob;
16use crate::prelude::inlineencodings::Handle;
17use crate::query::find;
18use crate::trible::TribleSet;
19use crate::inline::Inline;
20
21use hifitime::Epoch;
22
23pub enum ValidationError {
25 AmbiguousSignature,
27 MissingSignature,
29 FailedValidation,
31}
32
33impl From<SignatureError> for ValidationError {
34 fn from(_: SignatureError) -> Self {
36 ValidationError::FailedValidation
37 }
38}
39
40pub fn commit_metadata(
56 signing_key: &SigningKey,
57 parents: impl IntoIterator<Item = Inline<Handle<SimpleArchive>>>,
58 msg: Option<Inline<Handle<LongString>>>,
59 content: Option<Blob<SimpleArchive>>,
60 metadata: Option<Inline<Handle<SimpleArchive>>>,
61) -> TribleSet {
62 let (content_handle, signed_by, signature, created_at) = match content.as_ref() {
65 Some(blob) => {
66 let now = Epoch::now().expect("system time");
67 let timestamp: Inline<_> =
68 (now, now).try_to_inline().expect("point interval");
69 (
70 Some(blob.get_handle()),
71 Some(signing_key.verifying_key()),
72 Some(signing_key.sign(&blob.bytes)),
73 Some(timestamp),
74 )
75 }
76 None => (None, None, None, None),
77 };
78 let parents: Vec<_> = parents.into_iter().collect();
79
80 let fragment = entity! {
85 crate::metadata::created_at?: created_at,
86 super::content?: content_handle,
87 super::signed_by?: signed_by,
88 super::signature_r?: signature,
89 super::signature_s?: signature,
90 super::message?: msg,
91 super::metadata?: metadata,
92 super::parent*: parents,
93 };
94
95 fragment.into()
96}
97
98pub fn verify(content: Blob<SimpleArchive>, metadata: TribleSet) -> Result<(), ValidationError> {
104 let handle = content.get_handle();
105 let (pubkey, r, s) = match find!(
106 (pubkey: Inline<_>, r, s),
107 pattern!(&metadata, [
108 {
109 super::content: handle,
110 super::signed_by: ?pubkey,
111 super::signature_r: ?r,
112 super::signature_s: ?s
113 }]))
114 .at_most_one()
115 {
116 Ok(Some(result)) => result,
117 Ok(None) => return Err(ValidationError::MissingSignature),
118 Err(_) => return Err(ValidationError::AmbiguousSignature),
119 };
120
121 let pubkey: VerifyingKey = pubkey.try_from_inline()?;
122 let signature = Signature::from_components(r, s);
123 pubkey.verify(&content.bytes, &signature)?;
124 Ok(())
125}