1use serde::{Deserialize, Serialize};
4
5use super::CryptoHash;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum SyncCheckpoint {
11 Genesis,
13 EarliestAvailable,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum BlockReference {
22 Finality(Finality),
24 Height(u64),
26 Hash(CryptoHash),
28 SyncCheckpoint(SyncCheckpoint),
30}
31
32impl Default for BlockReference {
33 fn default() -> Self {
34 Self::Finality(Finality::Final)
35 }
36}
37
38impl BlockReference {
39 pub fn final_() -> Self {
41 Self::Finality(Finality::Final)
42 }
43
44 pub fn optimistic() -> Self {
46 Self::Finality(Finality::Optimistic)
47 }
48
49 pub fn near_final() -> Self {
51 Self::Finality(Finality::NearFinal)
52 }
53
54 pub fn at_height(height: u64) -> Self {
56 Self::Height(height)
57 }
58
59 pub fn at_hash(hash: CryptoHash) -> Self {
61 Self::Hash(hash)
62 }
63
64 pub fn genesis() -> Self {
66 Self::SyncCheckpoint(SyncCheckpoint::Genesis)
67 }
68
69 pub fn earliest_available() -> Self {
71 Self::SyncCheckpoint(SyncCheckpoint::EarliestAvailable)
72 }
73
74 pub fn to_rpc_params(&self) -> serde_json::Value {
76 match self {
77 BlockReference::Finality(f) => {
78 serde_json::json!({ "finality": f.as_str() })
79 }
80 BlockReference::Height(h) => {
81 serde_json::json!({ "block_id": *h })
82 }
83 BlockReference::Hash(h) => {
84 serde_json::json!({ "block_id": h.to_string() })
85 }
86 BlockReference::SyncCheckpoint(cp) => {
87 let cp_str = match cp {
88 SyncCheckpoint::Genesis => "genesis",
89 SyncCheckpoint::EarliestAvailable => "earliest_available",
90 };
91 serde_json::json!({ "sync_checkpoint": cp_str })
92 }
93 }
94 }
95}
96
97impl From<Finality> for BlockReference {
98 fn from(f: Finality) -> Self {
99 Self::Finality(f)
100 }
101}
102
103impl From<u64> for BlockReference {
104 fn from(height: u64) -> Self {
105 Self::Height(height)
106 }
107}
108
109impl From<CryptoHash> for BlockReference {
110 fn from(hash: CryptoHash) -> Self {
111 Self::Hash(hash)
112 }
113}
114
115#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(rename_all = "lowercase")]
118pub enum Finality {
119 Optimistic,
121 #[serde(rename = "near-final")]
123 NearFinal,
124 #[default]
126 Final,
127}
128
129impl Finality {
130 pub fn as_str(&self) -> &'static str {
132 match self {
133 Finality::Optimistic => "optimistic",
134 Finality::NearFinal => "near-final",
135 Finality::Final => "final",
136 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
142#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
143pub enum TxExecutionStatus {
144 None,
146 Included,
148 #[default]
150 ExecutedOptimistic,
151 IncludedFinal,
153 Executed,
155 Final,
157}
158
159impl TxExecutionStatus {
160 pub fn as_str(&self) -> &'static str {
162 match self {
163 Self::None => "NONE",
164 Self::Included => "INCLUDED",
165 Self::ExecutedOptimistic => "EXECUTED_OPTIMISTIC",
166 Self::IncludedFinal => "INCLUDED_FINAL",
167 Self::Executed => "EXECUTED",
168 Self::Final => "FINAL",
169 }
170 }
171
172 pub fn is_executed(&self) -> bool {
176 matches!(
177 self,
178 Self::ExecutedOptimistic | Self::Executed | Self::Final
179 )
180 }
181
182 pub fn is_block_final(&self) -> bool {
186 matches!(self, Self::IncludedFinal | Self::Executed | Self::Final)
187 }
188
189 pub fn is_final(&self) -> bool {
191 matches!(self, Self::Final)
192 }
193}
194
195impl PartialOrd for TxExecutionStatus {
212 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
213 use TxExecutionStatus::*;
214 use std::cmp::Ordering::*;
215
216 if self == other {
217 return Some(Equal);
218 }
219
220 fn axes(s: &TxExecutionStatus) -> (u8, u8) {
223 match s {
224 None => (0, 0),
225 Included => (1, 1),
226 ExecutedOptimistic => (2, 1),
227 IncludedFinal => (1, 2),
228 Executed => (2, 2),
229 Final => (3, 3),
230 }
231 }
232
233 let (ex_a, fin_a) = axes(self);
234 let (ex_b, fin_b) = axes(other);
235
236 match (ex_a.cmp(&ex_b), fin_a.cmp(&fin_b)) {
237 (Equal, Equal) => Some(Equal),
238 (Less | Equal, Less | Equal) => Some(Less),
239 (Greater | Equal, Greater | Equal) => Some(Greater),
240 _ => Option::None, }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_block_reference_rpc_params() {
251 let final_ref = BlockReference::final_();
252 let params = final_ref.to_rpc_params();
253 assert_eq!(params["finality"], "final");
254
255 let height_ref = BlockReference::Height(12345);
256 let params = height_ref.to_rpc_params();
257 assert_eq!(params["block_id"], 12345);
258 }
259
260 #[test]
261 fn test_finality_as_str() {
262 assert_eq!(Finality::Final.as_str(), "final");
263 assert_eq!(Finality::Optimistic.as_str(), "optimistic");
264 assert_eq!(Finality::NearFinal.as_str(), "near-final");
265 }
266
267 #[test]
268 fn test_tx_execution_status_as_str() {
269 assert_eq!(TxExecutionStatus::Final.as_str(), "FINAL");
270 assert_eq!(
271 TxExecutionStatus::ExecutedOptimistic.as_str(),
272 "EXECUTED_OPTIMISTIC"
273 );
274 }
275
276 #[test]
277 fn test_block_reference_hash_to_rpc_params() {
278 let hash = CryptoHash::hash(b"test block");
279 let block_ref = BlockReference::at_hash(hash);
280 let params = block_ref.to_rpc_params();
281 assert_eq!(params["block_id"], hash.to_string());
282 }
283
284 #[test]
285 fn test_block_reference_constructors() {
286 let final_ref = BlockReference::final_();
288 assert!(matches!(
289 final_ref,
290 BlockReference::Finality(Finality::Final)
291 ));
292
293 let optimistic_ref = BlockReference::optimistic();
294 assert!(matches!(
295 optimistic_ref,
296 BlockReference::Finality(Finality::Optimistic)
297 ));
298
299 let near_final_ref = BlockReference::near_final();
300 assert!(matches!(
301 near_final_ref,
302 BlockReference::Finality(Finality::NearFinal)
303 ));
304
305 let height_ref = BlockReference::at_height(12345);
306 assert!(matches!(height_ref, BlockReference::Height(12345)));
307
308 let hash = CryptoHash::hash(b"test");
309 let hash_ref = BlockReference::at_hash(hash);
310 assert!(matches!(hash_ref, BlockReference::Hash(_)));
311 }
312
313 #[test]
314 fn test_block_reference_default() {
315 let default = BlockReference::default();
316 assert_eq!(default, BlockReference::Finality(Finality::Final));
317 }
318
319 #[test]
320 fn test_block_reference_from_finality() {
321 let block_ref: BlockReference = Finality::Optimistic.into();
322 assert_eq!(block_ref, BlockReference::Finality(Finality::Optimistic));
323 }
324
325 #[test]
326 fn test_block_reference_from_height() {
327 let block_ref: BlockReference = 99999u64.into();
328 assert_eq!(block_ref, BlockReference::Height(99999));
329 }
330
331 #[test]
332 fn test_block_reference_from_hash() {
333 let hash = CryptoHash::hash(b"block");
334 let block_ref: BlockReference = hash.into();
335 assert_eq!(block_ref, BlockReference::Hash(hash));
336 }
337
338 #[test]
339 fn test_finality_default() {
340 let default = Finality::default();
341 assert_eq!(default, Finality::Final);
342 }
343
344 #[test]
345 fn test_tx_execution_status_default() {
346 let default = TxExecutionStatus::default();
347 assert_eq!(default, TxExecutionStatus::ExecutedOptimistic);
348 }
349
350 #[test]
351 fn test_tx_execution_status_all_variants() {
352 assert_eq!(TxExecutionStatus::None.as_str(), "NONE");
353 assert_eq!(TxExecutionStatus::Included.as_str(), "INCLUDED");
354 assert_eq!(
355 TxExecutionStatus::ExecutedOptimistic.as_str(),
356 "EXECUTED_OPTIMISTIC"
357 );
358 assert_eq!(TxExecutionStatus::IncludedFinal.as_str(), "INCLUDED_FINAL");
359 assert_eq!(TxExecutionStatus::Executed.as_str(), "EXECUTED");
360 assert_eq!(TxExecutionStatus::Final.as_str(), "FINAL");
361 }
362
363 #[test]
364 fn test_finality_serde_roundtrip() {
365 for finality in [Finality::Optimistic, Finality::NearFinal, Finality::Final] {
367 let json = serde_json::to_string(&finality).unwrap();
368 let parsed: Finality = serde_json::from_str(&json).unwrap();
369 assert_eq!(finality, parsed);
370 }
371 }
372
373 #[test]
374 fn test_tx_execution_status_serde_roundtrip() {
375 for status in [
376 TxExecutionStatus::None,
377 TxExecutionStatus::Included,
378 TxExecutionStatus::ExecutedOptimistic,
379 TxExecutionStatus::IncludedFinal,
380 TxExecutionStatus::Executed,
381 TxExecutionStatus::Final,
382 ] {
383 let json = serde_json::to_string(&status).unwrap();
384 let parsed: TxExecutionStatus = serde_json::from_str(&json).unwrap();
385 assert_eq!(status, parsed);
386 }
387 }
388
389 #[test]
390 fn test_block_reference_clone_and_eq() {
391 let original = BlockReference::Height(12345);
392 let cloned = original.clone();
393 assert_eq!(original, cloned);
394 }
395
396 #[test]
397 fn test_finality_clone_and_copy() {
398 let f1 = Finality::Optimistic;
399 let f2 = f1; #[allow(clippy::clone_on_copy)]
401 let f3 = f1.clone(); assert_eq!(f1, f2);
403 assert_eq!(f1, f3);
404 }
405
406 #[test]
407 fn test_sync_checkpoint_constructors() {
408 let genesis = BlockReference::genesis();
409 assert!(matches!(
410 genesis,
411 BlockReference::SyncCheckpoint(SyncCheckpoint::Genesis)
412 ));
413
414 let earliest = BlockReference::earliest_available();
415 assert!(matches!(
416 earliest,
417 BlockReference::SyncCheckpoint(SyncCheckpoint::EarliestAvailable)
418 ));
419 }
420
421 #[test]
422 fn test_sync_checkpoint_rpc_params() {
423 let genesis = BlockReference::genesis();
424 let params = genesis.to_rpc_params();
425 assert_eq!(params["sync_checkpoint"], "genesis");
426
427 let earliest = BlockReference::earliest_available();
428 let params = earliest.to_rpc_params();
429 assert_eq!(params["sync_checkpoint"], "earliest_available");
430 }
431
432 #[test]
433 fn test_sync_checkpoint_serde_roundtrip() {
434 for cp in [SyncCheckpoint::Genesis, SyncCheckpoint::EarliestAvailable] {
435 let json = serde_json::to_string(&cp).unwrap();
436 let parsed: SyncCheckpoint = serde_json::from_str(&json).unwrap();
437 assert_eq!(cp, parsed);
438 }
439 }
440
441 #[test]
442 fn test_tx_execution_status_is_executed() {
443 assert!(!TxExecutionStatus::None.is_executed());
444 assert!(!TxExecutionStatus::Included.is_executed());
445 assert!(TxExecutionStatus::ExecutedOptimistic.is_executed());
446 assert!(!TxExecutionStatus::IncludedFinal.is_executed());
447 assert!(TxExecutionStatus::Executed.is_executed());
448 assert!(TxExecutionStatus::Final.is_executed());
449 }
450
451 #[test]
452 fn test_tx_execution_status_is_block_final() {
453 assert!(!TxExecutionStatus::None.is_block_final());
454 assert!(!TxExecutionStatus::Included.is_block_final());
455 assert!(!TxExecutionStatus::ExecutedOptimistic.is_block_final());
456 assert!(TxExecutionStatus::IncludedFinal.is_block_final());
457 assert!(TxExecutionStatus::Executed.is_block_final());
458 assert!(TxExecutionStatus::Final.is_block_final());
459 }
460
461 #[test]
462 fn test_tx_execution_status_is_final() {
463 assert!(!TxExecutionStatus::None.is_final());
464 assert!(!TxExecutionStatus::Included.is_final());
465 assert!(!TxExecutionStatus::ExecutedOptimistic.is_final());
466 assert!(!TxExecutionStatus::IncludedFinal.is_final());
467 assert!(!TxExecutionStatus::Executed.is_final());
468 assert!(TxExecutionStatus::Final.is_final());
469 }
470
471 #[test]
472 fn test_tx_execution_status_partial_ord_linear() {
473 assert!(TxExecutionStatus::None < TxExecutionStatus::Included);
475 assert!(TxExecutionStatus::Included < TxExecutionStatus::Executed);
476 assert!(TxExecutionStatus::Executed < TxExecutionStatus::Final);
477 assert!(TxExecutionStatus::None < TxExecutionStatus::Final);
478 }
479
480 #[test]
481 fn test_tx_execution_status_partial_ord_branches() {
482 assert!(TxExecutionStatus::ExecutedOptimistic > TxExecutionStatus::Included);
484 assert!(TxExecutionStatus::IncludedFinal > TxExecutionStatus::Included);
485 assert!(TxExecutionStatus::ExecutedOptimistic < TxExecutionStatus::Executed);
487 assert!(TxExecutionStatus::IncludedFinal < TxExecutionStatus::Executed);
488 }
489
490 #[test]
491 fn test_tx_execution_status_partial_ord_incomparable() {
492 assert_eq!(
494 TxExecutionStatus::ExecutedOptimistic.partial_cmp(&TxExecutionStatus::IncludedFinal),
495 Option::None,
496 );
497 assert_eq!(
498 TxExecutionStatus::IncludedFinal.partial_cmp(&TxExecutionStatus::ExecutedOptimistic),
499 Option::None,
500 );
501 assert_ne!(
503 TxExecutionStatus::ExecutedOptimistic,
504 TxExecutionStatus::IncludedFinal
505 );
506 }
507}