1use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
10use std::collections::BTreeMap;
11use subsoil::api::ApiError;
12use subsoil::version::RuntimeVersion;
13
14use crate::v2::common::events::StorageResult;
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct ErrorEvent {
20 pub error: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
30#[serde(rename_all = "camelCase")]
31pub struct RuntimeVersionEvent {
32 pub spec: ChainHeadRuntimeVersion,
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct ChainHeadRuntimeVersion {
41 pub spec_name: String,
43 pub impl_name: String,
45 pub spec_version: u32,
47 pub impl_version: u32,
49 pub apis: BTreeMap<String, u32>,
51 pub transaction_version: u32,
53}
54
55impl From<RuntimeVersion> for ChainHeadRuntimeVersion {
56 fn from(val: RuntimeVersion) -> Self {
57 Self {
58 spec_name: val.spec_name.into(),
59 impl_name: val.impl_name.into(),
60 spec_version: val.spec_version,
61 impl_version: val.impl_version,
62 apis: val
63 .apis
64 .into_iter()
65 .map(|(api, version)| (subsoil::core::bytes::to_hex(api, false), *version))
66 .collect(),
67 transaction_version: val.transaction_version,
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76#[serde(tag = "type")]
77pub enum RuntimeEvent {
78 Valid(RuntimeVersionEvent),
80 Invalid(ErrorEvent),
82}
83
84impl From<ApiError> for RuntimeEvent {
85 fn from(err: ApiError) -> Self {
86 RuntimeEvent::Invalid(ErrorEvent { error: format!("Api error: {}", err) })
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct Initialized<Hash> {
102 pub finalized_block_hashes: Vec<Hash>,
104 pub finalized_block_runtime: Option<RuntimeEvent>,
111 #[serde(default)]
114 pub(crate) with_runtime: bool,
115}
116
117impl<Hash: Serialize> Serialize for Initialized<Hash> {
118 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
121 where
122 S: Serializer,
123 {
124 if self.with_runtime {
125 let mut state = serializer.serialize_struct("Initialized", 2)?;
126 state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?;
127 state.serialize_field("finalizedBlockRuntime", &self.finalized_block_runtime)?;
128 state.end()
129 } else {
130 let mut state = serializer.serialize_struct("Initialized", 1)?;
131 state.serialize_field("finalizedBlockHashes", &self.finalized_block_hashes)?;
132 state.end()
133 }
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct NewBlock<Hash> {
141 pub block_hash: Hash,
143 pub parent_block_hash: Hash,
145 pub new_runtime: Option<RuntimeEvent>,
152 #[serde(default)]
155 pub(crate) with_runtime: bool,
156}
157
158impl<Hash: Serialize> Serialize for NewBlock<Hash> {
159 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162 where
163 S: Serializer,
164 {
165 if self.with_runtime {
166 let mut state = serializer.serialize_struct("NewBlock", 3)?;
167 state.serialize_field("blockHash", &self.block_hash)?;
168 state.serialize_field("parentBlockHash", &self.parent_block_hash)?;
169 state.serialize_field("newRuntime", &self.new_runtime)?;
170 state.end()
171 } else {
172 let mut state = serializer.serialize_struct("NewBlock", 2)?;
173 state.serialize_field("blockHash", &self.block_hash)?;
174 state.serialize_field("parentBlockHash", &self.parent_block_hash)?;
175 state.end()
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct BestBlockChanged<Hash> {
184 pub best_block_hash: Hash,
186}
187
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct Finalized<Hash> {
192 pub finalized_block_hashes: Vec<Hash>,
194 pub pruned_block_hashes: Vec<Hash>,
196}
197
198#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct OperationId {
202 pub operation_id: String,
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct OperationBodyDone {
210 pub operation_id: String,
212 pub value: Vec<String>,
214}
215
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218#[serde(rename_all = "camelCase")]
219pub struct OperationCallDone {
220 pub operation_id: String,
222 pub output: String,
224}
225
226#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct OperationStorageItems {
230 pub operation_id: String,
232 pub items: Vec<StorageResult>,
234}
235
236#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct OperationError {
240 pub operation_id: String,
242 pub error: String,
244}
245
246#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270#[serde(tag = "event")]
271pub enum FollowEvent<Hash> {
272 Initialized(Initialized<Hash>),
276 NewBlock(NewBlock<Hash>),
278 BestBlockChanged(BestBlockChanged<Hash>),
280 Finalized(Finalized<Hash>),
282 OperationBodyDone(OperationBodyDone),
284 OperationCallDone(OperationCallDone),
286 OperationStorageItems(OperationStorageItems),
288 OperationWaitingForContinue(OperationId),
291 OperationStorageDone(OperationId),
293 OperationInaccessible(OperationId),
297 OperationError(OperationError),
301 Stop,
304}
305
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
308#[serde(rename_all = "camelCase")]
309#[serde(tag = "result")]
310pub enum MethodResponse {
311 Started(MethodResponseStarted),
313 LimitReached,
315}
316
317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
319#[serde(rename_all = "camelCase")]
320pub struct MethodResponseStarted {
321 pub operation_id: String,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 #[serde(default)]
326 pub discarded_items: Option<usize>,
327}
328
329#[cfg(test)]
330mod tests {
331 use crate::v2::common::events::StorageResultType;
332
333 use super::*;
334
335 #[test]
336 fn follow_initialized_event_no_updates() {
337 let event: FollowEvent<String> = FollowEvent::Initialized(Initialized {
339 finalized_block_hashes: vec!["0x1".into()],
340 finalized_block_runtime: None,
341 with_runtime: false,
342 });
343
344 let ser = serde_json::to_string(&event).unwrap();
345 let exp = r#"{"event":"initialized","finalizedBlockHashes":["0x1"]}"#;
346 assert_eq!(ser, exp);
347
348 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
349 assert_eq!(event_dec, event);
350 }
351
352 #[test]
353 fn follow_initialized_event_with_updates() {
354 let runtime = RuntimeVersion {
356 spec_name: "ABC".into(),
357 impl_name: "Impl".into(),
358 spec_version: 1,
359 ..Default::default()
360 };
361
362 let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() });
363 let mut initialized = Initialized {
364 finalized_block_hashes: vec!["0x1".into()],
365 finalized_block_runtime: Some(runtime_event),
366 with_runtime: true,
367 };
368 let event: FollowEvent<String> = FollowEvent::Initialized(initialized.clone());
369
370 let ser = serde_json::to_string(&event).unwrap();
371 let exp = concat!(
372 r#"{"event":"initialized","finalizedBlockHashes":["0x1"],"#,
373 r#""finalizedBlockRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#,
374 r#""specVersion":1,"implVersion":0,"apis":{},"transactionVersion":0}}}"#,
375 );
376 assert_eq!(ser, exp);
377
378 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
379 initialized.with_runtime = false;
381 assert!(matches!(
382 event_dec, FollowEvent::Initialized(ref dec) if dec == &initialized
383 ));
384 }
385
386 #[test]
387 fn follow_new_block_event_no_updates() {
388 let event: FollowEvent<String> = FollowEvent::NewBlock(NewBlock {
390 block_hash: "0x1".into(),
391 parent_block_hash: "0x2".into(),
392 new_runtime: None,
393 with_runtime: false,
394 });
395
396 let ser = serde_json::to_string(&event).unwrap();
397 let exp = r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2"}"#;
398 assert_eq!(ser, exp);
399
400 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
401 assert_eq!(event_dec, event);
402 }
403
404 #[test]
405 fn follow_new_block_event_with_updates() {
406 let runtime = RuntimeVersion {
408 spec_name: "ABC".into(),
409 impl_name: "Impl".into(),
410 spec_version: 1,
411 apis: vec![([0, 0, 0, 0, 0, 0, 0, 0], 2), ([1, 0, 0, 0, 0, 0, 0, 0], 3)].into(),
412 ..Default::default()
413 };
414
415 let runtime_event = RuntimeEvent::Valid(RuntimeVersionEvent { spec: runtime.into() });
416 let mut new_block = NewBlock {
417 block_hash: "0x1".into(),
418 parent_block_hash: "0x2".into(),
419 new_runtime: Some(runtime_event),
420 with_runtime: true,
421 };
422
423 let event: FollowEvent<String> = FollowEvent::NewBlock(new_block.clone());
424
425 let ser = serde_json::to_string(&event).unwrap();
426 let exp = concat!(
427 r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2","#,
428 r#""newRuntime":{"type":"valid","spec":{"specName":"ABC","implName":"Impl","#,
429 r#""specVersion":1,"implVersion":0,"apis":{"0x0000000000000000":2,"0x0100000000000000":3},"transactionVersion":0}}}"#,
430 );
431 assert_eq!(ser, exp);
432
433 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
434 new_block.with_runtime = false;
436 assert!(matches!(
437 event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
438 ));
439
440 let mut new_block = NewBlock {
442 block_hash: "0x1".into(),
443 parent_block_hash: "0x2".into(),
444 new_runtime: None,
445 with_runtime: true,
446 };
447 let event: FollowEvent<String> = FollowEvent::NewBlock(new_block.clone());
448
449 let ser = serde_json::to_string(&event).unwrap();
450 let exp =
451 r#"{"event":"newBlock","blockHash":"0x1","parentBlockHash":"0x2","newRuntime":null}"#;
452 assert_eq!(ser, exp);
453 new_block.with_runtime = false;
454 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
455 assert!(matches!(
456 event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
457 ));
458 }
459
460 #[test]
461 fn follow_best_block_changed_event() {
462 let event: FollowEvent<String> =
463 FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash: "0x1".into() });
464
465 let ser = serde_json::to_string(&event).unwrap();
466 let exp = r#"{"event":"bestBlockChanged","bestBlockHash":"0x1"}"#;
467 assert_eq!(ser, exp);
468
469 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
470 assert_eq!(event_dec, event);
471 }
472
473 #[test]
474 fn follow_finalized_event() {
475 let event: FollowEvent<String> = FollowEvent::Finalized(Finalized {
476 finalized_block_hashes: vec!["0x1".into()],
477 pruned_block_hashes: vec!["0x2".into()],
478 });
479
480 let ser = serde_json::to_string(&event).unwrap();
481 let exp =
482 r#"{"event":"finalized","finalizedBlockHashes":["0x1"],"prunedBlockHashes":["0x2"]}"#;
483 assert_eq!(ser, exp);
484
485 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
486 assert_eq!(event_dec, event);
487 }
488
489 #[test]
490 fn follow_op_body_event() {
491 let event: FollowEvent<String> = FollowEvent::OperationBodyDone(OperationBodyDone {
492 operation_id: "123".into(),
493 value: vec!["0x1".into()],
494 });
495
496 let ser = serde_json::to_string(&event).unwrap();
497 let exp = r#"{"event":"operationBodyDone","operationId":"123","value":["0x1"]}"#;
498 assert_eq!(ser, exp);
499
500 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
501 assert_eq!(event_dec, event);
502 }
503
504 #[test]
505 fn follow_op_call_event() {
506 let event: FollowEvent<String> = FollowEvent::OperationCallDone(OperationCallDone {
507 operation_id: "123".into(),
508 output: "0x1".into(),
509 });
510
511 let ser = serde_json::to_string(&event).unwrap();
512 let exp = r#"{"event":"operationCallDone","operationId":"123","output":"0x1"}"#;
513 assert_eq!(ser, exp);
514
515 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
516 assert_eq!(event_dec, event);
517 }
518
519 #[test]
520 fn follow_op_storage_items_event() {
521 let event: FollowEvent<String> =
522 FollowEvent::OperationStorageItems(OperationStorageItems {
523 operation_id: "123".into(),
524 items: vec![StorageResult {
525 key: "0x1".into(),
526 result: StorageResultType::Value("0x123".to_string()),
527 child_trie_key: None,
528 }],
529 });
530
531 let ser = serde_json::to_string(&event).unwrap();
532 let exp = r#"{"event":"operationStorageItems","operationId":"123","items":[{"key":"0x1","value":"0x123"}]}"#;
533 assert_eq!(ser, exp);
534
535 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
536 assert_eq!(event_dec, event);
537 }
538
539 #[test]
540 fn follow_op_wait_event() {
541 let event: FollowEvent<String> =
542 FollowEvent::OperationWaitingForContinue(OperationId { operation_id: "123".into() });
543
544 let ser = serde_json::to_string(&event).unwrap();
545 let exp = r#"{"event":"operationWaitingForContinue","operationId":"123"}"#;
546 assert_eq!(ser, exp);
547
548 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
549 assert_eq!(event_dec, event);
550 }
551
552 #[test]
553 fn follow_op_storage_done_event() {
554 let event: FollowEvent<String> =
555 FollowEvent::OperationStorageDone(OperationId { operation_id: "123".into() });
556
557 let ser = serde_json::to_string(&event).unwrap();
558 let exp = r#"{"event":"operationStorageDone","operationId":"123"}"#;
559 assert_eq!(ser, exp);
560
561 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
562 assert_eq!(event_dec, event);
563 }
564
565 #[test]
566 fn follow_op_inaccessible_event() {
567 let event: FollowEvent<String> =
568 FollowEvent::OperationInaccessible(OperationId { operation_id: "123".into() });
569
570 let ser = serde_json::to_string(&event).unwrap();
571 let exp = r#"{"event":"operationInaccessible","operationId":"123"}"#;
572 assert_eq!(ser, exp);
573
574 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
575 assert_eq!(event_dec, event);
576 }
577
578 #[test]
579 fn follow_op_error_event() {
580 let event: FollowEvent<String> = FollowEvent::OperationError(OperationError {
581 operation_id: "123".into(),
582 error: "reason".into(),
583 });
584
585 let ser = serde_json::to_string(&event).unwrap();
586 let exp = r#"{"event":"operationError","operationId":"123","error":"reason"}"#;
587 assert_eq!(ser, exp);
588
589 let event_dec: FollowEvent<String> = serde_json::from_str(exp).unwrap();
590 assert_eq!(event_dec, event);
591 }
592
593 #[test]
594 fn follow_stop_event() {
595 let event: FollowEvent<String> = FollowEvent::Stop;
596
597 let ser = serde_json::to_string(&event).unwrap();
598 let exp = r#"{"event":"stop"}"#;
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 method_response() {
607 let event = MethodResponse::Started(MethodResponseStarted {
609 operation_id: "123".into(),
610 discarded_items: None,
611 });
612
613 let ser = serde_json::to_string(&event).unwrap();
614 let exp = r#"{"result":"started","operationId":"123"}"#;
615 assert_eq!(ser, exp);
616
617 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
618 assert_eq!(event_dec, event);
619
620 let event = MethodResponse::Started(MethodResponseStarted {
622 operation_id: "123".into(),
623 discarded_items: Some(1),
624 });
625
626 let ser = serde_json::to_string(&event).unwrap();
627 let exp = r#"{"result":"started","operationId":"123","discardedItems":1}"#;
628 assert_eq!(ser, exp);
629
630 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
631 assert_eq!(event_dec, event);
632
633 let event = MethodResponse::LimitReached;
635
636 let ser = serde_json::to_string(&event).unwrap();
637 let exp = r#"{"result":"limitReached"}"#;
638 assert_eq!(ser, exp);
639
640 let event_dec: MethodResponse = serde_json::from_str(exp).unwrap();
641 assert_eq!(event_dec, event);
642 }
643}