diff --git a/ethcore/src/miner/filter_options.rs b/ethcore/src/miner/filter_options.rs index ae1a206b0..798006da8 100644 --- a/ethcore/src/miner/filter_options.rs +++ b/ethcore/src/miner/filter_options.rs @@ -14,39 +14,81 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethereum_types::{Address, U256}; -use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor}; use std::fmt; use std::marker::PhantomData; -/// This structure provides filtering options for the pending transactions RPC API call +use ethereum_types::{Address, U256}; +use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor}; +use types::transaction::SignedTransaction; + +/// Filtering options for the pending transactions +/// May be used for filtering transactions based on gas, gas price, value and/or nonce. +// NOTE: the fields are only `pub` because they are needed for tests #[derive(Debug, Clone, Eq, PartialEq)] pub struct FilterOptions { - /// Contains the operator to filter the from value of the transaction - pub from: FilterOperator
, - /// Contains the operator to filter the to value of the transaction - pub to: FilterOperator>, - /// Contains the operator to filter the gas value of the transaction - pub gas: FilterOperator, - /// Contains the operator to filter the gas price value of the transaction - pub gas_price: FilterOperator, - /// Contains the operator to filter the transaction value - pub value: FilterOperator, - /// Contains the operator to filter the nonce value of the transaction - pub nonce: FilterOperator, + /// Filter based on the `sender` of the transaction. + pub from: FilterOperator
, + /// Filter based on `receiver` of the transaction. + pub to: FilterOperator>, + /// Filter based on `gas` of the transaction. + pub gas: FilterOperator, + /// Filter based on `gas price` of the transaction. + pub gas_price: FilterOperator, + /// Filter based on `value` of the transaction. + pub value: FilterOperator, + /// Filter based on `nonce` of the transaction. + pub nonce: FilterOperator, +} + +impl FilterOptions { + fn sender_matcher(filter: &FilterOperator
, candidate: &Address) -> bool { + match filter { + FilterOperator::Eq(address) => candidate == address, + FilterOperator::Any => true, + // Handled during deserialization + _ => unreachable!(), + } + } + + fn receiver_matcher(filter: &FilterOperator>, candidate: &Option
) -> bool { + match filter { + FilterOperator::Eq(address) => candidate == address, + FilterOperator::Any => true, + // Handled during deserialization + _ => unreachable!(), + } + } + fn value_matcher(filter: &FilterOperator, tx_value: &U256) -> bool { + match filter { + FilterOperator::Eq(ref value) => tx_value == value, + FilterOperator::GreaterThan(ref value) => tx_value > value, + FilterOperator::LessThan(ref value) => tx_value < value, + FilterOperator::Any => true, + } + } + + /// Determines whether a transaction passes the configured filter + pub fn matches(&self, tx: &SignedTransaction) -> bool { + Self::sender_matcher(&self.from, &tx.sender()) && + Self::receiver_matcher(&self.to, &tx.receiver()) && + Self::value_matcher(&self.gas, &tx.gas) && + Self::value_matcher(&self.gas_price, &tx.gas_price) && + Self::value_matcher(&self.value, &tx.value) && + Self::value_matcher(&self.nonce, &tx.nonce) + } } impl Default for FilterOptions { - fn default() -> Self { - FilterOptions { - from: FilterOperator::Any, - to: FilterOperator::Any, - gas: FilterOperator::Any, - gas_price: FilterOperator::Any, - value: FilterOperator::Any, - nonce: FilterOperator::Any, - } - } + fn default() -> Self { + FilterOptions { + from: FilterOperator::Any, + to: FilterOperator::Any, + gas: FilterOperator::Any, + gas_price: FilterOperator::Any, + value: FilterOperator::Any, + nonce: FilterOperator::Any, + } + } } /// The highly generic use of implementing Deserialize for FilterOperator @@ -54,8 +96,10 @@ impl Default for FilterOptions { /// gets returned explicitly. Therefore this Wrapper will be used for /// deserialization, directly identifying the contract creation. enum Wrapper { - O(FilterOperator), - CC, // Contract Creation + /// FilterOperations + O(FilterOperator), + /// Contract Creation + CC, } /// Available operators for filtering options. @@ -63,10 +107,14 @@ enum Wrapper { /// The `to` filter only accepts Any, Eq(Address) and Eq(None) for contract creation. #[derive(Debug, Clone, Eq, PartialEq)] pub enum FilterOperator { - Any, - Eq(T), - GreaterThan(T), - LessThan(T), + /// Any (no filter) + Any, + /// Equal + Eq(T), + /// Greather than + GreaterThan(T), + /// Less than + LessThan(T), } /// Since there are multiple operators which are not supported equally by all filters, @@ -78,792 +126,792 @@ pub enum FilterOperator { /// The `to` filter validates with `validate_from` /// All other filters such as gas and price validate with `validate_value` trait Validate<'de, T, M: MapAccess<'de>> { - fn validate_from(&mut self) -> Result, M::Error>; - fn validate_to(&mut self) -> Result>, M::Error>; - fn validate_value(&mut self) -> Result, M::Error>; + fn validate_from(&mut self) -> Result, M::Error>; + fn validate_to(&mut self) -> Result>, M::Error>; + fn validate_value(&mut self) -> Result, M::Error>; } -impl<'de, T, M> Validate<'de, T, M> for M - where T: Deserialize<'de>, M: MapAccess<'de> +impl<'de, T, M> Validate<'de, T, M> for M +where + T: Deserialize<'de>, M: MapAccess<'de> { - fn validate_from(&mut self) -> Result, M::Error> { - use self::Wrapper as W; - use self::FilterOperator::*; - let wrapper = self.next_value()?; - match wrapper { - W::O(val) => { - match val { - Any | Eq(_) => Ok(val), - _ => { - Err(M::Error::custom( - "the `from` filter only supports the `eq` operator", - )) - } - } - }, - W::CC => { - Err(M::Error::custom( - "the `from` filter only supports the `eq` operator", - )) - } - } - } - fn validate_to(&mut self) -> Result>, M::Error> { - use self::Wrapper as W; - use self::FilterOperator::*; - let wrapper = self.next_value()?; - match wrapper { - W::O(val) => { - match val { - Any => Ok(Any), - Eq(address) => Ok(Eq(Some(address))), - _ => { - Err(M::Error::custom( - "the `to` filter only supports the `eq` or `action` operator", - )) - } - } - }, - W::CC => Ok(FilterOperator::Eq(None)), - } - } - fn validate_value(&mut self) -> Result, M::Error> { - use self::Wrapper as W; - let wrapper = self.next_value()?; - match wrapper { - W::O(val) => Ok(val), - W::CC => { - Err(M::Error::custom( - "the operator `action` is only supported by the `to` filter", - )) - } - } - } + fn validate_from(&mut self) -> Result, M::Error> { + use self::Wrapper as W; + use self::FilterOperator::*; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => { + match val { + Any | Eq(_) => Ok(val), + _ => { + Err(M::Error::custom( + "the `from` filter only supports the `eq` operator", + )) + } + } + }, + W::CC => { + Err(M::Error::custom( + "the `from` filter only supports the `eq` operator", + )) + } + } + } + fn validate_to(&mut self) -> Result>, M::Error> { + use self::Wrapper as W; + use self::FilterOperator::*; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => { + match val { + Any => Ok(Any), + Eq(address) => Ok(Eq(Some(address))), + _ => { + Err(M::Error::custom( + "the `to` filter only supports the `eq` or `action` operator", + )) + } + } + }, + W::CC => Ok(FilterOperator::Eq(None)), + } + } + fn validate_value(&mut self) -> Result, M::Error> { + use self::Wrapper as W; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => Ok(val), + W::CC => { + Err(M::Error::custom( + "the operator `action` is only supported by the `to` filter", + )) + } + } + } } impl<'de> Deserialize<'de> for FilterOptions { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct FilterOptionsVisitor; - impl<'de> Visitor<'de> for FilterOptionsVisitor { - type Value = FilterOptions; + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FilterOptionsVisitor; + impl<'de> Visitor<'de> for FilterOptionsVisitor { + type Value = FilterOptions; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - // "This Visitor expects to receive ..." - formatter.write_str("a map with one valid filter such as `from`, `to`, `gas`, `gas_price`, `value` or `nonce`") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // "This Visitor expects to receive ..." + formatter.write_str("a map with one valid filter such as `from`, `to`, `gas`, `gas_price`, `value` or `nonce`") + } - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - let mut filter = FilterOptions::default(); - while let Some(key) = map.next_key::()? { - match key.as_str() { - "from" => { - filter.from = map.validate_from()?; - }, - "to" => { - // Compiler cannot infer type, so set one (nothing specific for this method) - filter.to = Validate::<(), _>::validate_to(&mut map)?; - }, - "gas" => { - filter.gas = map.validate_value()?; - }, - "gas_price" => { - filter.gas_price = map.validate_value()?; - }, - "value" => { - filter.value = map.validate_value()?; - }, - "nonce" => { - filter.nonce = map.validate_value()?; - }, - unknown => { - return Err(M::Error::unknown_field( - unknown, - &["from", "to", "gas", "gas_price", "value", "nonce"], - )) - } - } - } + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + let mut filter = FilterOptions::default(); + while let Some(key) = map.next_key::()? { + match key.as_str() { + "from" => { + filter.from = map.validate_from()?; + }, + "to" => { + // Compiler cannot infer type, so set one (nothing specific for this method) + filter.to = Validate::<(), _>::validate_to(&mut map)?; + }, + "gas" => { + filter.gas = map.validate_value()?; + }, + "gas_price" => { + filter.gas_price = map.validate_value()?; + }, + "value" => { + filter.value = map.validate_value()?; + }, + "nonce" => { + filter.nonce = map.validate_value()?; + }, + unknown => { + return Err(M::Error::unknown_field( + unknown, + &["from", "to", "gas", "gas_price", "value", "nonce"], + )) + } + } + } + Ok(filter) + } + } - Ok(filter) - } - } + impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct WrapperVisitor { + data: PhantomData, + }; + impl<'de, T: Deserialize<'de>> Visitor<'de> for WrapperVisitor { + type Value = Wrapper; - impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapper { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct WrapperVisitor { - data: PhantomData, - }; - impl<'de, T: Deserialize<'de>> Visitor<'de> for WrapperVisitor { - type Value = Wrapper; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // "This Visitor expects to receive ..." + formatter.write_str( + "a map with one valid operator such as `eq`, `gt` or `lt`. \ + The to filter can also contain `action`", + ) + } - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - // "This Visitor expects to receive ..." - formatter.write_str( - "a map with one valid operator such as `eq`, `gt` or `lt`. \ - The to filter can also contain `action`", - ) - } + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + use self::Wrapper as W; + let mut counter = 0; + let mut f_op = Wrapper::O(FilterOperator::Any); - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - use self::Wrapper as W; - let mut counter = 0; - let mut f_op = Wrapper::O(FilterOperator::Any); + while let Some(key) = map.next_key::()? { + match key.as_str() { + "eq" => f_op = W::O(FilterOperator::Eq(map.next_value()?)), + "gt" => f_op = W::O(FilterOperator::GreaterThan(map.next_value()?)), + "lt" => f_op = W::O(FilterOperator::LessThan(map.next_value()?)), + "action" => { + match map.next_value()? { + "contract_creation" => { + f_op = W::CC; + }, + _ => { + return Err(M::Error::custom( + "`action` only supports the value `contract_creation`", + )) + } + } + } + unknown => { + // skip mentioning `action` since it's a special/rare + // case and might confuse the usage with other filters. + return Err(M::Error::unknown_field(unknown, &["eq", "gt", "lt"])); + } + } - while let Some(key) = map.next_key::()? { - match key.as_str() { - "eq" => f_op = W::O(FilterOperator::Eq(map.next_value()?)), - "gt" => f_op = W::O(FilterOperator::GreaterThan(map.next_value()?)), - "lt" => f_op = W::O(FilterOperator::LessThan(map.next_value()?)), - "action" => { - match map.next_value()? { - "contract_creation" => { - f_op = W::CC; - }, - _ => { - return Err(M::Error::custom( - "`action` only supports the value `contract_creation`", - )) - } - } - } - unknown => { - // skip mentioning `action` since it's a special/rare - // case and might confuse the usage with other filters. - return Err(M::Error::unknown_field(unknown, &["eq", "gt", "lt"])); - } - } + counter += 1; + } - counter += 1; - } + // Good practices ensured: only one operator per filter field is allowed. + // In case there is more than just one operator, this method must still process + // all of them, otherwise serde returns an error mentioning a trailing comma issue + // (even on valid JSON), which is misleading to the user of this software. + if counter > 1 { + return Err(M::Error::custom( + "only one operator per filter type allowed", + )); + } - // Good practices ensured: only one operator per filter field is allowed. - // In case there is more than just one operator, this method must still process - // all of them, otherwise serde returns an error mentioning a trailing comma issue - // (even on valid JSON), which is misleading to the user of this software. - if counter > 1 { - return Err(M::Error::custom( - "only one operator per filter type allowed", - )); - } + Ok(f_op) + } + } - Ok(f_op) - } - } + deserializer.deserialize_map(WrapperVisitor { data: PhantomData }) + } + } - deserializer.deserialize_map(WrapperVisitor { data: PhantomData }) - } - } - - deserializer.deserialize_map(FilterOptionsVisitor) - } + deserializer.deserialize_map(FilterOptionsVisitor) + } } #[cfg(test)] mod tests { - use ethereum_types::{Address, U256}; - use serde_json; - use super::*; + use ethereum_types::{Address, U256}; + use serde_json; + use super::*; use std::str::FromStr; - #[test] - fn valid_defaults() { - let default = FilterOptions::default(); - assert_eq!(default.from, FilterOperator::Any); - assert_eq!(default.to, FilterOperator::Any); - assert_eq!(default.gas, FilterOperator::Any); - assert_eq!(default.gas_price, FilterOperator::Any); - assert_eq!(default.value, FilterOperator::Any); - assert_eq!(default.nonce, FilterOperator::Any); + #[test] + fn valid_defaults() { + let default = FilterOptions::default(); + assert_eq!(default.from, FilterOperator::Any); + assert_eq!(default.to, FilterOperator::Any); + assert_eq!(default.gas, FilterOperator::Any); + assert_eq!(default.gas_price, FilterOperator::Any); + assert_eq!(default.value, FilterOperator::Any); + assert_eq!(default.nonce, FilterOperator::Any); - let json = r#"{}"#; - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, default); - } + let json = r#"{}"#; + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, default); + } - #[test] - fn valid_full_deserialization() { - let json = r#" - { - "from": { - "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" - }, - "to": { - "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" - }, - "gas": { - "eq": "0x493e0" - }, - "gas_price": { - "eq": "0x12a05f200" - }, - "value": { - "eq": "0x0" - }, - "nonce": { - "eq": "0x577" - } - } - "#; + #[test] + fn valid_full_deserialization() { + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + }, + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + }, + "gas": { + "eq": "0x493e0" + }, + "gas_price": { + "eq": "0x12a05f200" + }, + "value": { + "eq": "0x0" + }, + "nonce": { + "eq": "0x577" + } + } + "#; - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), - to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), - gas: FilterOperator::Eq(U256::from(300_000)), - gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), - value: FilterOperator::Eq(U256::from(0)), - nonce: FilterOperator::Eq(U256::from(1399)), - }) - } + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), + to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), + gas: FilterOperator::Eq(U256::from(300_000)), + gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), + value: FilterOperator::Eq(U256::from(0)), + nonce: FilterOperator::Eq(U256::from(1399)), + }) + } - #[test] - fn invalid_full_deserialization() { - // Invalid filter type `zyx` - let json = r#" - { - "from": { - "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" - }, - "to": { - "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" - }, - "zyx": { - "eq": "0x493e0" - }, - "gas_price": { - "eq": "0x12a05f200" - }, - "value": { - "eq": "0x0" - }, - "nonce": { - "eq": "0x577" - } - } - "#; + #[test] + fn invalid_full_deserialization() { + // Invalid filter type `zyx` + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + }, + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + }, + "zyx": { + "eq": "0x493e0" + }, + "gas_price": { + "eq": "0x12a05f200" + }, + "value": { + "eq": "0x0" + }, + "nonce": { + "eq": "0x577" + } + } + "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()) - } + let res = serde_json::from_str::(json); + assert!(res.is_err()) + } - #[test] - fn valid_from_operators() { - // Only one valid operator for from - let json = r#" - { - "from": { - "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), - ..default - }); - } + #[test] + fn valid_from_operators() { + // Only one valid operator for from + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), + ..default + }); + } - #[test] - fn invalid_from_operators() { - // Multiple operators are invalid - let json = r#" - { - "from": { - "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f", - "lt": "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_from_operators() { + // Multiple operators are invalid + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f", + "lt": "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Gt - let json = r#" - { - "from": { - "gt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Gt + let json = r#" + { + "from": { + "gt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Lt - let json = r#" - { - "from": { - "lt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Lt + let json = r#" + { + "from": { + "lt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action - let json = r#" - { - "from": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action + let json = r#" + { + "from": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "from": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "from": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } - #[test] - fn valid_to_operators() { - // Only two valid operator for to - // Eq - let json = r#" - { - "to": { - "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), - ..default.clone() - }); + #[test] + fn valid_to_operators() { + // Only two valid operator for to + // Eq + let json = r#" + { + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), + ..default.clone() + }); - // Action - let json = r#" - { - "to": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - to: FilterOperator::Eq(None), - ..default - }); - } + // Action + let json = r#" + { + "to": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + to: FilterOperator::Eq(None), + ..default + }); + } - #[test] - fn invalid_to_operators() { - // Multiple operators are invalid - let json = r#" - { - "to": { - "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6", - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_to_operators() { + // Multiple operators are invalid + let json = r#" + { + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6", + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Gt - let json = r#" - { - "to": { - "gt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Gt + let json = r#" + { + "to": { + "gt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Lt - let json = r#" - { - "to": { - "lt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Lt + let json = r#" + { + "to": { + "lt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action (invalid value, must be "contract_creation") - let json = r#" - { - "to": { - "action": "some_invalid_value" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action (invalid value, must be "contract_creation") + let json = r#" + { + "to": { + "action": "some_invalid_value" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "to": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "to": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } - #[test] - fn valid_gas_operators() { - // Eq - let json = r#" - { - "gas": { - "eq": "0x493e0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas: FilterOperator::Eq(U256::from(300_000)), - ..default.clone() - }); + #[test] + fn valid_gas_operators() { + // Eq + let json = r#" + { + "gas": { + "eq": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::Eq(U256::from(300_000)), + ..default.clone() + }); - // Gt - let json = r#" - { - "gas": { - "gt": "0x493e0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas: FilterOperator::GreaterThan(U256::from(300_000)), - ..default.clone() - }); + // Gt + let json = r#" + { + "gas": { + "gt": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::GreaterThan(U256::from(300_000)), + ..default.clone() + }); - // Lt - let json = r#" - { - "gas": { - "lt": "0x493e0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas: FilterOperator::LessThan(U256::from(300_000)), - ..default - }); - } + // Lt + let json = r#" + { + "gas": { + "lt": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::LessThan(U256::from(300_000)), + ..default + }); + } - #[test] - fn invalid_gas_operators() { - // Multiple operators are invalid - let json = r#" - { - "gas": { - "eq": "0x493e0", - "lt": "0x493e0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_gas_operators() { + // Multiple operators are invalid + let json = r#" + { + "gas": { + "eq": "0x493e0", + "lt": "0x493e0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action - let json = r#" - { - "gas": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action + let json = r#" + { + "gas": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "gas": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "gas": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } - #[test] - fn valid_gas_price_operators() { - // Eq - let json = r#" - { - "gas_price": { - "eq": "0x12a05f200" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), - ..default.clone() - }); + #[test] + fn valid_gas_price_operators() { + // Eq + let json = r#" + { + "gas_price": { + "eq": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), + ..default.clone() + }); - // Gt - let json = r#" - { - "gas_price": { - "gt": "0x12a05f200" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas_price: FilterOperator::GreaterThan(U256::from(5_000_000_000 as i64)), - ..default.clone() - }); + // Gt + let json = r#" + { + "gas_price": { + "gt": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::GreaterThan(U256::from(5_000_000_000 as i64)), + ..default.clone() + }); - // Lt - let json = r#" - { - "gas_price": { - "lt": "0x12a05f200" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - gas_price: FilterOperator::LessThan(U256::from(5_000_000_000 as i64)), - ..default - }); - } + // Lt + let json = r#" + { + "gas_price": { + "lt": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::LessThan(U256::from(5_000_000_000 as i64)), + ..default + }); + } - #[test] - fn invalid_gas_price_operators() { - // Multiple operators are invalid - let json = r#" - { - "gas_price": { - "eq": "0x12a05f200", - "lt": "0x12a05f200" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_gas_price_operators() { + // Multiple operators are invalid + let json = r#" + { + "gas_price": { + "eq": "0x12a05f200", + "lt": "0x12a05f200" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action - let json = r#" - { - "gas_price": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action + let json = r#" + { + "gas_price": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "gas_price": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "gas_price": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } - #[test] - fn valid_value_operators() { - // Eq - let json = r#" - { - "value": { - "eq": "0x0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - value: FilterOperator::Eq(U256::from(0)), - ..default.clone() - }); + #[test] + fn valid_value_operators() { + // Eq + let json = r#" + { + "value": { + "eq": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::Eq(U256::from(0)), + ..default.clone() + }); - // Gt - let json = r#" - { - "value": { - "gt": "0x0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - value: FilterOperator::GreaterThan(U256::from(0)), - ..default.clone() - }); + // Gt + let json = r#" + { + "value": { + "gt": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::GreaterThan(U256::from(0)), + ..default.clone() + }); - // Lt - let json = r#" - { - "value": { - "lt": "0x0" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - value: FilterOperator::LessThan(U256::from(0)), - ..default - }); - } + // Lt + let json = r#" + { + "value": { + "lt": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::LessThan(U256::from(0)), + ..default + }); + } - #[test] - fn invalid_value_operators() { - // Multiple operators are invalid - let json = r#" - { - "value": { - "eq": "0x0", - "lt": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_value_operators() { + // Multiple operators are invalid + let json = r#" + { + "value": { + "eq": "0x0", + "lt": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action - let json = r#" - { - "value": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action + let json = r#" + { + "value": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "value": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "value": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } - #[test] - fn valid_nonce_operators() { - // Eq - let json = r#" - { - "nonce": { - "eq": "0x577" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - nonce: FilterOperator::Eq(U256::from(1399)), - ..default.clone() - }); + #[test] + fn valid_nonce_operators() { + // Eq + let json = r#" + { + "nonce": { + "eq": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::Eq(U256::from(1399)), + ..default.clone() + }); - // Gt - let json = r#" - { - "nonce": { - "gt": "0x577" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - nonce: FilterOperator::GreaterThan(U256::from(1399)), - ..default.clone() - }); + // Gt + let json = r#" + { + "nonce": { + "gt": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::GreaterThan(U256::from(1399)), + ..default.clone() + }); - // Lt - let json = r#" - { - "nonce": { - "lt": "0x577" - } - } - "#; - let default = FilterOptions::default(); - let res = serde_json::from_str::(json).unwrap(); - assert_eq!(res, FilterOptions { - nonce: FilterOperator::LessThan(U256::from(1399)), - ..default - }); - } + // Lt + let json = r#" + { + "nonce": { + "lt": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::LessThan(U256::from(1399)), + ..default + }); + } - #[test] - fn invalid_nonce_operators() { - // Multiple operators are invalid - let json = r#" - { - "nonce": { - "eq": "0x577", - "lt": "0x577" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + #[test] + fn invalid_nonce_operators() { + // Multiple operators are invalid + let json = r#" + { + "nonce": { + "eq": "0x577", + "lt": "0x577" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Action - let json = r#" - { - "nonce": { - "action": "contract_creation" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); + // Action + let json = r#" + { + "nonce": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); - // Unknown operator - let json = r#" - { - "nonce": { - "abc": "0x0" - } - } - "#; - let res = serde_json::from_str::(json); - assert!(res.is_err()); - } + // Unknown operator + let json = r#" + { + "nonce": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index fef185e91..997ceb1bc 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -31,7 +31,7 @@ use ethcore_miner::work_notify::NotifyWork; use ethereum_types::{H256, U256, Address}; use futures::sync::mpsc; use io::IoChannel; -use miner::filter_options::{FilterOptions, FilterOperator}; +use miner::filter_options::FilterOptions; use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache}; use miner::{self, MinerService}; use parking_lot::{Mutex, RwLock}; @@ -1095,7 +1095,8 @@ impl miner::MinerService for Miner { max_len: usize, filter: Option, ordering: miner::PendingOrdering, - ) -> Vec> where + ) -> Vec> + where C: ChainInfo + Nonce + Sync, { let chain_info = chain.chain_info(); @@ -1106,7 +1107,7 @@ impl miner::MinerService for Miner { // those transactions are valid and will just be ready to be included in next block. let nonce_cap = None; - self.transaction_queue.pending( + let mut pending = self.transaction_queue.pending( CachedNonceClient::new(chain, &self.nonce_cache), pool::PendingSettings { block_number: chain_info.best_block_number, @@ -1115,77 +1116,26 @@ impl miner::MinerService for Miner { max_len, ordering, }, - ) + ); + + pending.retain(|tx| { + filter.as_ref().map_or(true, |filter| { + filter.matches(tx.signed()) + }) + }); + + pending }; - use miner::filter_options::FilterOperator::*; let from_pending = || { self.map_existing_pending_block(|sealing| { - // This filter is used for gas, gas price, value and nonce. - // Sender and receiver have their custom matches, since those - // allow/disallow different operators. - fn match_common_filter(operator: &FilterOperator, tx_value: &U256) -> bool { - match operator { - Eq(value) => tx_value == value, - GreaterThan(value) => tx_value > value, - LessThan(value) => tx_value < value, - // Will always occur on `Any`, other operators - // get handled during deserialization - _ => true, - } - } - sealing.transactions .iter() .map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone())) .map(Arc::new) - // Filter by sender .filter(|tx| { filter.as_ref().map_or(true, |filter| { - let sender = tx.signed().sender(); - match filter.from { - Eq(value) => sender == value, - // Will always occur on `Any`, other operators - // get handled during deserialization - _ => true, - } - }) - }) - // Filter by receiver - .filter(|tx| { - filter.as_ref().map_or(true, |filter| { - let receiver = (*tx.signed()).receiver(); - match filter.to { - // Could apply to `Some(Address)` or `None` (for contract creation) - Eq(value) => receiver == value, - // Will always occur on `Any`, other operators - // get handled during deserialization - _ => true, - } - }) - }) - // Filter by gas - .filter(|tx| { - filter.as_ref().map_or(true, |filter| { - match_common_filter(&filter.gas, &(*tx.signed()).gas) - }) - }) - // Filter by gas price - .filter(|tx| { - filter.as_ref().map_or(true, |filter| { - match_common_filter(&filter.gas_price, &(*tx.signed()).gas_price) - }) - }) - // Filter by tx value - .filter(|tx| { - filter.as_ref().map_or(true, |filter| { - match_common_filter(&filter.value, &(*tx.signed()).value) - }) - }) - // Filter by nonce - .filter(|tx| { - filter.as_ref().map_or(true, |filter| { - match_common_filter(&filter.nonce, &(*tx.signed()).nonce) + filter.matches(tx.signed()) }) }) .take(max_len) @@ -1520,7 +1470,7 @@ mod tests { use client_traits::ChainInfo; use client::ImportSealedBlock; - use miner::{MinerService, PendingOrdering}; + use miner::{MinerService, PendingOrdering, filter_options::FilterOperator}; use test_helpers::{ generate_dummy_client, generate_dummy_client_with_spec, TestBlockChainClient, EachBlockWith }; @@ -1937,4 +1887,93 @@ mod tests { assert!(received_error_msg == expected_error_msg); } + + fn filter_tester(option: PendingSet) { + let client = TestBlockChainClient::default(); + let mut miner = miner(); + miner.options.pending_set = option; + let transaction = transaction(); + let best_block = 10; + let mut sender = transaction.sender(); + + miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)).unwrap(); + + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(FilterOptions::default()), PendingOrdering::Priority).len(), + 1 + ); + + // sender filter + { + // reverse address for false match + for byte in sender.as_bytes_mut() { + *byte = !*byte; + } + let mut filter = FilterOptions::default(); + filter.from = FilterOperator::Eq(sender); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // receiver filter + { + let mut filter = FilterOptions::default(); + filter.to = FilterOperator::Eq(Some(sender)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // gas filter + { + let mut filter = FilterOptions::default(); + filter.gas = FilterOperator::LessThan(U256::from(100_000)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // gas_price filter + { + let mut filter = FilterOptions::default(); + filter.gas_price = FilterOperator::GreaterThan(U256::from(10)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // value filter + { + let mut filter = FilterOptions::default(); + filter.value = FilterOperator::Eq(U256::from(19)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // nonce filter + { + let mut filter = FilterOptions::default(); + filter.nonce = FilterOperator::GreaterThan(U256::from(10)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + } + + #[test] + fn transaction_filtering() { + filter_tester(PendingSet::AlwaysQueue); + filter_tester(PendingSet::AlwaysSealing); + filter_tester(PendingSet::SealingOrElseQueue); + } } diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index be7d83b57..27a4e9439 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -229,8 +229,23 @@ impl MinerService for TestMinerService { self.queued_transactions() } - fn ready_transactions_filtered(&self, _chain: &C, _max_len: usize, _filter: Option, _ordering: miner::PendingOrdering) -> Vec> { + fn ready_transactions_filtered( + &self, + _chain: &C, + max_len: usize, + filter: Option, + _ordering: miner::PendingOrdering + ) -> Vec> { self.queued_transactions() + .iter() + .cloned() + .filter(|tx| { + filter.as_ref().map_or(true, |filter| { + filter.matches(tx.signed()) + }) + }) + .take(max_len) + .collect() } fn pending_transaction_hashes(&self, _chain: &C) -> BTreeSet {