Skip to main content

rialo_types/
duties.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::{BTreeMap, HashMap};
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    websocket::BLS12381_PUBLIC_KEY_SIZE, OracleDutyConfig, OracleId, OracleUpdateResult,
10    TimestampMs,
11};
12
13/// Raw 96-byte authority public key (BLS12-381).
14/// Used to identify which authority submitted a particular update.
15/// The authority key represents the identity of an authority in the committee.
16pub type AuthorityKeyBytes = [u8; BLS12381_PUBLIC_KEY_SIZE];
17
18/// Monotonically increasing consensus round/commit index.
19///
20/// Used throughout the oracle pipeline to schedule, assign, and finalize duties.
21pub type CommitIndex = u64;
22
23/// Map structure for tracking oracle duties and their execution status.
24///
25/// The map is keyed by `OracleDutiesMapKey`.
26///
27/// The value is `OracleDutiesMapValue` which tracks validator assignments and their update results.
28///
29/// This structure enables efficient tracking of oracle duties ordered by round priority,
30/// with the most recent rounds processed first.
31pub type OracleDutiesMap = BTreeMap<OracleDutiesMapKey, OracleDutiesMapValue>;
32
33/// Key type for the oracle duties map, consisting of a target commit and oracle identifier.
34#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
35pub struct OracleDutiesMapKey {
36    pub target_timestamp: TimestampMs,
37    pub oracle_id: OracleId,
38}
39
40// Explicit implementation of `Ord` to make sure `target_timestamp` remains most important in the key comparison.
41impl Ord for OracleDutiesMapKey {
42    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
43        self.target_timestamp
44            .cmp(&other.target_timestamp)
45            .then_with(|| self.oracle_id.cmp(&other.oracle_id))
46    }
47}
48
49impl PartialOrd for OracleDutiesMapKey {
50    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
51        Some(self.cmp(other))
52    }
53}
54
55/// Value type for the oracle duties map, tracking validator assignments and their update results.
56#[derive(Clone, Debug, Serialize, Deserialize, Default)]
57pub struct OracleDutiesMapValue {
58    /// A map of authority key bytes to their oracle update results.
59    /// - Key: The validator's 96-byte authority public key (BLS12-381)
60    /// - Value: The result of the validator's oracle update,
61    ///   where `None` indicates the duty is still pending and `Some(result)` indicates completion
62    #[serde(with = "serde_utils")]
63    pub updates: HashMap<AuthorityKeyBytes, Option<OracleUpdateResult>>,
64
65    /// The configuration parameters that define how this oracle duty is scheduled and finalized,
66    /// including request delay, collection window size, and commitment buffer.
67    pub config: OracleDutyConfig,
68}
69
70// Helper module for serializing HashMap with array keys
71mod serde_utils {
72    use std::collections::HashMap;
73
74    use serde::{Deserialize, Deserializer, Serialize, Serializer};
75    use serde_big_array::BigArray;
76
77    use super::{AuthorityKeyBytes, OracleUpdateResult};
78
79    pub fn serialize<S>(
80        map: &HashMap<AuthorityKeyBytes, Option<OracleUpdateResult>>,
81        serializer: S,
82    ) -> Result<S::Ok, S::Error>
83    where
84        S: Serializer,
85    {
86        #[derive(Serialize)]
87        struct Entry {
88            #[serde(with = "BigArray")]
89            key: AuthorityKeyBytes,
90            value: Option<OracleUpdateResult>,
91        }
92
93        let entries: Vec<_> = map
94            .iter()
95            .map(|(key, value)| Entry {
96                key: *key,
97                value: value.clone(),
98            })
99            .collect();
100        entries.serialize(serializer)
101    }
102
103    pub fn deserialize<'de, D>(
104        deserializer: D,
105    ) -> Result<HashMap<AuthorityKeyBytes, Option<OracleUpdateResult>>, D::Error>
106    where
107        D: Deserializer<'de>,
108    {
109        #[derive(Deserialize)]
110        struct Entry {
111            #[serde(with = "BigArray")]
112            key: AuthorityKeyBytes,
113            value: Option<OracleUpdateResult>,
114        }
115
116        let entries = Vec::<Entry>::deserialize(deserializer)?;
117        Ok(entries.into_iter().map(|e| (e.key, e.value)).collect())
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use std::cmp::{max, min};
124
125    use rialo_s_pubkey::Pubkey;
126
127    use super::*;
128
129    #[test]
130    fn test_oracle_duties_map_key_ordering() {
131        let creator1 = Pubkey::new_unique();
132        let creator2 = Pubkey::new_unique();
133        let oracle_id1 = OracleId::new(creator1, "nonce1");
134        let oracle_id2 = OracleId::new(creator2, "nonce2");
135        // Ensure oracle_id1 < oracle_id2.
136        let [oracle_id1, oracle_id2] = [min(oracle_id1, oracle_id2), max(oracle_id1, oracle_id2)];
137        // Test ordering by target_timestamp
138        let key1 = OracleDutiesMapKey {
139            target_timestamp: 100,
140            oracle_id: oracle_id1,
141        };
142        let key2 = OracleDutiesMapKey {
143            target_timestamp: 200,
144            oracle_id: oracle_id1,
145        };
146        assert!(key1 < key2);
147        assert!(key2 > key1);
148
149        // Test ordering by oracle_id when target_timestamp is equal
150        let key3 = OracleDutiesMapKey {
151            target_timestamp: 100,
152            oracle_id: oracle_id1,
153        };
154        let key4 = OracleDutiesMapKey {
155            target_timestamp: 100,
156            oracle_id: oracle_id2,
157        };
158        assert!(key3 < key4);
159        assert!(key4 > key3);
160
161        let key5 = OracleDutiesMapKey {
162            target_timestamp: 100,
163            oracle_id: oracle_id2,
164        };
165        let key6 = OracleDutiesMapKey {
166            target_timestamp: 200,
167            oracle_id: oracle_id1,
168        };
169        assert!(key5 < key6);
170        assert!(key6 > key5);
171
172        // Test equality
173        let key7 = OracleDutiesMapKey {
174            target_timestamp: 100,
175            oracle_id: oracle_id1,
176        };
177        let key8 = OracleDutiesMapKey {
178            target_timestamp: 100,
179            oracle_id: oracle_id1,
180        };
181        assert_eq!(key7, key8);
182        assert!(key7 >= key8);
183        assert!(key7 <= key8);
184
185        // Test ordering in BTreeMap
186        let mut map = BTreeMap::new();
187        let key9 = OracleDutiesMapKey {
188            target_timestamp: 200,
189            oracle_id: oracle_id1,
190        };
191        let key10 = OracleDutiesMapKey {
192            target_timestamp: 100,
193            oracle_id: oracle_id2,
194        };
195        let key11 = OracleDutiesMapKey {
196            target_timestamp: 100,
197            oracle_id: oracle_id1,
198        };
199
200        map.insert(key9, "first");
201        map.insert(key10, "second");
202        map.insert(key11, "third");
203
204        let keys: Vec<_> = map.keys().copied().collect();
205        assert_eq!(keys[0], key11);
206        assert_eq!(keys[1], key10);
207        assert_eq!(keys[2], key9);
208    }
209}