Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

support for rpc polling #504

Merged
merged 16 commits into from
Mar 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions ethcore/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block total difficulty.
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;

/// Get block hash.
fn block_hash(&self, id: BlockId) -> Option<H256>;

/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;

Expand Down Expand Up @@ -533,6 +536,11 @@ impl BlockChainClient for Client {
Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty)
}

fn block_hash(&self, id: BlockId) -> Option<H256> {
let chain = self.chain.read().unwrap();
Self::block_hash(&chain, id)
}

fn code(&self, address: &Address) -> Option<Bytes> {
self.state().code(address)
}
Expand Down
16 changes: 16 additions & 0 deletions ethcore/src/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ pub struct Filter {
pub topics: [Option<Vec<H256>>; 4],
}

impl Clone for Filter {
fn clone(&self) -> Self {
let mut topics = [None, None, None, None];
for i in 0..4 {
topics[i] = self.topics[i].clone();
}

Filter {
from_block: self.from_block.clone(),
to_block: self.to_block.clone(),
address: self.address.clone(),
topics: topics
}
}
}

impl Filter {
/// Returns combinations of each address and topic.
pub fn bloom_possibilities(&self) -> Vec<H2048> {
Expand Down
1 change: 1 addition & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ethash = { path = "../ethash" }
ethsync = { path = "../sync" }
clippy = { version = "0.0.44", optional = true }
rustc-serialize = "0.3"
transient-hashmap = "0.1"
serde_macros = { version = "0.7.0", optional = true }

[build-dependencies]
Expand Down
1 change: 1 addition & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern crate jsonrpc_http_server;
extern crate ethcore_util as util;
extern crate ethcore;
extern crate ethsync;
extern crate transient_hashmap;

#[cfg(feature = "serde_macros")]
include!("lib.rs.in");
Expand Down
21 changes: 21 additions & 0 deletions rpc/src/v1/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.

mod poll_manager;
mod poll_filter;

pub use self::poll_manager::PollManager;
pub use self::poll_filter::PollFilter;
10 changes: 10 additions & 0 deletions rpc/src/v1/helpers/poll_filter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Helper type with all filter possibilities.

use ethcore::filter::Filter;

#[derive(Clone)]
pub enum PollFilter {
Block,
PendingTransaction,
Logs(Filter)
}
144 changes: 144 additions & 0 deletions rpc/src/v1/helpers/poll_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.

// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.

//! Indexes all rpc poll requests.

use transient_hashmap::{TransientHashMap, Timer, StandardTimer};

/// Lifetime of poll (in seconds).
const POLL_LIFETIME: u64 = 60;

pub type PollId = usize;
pub type BlockNumber = u64;

pub struct PollInfo<F> {
pub filter: F,
pub block_number: BlockNumber
}

impl<F> Clone for PollInfo<F> where F: Clone {
fn clone(&self) -> Self {
PollInfo {
filter: self.filter.clone(),
block_number: self.block_number.clone()
}
}
}

/// Indexes all poll requests.
///
/// Lazily garbage collects unused polls info.
pub struct PollManager<F, T = StandardTimer> where T: Timer {
polls: TransientHashMap<PollId, PollInfo<F>, T>,
next_available_id: PollId
}

impl<F> PollManager<F, StandardTimer> {
/// Creates new instance of indexer.
pub fn new() -> Self {
PollManager::new_with_timer(Default::default())
}
}

impl<F, T> PollManager<F, T> where T: Timer {
pub fn new_with_timer(timer: T) -> Self {
PollManager {
polls: TransientHashMap::new_with_timer(POLL_LIFETIME, timer),
next_available_id: 0,
}
}

/// Returns id which can be used for new poll.
///
/// Stores information when last poll happend.
pub fn create_poll(&mut self, filter: F, block: BlockNumber) -> PollId {
self.polls.prune();
let id = self.next_available_id;
self.next_available_id += 1;
self.polls.insert(id, PollInfo {
filter: filter,
block_number: block,
});
id
}

/// Updates information when last poll happend.
pub fn update_poll(&mut self, id: &PollId, block: BlockNumber) {
self.polls.prune();
if let Some(info) = self.polls.get_mut(id) {
info.block_number = block;
}
}

/// Returns number of block when last poll happend.
pub fn get_poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
self.polls.prune();
self.polls.get(id)
}

/// Removes poll info.
pub fn remove_poll(&mut self, id: &PollId) {
self.polls.remove(id);
}
}

#[cfg(test)]
mod tests {
use std::cell::RefCell;
use transient_hashmap::Timer;
use v1::helpers::PollManager;

struct TestTimer<'a> {
time: &'a RefCell<i64>,
}

impl<'a> Timer for TestTimer<'a> {
fn get_time(&self) -> i64 {
*self.time.borrow()
}
}

#[test]
fn test_poll_indexer() {
let time = RefCell::new(0);
let timer = TestTimer {
time: &time,
};

let mut indexer = PollManager::new_with_timer(timer);
assert_eq!(indexer.create_poll(false, 20), 0);
assert_eq!(indexer.create_poll(true, 20), 1);

*time.borrow_mut() = 10;
indexer.update_poll(&0, 21);
assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false);
assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21);

*time.borrow_mut() = 30;
indexer.update_poll(&1, 23);
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);

*time.borrow_mut() = 75;
indexer.update_poll(&0, 30);
assert!(indexer.get_poll_info(&0).is_none());
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);

indexer.remove_poll(&1);
assert!(indexer.get_poll_info(&1).is_none());
}
}
90 changes: 81 additions & 9 deletions rpc/src/v1/impls/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! Eth rpc implementation.
use std::collections::HashMap;
use std::sync::{Arc, Weak, Mutex, RwLock};
use ethsync::{EthSync, SyncState};
use jsonrpc_core::*;
use util::numbers::*;
use util::sha3::*;
use util::standard::{RwLock, HashMap, Arc, Weak};
use util::rlp::encode;
use ethcore::client::*;
use ethcore::block::{IsBlock};
Expand All @@ -29,6 +30,7 @@ use ethcore::ethereum::Ethash;
use ethcore::ethereum::denominations::shannon;
use v1::traits::{Eth, EthFilter};
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log};
use v1::helpers::{PollFilter, PollManager};

/// Eth rpc implementation.
pub struct EthClient {
Expand Down Expand Up @@ -255,28 +257,98 @@ impl Eth for EthClient {

/// Eth filter rpc implementation.
pub struct EthFilterClient {
client: Weak<Client>
client: Weak<Client>,
polls: Mutex<PollManager<PollFilter>>,
}

impl EthFilterClient {
/// Creates new Eth filter client.
pub fn new(client: &Arc<Client>) -> Self {
EthFilterClient {
client: Arc::downgrade(client)
client: Arc::downgrade(client),
polls: Mutex::new(PollManager::new())
}
}
}

impl EthFilter for EthFilterClient {
fn new_block_filter(&self, _params: Params) -> Result<Value, Error> {
Ok(Value::U64(0))
fn new_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
let mut polls = self.polls.lock().unwrap();
let id = polls.create_poll(PollFilter::Logs(filter.into()), take_weak!(self.client).chain_info().best_block_number);
to_value(&U256::from(id))
})
}

fn new_pending_transaction_filter(&self, _params: Params) -> Result<Value, Error> {
Ok(Value::U64(1))
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let id = polls.create_poll(PollFilter::Block, take_weak!(self.client).chain_info().best_block_number);
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}

fn filter_changes(&self, _: Params) -> Result<Value, Error> {
to_value(&take_weak!(self.client).chain_info().best_block_hash).map(|v| Value::Array(vec![v]))
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => {
let mut polls = self.polls.lock().unwrap();
let id = polls.create_poll(PollFilter::PendingTransaction, take_weak!(self.client).chain_info().best_block_number);
to_value(&U256::from(id))
},
_ => Err(Error::invalid_params())
}
}

fn filter_changes(&self, params: Params) -> Result<Value, Error> {
let client = take_weak!(self.client);
from_params::<(Index,)>(params)
.and_then(|(index,)| {
let info = self.polls.lock().unwrap().get_poll_info(&index.value()).cloned();
match info {
None => Ok(Value::Array(vec![] as Vec<Value>)),
Some(info) => match info.filter {
PollFilter::Block => {
let current_number = client.chain_info().best_block_number;
let hashes = (info.block_number..current_number).into_iter()
.map(BlockId::Number)
.filter_map(|id| client.block_hash(id))
.collect::<Vec<H256>>();

self.polls.lock().unwrap().update_poll(&index.value(), current_number);

to_value(&hashes)
},
PollFilter::PendingTransaction => {
// TODO: fix implementation once TransactionQueue is merged
to_value(&vec![] as &Vec<H256>)
},
PollFilter::Logs(mut filter) => {
filter.from_block = BlockId::Number(info.block_number);
filter.to_block = BlockId::Latest;
let logs = client.logs(filter)
.into_iter()
.map(From::from)
.collect::<Vec<Log>>();

let current_number = client.chain_info().best_block_number;
self.polls.lock().unwrap().update_poll(&index.value(), current_number);

to_value(&logs)
}
}
}
})
}

fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
from_params::<(Index,)>(params)
.and_then(|(index,)| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a trailing , valid there?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is required here and it means that value is a tuple of one elem. And as a tuple it could be easily deserialised from Params::Array of len 1.

self.polls.lock().unwrap().remove_poll(&index.value());
to_value(&true)
})
}
}
1 change: 1 addition & 0 deletions rpc/src/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod impls;
mod types;
#[cfg(test)]
mod tests;
mod helpers;

pub use self::traits::{Web3, Eth, EthFilter, Net};
pub use self::impls::*;