// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // OpenEthereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with OpenEthereum. If not, see . //! Pub-Sub types. use ethereum_types::H256; use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{from_value, Value}; use v1::types::{Filter, Log, RichHeader}; /// Subscription result. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Result { /// New block header. Header(Box), /// Log Log(Box), /// Transaction hash TransactionHash(H256), } impl Serialize for Result { fn serialize(&self, serializer: S) -> ::std::result::Result where S: Serializer, { match *self { Result::Header(ref header) => header.serialize(serializer), Result::Log(ref log) => log.serialize(serializer), Result::TransactionHash(ref hash) => hash.serialize(serializer), } } } /// Subscription kind. #[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub enum Kind { /// New block headers subscription. NewHeads, /// Logs subscription. Logs, /// New Pending Transactions subscription. NewPendingTransactions, /// Node syncing status subscription. Syncing, } /// Subscription kind. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Params { /// No parameters passed. None, /// Log parameters. Logs(Filter), } impl Default for Params { fn default() -> Self { Params::None } } impl<'a> Deserialize<'a> for Params { fn deserialize(deserializer: D) -> ::std::result::Result where D: Deserializer<'a>, { let v: Value = Deserialize::deserialize(deserializer)?; if v.is_null() { return Ok(Params::None); } from_value(v.clone()) .map(Params::Logs) .map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e))) } } #[cfg(test)] mod tests { use super::{Kind, Params, Result}; use serde_json; use v1::types::{filter::VariadicValue, Filter, Header, RichHeader}; #[test] fn should_deserialize_kind() { assert_eq!( serde_json::from_str::(r#""newHeads""#).unwrap(), Kind::NewHeads ); assert_eq!( serde_json::from_str::(r#""logs""#).unwrap(), Kind::Logs ); assert_eq!( serde_json::from_str::(r#""newPendingTransactions""#).unwrap(), Kind::NewPendingTransactions ); assert_eq!( serde_json::from_str::(r#""syncing""#).unwrap(), Kind::Syncing ); } #[test] fn should_deserialize_logs() { let none = serde_json::from_str::(r#"null"#).unwrap(); assert_eq!(none, Params::None); let logs1 = serde_json::from_str::(r#"{}"#).unwrap(); let logs2 = serde_json::from_str::(r#"{"limit":10}"#).unwrap(); let logs3 = serde_json::from_str::( r#"{"topics":["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"]}"#, ) .unwrap(); assert_eq!( logs1, Params::Logs(Filter { from_block: None, to_block: None, block_hash: None, address: None, topics: None, limit: None, }) ); assert_eq!( logs2, Params::Logs(Filter { from_block: None, to_block: None, block_hash: None, address: None, topics: None, limit: Some(10), }) ); assert_eq!( logs3, Params::Logs(Filter { from_block: None, to_block: None, block_hash: None, address: None, topics: Some(vec![VariadicValue::Single( "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b" .parse() .unwrap() )]), limit: None, }) ); } #[test] fn should_serialize_header() { let header = Result::Header(Box::new(RichHeader { extra_info: Default::default(), inner: Header { hash: Some(Default::default()), parent_hash: Default::default(), uncles_hash: Default::default(), author: Default::default(), miner: Default::default(), state_root: Default::default(), transactions_root: Default::default(), receipts_root: Default::default(), number: Some(Default::default()), gas_used: Default::default(), gas_limit: Default::default(), extra_data: Default::default(), logs_bloom: Default::default(), timestamp: Default::default(), difficulty: Default::default(), seal_fields: vec![Default::default(), Default::default()], base_fee_per_gas: None, size: Some(69.into()), }, })); let expected = r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#; assert_eq!(serde_json::to_string(&header).unwrap(), expected); } }