Hash Content RPC method (#2355)

* Moving file fetching to separate crate.

* ethcore_hashContent

* Tests running on mocked fetch.

* Limiting size of downloadable assets
This commit is contained in:
Tomasz Drwięga
2016-09-27 16:27:06 +02:00
committed by Gav Wood
parent 3fb3f1f54e
commit d7bbc5cc3f
21 changed files with 486 additions and 173 deletions

View File

@@ -15,30 +15,33 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Ethcore-specific rpc implementation.
use std::sync::{Arc, Weak};
use std::{fs, io};
use std::sync::{mpsc, Arc, Weak};
use std::str::FromStr;
use std::collections::{BTreeMap};
use util::{RotatingLogger, Address};
use util::{RotatingLogger, Address, Mutex, sha3};
use util::misc::version_data;
use crypto::ecies;
use fetch::{Client as FetchClient, Fetch};
use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::{SyncProvider, ManageNetwork};
use ethcore::miner::MinerService;
use ethcore::client::{MiningBlockChainClient};
use jsonrpc_core::*;
use jsonrpc_core::{from_params, to_value, Value, Error, Params, Ready};
use v1::traits::Ethcore;
use v1::types::{Bytes, U256, H160, H512, Peers, Transaction};
use v1::types::{Bytes, U256, H160, H256, H512, Peers, Transaction};
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
use v1::helpers::params::expect_no_params;
/// Ethcore implementation.
pub struct EthcoreClient<C, M, S: ?Sized> where
pub struct EthcoreClient<C, M, S: ?Sized, F=FetchClient> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider {
S: SyncProvider,
F: Fetch {
client: Weak<C>,
miner: Weak<M>,
@@ -47,10 +50,14 @@ pub struct EthcoreClient<C, M, S: ?Sized> where
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>,
fetch: Mutex<F>
}
impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where C: MiningBlockChainClient, M: MinerService, S: SyncProvider {
/// Creates new `EthcoreClient`.
impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider, {
/// Creates new `EthcoreClient` with default `Fetch`.
pub fn new(
client: &Arc<C>,
miner: &Arc<M>,
@@ -60,6 +67,26 @@ impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where C: MiningBlockChainClient, M:
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>
) -> Self {
Self::with_fetch(client, miner, sync, net, logger, settings, signer)
}
}
impl<C, M, S: ?Sized, F> EthcoreClient<C, M, S, F> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
F: Fetch, {
/// Creates new `EthcoreClient` with customizable `Fetch`.
pub fn with_fetch(
client: &Arc<C>,
miner: &Arc<M>,
sync: &Arc<S>,
net: &Arc<ManageNetwork>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>
) -> Self {
EthcoreClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
@@ -68,6 +95,7 @@ impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where C: MiningBlockChainClient, M:
logger: logger,
settings: settings,
signer: signer,
fetch: Mutex::new(F::default()),
}
}
@@ -78,7 +106,11 @@ impl<C, M, S: ?Sized> EthcoreClient<C, M, S> where C: MiningBlockChainClient, M:
}
}
impl<C, M, S: ?Sized> Ethcore for EthcoreClient<C, M, S> where M: MinerService + 'static, C: MiningBlockChainClient + 'static, S: SyncProvider + 'static {
impl<C, M, S: ?Sized, F> Ethcore for EthcoreClient<C, M, S, F> where
M: MinerService + 'static,
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
F: Fetch + 'static {
fn transactions_limit(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
@@ -233,4 +265,42 @@ impl<C, M, S: ?Sized> Ethcore for EthcoreClient<C, M, S> where M: MinerService +
Ok(to_value(&take_weak!(self.miner).all_transactions().into_iter().map(Into::into).collect::<Vec<Transaction>>()))
}
fn hash_content(&self, params: Params, ready: Ready) {
let res = self.active().and_then(|_| from_params::<(String,)>(params));
let hash_content = |result| {
let path = try!(result);
let mut file = io::BufReader::new(try!(fs::File::open(&path)));
// Try to hash
let result = sha3(&mut file);
// Remove file (always)
try!(fs::remove_file(&path));
// Return the result
Ok(try!(result))
};
match res {
Err(e) => ready.ready(Err(e)),
Ok((url, )) => {
let (tx, rx) = mpsc::channel();
let res = self.fetch.lock().request_async(&url, Default::default(), Box::new(move |result| {
let result = hash_content(result)
.map_err(errors::from_fetch_error)
.map(|hash| to_value(H256::from(hash)));
// Receive ready and invoke with result.
let ready: Ready = rx.try_recv().expect("When on_done is invoked ready object is always sent.");
ready.ready(result);
}));
// Either invoke ready right away or transfer it to the closure.
if let Err(e) = res {
ready.ready(Err(errors::from_fetch_error(e)));
} else {
tx.send(ready).expect("Rx end is sent to on_done closure.");
}
}
}
}
}