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