ng_repo/
commit.rs

1// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
2// All rights reserved.
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! Commit that composes the DAG of a Branch
11
12use 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    /// New commit
50    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        // sign commit
78        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    /// New commit with invalid header, only for test purposes
92    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        // sign commit
113        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        // log_debug!("{:?}", self.header);
133        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    /// Get ID of including `Object`,
156    /// only available if the Commit was loaded from store or saved
157    fn id(&self) -> Option<ObjectId> {
158        match self {
159            Commit::V0(c) => c.id,
160        }
161    }
162
163    /// Get key of including `Object`
164    /// only available if the Commit was loaded from store or saved
165    fn key(&self) -> Option<SymKey> {
166        match self {
167            Commit::V0(c) => c.key.clone(),
168        }
169    }
170}
171
172impl Commit {
173    /// New commit
174    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    /// New commit with a body. everything is saved
208    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    /// New commit with a body. everything is saved
237    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    /// Load commit from store
308    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    /// Load commit body from store
364    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    /// Get ID of header `Object`
395    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    /// Set ID of including `Object`
405    fn set_id(&mut self, id: ObjectId) {
406        match self {
407            Commit::V0(c) => c.id = Some(id),
408        }
409    }
410
411    /// Set key of including `Object`
412    fn set_key(&mut self, key: SymKey) {
413        match self {
414            Commit::V0(c) => c.key = Some(key),
415        }
416    }
417
418    /// Set header of including `Object`
419    fn set_header(&mut self, header: Option<CommitHeader>) {
420        match self {
421            Commit::V0(c) => c.header = header,
422        }
423    }
424
425    /// Get commit signature
426    pub fn sig(&self) -> &Sig {
427        match self {
428            Commit::V0(c) => &c.sig,
429        }
430    }
431
432    /// Get branch ID this commit is about
433    pub fn branch(&self) -> &BranchId {
434        self.content().branch()
435    }
436
437    /// Get commit header
438    pub fn header(&self) -> &Option<CommitHeader> {
439        match self {
440            Commit::V0(c) => &c.header,
441        }
442    }
443
444    /// Get commit content V0
445    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    /// Get quorum_type
455    pub fn quorum_type(&self) -> &QuorumType {
456        &self.content_v0().quorum
457    }
458
459    /// Get commit content
460    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                // load deps (the previous RootBranch commit)
476                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                    // we check that the ACK is the repository singleton commit. in this case, it means we are dealing with the first RootBranch commit, which is fine to have no deps.
497                    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    /// This commit is the first one in the branch (doesn't have any ACKs nor Nacks)
510    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    /// Get acks (that have both an ID in the header and a key in the header_keys)
523    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    /// Get files
542    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    /// Get deps (that have both an ID in the header and a key in the header_keys)
558    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    /// Get all commits that are in the direct causal past of the commit (`acks` and `nacks`)
577    /// only returns objectRefs that have both an ID from header and a KEY from header_keys (they all have a key)
578    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    // /// Get seq
597    // pub fn seq(&self) -> u64 {
598    //     match self {
599    //         Commit::V0(CommitV0 {
600    //             content: CommitContent::V0(c),
601    //             ..
602    //         }) => c.seq,
603    //     }
604    // }
605
606    /// Verify commit signature
607    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    /// Verify commit permissions
633    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            // a user must be provided to verify all subsequent commits of a Repository commit, that have the same author and that are signed with the repository key
645            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    /// Verify if the commit's `body` and its direct_causal_past, and recursively all their refs are available in the `store`
655    /// returns a list of all the ObjectIds that have been visited (only commits in the DAG)
656    /// or a list of missing blocks
657    pub fn verify_full_object_refs_of_branch_at_commit(
658        &self,
659        store: &Store,
660    ) -> Result<Vec<ObjectId>, CommitLoadError> {
661        //log_debug!(">> verify_full_object_refs_of_branch_at_commit: #{}", self.seq());
662
663        /// Load `Commit`s of a `Branch` from the `Store` starting from the given `Commit`,
664        /// and collect missing `ObjectId`s
665        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            //log_debug!(">>> load_branch: #{}", commit.seq());
672
673            // if the self of verify_full_object_refs_of_branch_at_commit() has not been saved yet, then it doesn't have an ID
674            match commit.id() {
675                Some(id) => {
676                    if visited.contains(&id) {
677                        return Ok(());
678                    }
679                    visited.insert(id);
680                    // not adding the ObjectId of the header of this commit as it is not part of the DAG (neither is the CommitBody added to visited)
681                    // // commit.header_id().map(|hid| visited.insert(hid));
682                }
683                None => {
684                    if !visited.is_empty() {
685                        // we are not at the beginning (meaning, the self/the commit object) so this is a panic error as all causal
686                        // past commits have been loaded from store and should have an id
687                        panic!("A Commit in the causal past doesn't have an ID");
688                    }
689                }
690            }
691
692            // load body & check if it's the Branch root commit
693            match commit.load_body(store) {
694                Ok(_) => Ok(()),
695                Err(CommitLoadError::MissingBlocks(m)) => {
696                    // The commit body is missing.
697                    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            // load direct causal past
719            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    /// Verify signature, permissions, and full causal past
745    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    /// the kind of permissions that can be added and removed with AddWritePermission and RemoveWritePermission permissions respectively
758    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::Quorum(_) => true,
857                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::AddMember(_) => true,
935                CommitBodyV0::RemoveMember(_) => true,
936                CommitBodyV0::RemovePermission(_) => true,
937                //CommitBodyV0::Quorum(_) => true,
938                CommitBodyV0::Compact(_) => true,
939                CommitBodyV0::SyncTransaction(_) => true, // check Quorum::TotalOrder in CommitContent
940                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        //assert!(!deps.is_empty() || !acks.is_empty());
1337        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    /// we do not check the deps because in a forked branch, they point to previous branch heads.
1352    pub fn is_root(&self) -> bool {
1353        //self.deps.is_empty()
1354        //    && self.ndeps.is_empty()
1355        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                    //
1391                    // for root branch:
1392                    //
1393                    CommitBodyV0::Repository(b) => write!(f, "Repository {}", b),
1394                    CommitBodyV0::RootBranch(b) => write!(f, "RootBranch {}", b),
1395
1396                    CommitBodyV0::UpdateRootBranch(b) => write!(f, "UpdateRootBranch {}", b), // total order enforced with total_order_quorum
1397                    // CommitBodyV0::AddMember(b) => write!(f, "AddMember {}", b), // total order enforced with total_order_quorum
1398                    // CommitBodyV0::RemoveMember(b) => write!(f, "RemoveMember {}", b), // total order enforced with total_order_quorum
1399                    // CommitBodyV0::AddPermission(b) => write!(f, "AddPermission {}", b),
1400                    // CommitBodyV0::RemovePermission(b) => {
1401                    //     write!(f, "RemovePermission {}", b)
1402                    // }
1403                    CommitBodyV0::AddBranch(b) => write!(f, "AddBranch {}", b),
1404                    // CommitBodyV0::RemoveBranch(b) => write!(f, "RemoveBranch {}", b),
1405                    // CommitBodyV0::AddName(b) => write!(f, "AddName {}", b),
1406                    // CommitBodyV0::RemoveName(b) => write!(f, "RemoveName {}", b),
1407                    // TODO? Quorum(Quorum) => write!(f, "RootBranch {}", b), // changes the quorum without changing the RootBranch
1408
1409                    //
1410                    // For transactional branches:
1411                    //
1412                    CommitBodyV0::Branch(b) => write!(f, "Branch {}", b), // singleton and should be first in branch
1413                    // CommitBodyV0::UpdateBranch(b) => write!(f, "UpdateBranch {}", b), // total order enforced with total_order_quorum
1414                    // CommitBodyV0::Snapshot(b) => write!(f, "Snapshot {}", b), // a soft snapshot
1415                    // CommitBodyV0::AsyncTransaction(b) => write!(f, "AsyncTransaction {}", b), // partial_order
1416                    // CommitBodyV0::SyncTransaction(b) => write!(f, "SyncTransaction {}", b), // total_order
1417                    CommitBodyV0::AddFile(b) => write!(f, "AddFile {}", b),
1418                    // CommitBodyV0::RemoveFile(b) => write!(f, "RemoveFile {}", b),
1419                    // CommitBodyV0::Compact(b) => write!(f, "Compact {}", b), // a hard snapshot. total order enforced with total_order_quorum
1420                    //Merge(Merge) => write!(f, "RootBranch {}", b),
1421                    //Revert(Revert) => write!(f, "RootBranch {}", b), // only possible on partial order commit
1422                    // CommitBodyV0::AsyncSignature(b) => write!(f, "AsyncSignature {}", b),
1423
1424                    //
1425                    // For both
1426                    //
1427                    // CommitBodyV0::RootCapRefresh(b) => write!(f, "RootCapRefresh {}", b),
1428                    // CommitBodyV0::BranchCapRefresh(b) => {
1429                    //     write!(f, "BranchCapRefresh {}", b)
1430                    // }
1431                    CommitBodyV0::SyncSignature(b) => write!(f, "SyncSignature {}", b),
1432                    //CommitBodyV0::AddRepo(b) => write!(f, "AddRepo {}", b),
1433                    //CommitBodyV0::RemoveRepo(b) => write!(f, "RemoveRepo {}", b),
1434                    CommitBodyV0::AddSignerCap(b) => write!(f, "AddSignerCap {}", b),
1435                    CommitBodyV0::StoreUpdate(b) => write!(f, "StoreUpdate {}", b),
1436                    /*    AddLink(AddLink),
1437                    RemoveLink(RemoveLink),
1438                    AddSignerCap(AddSignerCap),
1439                    RemoveSignerCap(RemoveSignerCap),
1440                    WalletUpdate(WalletUpdate),
1441                    StoreUpdate(StoreUpdate), */
1442                    _ => 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, "====== seq:      {}", v0.seq)?;
1456                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        // log_debug!("blocks:     {}", commit_object.blocks_len());
1563        // log_debug!("header blocks:     {}", commit_object.header_blocks_len());
1564        // log_debug!("object size:     {}", commit_object.size());
1565
1566        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        // with 1 refs in header
1579        test_commit_header_ref_content_fits(obj_refs.clone(), 3592, 1); // block 4090
1580        test_commit_header_ref_content_fits(obj_refs.clone(), 3593, 2); //block 4012 header 117 total: 4129
1581        test_commit_header_ref_content_fits(obj_refs.clone(), 3741, 2); //block 4094 block 219 total: 4313
1582        test_commit_header_ref_content_fits(obj_refs.clone(), 3742, 3); // block 4094 block 9 block 285
1583
1584        // with 2 refs in header
1585        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        // match commit.load_body(repo.store.unwrap()) {
1696        //     Ok(_b) => panic!("Body should not exist"),
1697        //     Err(CommitLoadError::MissingBlocks(missing)) => {
1698        //         assert_eq!(missing.len(), 1);
1699        //     }
1700        //     Err(e) => panic!("Commit load error: {:?}", e),
1701        // }
1702
1703        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_full_object_refs_of_branch_at_commit(repo.store.unwrap()) {
1713        //     Ok(_) => panic!("Commit should not be Ok"),
1714        //     Err(CommitLoadError::MissingBlocks(missing)) => {
1715        //         assert_eq!(missing.len(), 1);
1716        //     }
1717        //     Err(e) => panic!("Commit verify error: {:?}", e),
1718        // }
1719
1720        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![], //acks.clone(),
1751            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 max_object_size = 0;
1789        //let store = Store::dummy_public_v0();
1790
1791        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}