ave-core 0.11.0

Averiun Ledger core runtime and node API
Documentation
use std::path::PathBuf;

use async_trait::async_trait;
use ave_actors::{
    Actor, ActorContext, ActorError, ActorPath, Handler, Message,
    NotPersistentActor,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::{Span, debug, error, info_span};

use ave_common::identity::{DigestIdentifier, HashAlgorithm, hash_borsh};

use super::{CompilerResponse, CompilerSupport};
use crate::metrics::try_core_metrics;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractCompiler {
    contract: DigestIdentifier,
    hash: HashAlgorithm,
}

impl ContractCompiler {
    pub fn new(hash: HashAlgorithm) -> Self {
        Self {
            contract: DigestIdentifier::default(),
            hash,
        }
    }
}

#[derive(Debug, Clone)]
pub enum ContractCompilerMessage {
    Compile {
        contract: String,
        contract_name: String,
        initial_value: Value,
        contract_path: PathBuf,
    },
}

impl Message for ContractCompilerMessage {}

impl NotPersistentActor for ContractCompiler {}

#[async_trait]
impl Actor for ContractCompiler {
    type Event = ();
    type Message = ContractCompilerMessage;
    type Response = CompilerResponse;

    fn get_span(id: &str, parent_span: Option<Span>) -> tracing::Span {
        parent_span.map_or_else(
            || info_span!("ContractCompiler", id),
            |parent_span| {
                info_span!(parent: parent_span, "ContractCompiler", id)
            },
        )
    }
}

#[async_trait]
impl Handler<Self> for ContractCompiler {
    async fn handle_message(
        &mut self,
        _sender: ActorPath,
        msg: ContractCompilerMessage,
        ctx: &mut ActorContext<Self>,
    ) -> Result<CompilerResponse, ActorError> {
        match msg {
            ContractCompilerMessage::Compile {
                contract,
                contract_name,
                initial_value,
                contract_path,
            } => {
                let contract_hash =
                    match hash_borsh(&*self.hash.hasher(), &contract) {
                        Ok(hash) => hash,
                        Err(e) => {
                            error!(
                                msg_type = "Compile",
                                error = %e,
                                "Failed to hash contract"
                            );
                            return Err(ActorError::FunctionalCritical {
                                description: format!(
                                    "Can not hash contract: {}",
                                    e
                                ),
                            });
                        }
                    };

                if contract_hash != self.contract {
                    let (module, metadata) =
                        match CompilerSupport::compile_or_load_registered(
                            self.hash,
                            ctx,
                            &contract_name,
                            &contract,
                            &contract_path,
                            initial_value,
                        )
                        .await
                        {
                            Ok(result) => result,
                            Err(e) => {
                                error!(
                                    msg_type = "Compile",
                                    error = %e,
                                    contract_name = %contract_name,
                                    path = %contract_path.display(),
                                    "Contract compilation or validation failed"
                                );
                                return Ok(CompilerResponse::Error(e));
                            }
                        };

                    {
                        let contracts =
                            CompilerSupport::contracts_helper(ctx).await?;
                        let mut contracts = contracts.write().await;
                        contracts.insert(contract_name.clone(), module);
                    }

                    self.contract = metadata.contract_hash.clone();

                    debug!(
                        msg_type = "Compile",
                        contract_name = %contract_name,
                        contract_hash = %metadata.contract_hash,
                        "Contract compiled and validated successfully"
                    );
                } else {
                    if let Some(metrics) = try_core_metrics() {
                        metrics.observe_contract_prepare(
                            "registered",
                            "skipped",
                            std::time::Duration::default(),
                        );
                    }
                    debug!(
                        msg_type = "Compile",
                        contract_name = %contract_name,
                        "Contract already compiled, skipping"
                    );
                }

                Ok(CompilerResponse::Ok)
            }
        }
    }
}