Skip to main content

ave_core/evaluation/compiler/
contract_compiler.rs

1use std::path::PathBuf;
2
3use async_trait::async_trait;
4use ave_actors::{
5    Actor, ActorContext, ActorError, ActorPath, Handler, Message,
6    NotPersistentActor,
7};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use tracing::{Span, debug, error, info_span};
11
12use ave_common::identity::{DigestIdentifier, HashAlgorithm, hash_borsh};
13
14use super::{CompilerResponse, CompilerSupport};
15use crate::metrics::try_core_metrics;
16
17#[derive(Clone, Debug, Serialize, Deserialize)]
18pub struct ContractCompiler {
19    contract: DigestIdentifier,
20    hash: HashAlgorithm,
21}
22
23impl ContractCompiler {
24    pub fn new(hash: HashAlgorithm) -> Self {
25        Self {
26            contract: DigestIdentifier::default(),
27            hash,
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub enum ContractCompilerMessage {
34    Compile {
35        contract: String,
36        contract_name: String,
37        initial_value: Value,
38        contract_path: PathBuf,
39    },
40}
41
42impl Message for ContractCompilerMessage {}
43
44impl NotPersistentActor for ContractCompiler {}
45
46#[async_trait]
47impl Actor for ContractCompiler {
48    type Event = ();
49    type Message = ContractCompilerMessage;
50    type Response = CompilerResponse;
51
52    fn get_span(id: &str, parent_span: Option<Span>) -> tracing::Span {
53        parent_span.map_or_else(
54            || info_span!("ContractCompiler", id),
55            |parent_span| {
56                info_span!(parent: parent_span, "ContractCompiler", id)
57            },
58        )
59    }
60}
61
62#[async_trait]
63impl Handler<Self> for ContractCompiler {
64    async fn handle_message(
65        &mut self,
66        _sender: ActorPath,
67        msg: ContractCompilerMessage,
68        ctx: &mut ActorContext<Self>,
69    ) -> Result<CompilerResponse, ActorError> {
70        match msg {
71            ContractCompilerMessage::Compile {
72                contract,
73                contract_name,
74                initial_value,
75                contract_path,
76            } => {
77                let contract_hash =
78                    match hash_borsh(&*self.hash.hasher(), &contract) {
79                        Ok(hash) => hash,
80                        Err(e) => {
81                            error!(
82                                msg_type = "Compile",
83                                error = %e,
84                                "Failed to hash contract"
85                            );
86                            return Err(ActorError::FunctionalCritical {
87                                description: format!(
88                                    "Can not hash contract: {}",
89                                    e
90                                ),
91                            });
92                        }
93                    };
94
95                if contract_hash != self.contract {
96                    let (module, metadata) =
97                        match CompilerSupport::compile_or_load_registered(
98                            self.hash,
99                            ctx,
100                            &contract_name,
101                            &contract,
102                            &contract_path,
103                            initial_value,
104                        )
105                        .await
106                        {
107                            Ok(result) => result,
108                            Err(e) => {
109                                error!(
110                                    msg_type = "Compile",
111                                    error = %e,
112                                    contract_name = %contract_name,
113                                    path = %contract_path.display(),
114                                    "Contract compilation or validation failed"
115                                );
116                                return Ok(CompilerResponse::Error(e));
117                            }
118                        };
119
120                    {
121                        let contracts =
122                            CompilerSupport::contracts_helper(ctx).await?;
123                        let mut contracts = contracts.write().await;
124                        contracts.insert(contract_name.clone(), module);
125                    }
126
127                    self.contract = metadata.contract_hash.clone();
128
129                    debug!(
130                        msg_type = "Compile",
131                        contract_name = %contract_name,
132                        contract_hash = %metadata.contract_hash,
133                        "Contract compiled and validated successfully"
134                    );
135                } else {
136                    if let Some(metrics) = try_core_metrics() {
137                        metrics.observe_contract_prepare(
138                            "registered",
139                            "skipped",
140                            std::time::Duration::default(),
141                        );
142                    }
143                    debug!(
144                        msg_type = "Compile",
145                        contract_name = %contract_name,
146                        "Contract already compiled, skipping"
147                    );
148                }
149
150                Ok(CompilerResponse::Ok)
151            }
152        }
153    }
154}