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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * AquaVM Workflow Engine
 *
 * Copyright (C) 2024 Fluence DAO
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

pub(crate) mod errors;
pub(crate) mod repr;
pub mod verification;

pub use self::repr::InterpreterDataEnvelopeFormat;
pub use self::repr::InterpreterDataEnvelopeRepr;
use crate::CidInfo;
use crate::ExecutionTrace;

use air_interpreter_sede::FromSerialized;
use air_interpreter_sede::Representation;
use air_interpreter_signatures::SignatureStore;

use serde::Deserialize;
use serde::Serialize;

use std::borrow::Cow;

#[derive(Debug, thiserror::Error)]
pub enum DataDeserializationError {
    #[error("failed to deserialize envelope: {0}")]
    Envelope(rmp_serde::decode::Error),
    #[error("failed to deserialize data: {0}")]
    Data(crate::rkyv::RkyvDeserializeError),
}

/// An envelope for the AIR interpreter data that makes AIR data version info accessible in a stable way.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterpreterDataEnvelope<'a> {
    /// Versions of data and an interpreter produced this data.
    #[serde(flatten)]
    pub versions: Versions,
    #[serde(with = "serde_bytes", borrow)]
    pub inner_data: Cow<'a, [u8]>,
}

/// The AIR interpreter could be considered as a function
/// f(prev_data: InterpreterData, current_data: InterpreterData, ... ) -> (result_data: InterpreterData, ...).
/// This function receives prev and current data and produces a result data. All these data
/// have the following format.
#[derive(
    Debug,
    Clone,
    Default,
    ::serde::Serialize,
    ::serde::Deserialize,
    ::rkyv::Archive,
    ::rkyv::Serialize,
    ::rkyv::Deserialize,
)]
#[archive(check_bytes)]
pub struct InterpreterData {
    /// Trace of AIR execution, which contains executed call, par, fold, and ap states.
    pub trace: ExecutionTrace,

    /// Last exposed to a peer call request id. All next call request ids will be bigger than this.
    #[serde(default)]
    #[serde(rename = "lcid")]
    pub last_call_request_id: u32,

    /// CID-to-somethings mappings.
    pub cid_info: CidInfo,

    /// Signature store.
    ///
    /// Every peer signs call results and canon values it produced (all together), and stores the signatures
    /// in this store.
    pub signatures: SignatureStore,
}

impl InterpreterData {
    #[tracing::instrument(skip_all, level = "info")]
    pub fn try_from_slice(slice: &[u8]) -> Result<Self, DataDeserializationError> {
        let mut aligned_data = rkyv::AlignedVec::with_capacity(slice.len());
        aligned_data.extend_from_slice(slice);

        crate::rkyv::from_aligned_slice(&aligned_data).map_err(DataDeserializationError::Data)
    }

    #[tracing::instrument(skip_all, level = "info")]
    pub fn serialize(&self) -> Result<Vec<u8>, crate::rkyv::RkyvSerializeError> {
        crate::rkyv::to_vec(self)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Versions {
    /// Version of this data format.
    #[serde(rename = "version")] // for compatibility with versions <= 0.6.0
    pub data_version: semver::Version,

    /// Version of an interpreter produced this data.
    pub interpreter_version: semver::Version,
}

impl InterpreterDataEnvelope<'_> {
    pub fn new(interpreter_version: semver::Version) -> Self {
        let versions = Versions::new(interpreter_version);

        let inner_data = InterpreterData::default()
            .serialize()
            .expect("shouldn't fail on empty data")
            .into();

        Self {
            versions,
            inner_data,
        }
    }

    #[allow(clippy::too_many_arguments)]
    pub fn from_execution_result(
        trace: ExecutionTrace,
        cid_info: CidInfo,
        signatures: SignatureStore,
        last_call_request_id: u32,
        interpreter_version: semver::Version,
    ) -> Self {
        let versions = Versions::new(interpreter_version);

        let inner_data = InterpreterData {
            trace,
            last_call_request_id,
            cid_info,
            signatures,
        };

        let inner_data = inner_data
            .serialize()
            .expect("shouldn't fail on valid data")
            .into();

        Self {
            versions,
            inner_data,
        }
    }

    /// Tries to de InterpreterData from slice according to the data version.
    /// Tries to de only versions part of interpreter data.
    pub fn try_get_versions(slice: &[u8]) -> Result<Versions, DataDeserializationError> {
        FromSerialized::deserialize(&InterpreterDataEnvelopeRepr, slice)
            .map_err(DataDeserializationError::Envelope)
    }

    pub fn serialize(
        &self,
    ) -> Result<Vec<u8>, <InterpreterDataEnvelopeRepr as Representation>::SerializeError> {
        // use rmp_serde explicitely until interpreter-sede handles types with lifetimes
        rmp_serde::to_vec_named(self)
    }
}

impl<'data> InterpreterDataEnvelope<'data> {
    #[tracing::instrument(skip_all, level = "info")]
    pub fn try_from_slice(slice: &'data [u8]) -> Result<Self, DataDeserializationError> {
        // use rmp_serde explicitely until interpreter-sede handles types with lifetimes
        rmp_serde::from_slice(slice).map_err(DataDeserializationError::Envelope)
    }
}

impl Versions {
    pub fn new(interpreter_version: semver::Version) -> Self {
        Self {
            data_version: crate::data_version().clone(),
            interpreter_version,
        }
    }
}