Rust implementation of NextGraph, a Decentralized and local-first web 3.0 ecosystem https://nextgraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nextgraph-rs/ng-verifier/src/commits/mod.rs

637 lines
18 KiB

// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Verifiers for each Commit type
pub mod transaction;
use std::collections::HashMap;
use std::sync::Arc;
use ng_repo::errors::VerifierError;
#[allow(unused_imports)]
use ng_repo::log::*;
use ng_repo::object::Object;
use ng_repo::repo::{BranchInfo, CommitInfo, Repo};
use ng_repo::store::Store;
use ng_repo::types::*;
use ng_net::app_protocol::*;
use crate::verifier::Verifier;
#[async_trait::async_trait]
pub trait CommitVerifier {
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError>;
}
fn list_dep_chain_until(
start: ObjectRef,
end: &ObjectId,
store: &Store,
) -> Result<Vec<Commit>, VerifierError> {
let mut res = vec![];
let mut pos = start;
loop {
let pos_id = pos.id.clone();
if pos_id == *end {
break;
}
let commit = Commit::load(pos, &store, true)?;
let deps = commit.deps();
if deps.len() != 1 {
return Err(VerifierError::MalformedSyncSignatureDeps);
}
res.push(commit);
pos = deps[0].clone();
}
res.reverse();
Ok(res)
}
#[async_trait::async_trait]
impl CommitVerifier for RootBranch {
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
match self {
RootBranch::V0(root_branch) => {
let repository_commit = Commit::load(root_branch.repo.clone(), &store, true)?;
let repository = match repository_commit
.body()
.ok_or(VerifierError::CommitBodyNotFound)?
{
CommitBody::V0(CommitBodyV0::Repository(r)) => r,
_ => return Err(VerifierError::InvalidRepositoryCommit),
};
//TODO: deal with quorum_type (verify signature)
let user_priv = verifier.user_privkey();
let user_id = verifier.user_id();
let repo_write_cap_secret = if store.is_private() {
Some(SymKey::nil())
} else if let Some(pos) = root_branch.owners.iter().position(|o| o == user_id) {
let cryptobox = &root_branch.owners_write_cap[pos];
Some(RootBranch::decrypt_write_cap(user_priv, cryptobox)?)
} else {
None
};
let topic_priv_key = if let Some(rwcs) = repo_write_cap_secret.as_ref() {
Branch::decrypt_branch_write_cap_secret(
root_branch.topic_privkey.clone(),
root_branch.topic.clone(),
root_branch.id.clone(),
rwcs,
)
.map_or(None, |k| Some(k))
} else {
None
};
let reference = commit.reference().unwrap();
let root_branch = BranchInfo {
id: root_branch.id.clone(),
branch_type: BranchType::Root,
topic: root_branch.topic,
topic_priv_key,
read_cap: reference.clone(),
current_heads: vec![reference.clone()],
commits_nbr: 1,
};
let id = root_branch.id;
let branches = vec![(root_branch.id, root_branch)];
let repo = Repo {
id,
repo_def: repository.clone(),
signer: None, //TO BE ADDED LATER when AddSignerCap commit is found
members: HashMap::new(),
store: Arc::clone(&store),
read_cap: Some(reference),
write_cap: repo_write_cap_secret,
branches: branches.into_iter().collect(),
opened_branches: HashMap::new(),
};
verifier.populate_topics(&repo);
let _repo_ref = verifier.add_repo_and_save(repo);
}
}
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for Branch {
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
match self {
Branch::V0(branch) => {
//TODO: deal with root_branch_readcap_id (the epoch)
//TODO: deal with quorum_type (verify signature)
let repository_commit = Commit::load(branch.repo.clone(), &store, true)?;
let repository = match repository_commit
.body()
.ok_or(VerifierError::CommitBodyNotFound)?
{
CommitBody::V0(CommitBodyV0::Repository(r)) => r,
_ => return Err(VerifierError::InvalidRepositoryCommit),
};
// check that the repository exists
let repo = verifier.get_repo_mut(repository.id(), store.get_store_repo())?;
let topic_priv_key = if let Some(rwcs) = repo.write_cap.as_ref() {
Branch::decrypt_branch_write_cap_secret(
branch.topic_privkey.clone(),
branch.topic.clone(),
branch.id.clone(),
rwcs,
)
.map_or(None, |k| Some(k))
} else {
None
};
let reference = commit.reference().unwrap();
let branch_info = repo.branch_mut(&branch.id)?;
if branch_info.read_cap != reference {
return Err(VerifierError::InvalidBranch);
}
branch_info.topic_priv_key = topic_priv_key;
branch_info.current_heads = vec![reference];
verifier.update_branch(&repository.id(), &branch.id, store.get_store_repo())?;
Ok(())
}
}
}
}
#[async_trait::async_trait]
impl CommitVerifier for SyncSignature {
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
match self {
SyncSignature::V0(signature_ref) => {
let sign = Object::load_ref(signature_ref, &store)?;
match sign.content_v0()? {
ObjectContentV0::Signature(_sig) => {
//TODO: verify signature
}
_ => return Err(VerifierError::InvalidSignatureObject),
}
// process each deps
let acks = commit.acks();
if acks.len() != 1 {
return Err(VerifierError::MalformedSyncSignatureAcks);
}
let ack = &acks[0];
let deps = commit.deps();
if deps.len() != 1 {
return Err(VerifierError::MalformedSyncSignatureDeps);
}
let commits = list_dep_chain_until(deps[0].clone(), &ack.id, &store)?;
for commit in commits {
verifier
.verify_commit(&commit, branch_id, repo_id, Arc::clone(&store))
.await?;
}
}
}
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddBranch {
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
match self {
AddBranch::V0(v0) => {
if v0.branch_type == BranchType::Root {
return Err(VerifierError::InvalidBranch);
}
let branch_info = BranchInfo {
id: v0.branch_id,
branch_type: v0.branch_type.clone(),
topic: v0.topic_id,
topic_priv_key: None,
read_cap: v0.branch_read_cap.clone(),
current_heads: vec![],
commits_nbr: 0,
};
verifier.add_branch_and_save(
commit.branch(),
branch_info,
store.get_store_repo(),
)?;
}
}
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for Repository {
async fn verify(
&self,
_commit: &Commit,
_verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
_store: Arc<Store>,
) -> Result<(), VerifierError> {
// left empty intentionally
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for StoreUpdate {
async fn verify(
&self,
_commit: &Commit,
verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
_store: Arc<Store>,
) -> Result<(), VerifierError> {
verifier.new_store_from_update(self)
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddSignerCap {
async fn verify(
&self,
_commit: &Commit,
verifier: &mut Verifier,
_branch_id: &BranchId,
_repo_id: &RepoId,
_store: Arc<Store>,
) -> Result<(), VerifierError> {
match self {
AddSignerCap::V0(v0) => verifier.update_signer_cap(&v0.cap),
}
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddMember {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveMember {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddPermission {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemovePermission {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveBranch {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddName {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveName {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for () {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for Snapshot {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddFile {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
let files = commit.files();
if files.len() == 1 {
let refe = commit.files().remove(0);
let filename = FileName {
name: self.name().clone(),
nuri: NuriV0::object_ref(&refe),
reference: refe,
};
let commit_id = commit.id().unwrap();
verifier.user_storage.as_ref().unwrap().branch_add_file(
commit.id().unwrap(),
*branch_id,
filename.clone(),
)?;
let repo = verifier.get_repo(repo_id, store.get_store_repo())?;
verifier
.push_app_response(
branch_id,
AppResponse::V0(AppResponseV0::Patch(AppPatch {
commit_id,
commit_info: commit.as_info(repo),
graph: None,
discrete: None,
other: Some(OtherPatch::FileAdd(filename)),
})),
)
.await;
Ok(())
} else {
Err(VerifierError::InvalidCommit)
}
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveFile {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for Compact {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AsyncSignature {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RootCapRefresh {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for BranchCapRefresh {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddRepo {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveRepo {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for AddLink {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveLink {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for RemoveSignerCap {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}
#[async_trait::async_trait]
impl CommitVerifier for WalletUpdate {
#[allow(unused_variables)]
async fn verify(
&self,
commit: &Commit,
verifier: &mut Verifier,
branch_id: &BranchId,
repo_id: &RepoId,
store: Arc<Store>,
) -> Result<(), VerifierError> {
Ok(())
}
}