Add filtering capability to parity_pendingTransactions (issue 8269) (#10506)

* expand parameters for pending_transactions()

* move ready_transactions content into filtered method

* apply filter based on tx_hash, sender or receiver

* call filtered transactions from RPC interface

* attempt at testing...

* replace parameters with _ on light client

* addes some comments

* removed uncompleted tests in miner.rs

* attempt at testing, needs more work...

* Formatting for ready_transactions_filtered

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Use map_or instead of if-let

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* additional map_or replacement

* change receiver type to Option<Option<Address>>

* remove faulty MiningService tests, test RPC later

* remove tx hash from pending transaction filtering

* as_unsigned() method for SignedTransaction

* implement Deserialize for FilterOptions

* implement Validate for MapAccess type

* additional formatting

* directly name cover in pattern matching

* test valid vull deserialization

* test valid sender operators

* test valid receiver operators

* test valid gas operators

* test valid gas price operators

* test valid value operators

* test valid nonce operators

* additional tsets for defaults, unknown filter types, unknown operators and some renames

* move filter_options to ethcore to avoid package cycling

* adjusted function/method parameters for FilterOptions

* implement filter for sender and receiver

* implement filter for gas

* implement filter for gas price, tx value and nonce

* improve filtering implementation; use common function, use combinators

* improved documentation for FilterOptions

* small documentation adjustments

* remove warnings

* replace FilterOperator::ContractCreation with FilterOperator::Eq(None)

* implement validate_receiver

* make small changes like renames, preamble

* cleanup code according to suggestions, add docs

* small improvements like formatting and newline
This commit is contained in:
Fabio Lama 2019-06-28 08:27:59 +00:00 committed by Seun LanLege
parent 7f02a08741
commit 3f61f2d8d9
11 changed files with 982 additions and 7 deletions

1
Cargo.lock generated
View File

@ -924,6 +924,7 @@ dependencies = [
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0", "stats 0.1.0",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"time-utils 0.1.0", "time-utils 0.1.0",

View File

@ -82,6 +82,7 @@ fetch = { path = "../util/fetch" }
kvdb-rocksdb = "0.1.3" kvdb-rocksdb = "0.1.3"
parity-runtime = { path = "../util/runtime" } parity-runtime = { path = "../util/runtime" }
rlp_compress = { path = "../util/rlp-compress" } rlp_compress = { path = "../util/rlp-compress" }
serde_json = "1.0"
tempdir = "0.3" tempdir = "0.3"
trie-standardmap = "0.12.4" trie-standardmap = "0.12.4"

View File

@ -122,6 +122,8 @@ extern crate blooms_db;
extern crate env_logger; extern crate env_logger;
#[cfg(test)] #[cfg(test)]
extern crate rlp_compress; extern crate rlp_compress;
#[cfg(test)]
extern crate serde_json;
#[macro_use] #[macro_use]
extern crate ethabi_derive; extern crate ethabi_derive;

View File

@ -0,0 +1,869 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum 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.
// Parity Ethereum 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
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
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct FilterOptions {
/// Contains the operator to filter the from value of the transaction
pub from: FilterOperator<Address>,
/// Contains the operator to filter the to value of the transaction
pub to: FilterOperator<Option<Address>>,
/// Contains the operator to filter the gas value of the transaction
pub gas: FilterOperator<U256>,
/// Contains the operator to filter the gas price value of the transaction
pub gas_price: FilterOperator<U256>,
/// Contains the operator to filter the transaction value
pub value: FilterOperator<U256>,
/// Contains the operator to filter the nonce value of the transaction
pub nonce: FilterOperator<U256>,
}
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,
}
}
}
/// The highly generic use of implementing Deserialize for FilterOperator
/// will result in a compiler error if the type FilterOperator::Eq(None)
/// gets returned explicitly. Therefore this Wrapper will be used for
/// deserialization, directly identifying the contract creation.
enum Wrapper<T> {
O(FilterOperator<T>),
CC, // Contract Creation
}
/// Available operators for filtering options.
/// The `from` filter only accepts Any and Eq(Address)
/// The `to` filter only accepts Any, Eq(Address) and Eq(None) for contract creation.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FilterOperator<T> {
Any,
Eq(T),
GreaterThan(T),
LessThan(T),
}
/// Since there are multiple operators which are not supported equally by all filters,
/// this trait will validate each of those operators. The corresponding method is called
/// inside the `Deserialize` -> `Visitor` implementation for FilterOperator. In case new
/// operators get introduced, a whitelist instead of a blacklist is used.
///
/// The `from` filter validates with `validate_from`
/// 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<FilterOperator<T>, M::Error>;
fn validate_to(&mut self) -> Result<FilterOperator<Option<Address>>, M::Error>;
fn validate_value(&mut self) -> Result<FilterOperator<T>, M::Error>;
}
impl<'de, T, M> Validate<'de, T, M> for M
where T: Deserialize<'de>, M: MapAccess<'de>
{
fn validate_from(&mut self) -> Result<FilterOperator<T>, 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<FilterOperator<Option<Address>>, 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<FilterOperator<T>, 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<D>(deserializer: D) -> Result<Self, D::Error>
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 visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut filter = FilterOptions::default();
while let Some(key) = map.next_key()? {
match key {
"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)
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapper<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct WrapperVisitor<T> {
data: PhantomData<T>,
};
impl<'de, T: Deserialize<'de>> Visitor<'de> for WrapperVisitor<T> {
type Value = Wrapper<T>;
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<M>(self, mut map: M) -> Result<Self::Value, M::Error>
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 {
"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;
}
// 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)
}
}
deserializer.deserialize_map(WrapperVisitor { data: PhantomData })
}
}
deserializer.deserialize_map(FilterOptionsVisitor)
}
}
#[cfg(test)]
mod tests {
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);
let json = r#"{}"#;
let res = serde_json::from_str::<FilterOptions>(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"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Gt
let json = r#"
{
"from": {
"gt": "0x5f3dffcf347944d3739b0805c934d86c8621997f"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Lt
let json = r#"
{
"from": {
"lt": "0x5f3dffcf347944d3739b0805c934d86c8621997f"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Action
let json = r#"
{
"from": {
"action": "contract_creation"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"from": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Gt
let json = r#"
{
"to": {
"gt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Lt
let json = r#"
{
"to": {
"lt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"to": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Action
let json = r#"
{
"gas": {
"action": "contract_creation"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"gas": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Action
let json = r#"
{
"gas_price": {
"action": "contract_creation"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"gas_price": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Action
let json = r#"
{
"value": {
"action": "contract_creation"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"value": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(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::<FilterOptions>(json);
assert!(res.is_err());
// Action
let json = r#"
{
"nonce": {
"action": "contract_creation"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
// Unknown operator
let json = r#"
{
"nonce": {
"abc": "0x0"
}
}
"#;
let res = serde_json::from_str::<FilterOptions>(json);
assert!(res.is_err());
}
}

View File

@ -31,6 +31,7 @@ use ethcore_miner::work_notify::NotifyWork;
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use futures::sync::mpsc; use futures::sync::mpsc;
use io::IoChannel; use io::IoChannel;
use miner::filter_options::{FilterOptions, FilterOperator};
use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache}; use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache};
use miner; use miner;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
@ -1050,6 +1051,19 @@ impl miner::MinerService for Miner {
-> Vec<Arc<VerifiedTransaction>> -> Vec<Arc<VerifiedTransaction>>
where where
C: ChainInfo + Nonce + Sync, C: ChainInfo + Nonce + Sync,
{
// No special filtering options applied (neither tx_hash, receiver or sender)
self.ready_transactions_filtered(chain, max_len, None, ordering)
}
fn ready_transactions_filtered<C>(
&self,
chain: &C,
max_len: usize,
filter: Option<FilterOptions>,
ordering: miner::PendingOrdering,
) -> Vec<Arc<VerifiedTransaction>> where
C: ChainInfo + Nonce + Sync,
{ {
let chain_info = chain.chain_info(); let chain_info = chain.chain_info();
@ -1071,12 +1085,76 @@ impl miner::MinerService for Miner {
) )
}; };
use miner::filter_options::FilterOperator::*;
let from_pending = || { let from_pending = || {
self.map_existing_pending_block(|sealing| { 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<U256>, tx_value: &U256) -> bool {
match operator {
Eq(value) => tx_value == value,
GreaterThan(value) => tx_value > value,
LessThan(value) => tx_value < value,
// Will always occure on `Any`, other operators
// get handled during deserialization
_ => true,
}
}
sealing.transactions sealing.transactions
.iter() .iter()
.map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone())) .map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone()))
.map(Arc::new) .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 occure 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 occure 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)
})
})
.take(max_len) .take(max_len)
.collect() .collect()
}, chain_info.best_block_number) }, chain_info.best_block_number)

View File

@ -20,12 +20,13 @@
//! Keeps track of transactions and currently sealed pending block. //! Keeps track of transactions and currently sealed pending block.
mod miner; mod miner;
mod filter_options;
pub mod pool_client; pub mod pool_client;
#[cfg(feature = "stratum")] #[cfg(feature = "stratum")]
pub mod stratum; pub mod stratum;
pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams, Author}; pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams, Author};
pub use self::filter_options::FilterOptions;
pub use ethcore_miner::local_accounts::LocalAccounts; pub use ethcore_miner::local_accounts::LocalAccounts;
pub use ethcore_miner::pool::PendingOrdering; pub use ethcore_miner::pool::PendingOrdering;
@ -184,6 +185,14 @@ pub trait MinerService : Send + Sync {
fn ready_transactions<C>(&self, chain: &C, max_len: usize, ordering: PendingOrdering) -> Vec<Arc<VerifiedTransaction>> fn ready_transactions<C>(&self, chain: &C, max_len: usize, ordering: PendingOrdering) -> Vec<Arc<VerifiedTransaction>>
where C: ChainInfo + Nonce + Sync; where C: ChainInfo + Nonce + Sync;
/// Get a list of all ready transactions either ordered by priority or unordered (cheaper), optionally filtered by hash, sender or receiver.
///
/// Depending on the settings may look in transaction pool or only in pending block.
/// If you don't need a full set of transactions, you can add `max_len` and create only a limited set of
/// transactions.
fn ready_transactions_filtered<C>(&self, chain: &C, max_len: usize, filter: Option<FilterOptions>, ordering: PendingOrdering) -> Vec<Arc<VerifiedTransaction>>
where C: ChainInfo + Nonce + Sync;
/// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet). /// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet).
fn queued_transactions(&self) -> Vec<Arc<VerifiedTransaction>>; fn queued_transactions(&self) -> Vec<Arc<VerifiedTransaction>>;

View File

@ -313,6 +313,14 @@ impl UnverifiedTransaction {
self.r.is_zero() && self.s.is_zero() self.r.is_zero() && self.s.is_zero()
} }
/// Returns transaction receiver, if any
pub fn receiver(&self) -> Option<Address> {
match self.unsigned.action {
Action::Create => None,
Action::Call(receiver) => Some(receiver),
}
}
/// Append object with a signature into RLP stream /// Append object with a signature into RLP stream
fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
s.begin_list(9); s.begin_list(9);

View File

@ -26,6 +26,7 @@ use ethstore::random_phrase;
use sync::{LightSyncInfo, LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; use sync::{LightSyncInfo, LightSyncProvider, LightNetworkDispatcher, ManageNetwork};
use updater::VersionInfo as UpdaterVersionInfo; use updater::VersionInfo as UpdaterVersionInfo;
use ethereum_types::{H64, H160, H256, H512, U64, U256}; use ethereum_types::{H64, H160, H256, H512, U64, U256};
use ethcore::miner::FilterOptions;
use ethcore_logger::RotatingLogger; use ethcore_logger::RotatingLogger;
use jsonrpc_core::{Result, BoxFuture}; use jsonrpc_core::{Result, BoxFuture};
@ -217,7 +218,7 @@ where
.map(Into::into) .map(Into::into)
} }
fn pending_transactions(&self, limit: Option<usize>) -> Result<Vec<Transaction>> { fn pending_transactions(&self, limit: Option<usize>, _filter: Option<FilterOptions>) -> Result<Vec<Transaction>> {
let txq = self.light_dispatch.transaction_queue.read(); let txq = self.light_dispatch.transaction_queue.read();
let chain_info = self.light_dispatch.client.chain_info(); let chain_info = self.light_dispatch.client.chain_info();
Ok( Ok(

View File

@ -22,7 +22,7 @@ use std::collections::BTreeMap;
use crypto::DEFAULT_MAC; use crypto::DEFAULT_MAC;
use ethereum_types::{Address, H64, H160, H256, H512, U64, U256}; use ethereum_types::{Address, H64, H160, H256, H512, U64, U256};
use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::client::{BlockChainClient, StateClient, Call};
use ethcore::miner::{self, MinerService}; use ethcore::miner::{self, MinerService, FilterOptions};
use ethcore::snapshot::{SnapshotService, RestorationStatus}; use ethcore::snapshot::{SnapshotService, RestorationStatus};
use ethcore::state::StateInfo; use ethcore::state::StateInfo;
use ethcore_logger::RotatingLogger; use ethcore_logger::RotatingLogger;
@ -245,10 +245,11 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
.map(Into::into) .map(Into::into)
} }
fn pending_transactions(&self, limit: Option<usize>) -> Result<Vec<Transaction>> { fn pending_transactions(&self, limit: Option<usize>, filter: Option<FilterOptions>) -> Result<Vec<Transaction>> {
let ready_transactions = self.miner.ready_transactions( let ready_transactions = self.miner.ready_transactions_filtered(
&*self.client, &*self.client,
limit.unwrap_or_else(usize::max_value), limit.unwrap_or_else(usize::max_value),
filter,
miner::PendingOrdering::Priority, miner::PendingOrdering::Priority,
); );

View File

@ -24,7 +24,7 @@ use ethcore::block::SealedBlock;
use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo};
use ethcore::engines::{Engine, signer::EngineSigner}; use ethcore::engines::{Engine, signer::EngineSigner};
use ethcore::error::Error; use ethcore::error::Error;
use ethcore::miner::{self, MinerService, AuthoringParams}; use ethcore::miner::{self, MinerService, AuthoringParams, FilterOptions};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::local_transactions::Status as LocalTransactionStatus;
use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use miner::pool::{verifier, VerifiedTransaction, QueueStatus};
@ -222,6 +222,10 @@ impl MinerService for TestMinerService {
self.queued_transactions() self.queued_transactions()
} }
fn ready_transactions_filtered<C>(&self, _chain: &C, _max_len: usize, _filter: Option<FilterOptions>, _ordering: miner::PendingOrdering) -> Vec<Arc<VerifiedTransaction>> {
self.queued_transactions()
}
fn pending_transaction_hashes<C>(&self, _chain: &C) -> BTreeSet<H256> { fn pending_transaction_hashes<C>(&self, _chain: &C) -> BTreeSet<H256> {
self.queued_transactions().into_iter().map(|tx| tx.signed().hash()).collect() self.queued_transactions().into_iter().map(|tx| tx.signed().hash()).collect()
} }

View File

@ -19,6 +19,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use ethereum_types::{H64, H160, H256, H512, U64, U256}; use ethereum_types::{H64, H160, H256, H512, U64, U256};
use ethcore::miner::FilterOptions;
use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_derive::rpc; use jsonrpc_derive::rpc;
use v1::types::{ use v1::types::{
@ -125,7 +126,7 @@ pub trait Parity {
/// Returns all pending transactions from transaction queue. /// Returns all pending transactions from transaction queue.
#[rpc(name = "parity_pendingTransactions")] #[rpc(name = "parity_pendingTransactions")]
fn pending_transactions(&self, Option<usize>) -> Result<Vec<Transaction>>; fn pending_transactions(&self, Option<usize>, Option<FilterOptions>) -> Result<Vec<Transaction>>;
/// Returns all transactions from transaction queue. /// Returns all transactions from transaction queue.
/// ///