1use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
22use sp_api::ApiError;
23use sp_version::RuntimeVersion;
24use std::collections::BTreeMap;
25
26use crate::common::events::StorageResult;
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct ErrorEvent {
32 pub error: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(rename_all = "camelCase")]
43pub struct RuntimeVersionEvent {
44 pub spec: ChainHeadRuntimeVersion,
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct ChainHeadRuntimeVersion {
53 pub spec_name: String,
55 pub impl_name: String,
57 pub spec_version: u32,
59 pub impl_version: u32,
61 pub apis: BTreeMap<String, u32>,
63 pub transaction_version: u32,
65}
66
67impl From<RuntimeVersion> for ChainHeadRuntimeVersion {
68 fn from(val: RuntimeVersion) -> Self {
69 Self {
70 spec_name: val.spec_name.into(),
71 impl_name: val.impl_name.into(),
72 spec_version: val.spec_version,
73 impl_version: val.impl_version,
74 apis: val
75 .apis
76 .into_iter()
77 .map(|(api, version)| (sp_core::bytes::to_hex(api, false), *version))
78 .collect(),
79 transaction_version: val.transaction_version,
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88#[serde(tag = "type")]
89pub enum RuntimeEvent {
90 Valid(RuntimeVersionEvent),
92 Invalid(ErrorEvent),
94}
95
96impl From<ApiError> for RuntimeEvent {
97 fn from(err: ApiError) -> Self {
98 RuntimeEvent::Invalid(ErrorEvent { error: format!("Api error: {}", err) })
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct Initialized<Hash> {
114 pub finalized_block_hashes: Vec<Hash>,
116 pub finalized_block_runtime: Option<RuntimeEvent>,
123 #[serde(default)]
126 pub(crate) with_runtime: bool,
127}
128
129impl<Hash: Serialize> Serialize for Initialized<Hash> {
130 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
133 where
134 S: Serializer,
135 {
136 if self.with_runtime {
137 let mut state = serializer.serialize_struct("Initialized", 2)?;
138 state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?;
139 state.serialize_field("finalizedBlockRuntime", &self.finalized_block_runtime)?;
140 state.end()
141 } else {
142 let mut state = serializer.serialize_struct("Initialized", 1)?;
143 state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?;
144 state.end()
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct NewBlock<Hash> {
153 pub block_hash: Hash,
155 pub parent_block_hash: Hash,
157 pub new_runtime: Option<RuntimeEvent>,
164 #[serde(default)]
167 pub(crate) with_runtime: bool,
168}
169
170impl<Hash: Serialize> Serialize for NewBlock<Hash> {
171 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174 where
175 S: Serializer,
176 {
177 if self.with_runtime {
178 let mut state = serializer.serialize_struct("NewBlock", 3)?;
179 state.serialize_field("blockHash", &self.block_hash)?;
180 state.serialize_field("parentBlockHash", &self.parent_block_hash)?;
181 state.serialize_field("newRuntime", &self.new_runtime)?;
182 state.end()
183 } else {
184 let mut state = serializer.serialize_struct("NewBlock", 2)?;
185 state.serialize_field("blockHash", &self.block_hash)?;
186 state.serialize_field("parentBlockHash", &self.parent_block_hash)?;
187 state.end()
188 }
189 }
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194#[serde(rename_all = "camelCase")]
195pub struct BestBlockChanged<Hash> {
196 pub best_block_hash: Hash,
198}
199
200#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202#[serde(rename_all = "camelCase")]
203pub struct Finalized<Hash> {
204 pub finalized_block_hashes: Vec<Hash>,
206 pub pruned_block_hashes: Vec<Hash>,
208}
209
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct OperationId {
214 pub operation_id: String,
216}
217
218#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct OperationBodyDone {
222 pub operation_id: String,
224 pub value: Vec<String>,
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct OperationCallDone {
232 pub operation_id: String,
234 pub output: String,
236}
237
238#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct OperationStorageItems {
242 pub operation_id: String,
244 pub items: Vec<StorageResult>,
246}
247
248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
250#[serde(rename_all = "camelCase")]
251pub struct OperationError {
252 pub operation_id: String,
254 pub error: String,
256}
257
258#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
281#[serde(rename_all = "camelCase")]
282#[serde(tag = "event")]
283pub enum FollowEvent<Hash> {
284 Initialized(Initialized<Hash>),
288 NewBlock(NewBlock<Hash>),
290 BestBlockChanged(BestBlockChanged<Hash>),
292 Finalized(Finalized<Hash>),
294 OperationBodyDone(OperationBodyDone),
296 OperationCallDone(OperationCallDone),
298 OperationStorageItems(OperationStorageItems),
300 OperationWaitingForContinue(OperationId),
303 OperationStorageDone(OperationId),
305 OperationInaccessible(OperationId),
309 OperationError(OperationError),
313 Stop,
316}
317
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
320#[serde(rename_all = "camelCase")]
321#[serde(tag = "result")]
322pub enum MethodResponse {
323 Started(MethodResponseStarted),
325 LimitReached,
327}
328
329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
331#[serde(rename_all = "camelCase")]
332pub struct MethodResponseStarted {
333 pub operation_id: String,
335 #[serde(skip_serializing_if = "Option::is_none")]
337 #[serde(default)]
338 pub discarded_items: Option<usize>,
339}
340
341#[cfg(test)]
342mod tests {
343 use crate::common::events::StorageResultType;
344
345 use super::*;
346
347 #[test]
348 fn follow_initialized_event_no_updates() {
349 let event: FollowEvent<String> = FollowEvent::Initialized(Initialized {
351 finalized_block_hashes: vec!["0x1".into()],
352 finalized_block_runtime: None,
353 with_runtime: false,
354 });
355
356 let ser = serde_json::to_string(&event).unwrap();
357 let exp = r#"{"event":"initialized","finalizedBlockHashes":["0x1"]}"#;
358 assert_eq!(ser, exp);
359
360 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
361 assert_eq!(event_dec, event);
362 }
363
364 #[test]
365 fn follow_initialized_event_with_updates() {
366 let runtime = RuntimeVersion {
368 spec_name: "ABC".into(),
369 impl_name: "Impl".into(),
370 spec_version: 1,
371 ..Default::default()
372 };
373
374 let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() });
375 let mut initialized = Initialized {
376 finalized_block_hashes: vec!["0x1".into()],
377 finalized_block_runtime: Some(runtime_event),
378 with_runtime: true,
379 };
380 let event: FollowEvent<String> = FollowEvent::Initialized(initialized.clone());
381
382 let ser = serde_json::to_string(&event).unwrap();
383 let exp = concat!(
384 r#"{"event":"initialized","finalizedBlockHashes":["0x1"],"#,
385 r#""finalizedBlockRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#,
386 r#""specVersion":1,"implVersion":0,"apis":{},"transactionVersion":0}}}"#,
387 );
388 assert_eq!(ser, exp);
389
390 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
391 initialized.with_runtime = false;
393 assert!(matches!(
394 event_dec, FollowEvent::Initialized(ref dec) if dec == &initialized
395 ));
396 }
397
398 #[test]
399 fn follow_new_block_event_no_updates() {
400 let event: FollowEvent<String> = FollowEvent::NewBlock(NewBlock {
402 block_hash: "0x1".into(),
403 parent_block_hash: "0x2".into(),
404 new_runtime: None,
405 with_runtime: false,
406 });
407
408 let ser = serde_json::to_string(&event).unwrap();
409 let exp = r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2"}"#;
410 assert_eq!(ser, exp);
411
412 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
413 assert_eq!(event_dec, event);
414 }
415
416 #[test]
417 fn follow_new_block_event_with_updates() {
418 let runtime = RuntimeVersion {
420 spec_name: "ABC".into(),
421 impl_name: "Impl".into(),
422 spec_version: 1,
423 apis: vec![([0, 0, 0, 0, 0, 0, 0, 0], 2), ([1, 0, 0, 0, 0, 0, 0, 0], 3)].into(),
424 ..Default::default()
425 };
426
427 let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() });
428 let mut new_block = NewBlock {
429 block_hash: "0x1".into(),
430 parent_block_hash: "0x2".into(),
431 new_runtime: Some(runtime_event),
432 with_runtime: true,
433 };
434
435 let event: FollowEvent<String> = FollowEvent::NewBlock(new_block.clone());
436
437 let ser = serde_json::to_string(&event).unwrap();
438 let exp = concat!(
439 r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2","#,
440 r#""newRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#,
441 r#""specVersion":1,"implVersion":0,"apis":{"0x0000000000000000":2,"0x0100000000000000":3},"transactionVersion":0}}}"#,
442 );
443 assert_eq!(ser, exp);
444
445 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
446 new_block.with_runtime = false;
448 assert!(matches!(
449 event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
450 ));
451
452 let mut new_block = NewBlock {
454 block_hash: "0x1".into(),
455 parent_block_hash: "0x2".into(),
456 new_runtime: None,
457 with_runtime: true,
458 };
459 let event: FollowEvent<String> = FollowEvent::NewBlock(new_block.clone());
460
461 let ser = serde_json::to_string(&event).unwrap();
462 let exp =
463 r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2","newRuntime":null}"#;
464 assert_eq!(ser, exp);
465 new_block.with_runtime = false;
466 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
467 assert!(matches!(
468 event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
469 ));
470 }
471
472 #[test]
473 fn follow_best_block_changed_event() {
474 let event: FollowEvent<String> =
475 FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash: "0x1".into() });
476
477 let ser = serde_json::to_string(&event).unwrap();
478 let exp = r#"{"event":"bestBlockChanged","bestBlockHash":"0x1"}"#;
479 assert_eq!(ser, exp);
480
481 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
482 assert_eq!(event_dec, event);
483 }
484
485 #[test]
486 fn follow_finalized_event() {
487 let event: FollowEvent<String> = FollowEvent::Finalized(Finalized {
488 finalized_block_hashes: vec!["0x1".into()],
489 pruned_block_hashes: vec!["0x2".into()],
490 });
491
492 let ser = serde_json::to_string(&event).unwrap();
493 let exp =
494 r#"{"event":"finalized","finalizedBlockHashes":["0x1"],"prunedBlockHashes":["0x2"]}"#;
495 assert_eq!(ser, exp);
496
497 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
498 assert_eq!(event_dec, event);
499 }
500
501 #[test]
502 fn follow_op_body_event() {
503 let event: FollowEvent<String> = FollowEvent::OperationBodyDone(OperationBodyDone {
504 operation_id: "123".into(),
505 value: vec!["0x1".into()],
506 });
507
508 let ser = serde_json::to_string(&event).unwrap();
509 let exp = r#"{"event":"operationBodyDone","operationId":"123","value":["0x1"]}"#;
510 assert_eq!(ser, exp);
511
512 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
513 assert_eq!(event_dec, event);
514 }
515
516 #[test]
517 fn follow_op_call_event() {
518 let event: FollowEvent<String> = FollowEvent::OperationCallDone(OperationCallDone {
519 operation_id: "123".into(),
520 output: "0x1".into(),
521 });
522
523 let ser = serde_json::to_string(&event).unwrap();
524 let exp = r#"{"event":"operationCallDone","operationId":"123","output":"0x1"}"#;
525 assert_eq!(ser, exp);
526
527 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
528 assert_eq!(event_dec, event);
529 }
530
531 #[test]
532 fn follow_op_storage_items_event() {
533 let event: FollowEvent<String> =
534 FollowEvent::OperationStorageItems(OperationStorageItems {
535 operation_id: "123".into(),
536 items: vec![StorageResult {
537 key: "0x1".into(),
538 result: StorageResultType::Value("0x123".to_string()),
539 child_trie_key: None,
540 }],
541 });
542
543 let ser = serde_json::to_string(&event).unwrap();
544 let exp = r#"{"event":"operationStorageItems","operationId":"123","items":[{"key":"0x1","value":"0x123"}]}"#;
545 assert_eq!(ser, exp);
546
547 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
548 assert_eq!(event_dec, event);
549 }
550
551 #[test]
552 fn follow_op_wait_event() {
553 let event: FollowEvent<String> =
554 FollowEvent::OperationWaitingForContinue(OperationId { operation_id: "123".into() });
555
556 let ser = serde_json::to_string(&event).unwrap();
557 let exp = r#"{"event":"operationWaitingForContinue","operationId":"123"}"#;
558 assert_eq!(ser, exp);
559
560 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
561 assert_eq!(event_dec, event);
562 }
563
564 #[test]
565 fn follow_op_storage_done_event() {
566 let event: FollowEvent<String> =
567 FollowEvent::OperationStorageDone(OperationId { operation_id: "123".into() });
568
569 let ser = serde_json::to_string(&event).unwrap();
570 let exp = r#"{"event":"operationStorageDone","operationId":"123"}"#;
571 assert_eq!(ser, exp);
572
573 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
574 assert_eq!(event_dec, event);
575 }
576
577 #[test]
578 fn follow_op_inaccessible_event() {
579 let event: FollowEvent<String> =
580 FollowEvent::OperationInaccessible(OperationId { operation_id: "123".into() });
581
582 let ser = serde_json::to_string(&event).unwrap();
583 let exp = r#"{"event":"operationInaccessible","operationId":"123"}"#;
584 assert_eq!(ser, exp);
585
586 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
587 assert_eq!(event_dec, event);
588 }
589
590 #[test]
591 fn follow_op_error_event() {
592 let event: FollowEvent<String> = FollowEvent::OperationError(OperationError {
593 operation_id: "123".into(),
594 error: "reason".into(),
595 });
596
597 let ser = serde_json::to_string(&event).unwrap();
598 let exp = r#"{"event":"operationError","operationId":"123","error":"reason"}"#;
599 assert_eq!(ser, exp);
600
601 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
602 assert_eq!(event_dec, event);
603 }
604
605 #[test]
606 fn follow_stop_event() {
607 let event: FollowEvent<String> = FollowEvent::Stop;
608
609 let ser = serde_json::to_string(&event).unwrap();
610 let exp = r#"{"event":"stop"}"#;
611 assert_eq!(ser, exp);
612
613 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
614 assert_eq!(event_dec, event);
615 }
616
617 #[test]
618 fn method_response() {
619 let event = MethodResponse::Started(MethodResponseStarted {
621 operation_id: "123".into(),
622 discarded_items: None,
623 });
624
625 let ser = serde_json::to_string(&event).unwrap();
626 let exp = r#"{"result":"started","operationId":"123"}"#;
627 assert_eq!(ser, exp);
628
629 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
630 assert_eq!(event_dec, event);
631
632 let event = MethodResponse::Started(MethodResponseStarted {
634 operation_id: "123".into(),
635 discarded_items: Some(1),
636 });
637
638 let ser = serde_json::to_string(&event).unwrap();
639 let exp = r#"{"result":"started","operationId":"123","discardedItems":1}"#;
640 assert_eq!(ser, exp);
641
642 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
643 assert_eq!(event_dec, event);
644
645 let event = MethodResponse::LimitReached;
647
648 let ser = serde_json::to_string(&event).unwrap();
649 let exp = r#"{"result":"limitReached"}"#;
650 assert_eq!(ser, exp);
651
652 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
653 assert_eq!(event_dec, event);
654 }
655}