void_core/metadata/
commit.rs1use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
4use void_crypto::{CommitCid, CommitSignature, MetadataCid, SigningPubKey};
5
6use crate::cid::ToVoidCid;
7use crate::{Result, VoidCid, VoidError};
8
9use super::Commit;
10
11impl Commit {
12 pub fn new(
14 parent: Option<CommitCid>,
15 metadata_bundle: MetadataCid,
16 timestamp: u64,
17 message: String,
18 ) -> Self {
19 Self {
20 parents: parent.into_iter().collect(),
21 metadata_bundle,
22 timestamp,
23 message,
24 author: None,
25 signature: None,
26 manifest_cid: None,
27 stats: None,
28 repo_manifest_cid: None,
29 }
30 }
31
32 pub fn new_merge(
34 parents: Vec<CommitCid>,
35 metadata_bundle: MetadataCid,
36 timestamp: u64,
37 message: String,
38 ) -> Self {
39 Self {
40 parents,
41 metadata_bundle,
42 timestamp,
43 message,
44 author: None,
45 signature: None,
46 manifest_cid: None,
47 stats: None,
48 repo_manifest_cid: None,
49 }
50 }
51
52 pub fn is_initial(&self) -> bool {
54 self.parents.is_empty()
55 }
56
57 pub fn is_merge(&self) -> bool {
59 self.parents.len() > 1
60 }
61
62 pub fn first_parent(&self) -> Option<&CommitCid> {
64 self.parents.first()
65 }
66
67 pub fn parent(&self) -> Option<&CommitCid> {
69 if self.parents.len() == 1 {
70 self.parents.first()
71 } else {
72 None
73 }
74 }
75
76 pub fn referenced_object_cids(&self) -> Vec<VoidCid> {
88 let mut cids = Vec::with_capacity(3);
89 if let Ok(c) = self.metadata_bundle.to_void_cid() {
90 cids.push(c);
91 }
92 if let Some(ref m) = self.manifest_cid {
93 if let Ok(c) = m.to_void_cid() {
94 cids.push(c);
95 }
96 }
97 if let Some(ref r) = self.repo_manifest_cid {
98 if let Ok(c) = r.to_void_cid() {
99 cids.push(c);
100 }
101 }
102 cids
103 }
104
105 pub fn is_signed(&self) -> bool {
107 self.author.is_some() && self.signature.is_some()
108 }
109
110 pub fn signable_bytes(&self) -> Vec<u8> {
114 let mut buf = Vec::new();
115
116 buf.extend((self.parents.len() as u32).to_le_bytes());
118 for parent in &self.parents {
119 buf.extend((parent.as_bytes().len() as u32).to_le_bytes());
120 buf.extend(parent.as_bytes());
121 }
122
123 buf.extend((self.metadata_bundle.as_bytes().len() as u32).to_le_bytes());
125 buf.extend(self.metadata_bundle.as_bytes());
126
127 buf.extend(self.timestamp.to_le_bytes());
129
130 buf.extend((self.message.len() as u32).to_le_bytes());
132 buf.extend(self.message.as_bytes());
133
134 if let Some(author) = &self.author {
136 buf.push(1); buf.extend(author.as_bytes());
138 } else {
139 buf.push(0); }
141
142 if let Some(manifest_cid) = &self.manifest_cid {
144 buf.push(1); buf.extend((manifest_cid.as_bytes().len() as u32).to_le_bytes());
146 buf.extend(manifest_cid.as_bytes());
147 } else {
148 buf.push(0); }
150
151 if let Some(stats) = &self.stats {
153 buf.push(1); buf.extend(stats.total_files.to_le_bytes());
155 buf.extend(stats.total_bytes.to_le_bytes());
156 buf.extend(&stats.paths_hash);
157 } else {
158 buf.push(0); }
160
161 if let Some(repo_manifest_cid) = &self.repo_manifest_cid {
163 buf.push(1); buf.extend((repo_manifest_cid.as_bytes().len() as u32).to_le_bytes());
165 buf.extend(repo_manifest_cid.as_bytes());
166 } else {
167 buf.push(0); }
169
170 buf
171 }
172
173 pub fn sign(&mut self, signing_key: &SigningKey) {
177 self.author = Some(SigningPubKey::from_bytes(signing_key.verifying_key().to_bytes()));
178 let signable = self.signable_bytes();
179 let sig: Signature = signing_key.sign(&signable);
180 self.signature = Some(CommitSignature::from_bytes(sig.to_bytes()));
181 }
182
183 pub fn verify(&self) -> Result<bool> {
190 match (&self.author, &self.signature) {
191 (Some(pubkey_bytes), Some(sig_bytes)) => {
192 let verifying_key = VerifyingKey::from_bytes(pubkey_bytes.as_bytes()).map_err(|e| {
193 VoidError::InvalidSignature(format!("invalid public key: {}", e))
194 })?;
195
196 let signature = Signature::from_bytes(sig_bytes.as_bytes());
197 let signable = self.signable_bytes();
198
199 verifying_key
200 .verify_strict(&signable, &signature)
201 .map_err(|e| {
202 VoidError::InvalidSignature(format!("verification failed: {}", e))
203 })?;
204
205 Ok(true)
206 }
207 (None, None) => Ok(false), _ => Err(VoidError::InvalidSignature(
209 "commit has author without signature or vice versa".to_string(),
210 )),
211 }
212 }
213}