1use std::{
29 collections::BTreeMap,
30 convert::Infallible,
31 fmt,
32 ops::Deref,
33 str::FromStr,
34 sync::Arc,
35 time::{Duration, SystemTime, UNIX_EPOCH},
36};
37
38use borsh::{BorshDeserialize, BorshSerialize};
39#[cfg(feature = "non-pdk")]
40use clap::Subcommand;
41use rialo_cli_representable::Representable;
42use rialo_limits::{max_oracle_output_serialized_bytes, MIN_VIABLE_LIMIT_OF_ORACLE_OUTPUT_SIZE};
43use rialo_s_compute_budget::compute_budget_limits::{MAX_COMPUTE_UNIT_LIMIT, MAX_HEAP_FRAME_BYTES};
44use rialo_s_pubkey::Pubkey;
45use serde::{Deserialize, Serialize};
46use serde_big_array::BigArray;
47#[cfg(feature = "non-pdk")]
48use url::Url;
49
50use crate::{AttestationReport, AuthorityKeyBytes, Headers, HttpFilter, Nonce, OracleDutyConfig};
51
52pub type TimestampMs = u64;
55
56const MIN_UPDATE_PERIOD_MS: TimestampMs = 50;
60
61#[derive(
76 Debug,
77 Default,
78 Clone,
79 Copy,
80 PartialEq,
81 Eq,
82 Hash,
83 PartialOrd,
84 Ord,
85 Serialize,
86 Deserialize,
87 BorshSerialize,
88 BorshDeserialize,
89)]
90pub struct OracleId {
91 pub nonce: Nonce,
92 pub creator: Pubkey,
93}
94
95impl OracleId {
96 pub fn new(creator: Pubkey, nonce: impl Into<Nonce>) -> Self {
98 Self {
99 nonce: nonce.into(),
100 creator,
101 }
102 }
103}
104
105impl fmt::Display for OracleId {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 write!(f, "{}:{}", &self.nonce, &self.creator)
108 }
109}
110
111impl FromStr for OracleId {
112 type Err = String;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 serde_json::from_str(s).map_err(|e| format!("Failed to parse OracleId: {}", e))
117 }
118}
119
120#[cfg(feature = "non-pdk")]
122fn parse_oracle_id(s: &str) -> Result<OracleId, String> {
123 OracleId::from_str(s)
124}
125
126#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Representable)]
132#[representable(human_readable = "oracle_info_human_readable")]
133pub struct OracleInfo {
134 pub id: OracleId,
136 pub description: String,
138 pub update_frequency: UpdateFrequency,
140 pub target_oracles: Vec<TargetOracle>,
142 pub starting_timestamp: StartingTimestamp,
144 pub is_active: bool,
146 pub created_at_ms: i64,
148 #[serde(default = "default_validators_per_duty")]
150 pub validators_per_duty: u32,
151 #[serde(default = "default_oracle_request_delay_ms")]
153 pub oracle_request_delay_ms: TimestampMs,
154 pub compute_units_limit: Option<u32>,
156 pub heap_size_limit: Option<u32>,
158}
159
160fn oracle_info_human_readable(info: &OracleInfo) -> String {
161 let mut out = String::new();
162
163 out.push_str(&format!("Oracle ID: {}\n", info.id));
164 out.push_str(&format!("Description: {}\n", info.description));
165 out.push_str(&format!("Active: {}\n", info.is_active));
166 out.push_str(&format!(
167 "Starting Timestamp: {:?}\n",
168 info.starting_timestamp
169 ));
170 out.push_str(&format!("Update Frequency: {:?}\n", info.update_frequency));
171 out.push_str(&format!("Created At: {}\n", info.created_at_ms));
172 out.push_str(&format!(
173 "Validators Per Duty: {}\n",
174 info.validators_per_duty
175 ));
176 out.push_str(&format!(
177 "Oracle Request Delay: {}\n",
178 info.oracle_request_delay_ms
179 ));
180
181 if !info.target_oracles.is_empty() {
182 out.push_str(&format!(
183 "\nTarget Oracles ({}):\n",
184 info.target_oracles.len()
185 ));
186 for (i, target) in info.target_oracles.iter().enumerate() {
187 out.push_str(&format!(" {}. {:?}\n", i + 1, target));
188 }
189 }
190
191 if let Some(compute_units) = info.compute_units_limit {
192 out.push_str(&format!("\nCompute Units Limit: {}\n", compute_units));
193 }
194
195 if let Some(heap_size) = info.heap_size_limit {
196 out.push_str(&format!("Heap Size Limit: {}\n", heap_size));
197 }
198
199 out
200}
201
202impl Default for OracleInfo {
203 fn default() -> Self {
204 Self {
205 id: OracleId::default(),
206 description: String::new(),
207 update_frequency: UpdateFrequency::default(),
208 target_oracles: Vec::new(),
209 starting_timestamp: StartingTimestamp::default(),
210 is_active: false,
211 created_at_ms: 0,
212 validators_per_duty: default_validators_per_duty(),
213 oracle_request_delay_ms: default_oracle_request_delay_ms(),
214 compute_units_limit: None,
215 heap_size_limit: None,
216 }
217 }
218}
219
220impl OracleInfo {
221 pub fn is_asap(&self) -> bool {
224 matches!(self.starting_timestamp, StartingTimestamp::Asap)
225 }
226
227 pub fn target_timestamp(&self) -> Option<TimestampMs> {
228 match self.starting_timestamp {
229 StartingTimestamp::Timestamp(timestamp) => Some(timestamp),
230 StartingTimestamp::Asap => None,
231 }
232 }
233
234 pub fn validate(&self) -> Result<(), String> {
236 match self.starting_timestamp {
237 StartingTimestamp::Asap => {
238 if !matches!(self.update_frequency, UpdateFrequency::OneShot) {
239 return Err("ASAP oracles cannot be periodic".to_string());
240 }
241 }
242 StartingTimestamp::Timestamp(starting_timestamp) => {
243 match self.update_frequency {
244 UpdateFrequency::OneShot => {}
245 UpdateFrequency::Periodic(period)
246 | UpdateFrequency::LimitedPeriodic(period, _) => {
247 validate_periodic_frequency(period)?;
248
249 if let UpdateFrequency::LimitedPeriodic(_, end_timestamp) =
251 self.update_frequency
252 {
253 if starting_timestamp >= end_timestamp {
254 return Err("end_timestamp of a LimitedPeriodic oracle should be above starting_timestamp".to_string());
255 }
256 }
257 }
258 }
259 }
260 }
261
262 if self.target_oracles.is_empty() {
264 return Err("OracleTargets cannot be empty".to_string());
265 }
266
267 if self.oracle_request_delay_ms < OracleDutyConfig::MIN_ORACLE_REQUEST_DELAY {
269 return Err(format!(
270 "oracle_request_delay cannot be below {}",
271 OracleDutyConfig::MIN_ORACLE_REQUEST_DELAY
272 ));
273 }
274 if self.oracle_request_delay_ms > OracleDutyConfig::MAX_ORACLE_REQUEST_DELAY_MS {
275 return Err(format!(
276 "oracle_request_delay cannot be above {}",
277 OracleDutyConfig::MAX_ORACLE_REQUEST_DELAY_MS
278 ));
279 }
280
281 if self.validators_per_duty == 0 {
283 return Err("validators_per_duty cannot be 0".to_string());
284 }
285 let max_oracle_output_size = max_oracle_output_serialized_bytes(self.validators_per_duty);
286 if max_oracle_output_size < MIN_VIABLE_LIMIT_OF_ORACLE_OUTPUT_SIZE {
287 return Err(format!("validators_per_duty is too high, results in max size of oracle updates that is too low: {max_oracle_output_size} vs {MIN_VIABLE_LIMIT_OF_ORACLE_OUTPUT_SIZE}"));
288 }
289
290 if let Some(compute_units_limit) = self.compute_units_limit {
292 if compute_units_limit == 0 {
293 return Err("compute_usage_limit cannot be Some(0)".to_string());
294 }
295 if compute_units_limit > MAX_COMPUTE_UNIT_LIMIT {
296 return Err(format!("compute_usage_limit cannot be above MAX_COMPUTE_UNIT_LIMIT={MAX_COMPUTE_UNIT_LIMIT}"));
297 }
298 }
299
300 if let Some(heap_size_limit) = self.heap_size_limit {
302 if heap_size_limit == 0 {
303 return Err("heap_size_limit cannot be Some(0)".to_string());
304 }
305 if heap_size_limit > MAX_HEAP_FRAME_BYTES {
306 return Err(format!(
307 "heap_size_limit cannot be above MAX_HEAP_FRAME_BYTES={MAX_HEAP_FRAME_BYTES}"
308 ));
309 }
310 }
311
312 Ok(())
313 }
314
315 pub fn websocket_op(&self) -> Option<WebSocketOperation> {
317 self.target_oracles
318 .first()
319 .and_then(|target| target.websocket_op())
320 }
321}
322
323fn validate_periodic_frequency(period_ms: TimestampMs) -> Result<(), String> {
324 if period_ms == 0 {
325 return Err("update frequency cannot be zero".to_string());
326 }
327
328 if period_ms < MIN_UPDATE_PERIOD_MS {
329 return Err(format!(
330 "update frequency {period_ms} cannot be below {MIN_UPDATE_PERIOD_MS}"
331 ));
332 }
333
334 Ok(())
335}
336
337impl TargetOracle {
338 pub fn websocket_op(&self) -> Option<WebSocketOperation> {
340 if let TargetOracle::WebSocket(ws_op) = self {
341 Some(ws_op.clone())
342 } else {
343 None
344 }
345 }
346}
347
348fn default_validators_per_duty() -> u32 {
349 OracleDutyConfig::DEFAULT_VALIDATORS_PER_DUTY
350}
351
352fn default_oracle_request_delay_ms() -> TimestampMs {
353 OracleDutyConfig::DEFAULT_ORACLE_REQUEST_DELAY_MS
354}
355
356#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
361pub struct OracleEntry {
362 oracle_info: Arc<OracleInfo>,
363 data_hash: [u8; OracleEntry::HASH_LENGTH],
364 last_modified_timestamp: u64,
365}
366
367impl OracleEntry {
368 const HASH_LENGTH: usize = 32;
369
370 pub fn new(
378 oracle_info: OracleInfo,
379 data_hash: [u8; Self::HASH_LENGTH],
380 last_modified_round: u64,
381 ) -> Self {
382 Self {
383 oracle_info: Arc::new(oracle_info),
384 data_hash,
385 last_modified_timestamp: last_modified_round,
386 }
387 }
388
389 pub fn oracle_info(&self) -> Arc<OracleInfo> {
395 self.oracle_info.clone()
396 }
397
398 pub fn last_modified_timestamp(&self) -> u64 {
404 self.last_modified_timestamp
405 }
406
407 pub fn data_hash(&self) -> &[u8; Self::HASH_LENGTH] {
413 &self.data_hash
414 }
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
431pub enum OracleValue {
432 Plain(String),
433 Encrypted(String),
434}
435
436impl Default for OracleValue {
437 fn default() -> Self {
438 OracleValue::Plain(String::new())
439 }
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
464pub struct OracleUrl(OracleValue);
465
466impl fmt::Display for OracleUrl {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 match self.0 {
469 OracleValue::Plain(ref s) => write!(f, "{}", s),
470 OracleValue::Encrypted(ref s) => write!(f, "enc://{}", s),
471 }
472 }
473}
474
475impl Deref for OracleUrl {
476 type Target = OracleValue;
477
478 fn deref(&self) -> &Self::Target {
479 &self.0
480 }
481}
482
483#[cfg(feature = "non-pdk")]
484impl From<Url> for OracleUrl {
485 fn from(url: Url) -> Self {
486 url.to_string().into()
487 }
488}
489
490#[cfg(feature = "non-pdk")]
491impl From<&Url> for OracleUrl {
492 fn from(url: &Url) -> Self {
493 Self(OracleValue::Plain(url.to_string()))
494 }
495}
496
497impl From<String> for OracleUrl {
498 fn from(url: String) -> Self {
499 url.as_str().into()
500 }
501}
502
503impl From<&str> for OracleUrl {
504 fn from(s: &str) -> Self {
505 if let Some(encrypted) = s.strip_prefix("enc://") {
506 Self(OracleValue::Encrypted(encrypted.into()))
507 } else {
508 Self(OracleValue::Plain(s.into()))
510 }
511 }
512}
513
514impl FromStr for OracleUrl {
515 type Err = Infallible;
516 fn from_str(s: &str) -> Result<Self, Self::Err> {
517 Ok(s.into())
518 }
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
531pub enum OracleValueBody {
532 Plain(Vec<u8>),
533 Encrypted(Vec<u8>),
534}
535
536impl Default for OracleValueBody {
537 fn default() -> Self {
538 OracleValueBody::Plain(vec![])
539 }
540}
541
542impl FromStr for OracleValueBody {
543 type Err = Infallible;
544 fn from_str(s: &str) -> Result<Self, Self::Err> {
545 Ok(OracleValueBody::Plain(s.as_bytes().to_vec()))
546 }
547}
548
549#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, strum_macros::AsRefStr)]
555#[cfg_attr(feature = "non-pdk", derive(Subcommand))]
556pub enum TargetOracle {
557 HttpGet {
561 #[cfg_attr(feature = "non-pdk", clap(
562 long = "target-url",
563 value_parser = clap::value_parser!(OracleUrl)
564 ))]
565 url: OracleUrl,
566 #[cfg_attr(feature = "non-pdk", clap(long, default_value = None))]
567 filter: Option<Vec<HttpFilter>>,
568 #[cfg_attr(feature = "non-pdk", clap(long, default_value_t = Headers::default()))]
569 headers: Headers,
570 },
571 HttpPost {
575 #[cfg_attr(feature = "non-pdk", clap(
576 long = "target-url",
577 value_parser = clap::value_parser!(OracleUrl)
578 ))]
579 url: OracleUrl,
580 #[cfg_attr(feature = "non-pdk", clap(long))]
581 filter: Option<Vec<HttpFilter>>,
582 #[cfg_attr(feature = "non-pdk", clap(
583 long,
584 value_parser = clap::value_parser!(OracleValueBody)
585 ))]
586 body: OracleValueBody,
587 #[cfg_attr(feature = "non-pdk", clap(long))]
588 content_type: String,
589 #[cfg_attr(feature = "non-pdk", clap(long, default_value_t = Headers::default()))]
590 headers: Headers,
591 },
592 Time,
594 PriceReactor,
596 Number,
599 SecretKeyGeneration {
602 #[cfg_attr(feature = "non-pdk", clap(long))]
603 committee_id: String,
604 #[cfg_attr(feature = "non-pdk", clap(long))]
605 committee_members: Vec<String>,
606 },
607 SecretKeyEncryption {
610 #[cfg_attr(feature = "non-pdk", clap(long))]
611 target_tee_id: String,
612 #[cfg_attr(feature = "non-pdk", clap(long))]
613 secret_data: Vec<u8>,
614 #[cfg_attr(feature = "non-pdk", clap(long))]
615 committee_id: String,
616 },
617 SecretKeyDecryption {
620 #[cfg_attr(feature = "non-pdk", clap(long))]
621 encrypted_data: Vec<u8>,
622 #[cfg_attr(feature = "non-pdk", clap(long))]
623 source_committee_id: String,
624 },
625 Stonks,
627 #[cfg_attr(feature = "non-pdk", clap(subcommand))]
629 WebSocket(WebSocketOperation),
630}
631
632impl TargetOracle {
633 pub fn is_websocket(&self) -> bool {
635 matches!(self, TargetOracle::WebSocket(_))
636 }
637}
638
639pub const SYSTEM_MESSAGE_SIZE: usize = 128;
650
651#[derive(
655 Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
656)]
657pub enum WebSocketReadMode {
658 #[default]
660 Latest,
661 All,
663 FromIndex(u64),
669}
670
671#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
673pub enum MessageContent {
674 Data(Vec<u8>),
676 OversizedWarning {
678 original_size: usize,
680 limit: usize,
682 original_timestamp: String,
684 },
685}
686
687impl MessageContent {
688 pub fn byte_size(&self) -> usize {
693 match self {
694 MessageContent::Data(data) => data.len(),
695 MessageContent::OversizedWarning { .. } => SYSTEM_MESSAGE_SIZE,
696 }
697 }
698
699 pub fn is_system(&self) -> bool {
701 matches!(self, MessageContent::OversizedWarning { .. })
702 }
703
704 pub fn as_data(&self) -> Option<&[u8]> {
706 match self {
707 MessageContent::Data(data) => Some(data),
708 MessageContent::OversizedWarning { .. } => None,
709 }
710 }
711}
712
713#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
715pub struct BufferedMessage {
716 pub index: u64,
718 pub content: MessageContent,
720 pub received_at: String,
722}
723
724impl BufferedMessage {
725 pub fn byte_size(&self) -> usize {
727 self.content.byte_size()
728 }
729
730 pub fn is_system_message(&self) -> bool {
732 self.content.is_system()
733 }
734
735 pub fn as_data(&self) -> Option<&[u8]> {
737 self.content.as_data()
738 }
739}
740
741#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
764pub struct WebSocketReadResponse {
765 pub messages: Vec<BufferedMessage>,
767 pub latest_index: Option<u64>,
769 pub oldest_index: Option<u64>,
771}
772
773impl WebSocketReadResponse {
774 pub fn has_messages(&self) -> bool {
776 !self.messages.is_empty()
777 }
778
779 pub fn message_count(&self) -> usize {
781 self.messages.len()
782 }
783
784 pub fn first_message(&self) -> Option<&BufferedMessage> {
786 self.messages.first()
787 }
788
789 pub fn latest_message(&self) -> Option<&BufferedMessage> {
791 self.messages.last()
792 }
793
794 pub fn first_data(&self) -> Option<&[u8]> {
798 self.messages.first().and_then(|m| m.as_data())
799 }
800
801 pub fn latest_data(&self) -> Option<&[u8]> {
805 self.messages.last().and_then(|m| m.as_data())
806 }
807
808 pub fn data_messages(&self) -> impl Iterator<Item = &BufferedMessage> {
810 self.messages.iter().filter(|m| !m.is_system_message())
811 }
812
813 pub fn iter_data(&self) -> impl Iterator<Item = &[u8]> {
817 self.messages.iter().filter_map(|m| m.as_data())
818 }
819
820 pub fn data_message_count(&self) -> usize {
822 self.messages
823 .iter()
824 .filter(|m| !m.is_system_message())
825 .count()
826 }
827
828 pub fn has_system_messages(&self) -> bool {
830 self.messages.iter().any(|m| m.is_system_message())
831 }
832}
833
834#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
846#[cfg_attr(feature = "non-pdk", derive(Subcommand))]
847pub enum WebSocketOperation {
848 Connect {
856 #[cfg_attr(feature = "non-pdk", clap(
858 long = "target-url",
859 value_parser = clap::value_parser!(OracleUrl)
860 ))]
861 url: OracleUrl,
862 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_oracle_id))]
864 oracle_id: OracleId,
865 },
866 Read {
893 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_oracle_id))]
895 connection_oracle_id: OracleId,
896 #[serde(default)]
898 #[cfg_attr(feature = "non-pdk", clap(skip))]
899 mode: WebSocketReadMode,
900 },
901 Send {
925 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_oracle_id))]
927 connection_oracle_id: OracleId,
928 #[cfg_attr(feature = "non-pdk", clap(skip))]
931 messages: Vec<OracleValue>,
932 },
933 Close {
954 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_oracle_id))]
956 connection_oracle_id: OracleId,
957 },
958}
959
960impl WebSocketOperation {
961 pub fn connect(url: impl Into<OracleUrl>, oracle_id: OracleId) -> Self {
963 WebSocketOperation::Connect {
964 url: url.into(),
965 oracle_id,
966 }
967 }
968
969 pub fn read(connection_oracle_id: OracleId) -> Self {
971 WebSocketOperation::Read {
972 connection_oracle_id,
973 mode: WebSocketReadMode::default(),
974 }
975 }
976
977 pub fn read_with_mode(connection_oracle_id: OracleId, mode: WebSocketReadMode) -> Self {
979 WebSocketOperation::Read {
980 connection_oracle_id,
981 mode,
982 }
983 }
984
985 pub fn send(connection_oracle_id: OracleId, messages: Vec<OracleValue>) -> Self {
987 WebSocketOperation::Send {
988 connection_oracle_id,
989 messages,
990 }
991 }
992
993 pub fn close(connection_oracle_id: OracleId) -> Self {
995 WebSocketOperation::Close {
996 connection_oracle_id,
997 }
998 }
999
1000 pub fn is_connect(&self) -> bool {
1002 matches!(self, WebSocketOperation::Connect { .. })
1003 }
1004
1005 pub fn is_read(&self) -> bool {
1007 matches!(self, WebSocketOperation::Read { .. })
1008 }
1009
1010 pub fn is_send(&self) -> bool {
1012 matches!(self, WebSocketOperation::Send { .. })
1013 }
1014
1015 pub fn is_close(&self) -> bool {
1017 matches!(self, WebSocketOperation::Close { .. })
1018 }
1019
1020 pub fn url(&self) -> Option<&OracleUrl> {
1022 match self {
1023 WebSocketOperation::Connect { url, .. } => Some(url),
1024 _ => None,
1025 }
1026 }
1027
1028 pub fn oracle_id(&self) -> Option<&OracleId> {
1030 match self {
1031 WebSocketOperation::Connect { oracle_id, .. } => Some(oracle_id),
1032 _ => None,
1033 }
1034 }
1035
1036 pub fn connection_oracle_id(&self) -> Option<&OracleId> {
1038 match self {
1039 WebSocketOperation::Read {
1040 connection_oracle_id,
1041 ..
1042 }
1043 | WebSocketOperation::Send {
1044 connection_oracle_id,
1045 ..
1046 }
1047 | WebSocketOperation::Close {
1048 connection_oracle_id,
1049 } => Some(connection_oracle_id),
1050 _ => None,
1051 }
1052 }
1053
1054 pub fn read_mode(&self) -> Option<&WebSocketReadMode> {
1056 match self {
1057 WebSocketOperation::Read { mode, .. } => Some(mode),
1058 _ => None,
1059 }
1060 }
1061
1062 pub fn messages(&self) -> Option<&[OracleValue]> {
1064 match self {
1065 WebSocketOperation::Send { messages, .. } => Some(messages),
1066 _ => None,
1067 }
1068 }
1069}
1070
1071impl fmt::Display for WebSocketOperation {
1072 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1073 match self {
1074 WebSocketOperation::Connect { url, oracle_id } => {
1075 write!(f, "Connect(url={url}, oracle_id={oracle_id})")
1076 }
1077 WebSocketOperation::Read {
1078 connection_oracle_id,
1079 mode,
1080 } => {
1081 write!(
1082 f,
1083 "Read(connection_oracle_id={}, mode={:?})",
1084 connection_oracle_id, mode
1085 )
1086 }
1087 WebSocketOperation::Send {
1088 connection_oracle_id,
1089 messages,
1090 } => {
1091 write!(
1092 f,
1093 "Send(connection_oracle_id={}, messages_count={})",
1094 connection_oracle_id,
1095 messages.len()
1096 )
1097 }
1098 WebSocketOperation::Close {
1099 connection_oracle_id,
1100 } => {
1101 write!(f, "Close(connection_oracle_id={})", connection_oracle_id)
1102 }
1103 }
1104 }
1105}
1106
1107impl FromStr for TargetOracle {
1108 type Err = String;
1109
1110 fn from_str(s: &str) -> Result<Self, Self::Err> {
1111 if s == "Time" {
1112 Ok(TargetOracle::Time)
1113 } else if s == "PriceReactor" {
1114 Ok(TargetOracle::PriceReactor)
1115 } else if s == "SecretKeyGeneration" {
1116 Err("SecretKeyGeneration oracle requires committee_id and committee_members parameters. Use the appropriate API to create this oracle type.".to_string())
1117 } else if s == "SecretKeyEncryption" {
1118 Err("SecretKeyEncryption oracle requires target_tee_id, secret_data, and committee_id parameters. Use the appropriate API to create this oracle type.".to_string())
1119 } else if s == "SecretKeyDecryption" {
1120 Err("SecretKeyDecryption oracle requires encrypted_data and source_committee_id parameters. Use the appropriate API to create this oracle type.".to_string())
1121 } else if s == "number" {
1122 Err("The 'number' oracle is only for testing purposes and should not be used in production.".to_string())
1123 } else {
1124 if let Some(rest) = s.strip_prefix("HttpGet:") {
1125 let parts: Vec<&str> = rest.splitn(2, '|').collect();
1126 if parts.is_empty() {
1127 return Err(
1128 "Invalid HttpGet format. Use 'HttpGet:<url>[|<filter>]'.".to_string()
1129 );
1130 }
1131
1132 let url = parts[0].to_string();
1133 let filter = if parts.len() > 1 && !parts[1].is_empty() {
1134 Some(vec![HttpFilter::from_str(parts[1])?])
1135 } else {
1136 None
1137 };
1138
1139 #[cfg(feature = "non-pdk")]
1141 if Url::parse(&url).is_err() {
1142 return Err(format!("Invalid URL: {url}"));
1143 }
1144
1145 return Ok(TargetOracle::HttpGet {
1146 url: url.into(),
1147 filter,
1148 headers: Headers::default(),
1149 });
1150 }
1151
1152 Err(format!("Unknown TargetOracle type: {s}"))
1153 }
1154 }
1155}
1156
1157pub type InputCommitmentBytes = [u8; 32];
1166
1167pub type SignatureBytes = [u8; 64];
1171
1172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1176pub struct OracleUpdateResult {
1177 pub oracle_id: OracleId,
1179
1180 pub target_timestamp: TimestampMs,
1182
1183 #[serde(with = "BigArray")]
1185 pub response_hash: [u8; 32],
1186
1187 #[serde(with = "BigArray")]
1191 pub input_commitment: InputCommitmentBytes,
1192
1193 #[serde(with = "BigArray")]
1195 pub signature: SignatureBytes,
1196
1197 pub oracle_result: Vec<u8>,
1199
1200 pub attestation_report: Option<AttestationReport>,
1203
1204 #[serde(with = "BigArray")]
1206 pub authority_key: AuthorityKeyBytes,
1207}
1208
1209impl OracleUpdateResult {
1210 pub fn new(
1212 oracle_id: OracleId,
1213 target_timestamp: TimestampMs,
1214 oracle_result: Vec<u8>,
1215 input_commitment: InputCommitmentBytes,
1216 signature: SignatureBytes,
1217 attestation_report: Option<AttestationReport>,
1218 authority_key: AuthorityKeyBytes,
1219 ) -> Result<Self, &'static str> {
1220 let hash = blake3::hash(&oracle_result);
1221
1222 #[cfg(feature = "non-pdk")]
1224 let oracle_result = if oracle_result.len() > rialo_limits::MAX_TRANSACTION_SIZE as usize {
1225 tracing::error!(
1226 "Oracle result size {} exceeds maximum size {}, dropping the result.",
1227 oracle_result.len(),
1228 rialo_limits::MAX_TRANSACTION_SIZE
1229 );
1230 return Err("Oracle result exceeds maximum size");
1231 } else {
1232 oracle_result
1234 };
1235
1236 #[cfg(not(feature = "non-pdk"))]
1237 let oracle_result = oracle_result;
1238
1239 Ok(Self {
1240 oracle_id,
1241 target_timestamp,
1242 response_hash: *hash.as_bytes(),
1243 input_commitment,
1244 signature,
1245 oracle_result,
1246 attestation_report,
1247 authority_key,
1248 })
1249 }
1250}
1251
1252impl Default for OracleUpdateResult {
1253 fn default() -> Self {
1254 Self {
1255 oracle_id: OracleId::default(),
1256 target_timestamp: 0,
1257 response_hash: [0; 32],
1258 input_commitment: [0xee; 32],
1259 signature: [0; 64],
1260 oracle_result: vec![],
1261 attestation_report: None,
1262 authority_key: [0xff; 96],
1263 }
1264 }
1265}
1266
1267#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)]
1275pub struct OracleRequest {
1276 pub oracle_id: Option<OracleId>,
1278 pub target_timestamp: Option<TimestampMs>,
1279 pub authority_key: AuthorityKeyBytes,
1280 pub include_attestation: bool,
1281 pub max_oracle_output_size: u32,
1282
1283 pub params: BTreeMap<String, String>,
1285}
1286
1287impl Default for OracleRequest {
1288 fn default() -> Self {
1289 Self {
1290 oracle_id: None,
1291 target_timestamp: None,
1292 authority_key: [0; 96],
1293 include_attestation: true,
1294 max_oracle_output_size: 0,
1295 params: BTreeMap::default(),
1296 }
1297 }
1298}
1299
1300impl OracleRequest {
1301 pub fn input_commitment(&self) -> Result<blake3::Hash, &'static str> {
1302 let request_bytes = borsh::to_vec(self).map_err(|_| "Failed to serialize OracleRequest")?;
1303 Ok(blake3::hash(&request_bytes))
1304 }
1305}
1306
1307#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
1309pub enum UpdateFrequency {
1310 #[default]
1312 OneShot,
1313 Periodic(TimestampMs),
1315 LimitedPeriodic(TimestampMs, TimestampMs),
1318}
1319
1320#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
1322pub enum StartingTimestamp {
1323 Timestamp(TimestampMs),
1325 Asap,
1327}
1328
1329impl Default for StartingTimestamp {
1330 fn default() -> Self {
1331 StartingTimestamp::Timestamp(0)
1332 }
1333}
1334
1335impl UpdateFrequency {
1336 pub fn periodic(duration: Duration) -> Self {
1338 Self::Periodic(duration.as_millis() as TimestampMs)
1339 }
1340}
1341
1342impl StartingTimestamp {
1343 pub fn start_offset(offset: Duration) -> Self {
1344 let timestamp = SystemTime::now() + offset;
1345 Self::Timestamp(timestamp.duration_since(UNIX_EPOCH).unwrap().as_millis() as TimestampMs)
1346 }
1347}
1348
1349#[cfg(test)]
1350mod tests {
1351 use super::*;
1352
1353 fn base_valid_oracle_info() -> OracleInfo {
1354 OracleInfo {
1355 description: "test".to_string(),
1356 target_oracles: vec![TargetOracle::Time],
1357 update_frequency: UpdateFrequency::OneShot,
1359 starting_timestamp: StartingTimestamp::Timestamp(0),
1361 ..OracleInfo::default()
1363 }
1364 }
1365
1366 #[test]
1367 fn test_is_asap_true_and_false() {
1368 let mut info = base_valid_oracle_info();
1369 assert!(!info.is_asap());
1370 info.starting_timestamp = StartingTimestamp::Asap;
1371 assert!(info.is_asap());
1372 }
1373
1374 #[test]
1375 fn test_validate_success_minimal() {
1376 let info = base_valid_oracle_info();
1377 assert!(info.validate().is_ok());
1378 }
1379
1380 #[test]
1381 fn test_asap_cannot_be_periodic() {
1382 let mut info = base_valid_oracle_info();
1383 info.starting_timestamp = StartingTimestamp::Asap;
1384 info.update_frequency = UpdateFrequency::Periodic(10);
1385 let err = info.validate().unwrap_err();
1386 assert!(err.contains("ASAP oracles cannot be periodic"));
1387 }
1388
1389 #[test]
1390 fn test_asap_cannot_be_limited_periodic() {
1391 let mut info = base_valid_oracle_info();
1392 info.starting_timestamp = StartingTimestamp::Asap;
1393 info.update_frequency = UpdateFrequency::LimitedPeriodic(5, 100);
1394 let err = info.validate().unwrap_err();
1395 assert!(err.contains("ASAP oracles cannot be periodic"));
1396 }
1397
1398 #[test]
1399 fn test_periodic_with_zero_period_is_invalid() {
1400 let mut info = base_valid_oracle_info();
1401 info.starting_timestamp = StartingTimestamp::Timestamp(1);
1402 info.update_frequency = UpdateFrequency::Periodic(0);
1403 let err = info.validate().unwrap_err();
1404 assert!(err.contains("update frequency cannot be zero"));
1405 }
1406
1407 #[test]
1408 fn test_limited_periodic_with_zero_period_is_invalid() {
1409 let mut info = base_valid_oracle_info();
1410 info.starting_timestamp = StartingTimestamp::Timestamp(1);
1411 info.update_frequency = UpdateFrequency::LimitedPeriodic(0, 100);
1412 let err = info.validate().unwrap_err();
1413 assert!(err.contains("update frequency cannot be zero"));
1414 }
1415
1416 #[test]
1417 fn test_limited_periodic_end_timestamp_must_be_above_starting_timestamp() {
1418 let mut info = base_valid_oracle_info();
1419 info.starting_timestamp = StartingTimestamp::Timestamp(500);
1420 info.update_frequency = UpdateFrequency::LimitedPeriodic(300, 500);
1421 let err = info.validate().unwrap_err();
1422 assert!(err.contains(
1423 "end_timestamp of a LimitedPeriodic oracle should be above starting_timestamp"
1424 ));
1425 }
1426
1427 #[test]
1428 fn test_limited_periodic_end_timestamp_below_starting_timestamp_is_invalid() {
1429 let mut info = base_valid_oracle_info();
1430 info.starting_timestamp = StartingTimestamp::Timestamp(1000);
1431 info.update_frequency = UpdateFrequency::LimitedPeriodic(300, 900);
1432 let err = info.validate().unwrap_err();
1433 assert!(err.contains(
1434 "end_timestamp of a LimitedPeriodic oracle should be above starting_timestamp"
1435 ));
1436 }
1437
1438 #[test]
1439 fn test_target_oracles_cannot_be_empty() {
1440 let mut info = base_valid_oracle_info();
1441 info.target_oracles.clear();
1442 let err = info.validate().unwrap_err();
1443 assert!(err.contains("OracleTargets cannot be empty"));
1444 }
1445
1446 #[test]
1447 fn test_oracle_request_delay_bounds() {
1448 let mut info = base_valid_oracle_info();
1450 info.oracle_request_delay_ms = OracleDutyConfig::MIN_ORACLE_REQUEST_DELAY - 1;
1451 let err = info.validate().unwrap_err();
1452 assert!(err.contains(&format!(
1453 "oracle_request_delay cannot be below {}",
1454 OracleDutyConfig::MIN_ORACLE_REQUEST_DELAY
1455 )));
1456
1457 let mut info = base_valid_oracle_info();
1459 info.oracle_request_delay_ms = OracleDutyConfig::MAX_ORACLE_REQUEST_DELAY_MS + 1;
1460 let err = info.validate().unwrap_err();
1461 assert!(err.contains(&format!(
1462 "oracle_request_delay cannot be above {}",
1463 OracleDutyConfig::MAX_ORACLE_REQUEST_DELAY_MS
1464 )));
1465 }
1466
1467 #[test]
1468 fn test_validators_per_duty_cannot_be_zero() {
1469 let mut info = base_valid_oracle_info();
1470 info.validators_per_duty = 0;
1471 let err = info.validate().unwrap_err();
1472 assert!(err.contains("validators_per_duty cannot be 0"));
1473 }
1474
1475 #[test]
1476 fn test_validators_per_duty_too_high_results_in_too_low_output_size() {
1477 let mut info = base_valid_oracle_info();
1478 info.validators_per_duty = 1_000_000; let err = info.validate().unwrap_err();
1480 assert!(err.contains("validators_per_duty is too high"));
1481 }
1482
1483 #[test]
1484 fn test_compute_units_limit_checks() {
1485 let mut info = base_valid_oracle_info();
1487 info.compute_units_limit = Some(0);
1488 let err = info.validate().unwrap_err();
1489 assert!(err.contains("compute_usage_limit cannot be Some(0)"));
1490
1491 let mut info = base_valid_oracle_info();
1493 info.compute_units_limit = Some(MAX_COMPUTE_UNIT_LIMIT + 1);
1494 let err = info.validate().unwrap_err();
1495 assert!(err.contains(&format!(
1496 "compute_usage_limit cannot be above MAX_COMPUTE_UNIT_LIMIT={}",
1497 MAX_COMPUTE_UNIT_LIMIT
1498 )));
1499 }
1500
1501 #[test]
1502 fn test_heap_size_limit_checks() {
1503 let mut info = base_valid_oracle_info();
1505 info.heap_size_limit = Some(0);
1506 let err = info.validate().unwrap_err();
1507 assert!(err.contains("heap_size_limit cannot be Some(0)"));
1508
1509 let mut info = base_valid_oracle_info();
1511 info.heap_size_limit = Some(MAX_HEAP_FRAME_BYTES + 1);
1512 let err = info.validate().unwrap_err();
1513 assert!(err.contains(&format!(
1514 "heap_size_limit cannot be above MAX_HEAP_FRAME_BYTES={}",
1515 MAX_HEAP_FRAME_BYTES
1516 )));
1517 }
1518
1519 #[test]
1524 fn test_websocket_connect_operation_creation() {
1525 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1526 let op = WebSocketOperation::connect("wss://example.com/stream", oracle_id);
1527 assert!(op.is_connect());
1528 assert!(!op.is_read());
1529
1530 if let WebSocketOperation::Connect {
1531 url,
1532 oracle_id: op_oracle_id,
1533 } = op
1534 {
1535 assert_eq!(url.to_string(), "wss://example.com/stream");
1536 assert_eq!(op_oracle_id, oracle_id);
1537 } else {
1538 panic!("Expected Connect variant");
1539 }
1540 }
1541
1542 #[test]
1543 fn test_websocket_read_operation_creation() {
1544 let oracle_id = OracleId::new(Pubkey::default(), 42u64);
1545 let op = WebSocketOperation::read(oracle_id);
1546 assert!(op.is_read());
1547 assert!(!op.is_connect());
1548
1549 if let WebSocketOperation::Read {
1550 connection_oracle_id,
1551 mode,
1552 } = op
1553 {
1554 assert_eq!(connection_oracle_id, oracle_id);
1555 assert_eq!(mode, WebSocketReadMode::Latest);
1556 } else {
1557 panic!("Expected Read variant");
1558 }
1559 }
1560
1561 #[test]
1562 fn test_websocket_operation_serde_roundtrip_connect() {
1563 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1564 let op = WebSocketOperation::Connect {
1565 url: "wss://example.com/stream".into(),
1566 oracle_id,
1567 };
1568
1569 let json = serde_json::to_string(&op).expect("Failed to serialize");
1571 let deserialized: WebSocketOperation =
1573 serde_json::from_str(&json).expect("Failed to deserialize");
1574
1575 assert_eq!(op, deserialized);
1576 }
1577
1578 #[test]
1579 fn test_websocket_operation_serde_roundtrip_read() {
1580 let oracle_id = OracleId::new(Pubkey::default(), 123u64);
1581 let op = WebSocketOperation::Read {
1582 connection_oracle_id: oracle_id,
1583 mode: WebSocketReadMode::default(),
1584 };
1585
1586 let json = serde_json::to_string(&op).expect("Failed to serialize");
1588 let deserialized: WebSocketOperation =
1590 serde_json::from_str(&json).expect("Failed to deserialize");
1591
1592 assert_eq!(op, deserialized);
1593 }
1594
1595 #[test]
1596 fn test_target_oracle_websocket_serde_roundtrip() {
1597 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1598 let target = TargetOracle::WebSocket(WebSocketOperation::Connect {
1599 url: "wss://example.com/stream".into(),
1600 oracle_id,
1601 });
1602
1603 let json = serde_json::to_string(&target).expect("Failed to serialize");
1605 let deserialized: TargetOracle =
1607 serde_json::from_str(&json).expect("Failed to deserialize");
1608
1609 assert_eq!(target, deserialized);
1610 }
1611
1612 #[test]
1613 fn test_websocket_operation_display() {
1614 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1615 let connect_op = WebSocketOperation::Connect {
1616 url: "wss://example.com".into(),
1617 oracle_id,
1618 };
1619 let display = format!("{}", connect_op);
1620 assert!(display.contains("Connect"));
1621 assert!(display.contains("wss://example.com"));
1622
1623 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1624 let read_op = WebSocketOperation::Read {
1625 connection_oracle_id: oracle_id,
1626 mode: WebSocketReadMode::default(),
1627 };
1628 let display = format!("{}", read_op);
1629 assert!(display.contains("Read"));
1630 assert!(display.contains("connection_oracle_id"));
1631 }
1632
1633 #[test]
1634 fn test_websocket_connect_with_encrypted_url() {
1635 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1636 let op = WebSocketOperation::Connect {
1637 url: "enc://encrypted_websocket_url".into(),
1638 oracle_id,
1639 };
1640
1641 if let WebSocketOperation::Connect { url, .. } = &op {
1642 assert_eq!(url.to_string(), "enc://encrypted_websocket_url");
1643 }
1644
1645 let json = serde_json::to_string(&op).expect("Failed to serialize");
1647 let deserialized: WebSocketOperation =
1648 serde_json::from_str(&json).expect("Failed to deserialize");
1649 assert_eq!(op, deserialized);
1650 }
1651
1652 #[test]
1653 fn test_oracle_info_with_websocket_target() {
1654 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1655 let mut info = base_valid_oracle_info();
1656 info.target_oracles = vec![TargetOracle::WebSocket(WebSocketOperation::Connect {
1657 url: "wss://example.com/stream".into(),
1658 oracle_id,
1659 })];
1660
1661 assert!(info.validate().is_ok());
1663 }
1664
1665 #[test]
1666 fn test_websocket_operation_borsh_roundtrip_connect() {
1667 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1668 let op = WebSocketOperation::Connect {
1669 url: "wss://example.com/stream".into(),
1670 oracle_id,
1671 };
1672
1673 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1675 let deserialized: WebSocketOperation =
1677 borsh::from_slice(&bytes).expect("Failed to deserialize");
1678
1679 assert_eq!(op, deserialized);
1680 }
1681
1682 #[test]
1683 fn test_websocket_operation_borsh_roundtrip_read() {
1684 let oracle_id = OracleId::new(Pubkey::default(), 123u64);
1685 let op = WebSocketOperation::Read {
1686 connection_oracle_id: oracle_id,
1687 mode: WebSocketReadMode::default(),
1688 };
1689
1690 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1692 let deserialized: WebSocketOperation =
1694 borsh::from_slice(&bytes).expect("Failed to deserialize");
1695
1696 assert_eq!(op, deserialized);
1697 }
1698
1699 #[test]
1700 fn test_websocket_operation_borsh_roundtrip_encrypted_url() {
1701 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1702 let op = WebSocketOperation::Connect {
1703 url: "enc://encrypted_websocket_url".into(),
1704 oracle_id,
1705 };
1706
1707 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1709 let deserialized: WebSocketOperation =
1711 borsh::from_slice(&bytes).expect("Failed to deserialize");
1712
1713 assert_eq!(op, deserialized);
1714 }
1715
1716 #[test]
1717 fn test_websocket_operation_url_getter() {
1718 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1720 let connect_op = WebSocketOperation::Connect {
1721 url: "wss://example.com/stream".into(),
1722 oracle_id,
1723 };
1724 assert!(connect_op.url().is_some());
1725 assert_eq!(
1726 connect_op.url().unwrap().to_string(),
1727 "wss://example.com/stream"
1728 );
1729
1730 let oracle_id = OracleId::new(Pubkey::default(), 42u64);
1732 let read_op = WebSocketOperation::Read {
1733 connection_oracle_id: oracle_id,
1734 mode: WebSocketReadMode::default(),
1735 };
1736 assert!(read_op.url().is_none());
1737 }
1738
1739 #[test]
1740 fn test_websocket_operation_connection_oracle_id_getter() {
1741 let oracle_id = OracleId::new(Pubkey::default(), 42u64);
1743 let read_op = WebSocketOperation::Read {
1744 connection_oracle_id: oracle_id,
1745 mode: WebSocketReadMode::default(),
1746 };
1747 assert!(read_op.connection_oracle_id().is_some());
1748 assert_eq!(*read_op.connection_oracle_id().unwrap(), oracle_id);
1749
1750 let oracle_id = OracleId::new(Pubkey::default(), 1u64);
1752 let connect_op = WebSocketOperation::Connect {
1753 url: "wss://example.com/stream".into(),
1754 oracle_id,
1755 };
1756 assert!(connect_op.connection_oracle_id().is_none());
1757
1758 let oracle_id = OracleId::new(Pubkey::default(), 99u64);
1760 let send_op = WebSocketOperation::Send {
1761 connection_oracle_id: oracle_id,
1762 messages: vec![],
1763 };
1764 assert!(send_op.connection_oracle_id().is_some());
1765 assert_eq!(*send_op.connection_oracle_id().unwrap(), oracle_id);
1766 }
1767
1768 #[test]
1773 fn test_websocket_send_operation_creation() {
1774 let oracle_id = OracleId::new(Pubkey::default(), 5u64);
1775 let messages = vec![
1776 OracleValue::Plain("message1".to_string()),
1777 OracleValue::Plain("message2".to_string()),
1778 ];
1779 let op = WebSocketOperation::send(oracle_id, messages.clone());
1780 assert!(op.is_send());
1781 assert!(!op.is_connect());
1782 assert!(!op.is_read());
1783
1784 if let WebSocketOperation::Send {
1785 connection_oracle_id,
1786 messages: op_messages,
1787 } = op
1788 {
1789 assert_eq!(connection_oracle_id, oracle_id);
1790 assert_eq!(op_messages, messages);
1791 } else {
1792 panic!("Expected Send variant");
1793 }
1794 }
1795
1796 #[test]
1797 fn test_websocket_send_messages_getter() {
1798 let oracle_id = OracleId::new(Pubkey::default(), 10u64);
1799 let messages = vec![OracleValue::Plain("test".to_string())];
1800 let send_op = WebSocketOperation::Send {
1801 connection_oracle_id: oracle_id,
1802 messages: messages.clone(),
1803 };
1804
1805 assert!(send_op.messages().is_some());
1806 assert_eq!(send_op.messages().unwrap(), &messages[..]);
1807
1808 let connect_op = WebSocketOperation::Connect {
1810 url: "wss://example.com".into(),
1811 oracle_id,
1812 };
1813 assert!(connect_op.messages().is_none());
1814
1815 let read_op = WebSocketOperation::Read {
1816 connection_oracle_id: oracle_id,
1817 mode: WebSocketReadMode::default(),
1818 };
1819 assert!(read_op.messages().is_none());
1820 }
1821
1822 #[test]
1823 fn test_websocket_operation_serde_roundtrip_send() {
1824 let oracle_id = OracleId::new(Pubkey::default(), 7u64);
1825 let messages = vec![
1826 OracleValue::Plain("plain_message".to_string()),
1827 OracleValue::Encrypted("encrypted_data".to_string()),
1828 ];
1829 let op = WebSocketOperation::Send {
1830 connection_oracle_id: oracle_id,
1831 messages,
1832 };
1833
1834 let json = serde_json::to_string(&op).expect("Failed to serialize");
1836 let deserialized: WebSocketOperation =
1838 serde_json::from_str(&json).expect("Failed to deserialize");
1839
1840 assert_eq!(op, deserialized);
1841 }
1842
1843 #[test]
1844 fn test_websocket_operation_borsh_roundtrip_send() {
1845 let oracle_id = OracleId::new(Pubkey::default(), 8u64);
1846 let messages = vec![
1847 OracleValue::Plain("hello".to_string()),
1848 OracleValue::Plain("world".to_string()),
1849 ];
1850 let op = WebSocketOperation::Send {
1851 connection_oracle_id: oracle_id,
1852 messages,
1853 };
1854
1855 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1857 let deserialized: WebSocketOperation =
1859 borsh::from_slice(&bytes).expect("Failed to deserialize");
1860
1861 assert_eq!(op, deserialized);
1862 }
1863
1864 #[test]
1865 fn test_websocket_send_display() {
1866 let oracle_id = OracleId::new(Pubkey::default(), 20u64);
1867 let messages = vec![
1868 OracleValue::Plain("msg1".to_string()),
1869 OracleValue::Plain("msg2".to_string()),
1870 OracleValue::Plain("msg3".to_string()),
1871 ];
1872 let send_op = WebSocketOperation::Send {
1873 connection_oracle_id: oracle_id,
1874 messages,
1875 };
1876
1877 let display = format!("{}", send_op);
1878 assert!(display.contains("Send"));
1879 assert!(display.contains("connection_oracle_id"));
1880 assert!(display.contains("messages_count=3"));
1881 }
1882
1883 #[test]
1884 fn test_websocket_send_empty_messages() {
1885 let oracle_id = OracleId::new(Pubkey::default(), 15u64);
1886 let op = WebSocketOperation::send(oracle_id, vec![]);
1887
1888 assert!(op.is_send());
1889 assert_eq!(op.messages().unwrap().len(), 0);
1890
1891 let display = format!("{}", op);
1892 assert!(display.contains("messages_count=0"));
1893 }
1894
1895 #[test]
1896 fn test_websocket_send_with_encrypted_messages() {
1897 let oracle_id = OracleId::new(Pubkey::default(), 25u64);
1898 let messages = vec![
1899 OracleValue::Encrypted("encrypted1".to_string()),
1900 OracleValue::Encrypted("encrypted2".to_string()),
1901 ];
1902 let op = WebSocketOperation::Send {
1903 connection_oracle_id: oracle_id,
1904 messages: messages.clone(),
1905 };
1906
1907 let json = serde_json::to_string(&op).expect("Failed to serialize");
1909 let deserialized: WebSocketOperation =
1910 serde_json::from_str(&json).expect("Failed to deserialize");
1911 assert_eq!(op, deserialized);
1912
1913 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1915 let deserialized: WebSocketOperation =
1916 borsh::from_slice(&bytes).expect("Failed to deserialize");
1917 assert_eq!(op, deserialized);
1918 }
1919
1920 #[test]
1921 fn test_target_oracle_websocket_send_serde_roundtrip() {
1922 let oracle_id = OracleId::new(Pubkey::default(), 30u64);
1923 let messages = vec![OracleValue::Plain("test_message".to_string())];
1924 let target = TargetOracle::WebSocket(WebSocketOperation::Send {
1925 connection_oracle_id: oracle_id,
1926 messages,
1927 });
1928
1929 let json = serde_json::to_string(&target).expect("Failed to serialize");
1931 let deserialized: TargetOracle =
1933 serde_json::from_str(&json).expect("Failed to deserialize");
1934
1935 assert_eq!(target, deserialized);
1936 }
1937
1938 #[test]
1943 fn test_websocket_close_operation_creation() {
1944 let oracle_id = OracleId::new(Pubkey::default(), 50u64);
1945 let op = WebSocketOperation::close(oracle_id);
1946 assert!(op.is_close());
1947 assert!(!op.is_connect());
1948 assert!(!op.is_read());
1949 assert!(!op.is_send());
1950
1951 if let WebSocketOperation::Close {
1952 connection_oracle_id,
1953 } = op
1954 {
1955 assert_eq!(connection_oracle_id, oracle_id);
1956 } else {
1957 panic!("Expected Close variant");
1958 }
1959 }
1960
1961 #[test]
1962 fn test_websocket_close_connection_oracle_id_getter() {
1963 let oracle_id = OracleId::new(Pubkey::default(), 55u64);
1964 let close_op = WebSocketOperation::Close {
1965 connection_oracle_id: oracle_id,
1966 };
1967 assert!(close_op.connection_oracle_id().is_some());
1968 assert_eq!(*close_op.connection_oracle_id().unwrap(), oracle_id);
1969 }
1970
1971 #[test]
1972 fn test_websocket_operation_serde_roundtrip_close() {
1973 let oracle_id = OracleId::new(Pubkey::default(), 60u64);
1974 let op = WebSocketOperation::Close {
1975 connection_oracle_id: oracle_id,
1976 };
1977
1978 let json = serde_json::to_string(&op).expect("Failed to serialize");
1980 let deserialized: WebSocketOperation =
1982 serde_json::from_str(&json).expect("Failed to deserialize");
1983
1984 assert_eq!(op, deserialized);
1985 }
1986
1987 #[test]
1988 fn test_websocket_operation_borsh_roundtrip_close() {
1989 let oracle_id = OracleId::new(Pubkey::default(), 65u64);
1990 let op = WebSocketOperation::Close {
1991 connection_oracle_id: oracle_id,
1992 };
1993
1994 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
1996 let deserialized: WebSocketOperation =
1998 borsh::from_slice(&bytes).expect("Failed to deserialize");
1999
2000 assert_eq!(op, deserialized);
2001 }
2002
2003 #[test]
2004 fn test_websocket_close_display() {
2005 let oracle_id = OracleId::new(Pubkey::default(), 70u64);
2006 let close_op = WebSocketOperation::Close {
2007 connection_oracle_id: oracle_id,
2008 };
2009
2010 let display = format!("{}", close_op);
2011 assert!(display.contains("Close"));
2012 assert!(display.contains("connection_oracle_id"));
2013 assert!(display.contains(&oracle_id.to_string()));
2014 }
2015
2016 #[test]
2017 fn test_target_oracle_websocket_close_serde_roundtrip() {
2018 let oracle_id = OracleId::new(Pubkey::default(), 75u64);
2019 let target = TargetOracle::WebSocket(WebSocketOperation::Close {
2020 connection_oracle_id: oracle_id,
2021 });
2022
2023 let json = serde_json::to_string(&target).expect("Failed to serialize");
2025 let deserialized: TargetOracle =
2027 serde_json::from_str(&json).expect("Failed to deserialize");
2028
2029 assert_eq!(target, deserialized);
2030 }
2031}