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