Skip to main content

rialo_oracle_processor_interface/
state.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use rialo_cli_representable::Representable;
5use rialo_cli_representation::HumanReadable;
6use rialo_types::{AuthorityKeyBytes, OracleData, OracleId, OracleOutput};
7use serde::{Deserialize, Serialize};
8use serde_big_array::BigArray;
9
10use crate::errors::OracleProcessorError;
11
12/// Oracle update structure
13#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Representable)]
14#[representable(human_readable = "oracle_update_human_readable")]
15pub struct OracleUpdate {
16    /// The output that resulted from the Oracle Duty being performed, serialized as bytes with Borsh.
17    pub(crate) data: Vec<u8>,
18    /// The 96-byte BLS12-381 authority public key of the validator
19    #[serde(with = "BigArray")]
20    pub(crate) validator: AuthorityKeyBytes,
21}
22
23fn oracle_update_human_readable(update: &OracleUpdate) -> String {
24    let mut out = String::new();
25    // Format authority key as hex string for readability
26    let auth_key_hex = hex::encode(update.validator);
27    out.push_str(&format!("Validator (authority key): {}; ", auth_key_hex));
28    out.push_str("Data: ");
29    // Try to decode the oracle output
30    match update.try_data_as_output() {
31        Ok(oracle_output) => {
32            out.push_str(&format!("{oracle_output:?}"));
33        }
34        Err(_) => {
35            out.push_str("<Unable to decode>: \n");
36        }
37    }
38    out
39}
40
41impl OracleUpdate {
42    pub fn new(data: Vec<u8>, validator: AuthorityKeyBytes) -> Self {
43        Self { data, validator }
44    }
45
46    /// Returns the Oracle data as a Base64 encoded string
47    ///
48    /// # Returns
49    ///
50    /// The output that resulted from the Oracle Duty being performed, serialized as bytes with Borsh
51    pub fn data(&self) -> &[u8] {
52        &self.data
53    }
54
55    /// The 96-byte BLS12-381 authority public key of the validator.
56    pub fn validator(&self) -> AuthorityKeyBytes {
57        self.validator
58    }
59
60    /// Returns the data as [`OracleOutput`]
61    ///
62    /// # Returns
63    ///
64    /// The output that resulted from the Oracle Duty being performed, deserialized as [`OracleOutput`]
65    pub fn try_data_as_output(&self) -> Result<OracleOutput, OracleProcessorError> {
66        borsh::from_slice(&self.data).map_err(|_| OracleProcessorError::ReportData)
67    }
68
69    /// Returns the `OracleData`  payload returned by the oracle if the request was a success.
70    /// Otherwise, returns None.
71    ///
72    /// # Returns
73    ///
74    /// An Option containing the `OracleData`  payload if the oracle output was a success, or None otherwise.
75    pub fn success_payload(&self) -> Option<OracleData> {
76        match self.try_data_as_output() {
77            Ok(OracleOutput::Success(response)) => Some(response.response),
78            _ => None,
79        }
80    }
81
82    pub fn get_oracle_response_timestamp(&self) -> Option<String> {
83        if let Ok(OracleOutput::Success(response)) = self.try_data_as_output() {
84            Some(response.timestamp)
85        } else {
86            None
87        }
88    }
89}
90
91/// Represents an oracle's definition and configuration
92#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Representable)]
93#[representable(human_readable = "oracle_report_human_readable")]
94pub struct OracleReport {
95    pub oracle_id: OracleId,        // Unique identifier
96    pub round: u64,                 // Round that received the report
97    pub updates: Vec<OracleUpdate>, // Updates from validator for this oracle
98}
99
100fn oracle_report_human_readable(report: &OracleReport) -> String {
101    let mut out = String::new();
102
103    out.push_str("Oracle Report\n");
104    out.push_str("=============\n\n");
105    out.push_str(&format!("Oracle ID: {}\n", report.oracle_id));
106    out.push_str(&format!("Round: {}\n", report.round));
107    out.push_str(&format!("Number of Updates: {}\n", report.updates.len()));
108
109    if !report.updates.is_empty() {
110        out.push_str("\nUpdates:\n");
111        for (i, update) in report.updates.iter().enumerate() {
112            out.push_str(&format!("  Update {}:\n", i + 1));
113            let auth_key_hex = hex::encode(update.validator());
114            out.push_str(&format!("    Validator: {}\n", auth_key_hex));
115            out.push_str(&format!("    Data Size: {} bytes\n", update.data().len()));
116
117            // Show success payload if available
118            if let Ok(output) = update.try_data_as_output() {
119                match output {
120                    OracleOutput::Success(response) => {
121                        out.push_str(&format!("    Success: {:?}\n", response.human_readable()));
122                    }
123                    OracleOutput::OracleError(err) => {
124                        out.push_str(&format!("    OracleError: {err:?}\n"));
125                    }
126                    OracleOutput::UnserializableResponse(err) => {
127                        out.push_str(&format!("    UnserializableResponse: {err:?}\n"));
128                    }
129                    _ => {
130                        out.push_str(&format!("    Unknown Output Type: {:?}\n", output));
131                    }
132                }
133            } else {
134                out.push_str(&format!("    Output: {}\n", update.human_readable()));
135            }
136        }
137    }
138
139    out
140}
141
142impl OracleReport {
143    /// Converts the `OracleReport` into a `Vec<OracleUpdate>`
144    ///
145    /// This function is used to extract the updates from the `OracleReport`
146    ///
147    /// # Returns
148    ///
149    /// A vector of `OracleUpdate` instances
150    pub fn into_updates(self) -> Vec<OracleUpdate> {
151        self.updates
152    }
153
154    /// Returns an iterator over containing all the `OracleOutput`
155    ///
156    /// # Returns
157    ///
158    /// Returns an iterator over all the `OracleOutput`s.
159    pub fn outputs(&self) -> impl Iterator<Item = OracleOutput> + '_ {
160        self.updates
161            .iter()
162            .filter_map(|update| update.try_data_as_output().ok())
163    }
164}
165
166/// Extracts the round from the bincode data
167///
168/// # Arguments
169///
170/// * `bincode_data` - A byte slice containing the bincode data
171///
172/// # Returns
173///
174/// The extracted round value
175///
176/// # Errors
177///
178/// Returns an error if the data is invalid or if the conversion fails
179pub fn extract_round_raw(bincode_data: &[u8]) -> Result<u64, OracleProcessorError> {
180    // The OracleReport structure has changed, so we need to deserialize it properly
181    // to extract the round field
182    let report: OracleReport =
183        bincode::deserialize(bincode_data).map_err(|_| OracleProcessorError::ReportData)?;
184
185    Ok(report.round)
186}