ave-core 0.10.0

Averiun Ledger core runtime and node API
Documentation
use std::collections::HashMap;

use ave_common::{
    identity::PublicKey, request::EventRequest, schematype::ReservedWords,
};

use crate::{
    governance::{
        data::GovernanceData,
        model::{RoleTypes, WitnessesData},
    },
    request::types::{DistributionPlanEntry, DistributionPlanMode},
    subject::Metadata,
};

const fn merge_distribution_mode(
    current: DistributionPlanMode,
    next: DistributionPlanMode,
) -> DistributionPlanMode {
    match (current, next) {
        (DistributionPlanMode::Clear, ..)
        | (DistributionPlanMode::Opaque, DistributionPlanMode::Clear) => {
            DistributionPlanMode::Clear
        }
        (DistributionPlanMode::Opaque, DistributionPlanMode::Opaque) => {
            DistributionPlanMode::Opaque
        }
    }
}

fn upsert_distribution_plan(
    plan: &mut HashMap<PublicKey, DistributionPlanMode>,
    node: PublicKey,
    mode: DistributionPlanMode,
) {
    if let Some(current) = plan.get_mut(&node) {
        *current = merge_distribution_mode(current.clone(), mode);
    } else {
        plan.insert(node, mode);
    }
}

fn tracker_fact_mode_for_witness(
    governance_data: &GovernanceData,
    metadata: &Metadata,
    witness: &PublicKey,
    viewpoints: &std::collections::BTreeSet<String>,
) -> DistributionPlanMode {
    if metadata.owner == *witness
        || metadata.new_owner.as_ref().is_some_and(|x| x == witness)
    {
        return DistributionPlanMode::Clear;
    }

    let Some(witness_name) = governance_data
        .members
        .iter()
        .find(|(_, key)| *key == witness)
        .map(|(name, _)| name.clone())
    else {
        return DistributionPlanMode::Opaque;
    };

    let Some(owner_name) = governance_data
        .members
        .iter()
        .find(|(_, key)| *key == &metadata.owner)
        .map(|(name, _)| name.clone())
    else {
        return DistributionPlanMode::Opaque;
    };

    let Some(roles_schema) =
        governance_data.roles_schema.get(&metadata.schema_id)
    else {
        return DistributionPlanMode::Opaque;
    };

    let Some(role_creator) =
        roles_schema
            .creator
            .get(&ave_common::governance::RoleCreator::create(
                &owner_name,
                metadata.namespace.clone(),
            ))
    else {
        return DistributionPlanMode::Opaque;
    };

    let is_generic_witness =
        roles_schema.hash_this_rol(
            RoleTypes::Witness,
            metadata.namespace.clone(),
            &witness_name,
        ) || governance_data.roles_tracker_schemas.hash_this_rol(
            RoleTypes::Witness,
            metadata.namespace.clone(),
            &witness_name,
        );

    let allows_clear = role_creator.witnesses.iter().any(|creator_witness| {
        let applies = creator_witness.name == witness_name
            || (creator_witness.name == ReservedWords::Witnesses.to_string()
                && is_generic_witness);

        if !applies {
            return false;
        }

        creator_witness
            .viewpoints
            .contains(&ReservedWords::AllViewpoints.to_string())
            || viewpoints.is_empty()
            || viewpoints.is_subset(&creator_witness.viewpoints)
    });

    if allows_clear {
        DistributionPlanMode::Clear
    } else {
        DistributionPlanMode::Opaque
    }
}

pub fn build_tracker_event_distribution_plan(
    governance_data: &GovernanceData,
    event_request: &EventRequest,
    metadata: &Metadata,
    protocols_success: bool,
) -> Result<Vec<DistributionPlanEntry>, String> {
    let mut plan: HashMap<PublicKey, DistributionPlanMode> = HashMap::new();

    match event_request {
        EventRequest::Fact(fact_request) => {
            let witnesses = governance_data
                .get_witnesses(WitnessesData::Schema {
                    creator: metadata.owner.clone(),
                    schema_id: metadata.schema_id.clone(),
                    namespace: metadata.namespace.clone(),
                })
                .map_err(|e| e.to_string())?;

            for witness in witnesses {
                let mode = tracker_fact_mode_for_witness(
                    governance_data,
                    metadata,
                    &witness,
                    &fact_request.viewpoints,
                );

                upsert_distribution_plan(&mut plan, witness, mode);
            }
        }
        EventRequest::Transfer(transfer_request) => {
            let witnesses = governance_data
                .get_witnesses(WitnessesData::Schema {
                    creator: metadata.owner.clone(),
                    schema_id: metadata.schema_id.clone(),
                    namespace: metadata.namespace.clone(),
                })
                .map_err(|e| e.to_string())?;

            for witness in witnesses {
                upsert_distribution_plan(
                    &mut plan,
                    witness,
                    DistributionPlanMode::Clear,
                );
            }

            if protocols_success {
                upsert_distribution_plan(
                    &mut plan,
                    transfer_request.new_owner.clone(),
                    DistributionPlanMode::Clear,
                );
            }
        }
        EventRequest::Confirm(..) | EventRequest::Reject(..) => {
            let new_owner = metadata.new_owner.clone().ok_or_else(|| {
                "Tracker confirm/reject without new_owner".to_owned()
            })?;

            let witnesses = governance_data
                .get_witnesses(WitnessesData::Schema {
                    creator: new_owner,
                    schema_id: metadata.schema_id.clone(),
                    namespace: metadata.namespace.clone(),
                })
                .map_err(|e| e.to_string())?;

            for witness in witnesses {
                upsert_distribution_plan(
                    &mut plan,
                    witness,
                    DistributionPlanMode::Clear,
                );
            }

            if protocols_success {
                upsert_distribution_plan(
                    &mut plan,
                    metadata.owner.clone(),
                    DistributionPlanMode::Clear,
                );
            }
        }
        _ => {
            let witnesses = governance_data
                .get_witnesses(WitnessesData::Schema {
                    creator: metadata.owner.clone(),
                    schema_id: metadata.schema_id.clone(),
                    namespace: metadata.namespace.clone(),
                })
                .map_err(|e| e.to_string())?;

            for witness in witnesses {
                upsert_distribution_plan(
                    &mut plan,
                    witness,
                    DistributionPlanMode::Clear,
                );
            }
        }
    }

    Ok(plan
        .into_iter()
        .map(|(node, mode)| DistributionPlanEntry { node, mode })
        .collect())
}