Skip to main content

soil_rpc/v2/chain_head/
event.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! The chain head's event returned as json compatible object.
8
9use 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/// The operation could not be processed due to an error.
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "camelCase")]
19pub struct ErrorEvent {
20	/// Reason of the error.
21	pub error: String,
22}
23
24/// The runtime specification of the current block.
25///
26/// This event is generated for:
27///   - the first announced block by the follow subscription
28///   - blocks that suffered a change in runtime compared with their parents
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
30#[serde(rename_all = "camelCase")]
31pub struct RuntimeVersionEvent {
32	/// The runtime version.
33	pub spec: ChainHeadRuntimeVersion,
34}
35
36/// Simplified type clone of `subsoil::version::RuntimeVersion`. Used instead of
37/// `subsoil::version::RuntimeVersion` to conform to RPC spec V2.
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct ChainHeadRuntimeVersion {
41	/// Identifies the different Substrate runtimes.
42	pub spec_name: String,
43	/// Name of the implementation of the spec.
44	pub impl_name: String,
45	/// Version of the runtime specification.
46	pub spec_version: u32,
47	/// Version of the implementation of the specification.
48	pub impl_version: u32,
49	/// Map of all supported API "features" and their versions.
50	pub apis: BTreeMap<String, u32>,
51	/// Transaction version.
52	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/// The runtime event generated if the `follow` subscription
73/// has set the `with_runtime` flag.
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76#[serde(tag = "type")]
77pub enum RuntimeEvent {
78	/// The runtime version of this block.
79	Valid(RuntimeVersionEvent),
80	/// The runtime could not be obtained due to an error.
81	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/// Contain information about the latest finalized block.
91///
92/// # Note
93///
94/// This is the first event generated by the `follow` subscription
95/// and is submitted only once.
96///
97/// If the `with_runtime` flag is set, then this event contains
98/// the `RuntimeEvent`, otherwise the `RuntimeEvent` is not present.
99#[derive(Debug, Clone, PartialEq, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct Initialized<Hash> {
102	/// The hash of the latest finalized blocks.
103	pub finalized_block_hashes: Vec<Hash>,
104	/// The runtime version of the finalized block.
105	///
106	/// # Note
107	///
108	/// This is present only if the `with_runtime` flag is set for
109	/// the `follow` subscription.
110	pub finalized_block_runtime: Option<RuntimeEvent>,
111	/// Privately keep track if the `finalized_block_runtime` should be
112	/// serialized.
113	#[serde(default)]
114	pub(crate) with_runtime: bool,
115}
116
117impl<Hash: Serialize> Serialize for Initialized<Hash> {
118	/// Custom serialize implementation to include the `RuntimeEvent` depending
119	/// on the internal `with_runtime` flag.
120	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/// Indicate a new non-finalized block.
138#[derive(Debug, Clone, PartialEq, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct NewBlock<Hash> {
141	/// The hash of the new block.
142	pub block_hash: Hash,
143	/// The parent hash of the new block.
144	pub parent_block_hash: Hash,
145	/// The runtime version of the new block.
146	///
147	/// # Note
148	///
149	/// This is present only if the `with_runtime` flag is set for
150	/// the `follow` subscription.
151	pub new_runtime: Option<RuntimeEvent>,
152	/// Privately keep track if the `finalized_block_runtime` should be
153	/// serialized.
154	#[serde(default)]
155	pub(crate) with_runtime: bool,
156}
157
158impl<Hash: Serialize> Serialize for NewBlock<Hash> {
159	/// Custom serialize implementation to include the `RuntimeEvent` depending
160	/// on the internal `with_runtime` flag.
161	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/// Indicate the block hash of the new best block.
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct BestBlockChanged<Hash> {
184	/// The block hash of the new best block.
185	pub best_block_hash: Hash,
186}
187
188/// Indicate the finalized and pruned block hashes.
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct Finalized<Hash> {
192	/// Block hashes that are finalized.
193	pub finalized_block_hashes: Vec<Hash>,
194	/// Block hashes that are pruned (removed).
195	pub pruned_block_hashes: Vec<Hash>,
196}
197
198/// Indicate the operation id of the event.
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct OperationId {
202	/// The operation id of the event.
203	pub operation_id: String,
204}
205
206/// The response of the `chainHead_body` method.
207#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct OperationBodyDone {
210	/// The operation id of the event.
211	pub operation_id: String,
212	/// Array of hexadecimal-encoded scale-encoded extrinsics found in the block.
213	pub value: Vec<String>,
214}
215
216/// The response of the `chainHead_call` method.
217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218#[serde(rename_all = "camelCase")]
219pub struct OperationCallDone {
220	/// The operation id of the event.
221	pub operation_id: String,
222	/// Hexadecimal-encoded output of the runtime function call.
223	pub output: String,
224}
225
226/// The response of the `chainHead_storage` method.
227#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct OperationStorageItems {
230	/// The operation id of the event.
231	pub operation_id: String,
232	/// The resulting items.
233	pub items: Vec<StorageResult>,
234}
235
236/// Indicate a problem during the operation.
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct OperationError {
240	/// The operation id of the event.
241	pub operation_id: String,
242	/// The reason of the error.
243	pub error: String,
244}
245
246/// The event generated by the `follow` method.
247///
248/// The block events are generated in the following order:
249/// 1. Initialized - generated only once to signal the latest finalized block
250/// 2. NewBlock - a new block was added.
251/// 3. BestBlockChanged - indicate that the best block is now the one from this event. The block was
252///    announced priorly with the `NewBlock` event.
253/// 4. Finalized - State the finalized and pruned blocks.
254///
255/// The following events are related to operations:
256/// - OperationBodyDone: The response of the `chianHead_body`
257/// - OperationCallDone: The response of the `chianHead_call`
258/// - OperationStorageItems: Items produced by the `chianHead_storage`
259/// - OperationWaitingForContinue: Generated after OperationStorageItems and requires the user to
260///   call `chainHead_continue`
261/// - OperationStorageDone: The `chianHead_storage` method has produced all the results
262/// - OperationInaccessible: The server was unable to provide the result, retries might succeed in
263///   the future
264/// - OperationError: The server encountered an error, retries will not succeed
265///
266/// The stop event indicates that the JSON-RPC server was unable to provide a consistent list of
267/// the blocks at the head of the chain.
268#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270#[serde(tag = "event")]
271pub enum FollowEvent<Hash> {
272	/// The latest finalized block.
273	///
274	/// This event is generated only once.
275	Initialized(Initialized<Hash>),
276	/// A new non-finalized block was added.
277	NewBlock(NewBlock<Hash>),
278	/// The best block of the chain.
279	BestBlockChanged(BestBlockChanged<Hash>),
280	/// A list of finalized and pruned blocks.
281	Finalized(Finalized<Hash>),
282	/// The response of the `chainHead_body` method.
283	OperationBodyDone(OperationBodyDone),
284	/// The response of the `chainHead_call` method.
285	OperationCallDone(OperationCallDone),
286	/// Yield one or more items found in the storage.
287	OperationStorageItems(OperationStorageItems),
288	/// Ask the user to call `chainHead_continue` to produce more events
289	/// regarding the operation id.
290	OperationWaitingForContinue(OperationId),
291	/// The responses of the `chainHead_storage` method have been produced.
292	OperationStorageDone(OperationId),
293	/// The RPC server was unable to provide the response of the following operation id.
294	///
295	/// Repeating the same operation in the future might succeed.
296	OperationInaccessible(OperationId),
297	/// The RPC server encountered an error while processing an operation id.
298	///
299	/// Repeating the same operation in the future will not succeed.
300	OperationError(OperationError),
301	/// The subscription is dropped and no further events
302	/// will be generated.
303	Stop,
304}
305
306/// The method response of `chainHead_body`, `chainHead_call` and `chainHead_storage`.
307#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
308#[serde(rename_all = "camelCase")]
309#[serde(tag = "result")]
310pub enum MethodResponse {
311	/// The method has started.
312	Started(MethodResponseStarted),
313	/// The RPC server cannot handle the request at the moment.
314	LimitReached,
315}
316
317/// The `started` result of a method.
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
319#[serde(rename_all = "camelCase")]
320pub struct MethodResponseStarted {
321	/// The operation id of the response.
322	pub operation_id: String,
323	/// The number of items from the back of the `chainHead_storage` that have been discarded.
324	#[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		// Runtime flag is false.
338		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		// Runtime flag is true, block runtime must always be reported for this event.
355		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		// The `with_runtime` field is used for serialization purposes.
380		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		// Runtime flag is false.
389		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		// Runtime flag is true, block runtime must always be reported for this event.
407		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		// The `with_runtime` field is used for serialization purposes.
435		new_block.with_runtime = false;
436		assert!(matches!(
437			event_dec, FollowEvent::NewBlock(ref dec) if dec == &new_block
438		));
439
440		// Runtime flag is true, runtime didn't change compared to parent.
441		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		// Response of `call` and `body`
608		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		// Response of `storage`
621		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		// Limit reached.
634		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}