referrerpolicy=no-referrer-when-downgrade

sc_rpc_spec_v2/chain_head/
event.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! The chain head's event returned as json compatible object.
20
21use 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/// The operation could not be processed due to an error.
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct ErrorEvent {
32	/// Reason of the error.
33	pub error: String,
34}
35
36/// The runtime specification of the current block.
37///
38/// This event is generated for:
39///   - the first announced block by the follow subscription
40///   - blocks that suffered a change in runtime compared with their parents
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(rename_all = "camelCase")]
43pub struct RuntimeVersionEvent {
44	/// The runtime version.
45	pub spec: ChainHeadRuntimeVersion,
46}
47
48/// Simplified type clone of `sp_version::RuntimeVersion`. Used instead of
49/// `sp_version::RuntimeVersion` to conform to RPC spec V2.
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct ChainHeadRuntimeVersion {
53	/// Identifies the different Substrate runtimes.
54	pub spec_name: String,
55	/// Name of the implementation of the spec.
56	pub impl_name: String,
57	/// Version of the runtime specification.
58	pub spec_version: u32,
59	/// Version of the implementation of the specification.
60	pub impl_version: u32,
61	/// Map of all supported API "features" and their versions.
62	pub apis: BTreeMap<String, u32>,
63	/// Transaction version.
64	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/// The runtime event generated if the `follow` subscription
85/// has set the `with_runtime` flag.
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87#[serde(rename_all = "camelCase")]
88#[serde(tag = "type")]
89pub enum RuntimeEvent {
90	/// The runtime version of this block.
91	Valid(RuntimeVersionEvent),
92	/// The runtime could not be obtained due to an error.
93	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/// Contain information about the latest finalized block.
103///
104/// # Note
105///
106/// This is the first event generated by the `follow` subscription
107/// and is submitted only once.
108///
109/// If the `with_runtime` flag is set, then this event contains
110/// the `RuntimeEvent`, otherwise the `RuntimeEvent` is not present.
111#[derive(Debug, Clone, PartialEq, Deserialize)]
112#[serde(rename_all = "camelCase")]
113pub struct Initialized<Hash> {
114	/// The hash of the latest finalized blocks.
115	pub finalized_block_hashes: Vec<Hash>,
116	/// The runtime version of the finalized block.
117	///
118	/// # Note
119	///
120	/// This is present only if the `with_runtime` flag is set for
121	/// the `follow` subscription.
122	pub finalized_block_runtime: Option<RuntimeEvent>,
123	/// Privately keep track if the `finalized_block_runtime` should be
124	/// serialized.
125	#[serde(default)]
126	pub(crate) with_runtime: bool,
127}
128
129impl<Hash: Serialize> Serialize for Initialized<Hash> {
130	/// Custom serialize implementation to include the `RuntimeEvent` depending
131	/// on the internal `with_runtime` flag.
132	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/// Indicate a new non-finalized block.
150#[derive(Debug, Clone, PartialEq, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct NewBlock<Hash> {
153	/// The hash of the new block.
154	pub block_hash: Hash,
155	/// The parent hash of the new block.
156	pub parent_block_hash: Hash,
157	/// The runtime version of the new block.
158	///
159	/// # Note
160	///
161	/// This is present only if the `with_runtime` flag is set for
162	/// the `follow` subscription.
163	pub new_runtime: Option<RuntimeEvent>,
164	/// Privately keep track if the `finalized_block_runtime` should be
165	/// serialized.
166	#[serde(default)]
167	pub(crate) with_runtime: bool,
168}
169
170impl<Hash: Serialize> Serialize for NewBlock<Hash> {
171	/// Custom serialize implementation to include the `RuntimeEvent` depending
172	/// on the internal `with_runtime` flag.
173	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/// Indicate the block hash of the new best block.
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194#[serde(rename_all = "camelCase")]
195pub struct BestBlockChanged<Hash> {
196	/// The block hash of the new best block.
197	pub best_block_hash: Hash,
198}
199
200/// Indicate the finalized and pruned block hashes.
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202#[serde(rename_all = "camelCase")]
203pub struct Finalized<Hash> {
204	/// Block hashes that are finalized.
205	pub finalized_block_hashes: Vec<Hash>,
206	/// Block hashes that are pruned (removed).
207	pub pruned_block_hashes: Vec<Hash>,
208}
209
210/// Indicate the operation id of the event.
211#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct OperationId {
214	/// The operation id of the event.
215	pub operation_id: String,
216}
217
218/// The response of the `chainHead_body` method.
219#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct OperationBodyDone {
222	/// The operation id of the event.
223	pub operation_id: String,
224	/// Array of hexadecimal-encoded scale-encoded extrinsics found in the block.
225	pub value: Vec<String>,
226}
227
228/// The response of the `chainHead_call` method.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct OperationCallDone {
232	/// The operation id of the event.
233	pub operation_id: String,
234	/// Hexadecimal-encoded output of the runtime function call.
235	pub output: String,
236}
237
238/// The response of the `chainHead_storage` method.
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct OperationStorageItems {
242	/// The operation id of the event.
243	pub operation_id: String,
244	/// The resulting items.
245	pub items: Vec<StorageResult>,
246}
247
248/// Indicate a problem during the operation.
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
250#[serde(rename_all = "camelCase")]
251pub struct OperationError {
252	/// The operation id of the event.
253	pub operation_id: String,
254	/// The reason of the error.
255	pub error: String,
256}
257
258/// The event generated by the `follow` method.
259///
260/// The block events are generated in the following order:
261/// 1. Initialized - generated only once to signal the latest finalized block
262/// 2. NewBlock - a new block was added.
263/// 3. BestBlockChanged - indicate that the best block is now the one from this event. The block was
264///    announced priorly with the `NewBlock` event.
265/// 4. Finalized - State the finalized and pruned blocks.
266///
267/// The following events are related to operations:
268/// - OperationBodyDone: The response of the `chianHead_body`
269/// - OperationCallDone: The response of the `chianHead_call`
270/// - OperationStorageItems: Items produced by the `chianHead_storage`
271/// - OperationWaitingForContinue: Generated after OperationStorageItems and requires the user to
272///   call `chainHead_continue`
273/// - OperationStorageDone: The `chianHead_storage` method has produced all the results
274/// - OperationInaccessible: The server was unable to provide the result, retries might succeed in
275///   the future
276/// - OperationError: The server encountered an error, retries will not succeed
277///
278/// The stop event indicates that the JSON-RPC server was unable to provide a consistent list of
279/// the blocks at the head of the chain.
280#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
281#[serde(rename_all = "camelCase")]
282#[serde(tag = "event")]
283pub enum FollowEvent<Hash> {
284	/// The latest finalized block.
285	///
286	/// This event is generated only once.
287	Initialized(Initialized<Hash>),
288	/// A new non-finalized block was added.
289	NewBlock(NewBlock<Hash>),
290	/// The best block of the chain.
291	BestBlockChanged(BestBlockChanged<Hash>),
292	/// A list of finalized and pruned blocks.
293	Finalized(Finalized<Hash>),
294	/// The response of the `chainHead_body` method.
295	OperationBodyDone(OperationBodyDone),
296	/// The response of the `chainHead_call` method.
297	OperationCallDone(OperationCallDone),
298	/// Yield one or more items found in the storage.
299	OperationStorageItems(OperationStorageItems),
300	/// Ask the user to call `chainHead_continue` to produce more events
301	/// regarding the operation id.
302	OperationWaitingForContinue(OperationId),
303	/// The responses of the `chainHead_storage` method have been produced.
304	OperationStorageDone(OperationId),
305	/// The RPC server was unable to provide the response of the following operation id.
306	///
307	/// Repeating the same operation in the future might succeed.
308	OperationInaccessible(OperationId),
309	/// The RPC server encountered an error while processing an operation id.
310	///
311	/// Repeating the same operation in the future will not succeed.
312	OperationError(OperationError),
313	/// The subscription is dropped and no further events
314	/// will be generated.
315	Stop,
316}
317
318/// The method response of `chainHead_body`, `chainHead_call` and `chainHead_storage`.
319#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
320#[serde(rename_all = "camelCase")]
321#[serde(tag = "result")]
322pub enum MethodResponse {
323	/// The method has started.
324	Started(MethodResponseStarted),
325	/// The RPC server cannot handle the request at the moment.
326	LimitReached,
327}
328
329/// The `started` result of a method.
330#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
331#[serde(rename_all = "camelCase")]
332pub struct MethodResponseStarted {
333	/// The operation id of the response.
334	pub operation_id: String,
335	/// The number of items from the back of the `chainHead_storage` that have been discarded.
336	#[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		// Runtime flag is false.
350		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		// Runtime flag is true, block runtime must always be reported for this event.
367		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		// The `with_runtime` field is used for serialization purposes.
392		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		// Runtime flag is false.
401		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		// Runtime flag is true, block runtime must always be reported for this event.
419		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		// The `with_runtime` field is used for serialization purposes.
447		new_block.with_runtime = false;
448		assert!(matches!(
449			event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
450		));
451
452		// Runtime flag is true, runtime didn't change compared to parent.
453		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		// Response of `call` and `body`
620		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		// Response of `storage`
633		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		// Limit reached.
646		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}