Merge remote-tracking branch 'origin/master' into vmtracing
This commit is contained in:
@@ -18,7 +18,6 @@ ethcore-util = { path = "../util" }
|
||||
ethcore = { path = "../ethcore" }
|
||||
ethash = { path = "../ethash" }
|
||||
ethsync = { path = "../sync" }
|
||||
ethminer = { path = "../miner" }
|
||||
ethjson = { path = "../json" }
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
rustc-serialize = "0.3"
|
||||
@@ -34,4 +33,4 @@ syntex = "^0.32.0"
|
||||
[features]
|
||||
default = ["serde_codegen"]
|
||||
nightly = ["serde_macros"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethminer/dev"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
|
||||
|
||||
@@ -30,7 +30,6 @@ extern crate jsonrpc_http_server;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore;
|
||||
extern crate ethsync;
|
||||
extern crate ethminer;
|
||||
extern crate transient_hashmap;
|
||||
extern crate json_ipc_server as ipc;
|
||||
|
||||
@@ -45,12 +44,26 @@ use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||
|
||||
pub use jsonrpc_http_server::{Server, RpcServerError};
|
||||
pub mod v1;
|
||||
pub use v1::{SigningQueue, ConfirmationsQueue};
|
||||
|
||||
/// An object that can be extended with `IoDelegates`
|
||||
pub trait Extendable {
|
||||
/// Add `Delegate` to this object.
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>);
|
||||
}
|
||||
|
||||
/// Http server.
|
||||
pub struct RpcServer {
|
||||
handler: Arc<jsonrpc_core::io::IoHandler>,
|
||||
}
|
||||
|
||||
impl Extendable for RpcServer {
|
||||
/// Add io delegate.
|
||||
fn add_delegate<D: Send + Sync + 'static>(&self, delegate: IoDelegate<D>) {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcServer {
|
||||
/// Construct new http server object.
|
||||
pub fn new() -> RpcServer {
|
||||
@@ -59,11 +72,6 @@ impl RpcServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add io delegate.
|
||||
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
|
||||
let cors_domains = cors_domains.into_iter()
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
mod poll_manager;
|
||||
mod poll_filter;
|
||||
mod signing_queue;
|
||||
|
||||
pub use self::poll_manager::PollManager;
|
||||
pub use self::poll_filter::PollFilter;
|
||||
pub use self::signing_queue::{ConfirmationsQueue, SigningQueue};
|
||||
|
||||
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
108
rpc/src/v1/helpers/signing_queue.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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/>.
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use v1::types::{TransactionRequest, TransactionConfirmation};
|
||||
use util::U256;
|
||||
|
||||
/// A queue of transactions awaiting to be confirmed and signed.
|
||||
pub trait SigningQueue: Send + Sync {
|
||||
/// Add new request to the queue.
|
||||
fn add_request(&self, transaction: TransactionRequest) -> U256;
|
||||
|
||||
/// Remove request from the queue.
|
||||
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation>;
|
||||
|
||||
/// Return copy of all the requests in the queue.
|
||||
fn requests(&self) -> Vec<TransactionConfirmation>;
|
||||
}
|
||||
|
||||
/// Queue for all unconfirmed transactions.
|
||||
pub struct ConfirmationsQueue {
|
||||
id: Mutex<U256>,
|
||||
queue: Mutex<HashMap<U256, TransactionConfirmation>>,
|
||||
}
|
||||
|
||||
impl Default for ConfirmationsQueue {
|
||||
fn default() -> Self {
|
||||
ConfirmationsQueue {
|
||||
id: Mutex::new(U256::from(0)),
|
||||
queue: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningQueue for ConfirmationsQueue {
|
||||
fn add_request(&self, transaction: TransactionRequest) -> U256 {
|
||||
// Increment id
|
||||
let id = {
|
||||
let mut last_id = self.id.lock().unwrap();
|
||||
*last_id = *last_id + U256::from(1);
|
||||
*last_id
|
||||
};
|
||||
let mut queue = self.queue.lock().unwrap();
|
||||
queue.insert(id, TransactionConfirmation {
|
||||
id: id,
|
||||
transaction: transaction,
|
||||
});
|
||||
id
|
||||
}
|
||||
|
||||
fn remove_request(&self, id: U256) -> Option<TransactionConfirmation> {
|
||||
self.queue.lock().unwrap().remove(&id)
|
||||
}
|
||||
|
||||
fn requests(&self) -> Vec<TransactionConfirmation> {
|
||||
let queue = self.queue.lock().unwrap();
|
||||
queue.values().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use util::hash::Address;
|
||||
use util::numbers::U256;
|
||||
use v1::types::TransactionRequest;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_work_for_hashset() {
|
||||
// given
|
||||
let queue = ConfirmationsQueue::default();
|
||||
|
||||
let request = TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from(2)),
|
||||
gas_price: None,
|
||||
gas: None,
|
||||
value: Some(U256::from(10_000_000)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
};
|
||||
|
||||
// when
|
||||
queue.add_request(request.clone());
|
||||
let all = queue.requests();
|
||||
|
||||
// then
|
||||
assert_eq!(all.len(), 1);
|
||||
let el = all.get(0).unwrap();
|
||||
assert_eq!(el.id, U256::from(1));
|
||||
assert_eq!(el.transaction, request);
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,16 @@
|
||||
|
||||
extern crate ethash;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Weak, Mutex};
|
||||
use std::ops::Deref;
|
||||
use ethsync::{SyncProvider, SyncState};
|
||||
use ethminer::{MinerService, ExternalMinerService};
|
||||
use ethcore::miner::{MinerService, ExternalMinerService};
|
||||
use jsonrpc_core::*;
|
||||
use util::numbers::*;
|
||||
use util::sha3::*;
|
||||
use util::rlp::{encode, decode, UntrustedRlp, View};
|
||||
use util::keys::store::AccountProvider;
|
||||
use ethcore::client::{BlockChainClient, BlockID, TransactionID, UncleID};
|
||||
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
|
||||
use ethcore::block::IsBlock;
|
||||
use ethcore::views::*;
|
||||
use ethcore::ethereum::Ethash;
|
||||
@@ -36,15 +35,14 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act
|
||||
use ethcore::log_entry::LogEntry;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use self::ethash::SeedHashCompute;
|
||||
use v1::traits::{Eth, EthFilter};
|
||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||
use v1::helpers::{PollFilter, PollManager};
|
||||
use v1::impls::{dispatch_transaction, sign_and_dispatch};
|
||||
use v1::traits::Eth;
|
||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||
use v1::impls::dispatch_transaction;
|
||||
use serde;
|
||||
|
||||
/// Eth rpc implementation.
|
||||
pub struct EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient,
|
||||
C: MiningBlockChainClient,
|
||||
S: SyncProvider,
|
||||
A: AccountProvider,
|
||||
M: MinerService,
|
||||
@@ -59,7 +57,7 @@ pub struct EthClient<C, S, A, M, EM> where
|
||||
}
|
||||
|
||||
impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient,
|
||||
C: MiningBlockChainClient,
|
||||
S: SyncProvider,
|
||||
A: AccountProvider,
|
||||
M: MinerService,
|
||||
@@ -170,6 +168,25 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts();
|
||||
|
||||
let pending_logs = receipts.into_iter()
|
||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||
.collect::<Vec<(H256, LogEntry)>>();
|
||||
|
||||
let result = pending_logs.into_iter()
|
||||
.filter(|pair| filter.matches(&pair.1))
|
||||
.map(|pair| {
|
||||
let mut log = Log::from(pair.1);
|
||||
log.transaction_hash = Some(pair.0);
|
||||
log
|
||||
})
|
||||
.collect();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
|
||||
|
||||
fn params_len(params: &Params) -> usize {
|
||||
@@ -193,25 +210,6 @@ fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNum
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
|
||||
let receipts = miner.pending_receipts();
|
||||
|
||||
let pending_logs = receipts.into_iter()
|
||||
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
|
||||
.collect::<Vec<(H256, LogEntry)>>();
|
||||
|
||||
let result = pending_logs.into_iter()
|
||||
.filter(|pair| filter.matches(&pair.1))
|
||||
.map(|pair| {
|
||||
let mut log = Log::from(pair.1);
|
||||
log.transaction_hash = Some(pair.0);
|
||||
log
|
||||
})
|
||||
.collect();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// must be in range [-32099, -32000]
|
||||
const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
|
||||
|
||||
@@ -224,7 +222,7 @@ fn make_unsupported_err() -> Error {
|
||||
}
|
||||
|
||||
impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
C: BlockChainClient + 'static,
|
||||
C: MiningBlockChainClient + 'static,
|
||||
S: SyncProvider + 'static,
|
||||
A: AccountProvider + 'static,
|
||||
M: MinerService + 'static,
|
||||
@@ -496,23 +494,6 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
})
|
||||
}
|
||||
|
||||
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
|
||||
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&request.from) {
|
||||
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
||||
Err(_) => to_value(&H256::zero())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Bytes, )>(params)
|
||||
.and_then(|(raw_transaction, )| {
|
||||
@@ -563,186 +544,3 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Eth filter rpc implementation.
|
||||
pub struct EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
polls: Mutex<PollManager<PollFilter>>,
|
||||
}
|
||||
|
||||
impl<C, M> EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new Eth filter client.
|
||||
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
EthFilterClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
polls: Mutex::new(PollManager::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
C: BlockChainClient + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Filter,)>(params)
|
||||
.and_then(|(filter,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
||||
to_value(&U256::from(id))
|
||||
})
|
||||
}
|
||||
|
||||
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 new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||
|
||||
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 mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll_mut(&index.value()) {
|
||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
Some(filter) => match *filter {
|
||||
PollFilter::Block(ref mut block_number) => {
|
||||
// + 1, cause we want to return hashes including current block hash.
|
||||
let current_number = client.chain_info().best_block_number + 1;
|
||||
let hashes = (*block_number..current_number).into_iter()
|
||||
.map(BlockID::Number)
|
||||
.filter_map(|id| client.block_hash(id))
|
||||
.collect::<Vec<H256>>();
|
||||
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&hashes)
|
||||
},
|
||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||
// get hashes of pending transactions
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||
|
||||
let new_hashes =
|
||||
{
|
||||
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
||||
|
||||
// find all new hashes
|
||||
current_hashes
|
||||
.iter()
|
||||
.filter(|hash| !previous_hashes_set.contains(hash))
|
||||
.cloned()
|
||||
.collect::<Vec<H256>>()
|
||||
};
|
||||
|
||||
// save all hashes of pending transactions
|
||||
*previous_hashes = current_hashes;
|
||||
|
||||
// return new hashes
|
||||
to_value(&new_hashes)
|
||||
},
|
||||
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
||||
// retrive the current block number
|
||||
let current_number = client.chain_info().best_block_number;
|
||||
|
||||
// check if we need to check pending hashes
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
|
||||
// build appropriate filter
|
||||
let mut filter: EthcoreFilter = filter.clone().into();
|
||||
filter.from_block = BlockID::Number(*block_number);
|
||||
filter.to_block = BlockID::Latest;
|
||||
|
||||
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
||||
let mut logs = client.logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
// additionally retrieve pending logs
|
||||
if include_pending {
|
||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
||||
|
||||
// remove logs about which client was already notified about
|
||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||
.filter(|p| !previous_logs.contains(p))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// save all logs retrieved by client
|
||||
*previous_logs = pending_logs.into_iter().collect();
|
||||
|
||||
// append logs array with new pending logs
|
||||
logs.extend(new_pending_logs);
|
||||
}
|
||||
|
||||
// save current block number as next from block number
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&logs)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll(&index.value()) {
|
||||
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = filter.clone().into();
|
||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
||||
}
|
||||
|
||||
to_value(&logs)
|
||||
},
|
||||
// just empty array
|
||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||
to_value(&true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
214
rpc/src/v1/impls/eth_filter.rs
Normal file
214
rpc/src/v1/impls/eth_filter.rs
Normal file
@@ -0,0 +1,214 @@
|
||||
// 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/>.
|
||||
|
||||
//! Eth Filter RPC implementation
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Weak, Mutex};
|
||||
use std::collections::HashSet;
|
||||
use jsonrpc_core::*;
|
||||
use util::numbers::*;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::client::{BlockChainClient, BlockID};
|
||||
use v1::traits::EthFilter;
|
||||
use v1::types::{BlockNumber, Index, Filter, Log};
|
||||
use v1::helpers::{PollFilter, PollManager};
|
||||
use v1::impls::eth::pending_logs;
|
||||
|
||||
|
||||
/// Eth filter rpc implementation.
|
||||
pub struct EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
polls: Mutex<PollManager<PollFilter>>,
|
||||
}
|
||||
|
||||
impl<C, M> EthFilterClient<C, M> where
|
||||
C: BlockChainClient,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new Eth filter client.
|
||||
pub fn new(client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
EthFilterClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
polls: Mutex::new(PollManager::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, M> EthFilter for EthFilterClient<C, M> where
|
||||
C: BlockChainClient + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Filter,)>(params)
|
||||
.and_then(|(filter,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let block_number = take_weak!(self.client).chain_info().best_block_number;
|
||||
let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter));
|
||||
to_value(&U256::from(id))
|
||||
})
|
||||
}
|
||||
|
||||
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 new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
match params {
|
||||
Params::None => {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
|
||||
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
|
||||
|
||||
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 mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll_mut(&index.value()) {
|
||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
Some(filter) => match *filter {
|
||||
PollFilter::Block(ref mut block_number) => {
|
||||
// + 1, cause we want to return hashes including current block hash.
|
||||
let current_number = client.chain_info().best_block_number + 1;
|
||||
let hashes = (*block_number..current_number).into_iter()
|
||||
.map(BlockID::Number)
|
||||
.filter_map(|id| client.block_hash(id))
|
||||
.collect::<Vec<H256>>();
|
||||
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&hashes)
|
||||
},
|
||||
PollFilter::PendingTransaction(ref mut previous_hashes) => {
|
||||
// get hashes of pending transactions
|
||||
let current_hashes = take_weak!(self.miner).pending_transactions_hashes();
|
||||
|
||||
let new_hashes =
|
||||
{
|
||||
let previous_hashes_set = previous_hashes.iter().collect::<HashSet<_>>();
|
||||
|
||||
// find all new hashes
|
||||
current_hashes
|
||||
.iter()
|
||||
.filter(|hash| !previous_hashes_set.contains(hash))
|
||||
.cloned()
|
||||
.collect::<Vec<H256>>()
|
||||
};
|
||||
|
||||
// save all hashes of pending transactions
|
||||
*previous_hashes = current_hashes;
|
||||
|
||||
// return new hashes
|
||||
to_value(&new_hashes)
|
||||
},
|
||||
PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => {
|
||||
// retrive the current block number
|
||||
let current_number = client.chain_info().best_block_number;
|
||||
|
||||
// check if we need to check pending hashes
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
|
||||
// build appropriate filter
|
||||
let mut filter: EthcoreFilter = filter.clone().into();
|
||||
filter.from_block = BlockID::Number(*block_number);
|
||||
filter.to_block = BlockID::Latest;
|
||||
|
||||
// retrieve logs in range from_block..min(BlockID::Latest..to_block)
|
||||
let mut logs = client.logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
// additionally retrieve pending logs
|
||||
if include_pending {
|
||||
let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter);
|
||||
|
||||
// remove logs about which client was already notified about
|
||||
let new_pending_logs: Vec<_> = pending_logs.iter()
|
||||
.filter(|p| !previous_logs.contains(p))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// save all logs retrieved by client
|
||||
*previous_logs = pending_logs.into_iter().collect();
|
||||
|
||||
// append logs array with new pending logs
|
||||
logs.extend(new_pending_logs);
|
||||
}
|
||||
|
||||
// save current block number as next from block number
|
||||
*block_number = current_number;
|
||||
|
||||
to_value(&logs)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_logs(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
let mut polls = self.polls.lock().unwrap();
|
||||
match polls.poll(&index.value()) {
|
||||
Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = filter.clone().into();
|
||||
let mut logs = take_weak!(self.client).logs(filter.clone())
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>();
|
||||
|
||||
if include_pending {
|
||||
logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter));
|
||||
}
|
||||
|
||||
to_value(&logs)
|
||||
},
|
||||
// just empty array
|
||||
_ => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn uninstall_filter(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Index,)>(params)
|
||||
.and_then(|(index,)| {
|
||||
self.polls.lock().unwrap().remove_poll(&index.value());
|
||||
to_value(&true)
|
||||
})
|
||||
}
|
||||
}
|
||||
111
rpc/src/v1/impls/eth_signing.rs
Normal file
111
rpc/src/v1/impls/eth_signing.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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/>.
|
||||
|
||||
//! Eth Signing RPC implementation.
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use jsonrpc_core::*;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use util::numbers::*;
|
||||
use util::keys::store::AccountProvider;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::traits::EthSigning;
|
||||
use v1::types::TransactionRequest;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
|
||||
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct EthSigningQueueClient {
|
||||
queue: Weak<ConfirmationsQueue>,
|
||||
}
|
||||
|
||||
impl EthSigningQueueClient {
|
||||
/// Creates a new signing queue client given shared signing queue.
|
||||
pub fn new(queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||
EthSigningQueueClient {
|
||||
queue: Arc::downgrade(queue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EthSigning for EthSigningQueueClient {
|
||||
|
||||
fn sign(&self, _params: Params) -> Result<Value, Error> {
|
||||
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
|
||||
rpc_unimplemented!()
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let queue = take_weak!(self.queue);
|
||||
queue.add_request(request);
|
||||
// TODO [ToDr] Block and wait for confirmation?
|
||||
to_value(&H256::zero())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient,
|
||||
A: AccountProvider,
|
||||
M: MinerService {
|
||||
client: Weak<C>,
|
||||
accounts: Weak<A>,
|
||||
miner: Weak<M>,
|
||||
}
|
||||
|
||||
impl<C, A, M> EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient,
|
||||
A: AccountProvider,
|
||||
M: MinerService {
|
||||
|
||||
/// Creates new EthClient.
|
||||
pub fn new(client: &Arc<C>, accounts: &Arc<A>, miner: &Arc<M>)
|
||||
-> Self {
|
||||
EthSigningUnsafeClient {
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
accounts: Arc::downgrade(accounts),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, A, M> EthSigning for EthSigningUnsafeClient<C, A, M> where
|
||||
C: MiningBlockChainClient + 'static,
|
||||
A: AccountProvider + 'static,
|
||||
M: MinerService + 'static {
|
||||
|
||||
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(Address, H256)>(params).and_then(|(addr, msg)| {
|
||||
to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero()))
|
||||
})
|
||||
}
|
||||
|
||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(TransactionRequest, )>(params)
|
||||
.and_then(|(request, )| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
match accounts.account_secret(&request.from) {
|
||||
Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret),
|
||||
Err(_) => to_value(&H256::zero())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,10 +22,10 @@ use std::sync::{Arc, Weak};
|
||||
use std::ops::Deref;
|
||||
use std::collections::BTreeMap;
|
||||
use jsonrpc_core::*;
|
||||
use ethminer::MinerService;
|
||||
use ethcore::miner::MinerService;
|
||||
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
||||
use ethcore::client::BlockChainClient;
|
||||
use ethcore::trace::VMTrace;
|
||||
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
||||
use v1::traits::Ethcore;
|
||||
use v1::types::{Bytes, CallRequest};
|
||||
|
||||
|
||||
@@ -31,24 +31,30 @@ macro_rules! rpc_unimplemented {
|
||||
|
||||
mod web3;
|
||||
mod eth;
|
||||
mod eth_filter;
|
||||
mod eth_signing;
|
||||
mod net;
|
||||
mod personal;
|
||||
mod personal_signer;
|
||||
mod ethcore;
|
||||
mod traces;
|
||||
mod rpc;
|
||||
|
||||
pub use self::web3::Web3Client;
|
||||
pub use self::eth::{EthClient, EthFilterClient};
|
||||
pub use self::eth::EthClient;
|
||||
pub use self::eth_filter::EthFilterClient;
|
||||
pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient};
|
||||
pub use self::net::NetClient;
|
||||
pub use self::personal::PersonalClient;
|
||||
pub use self::personal_signer::SignerClient;
|
||||
pub use self::ethcore::EthcoreClient;
|
||||
pub use self::traces::TracesClient;
|
||||
pub use self::rpc::RpcClient;
|
||||
|
||||
use v1::types::TransactionRequest;
|
||||
use std::sync::Weak;
|
||||
use ethminer::{AccountDetails, MinerService};
|
||||
use ethcore::client::BlockChainClient;
|
||||
use ethcore::miner::{AccountDetails, MinerService};
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::transaction::{Action, SignedTransaction, Transaction};
|
||||
use util::numbers::*;
|
||||
use util::rlp::encode;
|
||||
@@ -56,7 +62,7 @@ use util::bytes::ToPretty;
|
||||
use jsonrpc_core::{Error, to_value, Value};
|
||||
|
||||
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
||||
where C: BlockChainClient, M: MinerService {
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
let hash = signed_transaction.hash();
|
||||
|
||||
let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| {
|
||||
@@ -70,7 +76,7 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
|
||||
}
|
||||
|
||||
fn sign_and_dispatch<C, M>(client: &Weak<C>, miner: &Weak<M>, request: TransactionRequest, secret: H256) -> Result<Value, Error>
|
||||
where C: BlockChainClient, M: MinerService {
|
||||
where C: MiningBlockChainClient, M: MinerService {
|
||||
let client = take_weak!(client);
|
||||
let miner = take_weak!(miner);
|
||||
|
||||
@@ -92,4 +98,4 @@ fn sign_and_dispatch<C, M>(client: &Weak<C>, miner: &Weak<M>, request: Transacti
|
||||
|
||||
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
|
||||
dispatch_transaction(&*client, &*miner, signed_transaction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,21 +20,21 @@ use jsonrpc_core::*;
|
||||
use v1::traits::Personal;
|
||||
use v1::types::TransactionRequest;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
use util::keys::store::*;
|
||||
use util::keys::store::AccountProvider;
|
||||
use util::numbers::*;
|
||||
use ethcore::client::BlockChainClient;
|
||||
use ethminer::MinerService;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct PersonalClient<A, C, M>
|
||||
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
accounts: Weak<A>,
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
}
|
||||
|
||||
impl<A, C, M> PersonalClient<A, C, M>
|
||||
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
/// Creates new PersonalClient
|
||||
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>) -> Self {
|
||||
PersonalClient {
|
||||
@@ -46,7 +46,7 @@ impl<A, C, M> PersonalClient<A, C, M>
|
||||
}
|
||||
|
||||
impl<A: 'static, C: 'static, M: 'static> Personal for PersonalClient<A, C, M>
|
||||
where A: AccountProvider, C: BlockChainClient, M: MinerService {
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
match store.accounts() {
|
||||
|
||||
93
rpc/src/v1/impls/personal_signer.rs
Normal file
93
rpc/src/v1/impls/personal_signer.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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/>.
|
||||
|
||||
//! Transactions Confirmations (personal) rpc implementation
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use jsonrpc_core::*;
|
||||
use v1::traits::PersonalSigner;
|
||||
use v1::types::TransactionModification;
|
||||
use v1::impls::sign_and_dispatch;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use util::keys::store::AccountProvider;
|
||||
use util::numbers::*;
|
||||
use ethcore::client::MiningBlockChainClient;
|
||||
use ethcore::miner::MinerService;
|
||||
|
||||
/// Transactions confirmation (personal) rpc implementation.
|
||||
pub struct SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
queue: Weak<ConfirmationsQueue>,
|
||||
accounts: Weak<A>,
|
||||
client: Weak<C>,
|
||||
miner: Weak<M>,
|
||||
}
|
||||
|
||||
impl<A: 'static, C: 'static, M: 'static> SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
/// Create new instance of signer client.
|
||||
pub fn new(store: &Arc<A>, client: &Arc<C>, miner: &Arc<M>, queue: &Arc<ConfirmationsQueue>) -> Self {
|
||||
SignerClient {
|
||||
queue: Arc::downgrade(queue),
|
||||
accounts: Arc::downgrade(store),
|
||||
client: Arc::downgrade(client),
|
||||
miner: Arc::downgrade(miner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: 'static, C: 'static, M: 'static> PersonalSigner for SignerClient<A, C, M>
|
||||
where A: AccountProvider, C: MiningBlockChainClient, M: MinerService {
|
||||
|
||||
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||
let queue = take_weak!(self.queue);
|
||||
to_value(&queue.requests())
|
||||
}
|
||||
|
||||
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
||||
|(id, modification, pass)| {
|
||||
let accounts = take_weak!(self.accounts);
|
||||
let queue = take_weak!(self.queue);
|
||||
queue.remove_request(id)
|
||||
.and_then(|confirmation| {
|
||||
let mut request = confirmation.transaction;
|
||||
// apply modification
|
||||
if let Some(gas_price) = modification.gas_price {
|
||||
request.gas_price = Some(gas_price);
|
||||
}
|
||||
match accounts.locked_account_secret(&request.from, &pass) {
|
||||
Ok(secret) => Some(sign_and_dispatch(&self.client, &self.miner, request, secret)),
|
||||
Err(_) => None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| to_value(&H256::zero()))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||
from_params::<(U256, )>(params).and_then(
|
||||
|(id, )| {
|
||||
let queue = take_weak!(self.queue);
|
||||
let res = queue.remove_request(id);
|
||||
to_value(&res.is_some())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@ pub mod traits;
|
||||
pub mod tests;
|
||||
pub mod types;
|
||||
|
||||
pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc};
|
||||
pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, Traces, Rpc};
|
||||
pub use self::impls::*;
|
||||
pub use self::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
|
||||
@@ -19,151 +19,26 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ethcore::client::{BlockChainClient, Client, ClientConfig};
|
||||
use ethcore::spec::Genesis;
|
||||
use ethcore::client::{MiningBlockChainClient, BlockChainClient, Client, ClientConfig};
|
||||
use ethcore::ids::BlockID;
|
||||
use ethcore::spec::{Genesis, Spec};
|
||||
use ethcore::block::Block;
|
||||
use ethcore::views::BlockView;
|
||||
use ethcore::ethereum;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethminer::{MinerService, ExternalMiner};
|
||||
use ethcore::miner::{MinerService, ExternalMiner, Miner};
|
||||
use devtools::RandomTempPath;
|
||||
use util::Hashable;
|
||||
use util::io::IoChannel;
|
||||
use util::hash::Address;
|
||||
use util::numbers::{Uint, U256};
|
||||
use util::hash::{Address, H256};
|
||||
use util::numbers::U256;
|
||||
use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
|
||||
use jsonrpc_core::IoHandler;
|
||||
use ethjson::blockchain::BlockChain;
|
||||
|
||||
use v1::traits::eth::Eth;
|
||||
use v1::impls::EthClient;
|
||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||
|
||||
struct EthTester {
|
||||
_client: Arc<BlockChainClient>,
|
||||
_miner: Arc<MinerService>,
|
||||
accounts: Arc<TestAccountProvider>,
|
||||
handler: IoHandler,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn harness_works() {
|
||||
let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest");
|
||||
chain_harness(chain, |_| {});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_get_balance() {
|
||||
let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs");
|
||||
chain_harness(chain, |tester| {
|
||||
// final account state
|
||||
let req_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest);
|
||||
|
||||
// non-existant account
|
||||
let req_new_acc = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
|
||||
"id": 3
|
||||
}"#;
|
||||
|
||||
let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_block_number() {
|
||||
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
|
||||
chain_harness(chain, |tester| {
|
||||
let req_number = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_blockNumber",
|
||||
"params": [],
|
||||
"id": 1
|
||||
}"#;
|
||||
|
||||
let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn eth_transaction_count() {
|
||||
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
|
||||
chain_harness(chain, |tester| {
|
||||
let address = tester.accounts.new_account("123").unwrap();
|
||||
let secret = tester.accounts.account_secret(&address).unwrap();
|
||||
|
||||
let req_before = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 15
|
||||
}"#;
|
||||
|
||||
let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#;
|
||||
|
||||
assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before);
|
||||
|
||||
let t = Transaction {
|
||||
nonce: U256::zero(),
|
||||
gas_price: U256::from(0x9184e72a000u64),
|
||||
gas: U256::from(0x76c0),
|
||||
action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
value: U256::from(0x9184e72au64),
|
||||
data: vec![]
|
||||
}.sign(&secret);
|
||||
|
||||
let req_send_trans = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x76c0",
|
||||
"gasPrice": "0x9184e72a000",
|
||||
"value": "0x9184e72a"
|
||||
}],
|
||||
"id": 16
|
||||
}"#;
|
||||
|
||||
let res_send_trans = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":16}"#;
|
||||
|
||||
// dispatch the transaction.
|
||||
assert_eq!(tester.handler.handle_request(&req_send_trans).unwrap(), res_send_trans);
|
||||
|
||||
// we have submitted the transaction -- but this shouldn't be reflected in a "latest" query.
|
||||
let req_after_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 17
|
||||
}"#;
|
||||
|
||||
let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest);
|
||||
|
||||
// the pending transactions should have been updated.
|
||||
let req_after_pending = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"],
|
||||
"id": 18
|
||||
}"#;
|
||||
|
||||
let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending);
|
||||
});
|
||||
}
|
||||
use v1::traits::eth::{Eth, EthSigning};
|
||||
use v1::impls::{EthClient, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config};
|
||||
|
||||
fn account_provider() -> Arc<TestAccountProvider> {
|
||||
let mut accounts = HashMap::new();
|
||||
@@ -179,51 +54,308 @@ fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn miner_service() -> Arc<TestMinerService> {
|
||||
Arc::new(TestMinerService::default())
|
||||
fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
Miner::with_accounts(true, spec, accounts)
|
||||
}
|
||||
|
||||
// given a blockchain, this harness will create an EthClient wrapping it
|
||||
// which tests can pass specially crafted requests to.
|
||||
fn chain_harness<F, U>(chain: BlockChain, mut cb: F) -> U
|
||||
where F: FnMut(&EthTester) -> U {
|
||||
fn make_spec(chain: &BlockChain) -> Spec {
|
||||
let genesis = Genesis::from(chain.genesis());
|
||||
let mut spec = ethereum::new_frontier_test();
|
||||
let state = chain.pre_state.clone().into();
|
||||
spec.set_genesis_state(state);
|
||||
spec.overwrite_genesis_params(genesis);
|
||||
assert!(spec.is_state_root_valid());
|
||||
spec
|
||||
}
|
||||
|
||||
let dir = RandomTempPath::new();
|
||||
let client = Client::new(ClientConfig::default(), spec, dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let sync_provider = sync_provider();
|
||||
let miner_service = miner_service();
|
||||
let account_provider = account_provider();
|
||||
let external_miner = Arc::new(ExternalMiner::default());
|
||||
struct EthTester {
|
||||
client: Arc<Client>,
|
||||
_miner: Arc<MinerService>,
|
||||
accounts: Arc<TestAccountProvider>,
|
||||
handler: IoHandler,
|
||||
}
|
||||
|
||||
for b in &chain.blocks_rlp() {
|
||||
if Block::is_good(&b) {
|
||||
let _ = client.import_block(b.clone());
|
||||
client.flush_queue();
|
||||
client.import_verified_blocks(&IoChannel::disconnected());
|
||||
impl EthTester {
|
||||
fn from_chain(chain: &BlockChain) -> Self {
|
||||
let tester = Self::from_spec_provider(|| make_spec(chain));
|
||||
|
||||
for b in &chain.blocks_rlp() {
|
||||
if Block::is_good(&b) {
|
||||
let _ = tester.client.import_block(b.clone());
|
||||
tester.client.flush_queue();
|
||||
tester.client.import_verified_blocks(&IoChannel::disconnected());
|
||||
}
|
||||
}
|
||||
|
||||
tester.client.flush_queue();
|
||||
|
||||
assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into());
|
||||
tester
|
||||
}
|
||||
|
||||
fn from_spec_provider<F>(spec_provider: F) -> Self
|
||||
where F: Fn() -> Spec {
|
||||
|
||||
let dir = RandomTempPath::new();
|
||||
let account_provider = account_provider();
|
||||
let miner_service = miner_service(spec_provider(), account_provider.clone());
|
||||
let client = Client::new(ClientConfig::default(), spec_provider(), dir.as_path(), miner_service.clone(), IoChannel::disconnected()).unwrap();
|
||||
let sync_provider = sync_provider();
|
||||
let external_miner = Arc::new(ExternalMiner::default());
|
||||
|
||||
let eth_client = EthClient::new(
|
||||
&client,
|
||||
&sync_provider,
|
||||
&account_provider,
|
||||
&miner_service,
|
||||
&external_miner
|
||||
);
|
||||
let eth_sign = EthSigningUnsafeClient::new(
|
||||
&client,
|
||||
&account_provider,
|
||||
&miner_service
|
||||
);
|
||||
|
||||
let handler = IoHandler::new();
|
||||
handler.add_delegate(eth_client.to_delegate());
|
||||
handler.add_delegate(eth_sign.to_delegate());
|
||||
|
||||
EthTester {
|
||||
_miner: miner_service,
|
||||
client: client,
|
||||
accounts: account_provider,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn harness_works() {
|
||||
let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest");
|
||||
let _ = EthTester::from_chain(&chain);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_get_balance() {
|
||||
let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs");
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
// final account state
|
||||
let req_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"],
|
||||
"id": 1
|
||||
}"#;
|
||||
let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest);
|
||||
|
||||
// non-existant account
|
||||
let req_new_acc = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
|
||||
"id": 3
|
||||
}"#;
|
||||
|
||||
let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth_block_number() {
|
||||
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
let req_number = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_blockNumber",
|
||||
"params": [],
|
||||
"id": 1
|
||||
}"#;
|
||||
|
||||
let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned();
|
||||
assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number);
|
||||
}
|
||||
|
||||
// a frontier-like test with an expanded gas limit and balance on known account.
|
||||
const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
|
||||
"name": "Frontier (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x50000",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x50000"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"faa34835af5c2ea724333018a515fbb7d5bc0b33": { "balance": "10000000000000", "nonce": "0" }
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn eth_transaction_count() {
|
||||
use util::crypto::Secret;
|
||||
|
||||
let address = Address::from_str("faa34835af5c2ea724333018a515fbb7d5bc0b33").unwrap();
|
||||
let secret = Secret::from_str("8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2").unwrap();
|
||||
|
||||
let tester = EthTester::from_spec_provider(|| Spec::load(TRANSACTION_COUNT_SPEC));
|
||||
tester.accounts.accounts.write().unwrap().insert(address, TestAccount {
|
||||
unlocked: false,
|
||||
password: "123".into(),
|
||||
secret: secret
|
||||
});
|
||||
|
||||
let req_before = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 15
|
||||
}"#;
|
||||
|
||||
let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#;
|
||||
|
||||
assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before);
|
||||
|
||||
let req_send_trans = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x30000",
|
||||
"gasPrice": "0x01",
|
||||
"value": "0x9184e72a"
|
||||
}],
|
||||
"id": 16
|
||||
}"#;
|
||||
|
||||
// dispatch the transaction.
|
||||
tester.handler.handle_request(&req_send_trans).unwrap();
|
||||
|
||||
// we have submitted the transaction -- but this shouldn't be reflected in a "latest" query.
|
||||
let req_after_latest = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
|
||||
"id": 17
|
||||
}"#;
|
||||
|
||||
let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest);
|
||||
|
||||
// the pending transactions should have been updated.
|
||||
let req_after_pending = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getTransactionCount",
|
||||
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"],
|
||||
"id": 18
|
||||
}"#;
|
||||
|
||||
let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#;
|
||||
|
||||
assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending);
|
||||
}
|
||||
|
||||
fn verify_transaction_counts(name: String, chain: BlockChain) {
|
||||
struct PanicHandler(String);
|
||||
impl Drop for PanicHandler {
|
||||
fn drop(&mut self) {
|
||||
if ::std::thread::panicking() {
|
||||
println!("Test failed: {}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(client.chain_info().best_block_hash == chain.best_block.into());
|
||||
let _panic = PanicHandler(name);
|
||||
|
||||
let eth_client = EthClient::new(&client, &sync_provider, &account_provider,
|
||||
&miner_service, &external_miner);
|
||||
fn by_hash(hash: H256, count: usize, id: &mut usize) -> (String, String) {
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBlockTransactionCountByHash",
|
||||
"params": [
|
||||
""#.to_owned() + format!("0x{:?}", hash).as_ref() + r#""
|
||||
],
|
||||
"id": "# + format!("{}", *id).as_ref() + r#"
|
||||
}"#;
|
||||
|
||||
let handler = IoHandler::new();
|
||||
let delegate = eth_client.to_delegate();
|
||||
handler.add_delegate(delegate);
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
|
||||
+ format!("0x{:02x}", count).as_ref()
|
||||
+ r#"","id":"#
|
||||
+ format!("{}", *id).as_ref() + r#"}"#;
|
||||
*id += 1;
|
||||
(req, res)
|
||||
}
|
||||
|
||||
let tester = EthTester {
|
||||
_miner: miner_service,
|
||||
_client: client,
|
||||
accounts: account_provider,
|
||||
handler: handler,
|
||||
};
|
||||
fn by_number(num: u64, count: usize, id: &mut usize) -> (String, String) {
|
||||
let req = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBlockTransactionCountByNumber",
|
||||
"params": [
|
||||
"#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#"
|
||||
],
|
||||
"id": "# + format!("{}", *id).as_ref() + r#"
|
||||
}"#;
|
||||
|
||||
cb(&tester)
|
||||
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
|
||||
+ format!("0x{:02x}", count).as_ref()
|
||||
+ r#"","id":"#
|
||||
+ format!("{}", *id).as_ref() + r#"}"#;
|
||||
*id += 1;
|
||||
(req, res)
|
||||
}
|
||||
|
||||
let tester = EthTester::from_chain(&chain);
|
||||
|
||||
let mut id = 1;
|
||||
for b in chain.blocks_rlp().iter().filter(|b| Block::is_good(b)).map(|b| BlockView::new(b)) {
|
||||
let count = b.transactions_count();
|
||||
|
||||
let hash = b.sha3();
|
||||
let number = b.header_view().number();
|
||||
|
||||
let (req, res) = by_hash(hash, count, &mut id);
|
||||
assert_eq!(tester.handler.handle_request(&req), Some(res));
|
||||
|
||||
// uncles can share block numbers, so skip them.
|
||||
if tester.client.block_hash(BlockID::Number(number)) == Some(hash) {
|
||||
let (req, res) = by_number(number, count, &mut id);
|
||||
assert_eq!(tester.handler.handle_request(&req), Some(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest");
|
||||
register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest");
|
||||
register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest");
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
use util::{Address, H256, Bytes, U256, FixedHash, Uint};
|
||||
use util::standard::*;
|
||||
use ethcore::error::{Error, ExecutionError};
|
||||
use ethcore::client::{BlockChainClient, Executed};
|
||||
use ethcore::client::{MiningBlockChainClient, Executed};
|
||||
use ethcore::block::{ClosedBlock, IsBlock};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
|
||||
use ethcore::miner::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
|
||||
|
||||
/// Test miner service.
|
||||
pub struct TestMinerService {
|
||||
@@ -132,7 +132,7 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// Imports transactions to transaction queue.
|
||||
fn import_own_transaction<T>(&self, chain: &BlockChainClient, transaction: SignedTransaction, _fetch_account: T) ->
|
||||
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, _fetch_account: T) ->
|
||||
Result<TransactionImportResult, Error>
|
||||
where T: Fn(&Address) -> AccountDetails {
|
||||
|
||||
@@ -154,21 +154,21 @@ impl MinerService for TestMinerService {
|
||||
}
|
||||
|
||||
/// Removes all transactions from the queue and restart mining operation.
|
||||
fn clear_and_reset(&self, _chain: &BlockChainClient) {
|
||||
fn clear_and_reset(&self, _chain: &MiningBlockChainClient) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Called when blocks are imported to chain, updates transactions queue.
|
||||
fn chain_new_blocks(&self, _chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
|
||||
fn chain_new_blocks(&self, _chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// New chain head event. Restart mining operation.
|
||||
fn update_sealing(&self, _chain: &BlockChainClient) {
|
||||
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn map_sealing_work<F, T>(&self, _chain: &BlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||
fn map_sealing_work<F, T>(&self, _chain: &MiningBlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -194,29 +194,29 @@ impl MinerService for TestMinerService {
|
||||
|
||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||
/// Will check the seal, but not actually insert the block into the chain.
|
||||
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn balance(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
|
||||
fn balance(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
|
||||
}
|
||||
|
||||
fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
|
||||
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _vm_tracing: bool) -> Result<Executed, ExecutionError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn storage_at(&self, _chain: &BlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||
fn storage_at(&self, _chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(H256::default, |b| b.block().fields().state.storage_at(address, position).clone())
|
||||
}
|
||||
|
||||
fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
|
||||
fn nonce(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
// we assume all transactions are in a pending block, ignoring the
|
||||
// reality of gas limits.
|
||||
self.last_nonce(address).unwrap_or(U256::zero())
|
||||
}
|
||||
|
||||
fn code(&self, _chain: &BlockChainClient, address: &Address) -> Option<Bytes> {
|
||||
fn code(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
|
||||
self.latest_closed_block.lock().unwrap().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone())
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, Transaction
|
||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||
use ethcore::receipt::LocalizedReceipt;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethminer::{ExternalMiner, MinerService};
|
||||
use ethcore::miner::{ExternalMiner, MinerService};
|
||||
use ethsync::SyncState;
|
||||
use v1::{Eth, EthClient};
|
||||
use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||
use rustc_serialize::hex::ToHex;
|
||||
|
||||
@@ -72,8 +72,11 @@ impl Default for EthTester {
|
||||
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
|
||||
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(eth);
|
||||
io.add_delegate(sign);
|
||||
|
||||
EthTester {
|
||||
client: client,
|
||||
sync: sync,
|
||||
|
||||
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
75
rpc/src/v1/tests/mocked/eth_signing.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::impls::EthSigningQueueClient;
|
||||
use v1::traits::EthSigning;
|
||||
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
||||
use util::keys::TestAccount;
|
||||
|
||||
struct EthSigningTester {
|
||||
pub queue: Arc<ConfirmationsQueue>,
|
||||
pub io: IoHandler,
|
||||
}
|
||||
|
||||
impl Default for EthSigningTester {
|
||||
fn default() -> Self {
|
||||
let queue = Arc::new(ConfirmationsQueue::default());
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate());
|
||||
|
||||
EthSigningTester {
|
||||
queue: queue,
|
||||
io: io,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eth_signing() -> EthSigningTester {
|
||||
EthSigningTester::default()
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_add_transaction_to_queue() {
|
||||
// given
|
||||
let tester = eth_signing();
|
||||
let account = TestAccount::new("123");
|
||||
let address = account.address();
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_sendTransaction",
|
||||
"params": [{
|
||||
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||
"gas": "0x76c0",
|
||||
"gasPrice": "0x9184e72a000",
|
||||
"value": "0x9184e72a"
|
||||
}],
|
||||
"id": 1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
}
|
||||
@@ -18,8 +18,8 @@ use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use v1::{Ethcore, EthcoreClient};
|
||||
use ethminer::MinerService;
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
use ethcore::miner::MinerService;
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use util::numbers::*;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
//! method calls properly.
|
||||
|
||||
mod eth;
|
||||
mod eth_signing;
|
||||
mod net;
|
||||
mod web3;
|
||||
mod personal;
|
||||
mod personal_signer;
|
||||
mod ethcore;
|
||||
mod rpc;
|
||||
|
||||
@@ -176,4 +176,4 @@ fn sign_and_send_transaction() {
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
|
||||
}
|
||||
}
|
||||
|
||||
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
169
rpc/src/v1/tests/mocked/personal_signer.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use util::numbers::*;
|
||||
use util::keys::{TestAccount, TestAccountProvider};
|
||||
use ethcore::client::TestBlockChainClient;
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use v1::{SignerClient, PersonalSigner};
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||
use v1::types::TransactionRequest;
|
||||
|
||||
|
||||
struct PersonalSignerTester {
|
||||
queue: Arc<ConfirmationsQueue>,
|
||||
accounts: Arc<TestAccountProvider>,
|
||||
io: IoHandler,
|
||||
miner: Arc<TestMinerService>,
|
||||
// these unused fields are necessary to keep the data alive
|
||||
// as the handler has only weak pointers.
|
||||
_client: Arc<TestBlockChainClient>,
|
||||
}
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
Arc::new(client)
|
||||
}
|
||||
|
||||
fn accounts_provider() -> Arc<TestAccountProvider> {
|
||||
let accounts = HashMap::new();
|
||||
let ap = TestAccountProvider::new(accounts);
|
||||
Arc::new(ap)
|
||||
}
|
||||
|
||||
fn miner_service() -> Arc<TestMinerService> {
|
||||
Arc::new(TestMinerService::default())
|
||||
}
|
||||
|
||||
fn signer_tester() -> PersonalSignerTester {
|
||||
let queue = Arc::new(ConfirmationsQueue::default());
|
||||
let accounts = accounts_provider();
|
||||
let client = blockchain_client();
|
||||
let miner = miner_service();
|
||||
|
||||
let io = IoHandler::new();
|
||||
io.add_delegate(SignerClient::new(&accounts, &client, &miner, &queue).to_delegate());
|
||||
|
||||
PersonalSignerTester {
|
||||
queue: queue,
|
||||
accounts: accounts,
|
||||
io: io,
|
||||
miner: miner,
|
||||
_client: client,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_return_list_of_transactions_in_queue() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_reject_transaction_from_queue_without_dispatching() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: Address::from(1),
|
||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_confirm_transaction_and_dispatch() {
|
||||
// given
|
||||
let tester = signer_tester();
|
||||
let account = TestAccount::new("test");
|
||||
let address = account.address();
|
||||
let secret = account.secret.clone();
|
||||
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
||||
tester.accounts.accounts
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(address, account);
|
||||
tester.queue.add_request(TransactionRequest {
|
||||
from: address,
|
||||
to: Some(recipient),
|
||||
gas_price: Some(U256::from(10_000)),
|
||||
gas: Some(U256::from(10_000_000)),
|
||||
value: Some(U256::from(1)),
|
||||
data: None,
|
||||
nonce: None,
|
||||
});
|
||||
let t = Transaction {
|
||||
nonce: U256::zero(),
|
||||
gas_price: U256::from(0x1000),
|
||||
gas: U256::from(10_000_000),
|
||||
action: Action::Call(recipient),
|
||||
value: U256::from(0x1),
|
||||
data: vec![]
|
||||
}.sign(&secret);
|
||||
|
||||
assert_eq!(tester.queue.requests().len(), 1);
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"personal_confirmTransaction",
|
||||
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
||||
"id":1
|
||||
}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
|
||||
|
||||
// then
|
||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||
assert_eq!(tester.queue.requests().len(), 0);
|
||||
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,15 @@ pub mod helpers;
|
||||
// extract the chain with that name. This will panic if no chain by that name
|
||||
// is found.
|
||||
macro_rules! extract_chain {
|
||||
($file:expr, $name:expr) => {{
|
||||
(iter $file:expr) => {{
|
||||
const RAW_DATA: &'static [u8] =
|
||||
include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json"));
|
||||
::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter()
|
||||
}};
|
||||
|
||||
($file:expr, $name:expr) => {{
|
||||
let mut chain = None;
|
||||
for (name, c) in ::ethjson::blockchain::Test::load(RAW_DATA).unwrap() {
|
||||
for (name, c) in extract_chain!(iter $file) {
|
||||
if name == $name {
|
||||
chain = Some(c);
|
||||
break;
|
||||
@@ -27,14 +31,41 @@ macro_rules! extract_chain {
|
||||
}};
|
||||
|
||||
($file:expr) => {{
|
||||
const RAW_DATA: &'static [u8] =
|
||||
include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json"));
|
||||
|
||||
::ethjson::blockchain::Test::load(RAW_DATA)
|
||||
.unwrap().into_iter().next().unwrap().1
|
||||
extract_chain!(iter $file).next().unwrap().1
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! register_test {
|
||||
($name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(heavy $name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
#[cfg(feature = "test-heavy")]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(ignore $name:ident, $cb:expr, $file:expr) => {
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn $name() {
|
||||
for (name, chain) in extract_chain!(iter $file) {
|
||||
$cb(name, chain);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocked;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -74,12 +74,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
||||
/// Returns the code at given address at given time (block number).
|
||||
fn code_at(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Signs the data with given address signature.
|
||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends transaction.
|
||||
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends signed transaction.
|
||||
fn send_raw_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
@@ -150,8 +144,6 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
||||
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash);
|
||||
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number);
|
||||
delegate.add_method("eth_getCode", Eth::code_at);
|
||||
delegate.add_method("eth_sign", Eth::sign);
|
||||
delegate.add_method("eth_sendTransaction", Eth::send_transaction);
|
||||
delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction);
|
||||
delegate.add_method("eth_call", Eth::call);
|
||||
delegate.add_method("eth_estimateGas", Eth::estimate_gas);
|
||||
@@ -208,3 +200,20 @@ pub trait EthFilter: Sized + Send + Sync + 'static {
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
/// Signing methods implementation relying on unlocked accounts.
|
||||
pub trait EthSigning: Sized + Send + Sync + 'static {
|
||||
/// Signs the data with given address signature.
|
||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Sends transaction.
|
||||
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("eth_sign", EthSigning::sign);
|
||||
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ pub mod traces;
|
||||
pub mod rpc;
|
||||
|
||||
pub use self::web3::Web3;
|
||||
pub use self::eth::{Eth, EthFilter};
|
||||
pub use self::eth::{Eth, EthFilter, EthSigning};
|
||||
pub use self::net::Net;
|
||||
pub use self::personal::Personal;
|
||||
pub use self::personal::{Personal, PersonalSigner};
|
||||
pub use self::ethcore::Ethcore;
|
||||
pub use self::traces::Traces;
|
||||
pub use self::rpc::Rpc;
|
||||
|
||||
|
||||
|
||||
@@ -43,3 +43,26 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
/// Personal extension for transactions confirmations rpc interface.
|
||||
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
||||
|
||||
/// Returns a list of transactions to confirm.
|
||||
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Confirm and send a specific transaction.
|
||||
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Reject the transaction request.
|
||||
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||
|
||||
/// Should be used to convert object to io delegate.
|
||||
fn to_delegate(self) -> IoDelegate<Self> {
|
||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
|
||||
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
|
||||
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
|
||||
delegate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ pub use self::log::Log;
|
||||
pub use self::optionals::OptionalValue;
|
||||
pub use self::sync::{SyncStatus, SyncInfo};
|
||||
pub use self::transaction::Transaction;
|
||||
pub use self::transaction_request::TransactionRequest;
|
||||
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
||||
pub use self::call_request::CallRequest;
|
||||
pub use self::receipt::Receipt;
|
||||
pub use self::trace::Trace;
|
||||
|
||||
@@ -21,7 +21,7 @@ use util::numbers::U256;
|
||||
use v1::types::bytes::Bytes;
|
||||
|
||||
/// Transaction request coming from RPC
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct TransactionRequest {
|
||||
/// Sender
|
||||
pub from: Address,
|
||||
@@ -40,6 +40,24 @@ pub struct TransactionRequest {
|
||||
pub nonce: Option<U256>,
|
||||
}
|
||||
|
||||
/// Transaction confirmation waiting in a queue
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct TransactionConfirmation {
|
||||
/// Id of this confirmation
|
||||
pub id: U256,
|
||||
/// TransactionRequest
|
||||
pub transaction: TransactionRequest,
|
||||
}
|
||||
|
||||
/// Possible modifications to the confirmed transaction sent by SystemUI
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
pub struct TransactionModification {
|
||||
/// Modified gas price
|
||||
#[serde(rename="gasPrice")]
|
||||
pub gas_price: Option<U256>,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
@@ -135,5 +153,26 @@ mod tests {
|
||||
nonce: None,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_modification() {
|
||||
// given
|
||||
let s1 = r#"{
|
||||
"gasPrice":"0x0ba43b7400"
|
||||
}"#;
|
||||
let s2 = r#"{}"#;
|
||||
|
||||
// when
|
||||
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
||||
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res1, TransactionModification {
|
||||
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||
});
|
||||
assert_eq!(res2, TransactionModification {
|
||||
gas_price: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user