1use core::fmt;
13use std::collections::HashSet;
14use std::iter::FromIterator;
15
16use ed25519_dalek::{PublicKey, Signature};
17use once_cell::sync::OnceCell;
18
19use crate::errors::*;
20#[allow(unused_imports)]
21use crate::log::*;
22use crate::object::*;
23use crate::repo::CommitInfo;
24use crate::repo::Repo;
25use crate::store::Store;
26use crate::types::*;
27use crate::utils::*;
28
29#[derive(Debug, PartialEq, Eq, Clone)]
30pub enum CommitLoadError {
31 MissingBlocks(Vec<BlockId>),
32 ObjectParseError,
33 NotACommit,
34 NotACommitBody,
35 CannotBeAtRootOfBranch,
36 MustBeAtRootOfBranch,
37 SingletonCannotHaveHeader,
38 MalformedHeader,
39 BodyTypeMismatch,
40 ContentParseError(ObjectParseError),
41}
42
43#[derive(Debug, PartialEq, Eq, Clone)]
44pub enum CommitVerifyError {
45 InvalidSignature,
46 InvalidHeader,
47 PermissionDenied,
48}
49
50impl CommitV0 {
51 pub fn new(
53 author_privkey: &PrivKey,
54 author_pubkey: &PubKey,
55 overlay: OverlayId,
56 branch: BranchId,
57 quorum: QuorumType,
58 deps: Vec<ObjectRef>,
59 ndeps: Vec<ObjectRef>,
60 acks: Vec<ObjectRef>,
61 nacks: Vec<ObjectRef>,
62 files: Vec<ObjectRef>,
63 nfiles: Vec<ObjectRef>,
64 metadata: Vec<u8>,
65 body: ObjectRef,
66 ) -> Result<CommitV0, NgError> {
67 let headers = CommitHeader::new_with(deps, ndeps, acks, nacks, files, nfiles);
68 let content = CommitContent::V0(CommitContentV0 {
69 perms: vec![],
70 author: CommitContent::author_digest(author_pubkey, overlay),
71 branch,
72 header_keys: headers.1,
73 quorum,
74 timestamp: now_timestamp(),
75 metadata,
76 body,
77 });
78 let content_ser = serde_bare::to_vec(&content).unwrap();
79
80 let sig = sign(author_privkey, author_pubkey, &content_ser)?;
82 Ok(CommitV0 {
83 content: content,
84 sig,
85 id: None,
86 key: None,
87 header: headers.0,
88 body: OnceCell::new(),
89 blocks: vec![],
90 })
91 }
92
93 #[cfg(test)]
94 pub fn new_with_invalid_header(
96 author_privkey: &PrivKey,
97 author_pubkey: &PubKey,
98 branch: BranchId,
99 quorum: QuorumType,
100 metadata: Vec<u8>,
101 body: ObjectRef,
102 ) -> Result<CommitV0, NgError> {
103 let headers = CommitHeader::new_invalid();
104 let content = CommitContent::V0(CommitContentV0 {
105 perms: vec![],
106 author: CommitContent::author_digest(&author_pubkey, OverlayId::dummy()),
107 branch,
108 header_keys: headers.1,
109 quorum,
110 timestamp: now_timestamp(),
111 metadata,
112 body,
113 });
114 let content_ser = serde_bare::to_vec(&content).unwrap();
115
116 let sig = sign(&author_privkey, &author_pubkey, &content_ser)?;
118 Ok(CommitV0 {
119 content: content,
120 sig,
121 id: None,
122 key: None,
123 header: headers.0,
124 body: OnceCell::new(),
125 blocks: vec![],
126 })
127 }
128
129 pub fn save(&mut self, block_size: usize, store: &Store) -> Result<ObjectRef, StorageError> {
130 if self.id.is_some() && self.key.is_some() {
131 return Ok(ObjectRef::from_id_key(
132 self.id.unwrap(),
133 self.key.as_ref().unwrap().clone(),
134 ));
135 }
136 let mut obj = Object::new(
138 ObjectContent::V0(ObjectContentV0::Commit(Commit::V0(self.clone()))),
139 self.header.clone(),
140 block_size,
141 store,
142 );
143 self.blocks = obj.save(store)?;
144 if let Some(h) = &mut self.header {
145 if let Some(id) = obj.header().as_ref().unwrap().id() {
146 h.set_id(*id);
147 }
148 }
149 self.id = Some(obj.get_and_save_id());
150 self.key = Some(obj.key().unwrap());
151 Ok(obj.reference().unwrap())
152 }
153}
154
155impl IObject for Commit {
156 fn block_ids(&self) -> Vec<BlockId> {
157 self.blocks().clone()
158 }
159 fn id(&self) -> Option<ObjectId> {
162 match self {
163 Commit::V0(c) => c.id,
164 }
165 }
166
167 fn key(&self) -> Option<SymKey> {
170 match self {
171 Commit::V0(c) => c.key.clone(),
172 }
173 }
174}
175
176impl Commit {
177 pub fn new(
179 author_privkey: &PrivKey,
180 author_pubkey: &PubKey,
181 overlay: OverlayId,
182 branch: BranchId,
183 quorum: QuorumType,
184 deps: Vec<ObjectRef>,
185 ndeps: Vec<ObjectRef>,
186 acks: Vec<ObjectRef>,
187 nacks: Vec<ObjectRef>,
188 files: Vec<ObjectRef>,
189 nfiles: Vec<ObjectRef>,
190 metadata: Vec<u8>,
191 body: ObjectRef,
192 ) -> Result<Commit, NgError> {
193 CommitV0::new(
194 author_privkey,
195 author_pubkey,
196 overlay,
197 branch,
198 quorum,
199 deps,
200 ndeps,
201 acks,
202 nacks,
203 files,
204 nfiles,
205 metadata,
206 body,
207 )
208 .map(|c| Commit::V0(c))
209 }
210
211 pub fn new_with_body_acks_deps_and_save(
213 author_privkey: &PrivKey,
214 author_pubkey: &PubKey,
215 branch: BranchId,
216 quorum: QuorumType,
217 deps: Vec<ObjectRef>,
218 acks: Vec<ObjectRef>,
219 body: CommitBody,
220 store: &Store,
221 ) -> Result<Commit, NgError> {
222 Self::new_with_body_and_save(
223 author_privkey,
224 author_pubkey,
225 branch,
226 quorum,
227 deps,
228 vec![],
229 acks,
230 vec![],
231 vec![],
232 vec![],
233 vec![],
234 body,
235 0,
236 store,
237 )
238 }
239
240 pub fn new_with_body_and_save(
242 author_privkey: &PrivKey,
243 author_pubkey: &PubKey,
244 branch: BranchId,
245 quorum: QuorumType,
246 deps: Vec<ObjectRef>,
247 ndeps: Vec<ObjectRef>,
248 acks: Vec<ObjectRef>,
249 nacks: Vec<ObjectRef>,
250 files: Vec<ObjectRef>,
251 nfiles: Vec<ObjectRef>,
252 metadata: Vec<u8>,
253 body: CommitBody,
254 block_size: usize,
255 store: &Store,
256 ) -> Result<Commit, NgError> {
257 let (body_ref, mut saved_body) = body.clone().save(block_size, store)?;
258 let overlay = store.get_store_repo().overlay_id_for_read_purpose();
259 let mut commit_v0 = CommitV0::new(
260 author_privkey,
261 author_pubkey,
262 overlay,
263 branch,
264 quorum,
265 deps,
266 ndeps,
267 acks,
268 nacks,
269 files,
270 nfiles,
271 metadata,
272 body_ref,
273 )?;
274 commit_v0.body.set(body).unwrap();
275 let _commit_ref = commit_v0.save(block_size, store)?;
276 commit_v0.blocks.append(&mut saved_body);
277
278 Ok(Commit::V0(commit_v0))
279 }
280
281 pub fn reference(&self) -> Option<ObjectRef> {
282 if self.key().is_some() && self.id().is_some() {
283 Some(ObjectRef {
284 id: self.id().unwrap(),
285 key: self.key().unwrap(),
286 })
287 } else {
288 None
289 }
290 }
291
292 pub fn save(&mut self, block_size: usize, store: &Store) -> Result<ObjectRef, StorageError> {
293 match self {
294 Commit::V0(v0) => v0.save(block_size, store),
295 }
296 }
297
298 pub fn blocks(&self) -> &Vec<BlockId> {
299 match self {
300 Commit::V0(v0) => &v0.blocks,
301 }
302 }
303
304 #[cfg(test)]
305 fn empty_blocks(&mut self) {
306 match self {
307 Commit::V0(v0) => v0.blocks = vec![],
308 }
309 }
310
311 pub fn load(
313 commit_ref: ObjectRef,
314 store: &Store,
315 with_body: bool,
316 ) -> Result<Commit, CommitLoadError> {
317 let (id, key) = (commit_ref.id, commit_ref.key);
318 match Object::load(id, Some(key.clone()), store) {
319 Err(ObjectParseError::MissingHeaderBlocks((obj, mut missing))) => {
320 if with_body {
322 let content = obj
323 .content()
324 .map_err(|e| CommitLoadError::ContentParseError(e))?;
325 let mut commit = match content {
326 ObjectContent::V0(ObjectContentV0::Commit(c)) => c,
327 _ => return Err(CommitLoadError::NotACommit),
328 };
329 commit.set_id(id);
330 commit.set_key(key);
331 match commit.load_body(store) {
332 Ok(_) => return Err(CommitLoadError::MissingBlocks(missing)),
333 Err(CommitLoadError::MissingBlocks(mut missing_body)) => {
334 missing.append(&mut missing_body);
335 return Err(CommitLoadError::MissingBlocks(missing));
336 }
337 Err(e) => return Err(e),
338 }
339 } else {
340 return Err(CommitLoadError::MissingBlocks(missing));
341 }
342 }
343 Ok(obj) => {
344 let content = obj
345 .content()
346 .map_err(|e| CommitLoadError::ContentParseError(e))?;
347 let mut commit = match content {
348 ObjectContent::V0(ObjectContentV0::Commit(c)) => c,
349 _ => return Err(CommitLoadError::NotACommit),
350 };
351 commit.set_id(id);
352 commit.set_key(key);
353 commit.set_header(obj.header().clone());
354
355 if with_body {
356 commit.load_body(store)?;
357 }
358
359 Ok(commit)
360 }
361 Err(ObjectParseError::MissingBlocks(missing)) => {
362 Err(CommitLoadError::MissingBlocks(missing))
363 }
364 Err(_e) => {
365 log_err!("{:?}", _e);
366 Err(CommitLoadError::ObjectParseError)
367 }
368 }
369 }
370
371 pub fn load_body(&self, store: &Store) -> Result<&CommitBody, CommitLoadError> {
373 if self.body().is_some() {
374 return Ok(self.body().unwrap());
375 }
376 let content = self.content_v0();
377 let (id, key) = (content.body.id, content.body.key.clone());
378 let obj = Object::load(id.clone(), Some(key.clone()), store).map_err(|e| match e {
379 ObjectParseError::MissingBlocks(missing) => CommitLoadError::MissingBlocks(missing),
380 _ => CommitLoadError::ObjectParseError,
381 })?;
382 let content = obj
383 .content()
384 .map_err(|_e| CommitLoadError::ObjectParseError)?;
385 match content {
386 ObjectContent::V0(ObjectContentV0::CommitBody(body)) => {
387 self.set_body(body);
388 Ok(self.body().unwrap())
389 }
390 _ => Err(CommitLoadError::NotACommitBody),
391 }
392 }
393
394 fn set_body(&self, body: CommitBody) {
395 match self {
396 Commit::V0(c) => {
397 c.body.set(body).unwrap();
398 }
399 }
400 }
401
402 pub fn header_id(&self) -> &Option<ObjectId> {
404 match self {
405 Commit::V0(CommitV0 {
406 header: Some(ch), ..
407 }) => ch.id(),
408 _ => &None,
409 }
410 }
411
412 fn set_id(&mut self, id: ObjectId) {
414 match self {
415 Commit::V0(c) => c.id = Some(id),
416 }
417 }
418
419 fn set_key(&mut self, key: SymKey) {
421 match self {
422 Commit::V0(c) => c.key = Some(key),
423 }
424 }
425
426 fn set_header(&mut self, header: Option<CommitHeader>) {
428 match self {
429 Commit::V0(c) => c.header = header,
430 }
431 }
432
433 pub fn sig(&self) -> &Sig {
435 match self {
436 Commit::V0(c) => &c.sig,
437 }
438 }
439
440 pub fn author(&self) -> &Digest {
442 self.content().author()
443 }
444
445 pub fn timestamp(&self) -> Timestamp {
446 self.content().timestamp()
447 }
448
449 pub fn final_consistency(&self) -> bool {
450 self.content().final_consistency()
451 || self.body().is_some_and(|body| body.total_order_required())
452 }
453
454 pub fn get_type(&self) -> Option<CommitType> {
455 self.body().map(|b| b.get_type())
456 }
457
458 pub fn get_signature_reference(&self) -> Option<ObjectRef> {
459 self.body().map_or(None, |b| b.get_signature_reference())
460 }
461
462 pub fn as_info(&self, repo: &Repo) -> CommitInfo {
463 let past = self.acks_ids();
464 CommitInfo {
470 past,
471 key: self.key().unwrap(),
472 signature: None,
473 author: repo.get_user_string(self.author()),
474 timestamp: self.timestamp(),
475 final_consistency: self.final_consistency(),
476 commit_type: self.get_type().unwrap(),
477 branch: None,
478 x: 0,
479 y: 0,
480 }
481 }
482
483 pub fn branch(&self) -> &BranchId {
485 self.content().branch()
486 }
487
488 pub fn header(&self) -> &Option<CommitHeader> {
490 match self {
491 Commit::V0(c) => &c.header,
492 }
493 }
494
495 pub fn body_ref(&self) -> &ObjectRef {
496 &self.content_v0().body
497 }
498
499 pub fn content_v0(&self) -> &CommitContentV0 {
501 match self {
502 Commit::V0(CommitV0 {
503 content: CommitContent::V0(c),
504 ..
505 }) => c,
506 }
507 }
508
509 pub fn quorum_type(&self) -> &QuorumType {
511 &self.content_v0().quorum
512 }
513
514 pub fn content(&self) -> &CommitContent {
516 match self {
517 Commit::V0(CommitV0 { content: c, .. }) => c,
518 }
519 }
520
521 pub fn body(&self) -> Option<&CommitBody> {
522 match self {
523 Commit::V0(c) => c.body.get(),
524 }
525 }
526
527 pub fn owners_signature_required(&self, store: &Store) -> Result<bool, CommitLoadError> {
528 match self.load_body(store)? {
529 CommitBody::V0(CommitBodyV0::UpdateRootBranch(new_root)) => {
530 let deps = self.deps();
532 if deps.len() != 1 {
533 Err(CommitLoadError::MalformedHeader)
534 } else {
535 let previous_rootbranch_commit = Commit::load(deps[0].clone(), store, true)?;
536 let previous_rootbranch = previous_rootbranch_commit
537 .body()
538 .unwrap()
539 .root_branch_commit()?;
540 if previous_rootbranch.owners() != new_root.owners() {
541 Ok(true)
542 } else {
543 Ok(false)
544 }
545 }
546 }
547 CommitBody::V0(CommitBodyV0::RootBranch(_)) => {
548 let deps = self.deps();
549 let acks = self.acks();
550 if deps.is_empty() && acks.len() == 1 {
551 let causal_past = Commit::load(acks[0].clone(), store, true)?;
553 if causal_past.body().unwrap().is_repository_singleton_commit() {
554 return Ok(false);
555 }
556 }
557 Err(CommitLoadError::MalformedHeader)
558 }
559 CommitBody::V0(CommitBodyV0::Delete(_)) => Ok(true),
560 _ => Ok(false),
561 }
562 }
563
564 pub fn is_root_commit_of_branch(&self) -> bool {
566 match self {
567 Commit::V0(CommitV0 {
568 content: CommitContent::V0(c),
569 ..
570 }) => match &c.header_keys {
571 Some(CommitHeaderKeys::V0(hk)) => hk.acks.is_empty() && hk.nacks.is_empty(),
572 None => true,
573 },
574 }
575 }
576
577 pub fn acks(&self) -> Vec<ObjectRef> {
579 let mut res: Vec<ObjectRef> = vec![];
580 match self {
581 Commit::V0(c) => match &c.header {
582 Some(CommitHeader::V0(header_v0)) => match &c.content.header_keys() {
583 Some(CommitHeaderKeys::V0(hk_v0)) => {
584 for ack in header_v0.acks.iter().zip(hk_v0.acks.iter()) {
585 res.push(ack.into());
586 }
587 }
588 None => {}
589 },
590 None => {}
591 },
592 };
593 res
594 }
595
596 pub fn acks_ids(&self) -> Vec<ObjectId> {
599 match self {
600 Commit::V0(c) => match &c.header {
601 Some(h) => h.acks(),
602 None => vec![],
603 },
604 }
605 }
606
607 pub fn deps_ids(&self) -> Vec<ObjectId> {
610 match self {
611 Commit::V0(c) => match &c.header {
612 Some(h) => h.deps(),
613 None => vec![],
614 },
615 }
616 }
617
618 pub fn files(&self) -> Vec<ObjectRef> {
620 let mut res: Vec<ObjectRef> = vec![];
621 match self {
622 Commit::V0(c) => match &c.content.header_keys() {
623 Some(CommitHeaderKeys::V0(hk_v0)) => {
624 for file in hk_v0.files.iter() {
625 res.push(file.clone());
626 }
627 }
628 None => {}
629 },
630 };
631 res
632 }
633
634 pub fn deps(&self) -> Vec<ObjectRef> {
636 let mut res: Vec<ObjectRef> = vec![];
637 match self {
638 Commit::V0(c) => match &c.header {
639 Some(CommitHeader::V0(header_v0)) => match &c.content.header_keys() {
640 Some(CommitHeaderKeys::V0(hk_v0)) => {
641 for dep in header_v0.deps.iter().zip(hk_v0.deps.iter()) {
642 res.push(dep.into());
643 }
644 }
645 None => {}
646 },
647 None => {}
648 },
649 };
650 res
651 }
652
653 pub fn direct_causal_past(&self) -> Vec<ObjectRef> {
656 let mut res: Vec<ObjectRef> = vec![];
657 match self {
658 Commit::V0(c) => match (&c.header, &c.content.header_keys()) {
659 (Some(CommitHeader::V0(header_v0)), Some(CommitHeaderKeys::V0(hk_v0))) => {
660 for ack in header_v0.acks.iter().zip(hk_v0.acks.iter()) {
661 res.push(ack.into());
662 }
663 for nack in header_v0.nacks.iter().zip(hk_v0.nacks.iter()) {
664 res.push(nack.into());
665 }
666 }
667 _ => {}
668 },
669 };
670 res
671 }
672
673 pub fn direct_causal_past_ids(&self) -> HashSet<ObjectId> {
674 let mut res: HashSet<ObjectId> = HashSet::with_capacity(1);
675 match self {
676 Commit::V0(c) => match &c.header {
677 Some(CommitHeader::V0(header_v0)) => {
678 res.extend(header_v0.acks.iter());
679 res.extend(header_v0.nacks.iter());
680 }
681 _ => {}
682 },
683 };
684 res
685 }
686
687 pub fn verify_sig(&self, repo: &Repo) -> Result<(), CommitVerifyError> {
699 let c = match self {
700 Commit::V0(c) => c,
701 };
702 let content_ser = serde_bare::to_vec(&c.content).unwrap();
703
704 let pubkey = repo
705 .member_pubkey(c.content.author())
706 .map_err(|_| CommitVerifyError::PermissionDenied)?;
707
708 let pubkey_slice = match pubkey {
709 PubKey::Ed25519PubKey(pk) => pk,
710 _ => panic!("author cannot have a Montgomery key"),
711 };
712 let pk = PublicKey::from_bytes(&pubkey_slice)
713 .map_err(|_| CommitVerifyError::InvalidSignature)?;
714 let sig_bytes = match c.sig {
715 Sig::Ed25519Sig(ss) => [ss[0], ss[1]].concat(),
716 };
717 let sig =
718 Signature::from_bytes(&sig_bytes).map_err(|_| CommitVerifyError::InvalidSignature)?;
719 pk.verify_strict(&content_ser, &sig)
720 .map_err(|_| CommitVerifyError::InvalidSignature)
721 }
722
723 pub fn verify_perm(&self, repo: &Repo) -> Result<(), NgError> {
725 repo.verify_permission(self)
726 }
727
728 pub fn verify_perm_creation(&self, user: Option<&Digest>) -> Result<&Digest, NgError> {
729 let digest = self.content().author();
730 if user.is_some() && *digest != *user.unwrap() {
731 return Err(NgError::PermissionDenied);
732 }
733 let body = self.body().ok_or(NgError::InvalidArgument)?;
734 if !(body.is_repository_singleton_commit() && user.is_none()) {
735 return Err(NgError::InvalidArgument);
737 }
738 if body.required_permission().contains(&PermissionV0::Create) {
739 Ok(digest)
740 } else {
741 Err(NgError::PermissionDenied)
742 }
743 }
744
745 pub fn verify_full_object_refs_of_branch_at_commit(
749 &self,
750 store: &Store,
751 ) -> Result<Vec<ObjectId>, CommitLoadError> {
752 fn load_direct_object_refs(
757 commit: &Commit,
758 store: &Store,
759 visited: &mut HashSet<ObjectId>,
760 missing: &mut HashSet<ObjectId>,
761 ) -> Result<(), CommitLoadError> {
762 match commit.id() {
766 Some(id) => {
767 if visited.contains(&id) {
768 return Ok(());
769 }
770 visited.insert(id);
771 }
774 None => {
775 if !visited.is_empty() {
776 panic!("A Commit in the causal past doesn't have an ID");
779 }
780 }
781 }
782
783 match commit.load_body(store) {
785 Ok(_) => Ok(()),
786 Err(CommitLoadError::MissingBlocks(m)) => {
787 missing.extend(m.clone());
789 Err(CommitLoadError::MissingBlocks(m))
790 }
791 Err(e) => Err(e),
792 }?;
793
794 let body = commit.body().unwrap();
795 visited.insert(commit.content_v0().body.id);
796 if commit.is_root_commit_of_branch() {
797 if !body.must_be_root_commit_in_branch() {
798 return Err(CommitLoadError::CannotBeAtRootOfBranch);
799 }
800 if body.is_repository_singleton_commit() && commit.header().is_some() {
801 return Err(CommitLoadError::SingletonCannotHaveHeader);
802 }
803 } else {
804 if body.must_be_root_commit_in_branch() {
805 return Err(CommitLoadError::MustBeAtRootOfBranch);
806 }
807 }
808
809 for blockref in commit.direct_causal_past() {
811 match Commit::load(blockref, store, true) {
812 Ok(mut c) => {
813 load_direct_object_refs(&mut c, store, visited, missing)?;
814 }
815 Err(CommitLoadError::MissingBlocks(m)) => {
816 missing.extend(m);
817 }
818 Err(e) => return Err(e),
819 }
820 }
821
822 Ok(())
823 }
824
825 let mut visited = HashSet::new();
826 let mut missing = HashSet::new();
827 load_direct_object_refs(self, store, &mut visited, &mut missing)?;
828
829 if !missing.is_empty() {
830 return Err(CommitLoadError::MissingBlocks(Vec::from_iter(missing)));
831 }
832 Ok(Vec::from_iter(visited))
833 }
834
835 pub fn verify(&self, repo: &Repo) -> Result<(), NgError> {
837 if !self.header().as_ref().map_or(true, |h| h.verify()) {
838 return Err(NgError::CommitVerifyError(CommitVerifyError::InvalidHeader));
839 }
840 self.verify_sig(repo)?;
841 self.verify_perm(repo)?;
842 self.verify_full_object_refs_of_branch_at_commit(&repo.store)?;
843 Ok(())
844 }
845}
846
847impl PermissionV0 {
848 pub fn is_write_permission(&self) -> bool {
850 match self {
851 Self::WriteAsync | Self::WriteSync | Self::RefreshWriteCap => true,
852 _ => false,
853 }
854 }
855
856 pub fn is_delegated_by_admin(&self) -> bool {
857 self.is_write_permission()
858 || match self {
859 Self::AddReadMember
860 | Self::RemoveMember
861 | Self::AddWritePermission
862 | Self::RemoveWritePermission
863 | Self::Compact
864 | Self::AddBranch
865 | Self::RemoveBranch
866 | Self::ChangeName
867 | Self::RefreshReadCap => true,
868 _ => false,
869 }
870 }
871
872 pub fn is_delegated_by_owner(&self) -> bool {
873 self.is_delegated_by_admin()
874 || match self {
875 Self::ChangeQuorum | Self::Admin | Self::ChangeMainBranch => true,
876 _ => false,
877 }
878 }
879}
880
881impl CommitBody {
882 pub fn save(
883 self,
884 block_size: usize,
885 store: &Store,
886 ) -> Result<(ObjectRef, Vec<BlockId>), StorageError> {
887 let obj = Object::new(
888 ObjectContent::V0(ObjectContentV0::CommitBody(self)),
889 None,
890 block_size,
891 store,
892 );
893 let blocks = obj.save(store)?;
894 Ok((obj.reference().unwrap(), blocks))
895 }
896
897 pub fn is_add_signer_cap(&self) -> bool {
898 match self {
899 Self::V0(v0) => match v0 {
900 CommitBodyV0::AddSignerCap(_) => true,
901 _ => false,
902 },
903 }
904 }
905
906 pub fn root_branch_commit(&self) -> Result<&RootBranch, CommitLoadError> {
907 match self {
908 Self::V0(v0) => match v0 {
909 CommitBodyV0::UpdateRootBranch(rb) | CommitBodyV0::RootBranch(rb) => Ok(rb),
910 _ => Err(CommitLoadError::BodyTypeMismatch),
911 },
912 }
913 }
914
915 pub fn is_repository_singleton_commit(&self) -> bool {
916 match self {
917 Self::V0(v0) => match v0 {
918 CommitBodyV0::Repository(_) => true,
919 _ => false,
920 },
921 }
922 }
923 pub fn must_be_root_commit_in_branch(&self) -> bool {
924 match self {
925 Self::V0(v0) => match v0 {
926 CommitBodyV0::Repository(_) => true,
927 CommitBodyV0::Branch(_) => true,
928 _ => false,
929 },
930 }
931 }
932
933 pub fn on_root_branch(&self) -> bool {
934 match self {
935 Self::V0(v0) => match v0 {
936 CommitBodyV0::Repository(_) => true,
937 CommitBodyV0::RootBranch(_) => true,
938 CommitBodyV0::UpdateRootBranch(_) => true,
939 CommitBodyV0::AddBranch(_) => true,
940 CommitBodyV0::RemoveBranch(_) => true,
941 CommitBodyV0::AddMember(_) => true,
942 CommitBodyV0::RemoveMember(_) => true,
943 CommitBodyV0::AddPermission(_) => true,
944 CommitBodyV0::RemovePermission(_) => true,
945 CommitBodyV0::AddName(_) => true,
946 CommitBodyV0::RemoveName(_) => true,
947 CommitBodyV0::RootCapRefresh(_) => true,
949 CommitBodyV0::CapRefreshed(_) => true,
950 CommitBodyV0::SyncSignature(_) => true,
951 CommitBodyV0::Delete(_) => true,
952 _ => false,
953 },
954 }
955 }
956
957 pub fn on_transactional_branch(&self) -> bool {
958 match self {
959 Self::V0(v0) => match v0 {
960 CommitBodyV0::Branch(_) => true,
961 CommitBodyV0::UpdateBranch(_) => true,
962 CommitBodyV0::Snapshot(_) => true,
963 CommitBodyV0::AsyncTransaction(_) => true,
964 CommitBodyV0::SyncTransaction(_) => true,
965 CommitBodyV0::AddFile(_) => true,
966 CommitBodyV0::RemoveFile(_) => true,
967 CommitBodyV0::Compact(_) => true,
968 CommitBodyV0::AsyncSignature(_) => true,
969 CommitBodyV0::BranchCapRefresh(_) => true,
970 CommitBodyV0::CapRefreshed(_) => true,
971 CommitBodyV0::SyncSignature(_) => true,
972 _ => false,
973 },
974 }
975 }
976
977 pub fn on_store_branch(&self) -> bool {
978 match self {
979 Self::V0(v0) => match v0 {
980 CommitBodyV0::AddRepo(_) => true,
981 CommitBodyV0::RemoveRepo(_) => true,
982 _ => false,
983 },
984 }
985 }
986
987 pub fn on_user_branch(&self) -> bool {
988 match self {
989 Self::V0(v0) => match v0 {
990 CommitBodyV0::AddLink(_) => true,
991 CommitBodyV0::RemoveLink(_) => true,
992 CommitBodyV0::AddSignerCap(_) => true,
993 CommitBodyV0::RemoveSignerCap(_) => true,
994 CommitBodyV0::WalletUpdate(_) => true,
995 CommitBodyV0::StoreUpdate(_) => true,
996 _ => false,
997 },
998 }
999 }
1000
1001 pub fn not_allowed_on_individual_private_site(&self) -> bool {
1002 match self {
1003 Self::V0(v0) => match v0 {
1004 CommitBodyV0::SyncTransaction(_) => true,
1005 CommitBodyV0::AddMember(_) => true,
1006 CommitBodyV0::RemoveMember(_) => true,
1007 CommitBodyV0::AddPermission(_) => true,
1008 CommitBodyV0::RemovePermission(_) => true,
1009 _ => false,
1010 },
1011 }
1012 }
1013
1014 pub fn total_order_required(&self) -> bool {
1015 match self {
1016 Self::V0(v0) => match v0 {
1017 CommitBodyV0::RootBranch(_) => true,
1018 CommitBodyV0::Branch(_) => true,
1019 CommitBodyV0::UpdateRootBranch(_) => true,
1020 CommitBodyV0::UpdateBranch(_) => true,
1021 CommitBodyV0::AddBranch(AddBranch::V0(AddBranchV0 {
1022 branch_type: BranchType::Transactional,
1023 ..
1024 })) => false,
1025 CommitBodyV0::AddBranch(AddBranch::V0(AddBranchV0 { branch_type: _, .. })) => true,
1026 CommitBodyV0::RemoveBranch(_) => true,
1027 CommitBodyV0::RemoveMember(_) => true,
1029 CommitBodyV0::RemovePermission(_) => true,
1030 CommitBodyV0::Compact(_) => true,
1032 CommitBodyV0::SyncTransaction(_) => true, CommitBodyV0::RootCapRefresh(_) => true,
1034 CommitBodyV0::BranchCapRefresh(_) => true,
1035 _ => false,
1036 },
1037 }
1038 }
1039 pub fn required_permission(&self) -> HashSet<PermissionV0> {
1040 let res: Vec<PermissionV0>;
1041 res = match self {
1042 Self::V0(v0) => match v0 {
1043 CommitBodyV0::Repository(_) => vec![PermissionV0::Create],
1044 CommitBodyV0::RootBranch(_) => vec![PermissionV0::Create],
1045 CommitBodyV0::UpdateRootBranch(_) => vec![
1046 PermissionV0::ChangeQuorum,
1047 PermissionV0::RefreshWriteCap,
1048 PermissionV0::RefreshReadCap,
1049 PermissionV0::RefreshOverlay,
1050 ],
1051 CommitBodyV0::AddMember(_) => {
1052 vec![PermissionV0::Create, PermissionV0::AddReadMember]
1053 }
1054 CommitBodyV0::RemoveMember(_) => vec![PermissionV0::RemoveMember],
1055 CommitBodyV0::AddPermission(addp) => {
1056 let mut perms = vec![PermissionV0::Create];
1057 if addp.permission_v0().is_delegated_by_admin() {
1058 perms.push(PermissionV0::Admin);
1059 }
1060 if addp.permission_v0().is_write_permission() {
1061 perms.push(PermissionV0::AddWritePermission);
1062 }
1063 perms
1064 }
1065 CommitBodyV0::RemovePermission(remp) => {
1066 let mut perms = vec![];
1067 if remp.permission_v0().is_delegated_by_admin() {
1068 perms.push(PermissionV0::Admin);
1069 }
1070 if remp.permission_v0().is_write_permission() {
1071 perms.push(PermissionV0::RemoveWritePermission);
1072 }
1073 perms
1074 }
1075 CommitBodyV0::AddBranch(_) => vec![
1076 PermissionV0::Create,
1077 PermissionV0::AddBranch,
1078 PermissionV0::RefreshReadCap,
1079 PermissionV0::RefreshWriteCap,
1080 PermissionV0::RefreshOverlay,
1081 PermissionV0::ChangeMainBranch,
1082 ],
1083 CommitBodyV0::RemoveBranch(_) => vec![PermissionV0::RemoveBranch],
1084 CommitBodyV0::UpdateBranch(_) => {
1085 vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1086 }
1087 CommitBodyV0::AddName(_) => vec![PermissionV0::AddBranch, PermissionV0::ChangeName],
1088 CommitBodyV0::RemoveName(_) => {
1089 vec![PermissionV0::ChangeName, PermissionV0::RemoveBranch]
1090 }
1091 CommitBodyV0::Branch(_) => vec![PermissionV0::Create, PermissionV0::AddBranch],
1092 CommitBodyV0::Snapshot(_) => vec![PermissionV0::WriteAsync],
1093 CommitBodyV0::Compact(_) => vec![PermissionV0::Compact],
1094 CommitBodyV0::AsyncTransaction(_) => vec![PermissionV0::WriteAsync],
1095 CommitBodyV0::AddFile(_) => vec![PermissionV0::WriteAsync, PermissionV0::WriteSync],
1096 CommitBodyV0::RemoveFile(_) => {
1097 vec![PermissionV0::WriteAsync, PermissionV0::WriteSync]
1098 }
1099 CommitBodyV0::SyncTransaction(_) => vec![PermissionV0::WriteSync],
1100 CommitBodyV0::AsyncSignature(_) => vec![PermissionV0::WriteAsync],
1101 CommitBodyV0::SyncSignature(_) => vec![
1102 PermissionV0::WriteSync,
1103 PermissionV0::ChangeQuorum,
1104 PermissionV0::RefreshWriteCap,
1105 PermissionV0::RefreshReadCap,
1106 PermissionV0::RefreshOverlay,
1107 PermissionV0::ChangeMainBranch,
1108 PermissionV0::AddBranch,
1109 PermissionV0::RemoveBranch,
1110 PermissionV0::AddReadMember,
1111 PermissionV0::RemoveMember,
1112 PermissionV0::RemoveWritePermission,
1113 PermissionV0::Compact,
1114 ],
1115 CommitBodyV0::RootCapRefresh(_) => {
1116 vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1117 }
1118 CommitBodyV0::BranchCapRefresh(_) => {
1119 vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1120 }
1121 CommitBodyV0::CapRefreshed(_) => {
1122 vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1123 }
1124 CommitBodyV0::Delete(_) => vec![],
1125 CommitBodyV0::AddRepo(_)
1126 | CommitBodyV0::RemoveRepo(_)
1127 | CommitBodyV0::AddLink(_)
1128 | CommitBodyV0::RemoveLink(_)
1129 | CommitBodyV0::AddSignerCap(_)
1130 | CommitBodyV0::RemoveSignerCap(_)
1131 | CommitBodyV0::WalletUpdate(_)
1132 | CommitBodyV0::StoreUpdate(_) => vec![],
1133 },
1134 };
1135 HashSet::from_iter(res.iter().cloned())
1136 }
1137}
1138
1139impl fmt::Display for CommitHeader {
1140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1141 match self {
1142 CommitHeader::V0(v0) => {
1143 writeln!(
1144 f,
1145 "v0 - compact:{} id:{}",
1146 v0.compact,
1147 v0.id.map_or("None".to_string(), |i| format!("{}", i))
1148 )?;
1149 writeln!(f, "==== acks : {}", v0.acks.len())?;
1150 for ack in &v0.acks {
1151 writeln!(f, "============== {}", ack)?;
1152 }
1153 writeln!(f, "==== nacks : {}", v0.nacks.len())?;
1154 for nack in &v0.nacks {
1155 writeln!(f, "============== {}", nack)?;
1156 }
1157 writeln!(f, "==== deps : {}", v0.deps.len())?;
1158 for dep in &v0.deps {
1159 writeln!(f, "============== {}", dep)?;
1160 }
1161 writeln!(f, "==== ndeps : {}", v0.ndeps.len())?;
1162 for ndep in &v0.ndeps {
1163 writeln!(f, "============== {}", ndep)?;
1164 }
1165 writeln!(f, "==== files : {}", v0.files.len())?;
1166 for file in &v0.files {
1167 writeln!(f, "============== {}", file)?;
1168 }
1169 writeln!(f, "==== nfiles : {}", v0.nfiles.len())?;
1170 for nfile in &v0.nfiles {
1171 writeln!(f, "============== {}", nfile)?;
1172 }
1173 Ok(())
1174 }
1175 }
1176 }
1177}
1178
1179impl CommitHeader {
1180 pub fn is_root(&self) -> bool {
1181 match self {
1182 CommitHeader::V0(v0) => v0.is_root(),
1183 }
1184 }
1185 pub fn deps(&self) -> Vec<ObjectId> {
1186 match self {
1187 CommitHeader::V0(v0) => v0.deps.clone(),
1188 }
1189 }
1190 pub fn acks(&self) -> Vec<ObjectId> {
1191 match self {
1192 CommitHeader::V0(v0) => v0.acks.clone(),
1193 }
1194 }
1195 pub fn files(&self) -> &Vec<ObjectId> {
1196 match self {
1197 CommitHeader::V0(v0) => &v0.files,
1198 }
1199 }
1200 pub fn acks_and_nacks(&self) -> Vec<ObjectId> {
1201 match self {
1202 CommitHeader::V0(v0) => {
1203 let mut res = v0.acks.clone();
1204 res.extend_from_slice(&v0.nacks);
1205 res
1206 }
1207 }
1208 }
1209 pub fn id(&self) -> &Option<ObjectId> {
1210 match self {
1211 CommitHeader::V0(v0) => &v0.id,
1212 }
1213 }
1214
1215 pub fn set_id(&mut self, id: Digest) {
1216 match self {
1217 CommitHeader::V0(v0) => v0.id = Some(id),
1218 }
1219 }
1220
1221 pub fn set_compact(&mut self) {
1222 match self {
1223 CommitHeader::V0(v0) => v0.set_compact(),
1224 }
1225 }
1226
1227 pub fn verify(&self) -> bool {
1228 match self {
1229 CommitHeader::V0(v0) => v0.verify(),
1230 }
1231 }
1232
1233 pub fn new_with(
1234 deps: Vec<ObjectRef>,
1235 ndeps: Vec<ObjectRef>,
1236 acks: Vec<ObjectRef>,
1237 nacks: Vec<ObjectRef>,
1238 files: Vec<ObjectRef>,
1239 nfiles: Vec<ObjectRef>,
1240 ) -> (Option<Self>, Option<CommitHeaderKeys>) {
1241 let res = CommitHeaderV0::new_with(deps, ndeps, acks, nacks, files, nfiles);
1242 (
1243 res.0.map(|h| CommitHeader::V0(h)),
1244 res.1.map(|h| CommitHeaderKeys::V0(h)),
1245 )
1246 }
1247
1248 #[cfg(test)]
1249 pub fn new_invalid() -> (Option<Self>, Option<CommitHeaderKeys>) {
1250 let res = CommitHeaderV0::new_invalid();
1251 (
1252 res.0.map(|h| CommitHeader::V0(h)),
1253 res.1.map(|h| CommitHeaderKeys::V0(h)),
1254 )
1255 }
1256
1257 #[cfg(test)]
1258 pub fn new_with_deps(deps: Vec<ObjectId>) -> Option<Self> {
1259 CommitHeaderV0::new_with_deps(deps).map(|ch| CommitHeader::V0(ch))
1260 }
1261
1262 #[cfg(test)]
1263 pub fn new_with_deps_and_acks(deps: Vec<ObjectId>, acks: Vec<ObjectId>) -> Option<Self> {
1264 CommitHeaderV0::new_with_deps_and_acks(deps, acks).map(|ch| CommitHeader::V0(ch))
1265 }
1266
1267 #[cfg(test)]
1268 pub fn new_with_acks(acks: Vec<ObjectId>) -> Option<Self> {
1269 CommitHeaderV0::new_with_acks(acks).map(|ch| CommitHeader::V0(ch))
1270 }
1271}
1272
1273impl CommitHeaderV0 {
1274 #[allow(dead_code)]
1275 fn new_empty() -> Self {
1276 Self {
1277 id: None,
1278 compact: false,
1279 deps: vec![],
1280 ndeps: vec![],
1281 acks: vec![],
1282 nacks: vec![],
1283 files: vec![],
1284 nfiles: vec![],
1285 }
1286 }
1287
1288 #[cfg(test)]
1289 fn new_invalid() -> (Option<Self>, Option<CommitHeaderKeysV0>) {
1290 let ideps: Vec<ObjectId> = vec![ObjectId::dummy()];
1291 let kdeps: Vec<ObjectKey> = vec![ObjectKey::dummy()];
1292
1293 let res = Self {
1294 id: None,
1295 compact: false,
1296 deps: ideps.clone(),
1297 ndeps: ideps,
1298 acks: vec![],
1299 nacks: vec![],
1300 files: vec![],
1301 nfiles: vec![],
1302 };
1303 (
1304 Some(res),
1305 Some(CommitHeaderKeysV0 {
1306 deps: kdeps,
1307 acks: vec![],
1308 nacks: vec![],
1309 files: vec![],
1310 }),
1311 )
1312 }
1313
1314 pub fn verify(&self) -> bool {
1315 if !self.deps.is_empty() && !self.ndeps.is_empty() {
1316 for ndep in self.ndeps.iter() {
1317 if self.deps.contains(ndep) {
1318 return false;
1319 }
1320 }
1321 }
1322 if !self.acks.is_empty() && !self.nacks.is_empty() {
1323 for nack in self.nacks.iter() {
1324 if self.acks.contains(nack) {
1325 return false;
1326 }
1327 }
1328 }
1329 if !self.files.is_empty() && !self.nfiles.is_empty() {
1330 for nref in self.nfiles.iter() {
1331 if self.files.contains(nref) {
1332 return false;
1333 }
1334 }
1335 }
1336 true
1337 }
1338
1339 pub fn set_compact(&mut self) {
1340 self.compact = true;
1341 }
1342
1343 pub fn new_with(
1344 deps: Vec<ObjectRef>,
1345 ndeps: Vec<ObjectRef>,
1346 acks: Vec<ObjectRef>,
1347 nacks: Vec<ObjectRef>,
1348 files: Vec<ObjectRef>,
1349 nfiles: Vec<ObjectRef>,
1350 ) -> (Option<Self>, Option<CommitHeaderKeysV0>) {
1351 if deps.is_empty()
1352 && ndeps.is_empty()
1353 && acks.is_empty()
1354 && nacks.is_empty()
1355 && files.is_empty()
1356 && nfiles.is_empty()
1357 {
1358 (None, None)
1359 } else {
1360 let mut ideps: Vec<ObjectId> = vec![];
1361 let mut indeps: Vec<ObjectId> = vec![];
1362 let mut iacks: Vec<ObjectId> = vec![];
1363 let mut inacks: Vec<ObjectId> = vec![];
1364 let mut ifiles: Vec<ObjectId> = vec![];
1365 let mut infiles: Vec<ObjectId> = vec![];
1366
1367 let mut kdeps: Vec<ObjectKey> = vec![];
1368 let mut kacks: Vec<ObjectKey> = vec![];
1369 let mut knacks: Vec<ObjectKey> = vec![];
1370 for d in deps {
1371 ideps.push(d.id);
1372 kdeps.push(d.key);
1373 }
1374 for d in ndeps {
1375 indeps.push(d.id);
1376 }
1377 for d in acks {
1378 iacks.push(d.id);
1379 kacks.push(d.key);
1380 }
1381 for d in nacks {
1382 inacks.push(d.id);
1383 knacks.push(d.key);
1384 }
1385 for d in files.clone() {
1386 ifiles.push(d.id);
1387 }
1388 for d in nfiles {
1389 infiles.push(d.id);
1390 }
1391 let res = Self {
1392 id: None,
1393 compact: false,
1394 deps: ideps,
1395 ndeps: indeps,
1396 acks: iacks,
1397 nacks: inacks,
1398 files: ifiles,
1399 nfiles: infiles,
1400 };
1401 if !res.verify() {
1402 panic!("cannot create a header with conflicting references");
1403 }
1404 (
1405 Some(res),
1406 Some(CommitHeaderKeysV0 {
1407 deps: kdeps,
1408 acks: kacks,
1409 nacks: knacks,
1410 files,
1411 }),
1412 )
1413 }
1414 }
1415
1416 #[cfg(test)]
1417 pub fn new_with_deps(deps: Vec<ObjectId>) -> Option<Self> {
1418 assert!(!deps.is_empty());
1419 let mut n = Self::new_empty();
1420 n.deps = deps;
1421 Some(n)
1422 }
1423
1424 #[cfg(test)]
1425 pub fn new_with_deps_and_acks(deps: Vec<ObjectId>, acks: Vec<ObjectId>) -> Option<Self> {
1426 if deps.is_empty() && acks.is_empty() {
1427 return None;
1428 }
1429 let mut n = Self::new_empty();
1431 n.deps = deps;
1432 n.acks = acks;
1433 Some(n)
1434 }
1435
1436 #[cfg(test)]
1437 pub fn new_with_acks(acks: Vec<ObjectId>) -> Option<Self> {
1438 assert!(!acks.is_empty());
1439 let mut n = Self::new_empty();
1440 n.acks = acks;
1441 Some(n)
1442 }
1443
1444 pub fn is_root(&self) -> bool {
1446 self.acks.is_empty() && self.nacks.is_empty()
1449 }
1450}
1451
1452impl fmt::Display for Commit {
1453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1454 match self {
1455 Self::V0(v0) => {
1456 writeln!(f, "====== Commit V0 ======")?;
1457 if v0.id.is_some() {
1458 writeln!(f, "== ID: {}", v0.id.as_ref().unwrap())?;
1459 }
1460 if v0.key.is_some() {
1461 writeln!(f, "== Key: {}", v0.key.as_ref().unwrap())?;
1462 }
1463 if v0.header.is_some() {
1464 write!(f, "== Header: {}", v0.header.as_ref().unwrap())?;
1465 }
1466 writeln!(f, "== Sig: {}", v0.sig)?;
1467 write!(f, "{}", v0.content)?;
1468 if v0.body.get().is_some() {
1469 write!(f, "== Body: {}", v0.body.get().unwrap())?;
1470 }
1471 }
1472 }
1473 Ok(())
1474 }
1475}
1476
1477impl fmt::Display for CommitBody {
1478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479 match self {
1480 Self::V0(v0) => {
1481 write!(f, "V0 ")?;
1482 match v0 {
1483 CommitBodyV0::Repository(b) => write!(f, "Repository {}", b),
1487 CommitBodyV0::RootBranch(b) => write!(f, "RootBranch {}", b),
1488
1489 CommitBodyV0::UpdateRootBranch(b) => write!(f, "UpdateRootBranch {}", b), CommitBodyV0::AddBranch(b) => write!(f, "AddBranch {}", b),
1497 CommitBodyV0::Branch(b) => write!(f, "Branch {}", b), CommitBodyV0::Snapshot(b) => write!(f, "Snapshot {}", b), CommitBodyV0::AddFile(b) => write!(f, "AddFile {}", b),
1511 CommitBodyV0::AsyncSignature(b) => write!(f, "AsyncSignature {}", b),
1516
1517 CommitBodyV0::SyncSignature(b) => write!(f, "SyncSignature {}", b),
1525 CommitBodyV0::AddSignerCap(b) => write!(f, "AddSignerCap {}", b),
1528 CommitBodyV0::StoreUpdate(b) => write!(f, "StoreUpdate {}", b),
1529 _ => unimplemented!(),
1536 }
1537 }
1538 }
1539 }
1540}
1541
1542impl fmt::Display for CommitContent {
1543 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1544 match self {
1545 Self::V0(v0) => {
1546 writeln!(f, "=== CommitContent V0 ===")?;
1547 writeln!(f, "====== author: {}", v0.author)?;
1548 writeln!(f, "====== BranchID: {}", v0.branch)?;
1550 writeln!(f, "====== quorum: {:?}", v0.quorum)?;
1551 writeln!(f, "====== Ref body: {}", v0.body)?;
1552 if v0.header_keys.is_none() {
1553 writeln!(f, "====== header keys: None")?;
1554 } else {
1555 write!(f, "{}", v0.header_keys.as_ref().unwrap())?;
1556 }
1557 writeln!(f, "====== Perms commits: {}", v0.perms.len())?;
1558 let mut i = 0;
1559 for block in &v0.perms {
1560 writeln!(f, "========== {:03}: {}", i, block)?;
1561 i += 1;
1562 }
1563 }
1564 }
1565 Ok(())
1566 }
1567}
1568
1569impl fmt::Display for CommitHeaderKeys {
1570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1571 match self {
1572 Self::V0(v0) => {
1573 writeln!(f, "=== CommitHeaderKeys V0 ===")?;
1574 writeln!(f, "==== acks : {}", v0.acks.len())?;
1575 for ack in &v0.acks {
1576 writeln!(f, "============== {}", ack)?;
1577 }
1578 writeln!(f, "==== nacks : {}", v0.nacks.len())?;
1579 for nack in &v0.nacks {
1580 writeln!(f, "============== {}", nack)?;
1581 }
1582 writeln!(f, "==== deps : {}", v0.deps.len())?;
1583 for dep in &v0.deps {
1584 writeln!(f, "============== {}", dep)?;
1585 }
1586 writeln!(f, "==== files : {}", v0.files.len())?;
1587 for file in &v0.files {
1588 writeln!(f, "============== {}", file)?;
1589 }
1590 }
1591 }
1592 Ok(())
1593 }
1594}
1595
1596#[cfg(test)]
1597mod test {
1598 use crate::commit::*;
1599 #[allow(unused_imports)]
1600 use crate::log::*;
1601
1602 fn test_commit_header_ref_content_fits(
1603 obj_refs: Vec<BlockRef>,
1604 metadata_size: usize,
1605 expect_blocks_len: usize,
1606 ) {
1607 let (priv_key, pub_key) = generate_keypair();
1608 let obj_ref = ObjectRef::dummy();
1609
1610 let branch = pub_key;
1611 let deps = obj_refs.clone();
1612 let acks = obj_refs.clone();
1613 let files = obj_refs.clone();
1614 let body_ref = obj_ref.clone();
1615 let overlay = OverlayId::dummy();
1616
1617 let metadata = vec![66; metadata_size];
1618
1619 let mut commit = Commit::new(
1620 &priv_key,
1621 &pub_key,
1622 overlay,
1623 branch,
1624 QuorumType::NoSigning,
1625 deps,
1626 vec![],
1627 acks.clone(),
1628 vec![],
1629 files,
1630 vec![],
1631 metadata,
1632 body_ref,
1633 )
1634 .unwrap();
1635
1636 log_debug!("{}", commit);
1637
1638 let max_object_size = 0;
1639
1640 let store = Store::dummy_public_v0();
1641
1642 let commit_ref = commit.save(max_object_size, &store).expect("save commit");
1643
1644 let commit_object =
1645 Object::load(commit_ref.id.clone(), Some(commit_ref.key.clone()), &store)
1646 .expect("load object from storage");
1647
1648 assert_eq!(
1649 commit_object.acks(),
1650 acks.iter().map(|a| a.id).collect::<Vec<ObjectId>>()
1651 );
1652
1653 log_debug!("{}", commit_object);
1654
1655 assert_eq!(commit_object.all_blocks_len(), expect_blocks_len);
1660
1661 let commit = Commit::load(commit_ref, &store, false).expect("load commit from storage");
1662
1663 log_debug!("{}", commit);
1664 }
1665
1666 #[test]
1667 pub fn test_commit_header_ref_content_fits_or_not() {
1668 let obj_ref = ObjectRef::dummy();
1669 let obj_refs2 = vec![obj_ref.clone(), obj_ref.clone()];
1670 let obj_refs = vec![obj_ref.clone()];
1671 test_commit_header_ref_content_fits(obj_refs.clone(), 3592, 1); test_commit_header_ref_content_fits(obj_refs.clone(), 3593, 2); test_commit_header_ref_content_fits(obj_refs.clone(), 3741, 2); test_commit_header_ref_content_fits(obj_refs.clone(), 3742, 3); test_commit_header_ref_content_fits(obj_refs2.clone(), 3360, 1);
1679 test_commit_header_ref_content_fits(obj_refs2.clone(), 3361, 2);
1680 test_commit_header_ref_content_fits(obj_refs2.clone(), 3609, 2);
1681 test_commit_header_ref_content_fits(obj_refs2.clone(), 3610, 3);
1682 }
1683
1684 #[test]
1685 pub fn test_load_commit_fails_on_non_commit_object() {
1686 let file = SmallFile::V0(SmallFileV0 {
1687 content_type: "file/test".into(),
1688 metadata: Vec::from("some meta data here"),
1689 content: [(0..255).collect::<Vec<u8>>().as_slice(); 320].concat(),
1690 });
1691 let content = ObjectContent::V0(ObjectContentV0::SmallFile(file));
1692
1693 let max_object_size = 0;
1694
1695 let store = Store::dummy_public_v0();
1696
1697 let obj = Object::new(content.clone(), None, max_object_size, &store);
1698
1699 _ = obj.save(&store).expect("save object");
1700
1701 let commit = Commit::load(obj.reference().unwrap(), &store, false);
1702
1703 assert_eq!(commit, Err(CommitLoadError::NotACommit));
1704 }
1705
1706 #[test]
1707 pub fn test_load_commit_with_body() {
1708 let (priv_key, pub_key) = generate_keypair();
1709 let obj_ref = ObjectRef::dummy();
1710
1711 let branch = pub_key;
1712 let obj_refs = vec![obj_ref.clone()];
1713 let deps = obj_refs.clone();
1714 let acks = obj_refs.clone();
1715 let files = obj_refs.clone();
1716
1717 let metadata = Vec::from("some metadata");
1718
1719 let body = CommitBody::V0(CommitBodyV0::Repository(Repository::new(&branch)));
1720
1721 let max_object_size = 0;
1722
1723 let store = Store::dummy_public_v0();
1724
1725 let mut commit = Commit::new_with_body_and_save(
1726 &priv_key,
1727 &pub_key,
1728 branch,
1729 QuorumType::NoSigning,
1730 deps,
1731 vec![],
1732 acks.clone(),
1733 vec![],
1734 files,
1735 vec![],
1736 metadata,
1737 body,
1738 max_object_size,
1739 &store,
1740 )
1741 .expect("commit::new_with_body_and_save");
1742
1743 log_debug!("{}", commit);
1744
1745 commit.empty_blocks();
1746
1747 let commit2 = Commit::load(commit.reference().unwrap(), &store, true)
1748 .expect("load commit with body after save");
1749
1750 log_debug!("{}", commit2);
1751
1752 assert_eq!(commit, commit2);
1753 }
1754
1755 #[test]
1756 pub fn test_commit_load_body_fails() {
1757 let (priv_key, pub_key) = generate_keypair();
1758 let obj_ref = ObjectRef::dummy();
1759 let obj_refs = vec![obj_ref.clone()];
1760 let branch = pub_key;
1761 let deps = obj_refs.clone();
1762 let acks = obj_refs.clone();
1763 let files = obj_refs.clone();
1764 let metadata = vec![1, 2, 3];
1765 let body_ref = obj_ref.clone();
1766 let store = Store::dummy_public_v0();
1767
1768 let commit = Commit::new(
1769 &priv_key,
1770 &pub_key,
1771 store.overlay_id,
1772 branch,
1773 QuorumType::NoSigning,
1774 deps,
1775 vec![],
1776 acks,
1777 vec![],
1778 files,
1779 vec![],
1780 metadata,
1781 body_ref,
1782 )
1783 .unwrap();
1784 log_debug!("{}", commit);
1785
1786 let repo = Repo::new_with_member(&pub_key, &pub_key, &[PermissionV0::Create], store);
1787
1788 commit.verify_sig(&repo).expect("verify signature");
1797 match commit.verify_perm(&repo) {
1798 Ok(_) => panic!("Commit should not be Ok"),
1799 Err(NgError::CommitLoadError(CommitLoadError::MissingBlocks(missing))) => {
1800 assert_eq!(missing.len(), 1);
1801 }
1802 Err(e) => panic!("Commit verify perm error: {:?}", e),
1803 }
1804
1805 match commit.verify(&repo) {
1814 Ok(_) => panic!("Commit should not be Ok"),
1815 Err(NgError::CommitLoadError(CommitLoadError::MissingBlocks(missing))) => {
1816 assert_eq!(missing.len(), 1);
1817 }
1818 Err(e) => panic!("Commit verify error: {:?}", e),
1819 }
1820 }
1821
1822 #[test]
1823 pub fn test_load_commit_with_body_verify_perms() {
1824 let (priv_key, pub_key) = generate_keypair();
1825
1826 let branch = pub_key;
1827
1828 let metadata = Vec::from("some metadata");
1829
1830 let body = CommitBody::V0(CommitBodyV0::Repository(Repository::new(&branch)));
1831
1832 let max_object_size = 0;
1833
1834 let store = Store::dummy_public_v0();
1835
1836 let commit = Commit::new_with_body_and_save(
1837 &priv_key,
1838 &pub_key,
1839 branch,
1840 QuorumType::NoSigning,
1841 vec![],
1842 vec![],
1843 vec![], vec![],
1845 vec![],
1846 vec![],
1847 metadata,
1848 body,
1849 max_object_size,
1850 &store,
1851 )
1852 .expect("commit::new_with_body_and_save");
1853
1854 log_debug!("{}", commit);
1855
1856 let repo = Repo::new_with_member(&pub_key, &pub_key, &[PermissionV0::Create], store);
1857
1858 commit.load_body(&repo.store).expect("load body");
1859
1860 commit.verify_sig(&repo).expect("verify signature");
1861 commit.verify_perm(&repo).expect("verify perms");
1862 commit
1863 .verify_perm_creation(None)
1864 .expect("verify_perm_creation");
1865
1866 commit
1867 .verify_full_object_refs_of_branch_at_commit(&repo.store)
1868 .expect("verify is at root of branch and singleton");
1869
1870 commit.verify(&repo).expect("verify");
1871 }
1872
1873 #[test]
1874 pub fn test_load_commit_with_invalid_header() {
1875 let (priv_key, pub_key) = generate_keypair();
1876 let obj_ref = ObjectRef::dummy();
1877
1878 let branch = pub_key;
1879 let metadata = Vec::from("some metadata");
1880
1881 let commit = Commit::V0(
1885 CommitV0::new_with_invalid_header(
1886 &priv_key,
1887 &pub_key,
1888 branch,
1889 QuorumType::NoSigning,
1890 metadata,
1891 obj_ref,
1892 )
1893 .expect("commit::new_with_invalid_header"),
1894 );
1895
1896 log_debug!("{}", commit);
1897
1898 let store = Store::dummy_public_v0();
1899 let repo = Repo::new_with_perms(&[PermissionV0::Create], store);
1900
1901 assert_eq!(
1902 commit.verify(&repo),
1903 Err(NgError::CommitVerifyError(CommitVerifyError::InvalidHeader))
1904 );
1905 }
1906}