1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Processor is reponsible for running the module.
//! This run is sound execution of the module.
//! This will be most abstract layer of the processor.

use alloy::dyn_abi::DynSolValue;
use alloy::primitives::{FixedBytes, Keccak256, B256, U256};
use alloy_merkle_tree::standard_binary_tree::StandardMerkleTree;
use anyhow::Result;
use hdp_cairo_runner::cairo_run;
use hdp_primitives::processed_types::cairo_format::AsCairoFormat;
use hdp_primitives::processed_types::mmr::MMRMeta;
use hdp_primitives::processed_types::query::ProcessorInput;
use serde::Serialize;
use std::path::PathBuf;
use tracing::{debug, info};

pub struct Processor {
    program_path: PathBuf,
}

#[derive(Debug, Serialize)]
pub struct ProcessorResult {
    /// raw results of the module
    pub raw_results: Vec<B256>,
    /// leaf of result merkle tree
    pub results_commitments: Vec<B256>,
    /// leaf of task merkle tree
    pub tasks_commitments: Vec<B256>,
    /// tasks inclusion proofs
    pub task_inclusion_proofs: Vec<Vec<FixedBytes<32>>>,
    /// results inclusion proofs
    pub results_inclusion_proofs: Vec<Vec<FixedBytes<32>>>,
    /// root of the results merkle tree
    pub results_root: B256,
    /// root of the tasks merkle tree
    pub tasks_root: B256,
    /// mmr metas related to processed tasks
    pub mmr_metas: Vec<MMRMeta>,
}

impl ProcessorResult {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        raw_results: Vec<B256>,
        results_commitments: Vec<B256>,
        tasks_commitments: Vec<B256>,
        task_inclusion_proofs: Vec<Vec<FixedBytes<32>>>,
        results_inclusion_proofs: Vec<Vec<FixedBytes<32>>>,
        results_root: B256,
        tasks_root: B256,
        mmr_metas: Vec<MMRMeta>,
    ) -> Self {
        Self {
            raw_results,
            results_commitments,
            tasks_commitments,
            task_inclusion_proofs,
            results_inclusion_proofs,
            results_root,
            tasks_root,
            mmr_metas,
        }
    }
}

impl Processor {
    pub fn new(program_path: PathBuf) -> Self {
        Self { program_path }
    }

    pub async fn process(
        &self,
        requset: ProcessorInput,
        pie_path: &PathBuf,
    ) -> Result<ProcessorResult> {
        // 1. pass the input file to the runner
        let input_string = serde_json::to_string_pretty(&requset.as_cairo_format())
            .expect("Failed to serialize module class");
        let result = cairo_run(&self.program_path, input_string, pie_path)?;
        let cairo_run_output = result.cairo_run_output;
        let tasks_commitments: Vec<B256> = requset
            .tasks
            .iter()
            .map(|task| task.get_task_commitment())
            .collect();
        let task_inclusion_proofs: Vec<Vec<B256>> = requset
            .tasks
            .iter()
            .map(|task| task.get_task_proof())
            .collect();
        let task_root = requset.tasks_root;

        let (results_tree, result_commitments) = self.build_result_merkle_tree(
            tasks_commitments.clone(),
            cairo_run_output.results.clone(),
        )?;
        let results_inclusion_proofs: Vec<_> = result_commitments
            .iter()
            .map(|rc| results_tree.get_proof(&DynSolValue::FixedBytes(*rc, 32)))
            .collect();
        let result_root = results_tree.root();
        let processor_result = ProcessorResult::new(
            cairo_run_output
                .results
                .iter()
                .map(|x| B256::from(*x))
                .collect(),
            result_commitments,
            tasks_commitments,
            task_inclusion_proofs,
            results_inclusion_proofs,
            result_root,
            task_root,
            requset.proofs.mmr_metas,
        );
        info!("2️⃣  Processor completed successfully");
        Ok(processor_result)
    }

    fn build_result_merkle_tree(
        &self,
        tasks_commitments: Vec<B256>,
        task_results: Vec<U256>,
    ) -> Result<(StandardMerkleTree, Vec<FixedBytes<32>>)> {
        let mut results_leaves = Vec::new();
        let mut results_commitments = Vec::new();
        for (task_commitment, task_result) in tasks_commitments.iter().zip(task_results.iter()) {
            debug!(
                "building result merkle tree | task_commitment: {:?}, task_result: {:?}",
                task_commitment, task_result
            );
            let result_commitment =
                self._raw_result_to_result_commitment(task_commitment, task_result);
            results_commitments.push(result_commitment);
            results_leaves.push(DynSolValue::FixedBytes(result_commitment, 32));
        }
        let tree = StandardMerkleTree::of(results_leaves);
        Ok((tree, results_commitments))
    }

    fn _raw_result_to_result_commitment(
        &self,
        task_commitment: &B256,
        compiled_result: &U256,
    ) -> B256 {
        let mut hasher = Keccak256::new();
        hasher.update(task_commitment);
        hasher.update(B256::from(*compiled_result));
        hasher.finalize()
    }
}