Skip to content

Commit

Permalink
feat: support parity_pendingTransactions
Browse files Browse the repository at this point in the history
  • Loading branch information
mohoff committed Jan 19, 2021
1 parent 94c5c4b commit f41991a
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 2 deletions.
50 changes: 48 additions & 2 deletions src/api/parity.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
api::Namespace,
helpers::{self, CallFuture},
types::{Bytes, CallRequest},
rpc::Value,
types::{Bytes, CallRequest, ParityPendingTransactionFilter, Transaction},
Transport,
};

Expand Down Expand Up @@ -31,6 +32,23 @@ impl<T: Transport> Parity<T> {

CallFuture::new(self.transport.execute("parity_call", vec![reqs]))
}

/// Get pending transactions
/// Blocked by https://github.com/openethereum/openethereum/issues/159
pub fn pending_transactions(
&self,
limit: Option<usize>,
filter: Option<ParityPendingTransactionFilter>,
) -> CallFuture<Vec<Transaction>, T::Out> {
let params = match (limit, filter) {
(Some(l), Some(f)) => vec![l.into(), helpers::serialize(&f)],
(Some(l), _) => vec![l.into()],
(_, Some(f)) => vec![Value::Null, helpers::serialize(&f)],
_ => vec![],
};

CallFuture::new(self.transport.execute("parity_pendingTransactions", params))
}
}

#[cfg(test)]
Expand All @@ -39,10 +57,24 @@ mod tests {
use crate::{
api::Namespace,
rpc::Value,
types::{Address, CallRequest},
types::{Address, CallRequest, FilterCondition, ParityPendingTransactionFilterBuilder, Transaction, U64},
};
use hex_literal::hex;

const EXAMPLE_PENDING_TX: &str = r#"{
"hash": "0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b",
"nonce": "0x0",
"blockHash": null,
"blockNumber": null,
"transactionIndex": null,
"from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
"to": "0x85dd43d8a49eeb85d32cf465507dd71d507100c1",
"value": "0x7f110",
"gas": "0x7f110",
"gasPrice": "0x09184e72a000",
"input": "0x603880600c6000396000f300603880600c6000396000f3603880600c6000396000f360"
}"#;

rpc_test!(
Parity:call,
vec![
Expand Down Expand Up @@ -75,4 +107,18 @@ mod tests {
];
Value::Array(vec![Value::String("0x010203".into()), Value::String("0x7198ab".into()), Value::String("0xde763f".into())]) => vec![hex!("010203").into(), hex!("7198ab").into(), hex!("de763f").into()]
);

rpc_test!(
Parity:pending_transactions,
1,
ParityPendingTransactionFilterBuilder::default()
.from(FilterCondition::Eq(Address::from_low_u64_be(0x32)))
.gas_price(FilterCondition::Gt(U64::from(100_000_000_000 as u64)))
.build()
=> "parity_pendingTransactions",
vec![r#"1"#, r#"{"from":{"eq":"0x0000000000000000000000000000000000000032"},"gas_price":{"gt":"0x174876e800"}}"#]
;
Value::Array(vec![::serde_json::from_str(EXAMPLE_PENDING_TX).unwrap()])
=> vec![::serde_json::from_str::<Transaction>(EXAMPLE_PENDING_TX).unwrap()]
);
}
4 changes: 4 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod bytes;
mod bytes_array;
mod log;
mod parity_peers;
mod parity_pending_transaction;
mod recovery;
mod signed;
mod sync_state;
Expand All @@ -25,6 +26,9 @@ pub use self::{
parity_peers::{
EthProtocolInfo, ParityPeerInfo, ParityPeerType, PeerNetworkInfo, PeerProtocolsInfo, PipProtocolInfo,
},
parity_pending_transaction::{
FilterCondition, ParityPendingTransactionFilter, ParityPendingTransactionFilterBuilder, ToFilter,
},
recovery::{Recovery, RecoveryMessage},
signed::{SignedData, SignedTransaction, TransactionParameters},
sync_state::{SyncInfo, SyncState},
Expand Down
135 changes: 135 additions & 0 deletions src/types/parity_pending_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use serde::{
ser::{SerializeMap, Serializer},
Serialize,
};

use super::{Address, U256, U64};

/// Condition to filter pending transactions
#[derive(Clone)]
pub enum FilterCondition<T> {
/// Lower Than
Lt(T),
/// Equal
Eq(T),
/// Greater Than
Gt(T),
}

/// To Filter
#[derive(Clone)]
pub enum ToFilter {
/// Address
Address(Address),
/// Action (i.e. contract creation)
Action,
}

/// Filter for pending transactions (only openethereum/Parity)
#[derive(Clone, Default, Serialize)]
pub struct ParityPendingTransactionFilter {
/// From address
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<FilterCondition<Address>>,
/// To address or action, i.e. contract creation
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<ToFilter>,
/// Gas
#[serde(skip_serializing_if = "Option::is_none")]
pub gas: Option<FilterCondition<U64>>,
/// Gas Price
#[serde(skip_serializing_if = "Option::is_none")]
pub gas_price: Option<FilterCondition<U64>>,
/// Value
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<FilterCondition<U256>>,
/// Nonce
#[serde(skip_serializing_if = "Option::is_none")]
pub nonce: Option<FilterCondition<U256>>,
}

impl<T> Serialize for FilterCondition<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
match self {
Self::Lt(v) => map.serialize_entry("lt", v),
Self::Eq(v) => map.serialize_entry("eq", v),
Self::Gt(v) => map.serialize_entry("gt", v),
}?;
map.end()
}
}

impl Serialize for ToFilter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;

match self {
Self::Address(a) => map.serialize_entry("eq", a)?,
Self::Action => map.serialize_entry("action", "contract_creation")?,
}
map.end()
}
}

/// Filter Builder
#[derive(Default, Clone)]
pub struct ParityPendingTransactionFilterBuilder {
filter: ParityPendingTransactionFilter,
}

impl ParityPendingTransactionFilterBuilder {
/// Sets `from`
pub fn from(mut self, from: FilterCondition<Address>) -> Self {
if let FilterCondition::Eq(_) = from {
self.filter.from = Some(from);
self
} else {
panic!("Must use FilterConditon::Eq to apply filter for `from` address")
}
}

/// Sets `to`
pub fn to(mut self, to_or_action: ToFilter) -> Self {
self.filter.to = Some(to_or_action);
self
}

/// Sets `gas`
pub fn gas(mut self, gas: FilterCondition<U64>) -> Self {
self.filter.gas = Some(gas);
self
}

/// Sets `gas_price`
pub fn gas_price(mut self, gas_price: FilterCondition<U64>) -> Self {
self.filter.gas_price = Some(gas_price);
self
}

/// Sets `value`
pub fn value(mut self, value: FilterCondition<U256>) -> Self {
self.filter.value = Some(value);
self
}

/// Sets `nonce`
pub fn nonce(mut self, nonce: FilterCondition<U256>) -> Self {
self.filter.nonce = Some(nonce);
self
}

/// Returns filter
pub fn build(&self) -> ParityPendingTransactionFilter {
self.filter.clone()
}
}

0 comments on commit f41991a

Please sign in to comment.