diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs
index 457f96552..cbae49cc3 100644
--- a/rpc/src/v1/helpers/dispatch.rs
+++ b/rpc/src/v1/helpers/dispatch.rs
@@ -14,84 +14,153 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+use rlp;
use util::{Address, H256, U256, Uint, Bytes};
use util::bytes::ToPretty;
+
use ethkey::Signature;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction};
use ethcore::account_provider::AccountProvider;
-use jsonrpc_core::{Error, Value, to_value};
-use v1::helpers::TransactionRequest;
-use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes};
-use v1::helpers::errors;
+
+use jsonrpc_core::Error;
+use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
+use v1::types::{
+ H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
+ ConfirmationPayload as RpcConfirmationPayload,
+ ConfirmationResponse,
+ SignRequest as RpcSignRequest,
+ DecryptRequest as RpcDecryptRequest,
+};
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
-pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result
- where C: MiningBlockChainClient, M: MinerService {
- let hash = RpcH256::from(signed_transaction.hash());
-
- miner.import_own_transaction(client, signed_transaction)
- .map_err(errors::from_transaction_error)
- .map(|_| hash)
+pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: Option) -> Result
+ where C: MiningBlockChainClient, M: MinerService
+{
+ match payload {
+ ConfirmationPayload::SendTransaction(request) => {
+ sign_and_dispatch(client, miner, accounts, request, pass)
+ .map(RpcH256::from)
+ .map(ConfirmationResponse::SendTransaction)
+ },
+ ConfirmationPayload::SignTransaction(request) => {
+ sign_no_dispatch(client, miner, accounts, request, pass)
+ .map(|tx| rlp::encode(&tx).to_vec())
+ .map(RpcBytes)
+ .map(ConfirmationResponse::SignTransaction)
+ },
+ ConfirmationPayload::Signature(address, hash) => {
+ signature(accounts, address, hash, pass)
+ .map(RpcH520::from)
+ .map(ConfirmationResponse::Signature)
+ },
+ ConfirmationPayload::Decrypt(address, data) => {
+ decrypt(accounts, address, data, pass)
+ .map(RpcBytes)
+ .map(ConfirmationResponse::Decrypt)
+ },
+ }
}
-fn signature(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result {
+fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option) -> Result {
accounts.sign(address, password.clone(), hash).map_err(|e| match password {
Some(_) => errors::from_password_error(e),
None => errors::from_signing_error(e),
})
}
-pub fn sign(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result {
- signature(accounts, address, password, hash)
- .map(RpcH520::from)
- .map(to_value)
-}
-
-pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option, msg: Bytes) -> Result {
+fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: Option) -> Result {
accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg)
.map_err(|e| match password {
Some(_) => errors::from_password_error(e),
None => errors::from_signing_error(e),
})
- .map(RpcBytes::from)
- .map(to_value)
}
-pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option) -> Result
+pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result
+ where C: MiningBlockChainClient, M: MinerService {
+ let hash = signed_transaction.hash();
+
+ miner.import_own_transaction(client, signed_transaction)
+ .map_err(errors::from_transaction_error)
+ .map(|_| hash)
+}
+
+pub fn sign_no_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result
where C: MiningBlockChainClient, M: MinerService {
let network_id = client.signing_network_id();
- let address = request.from;
+ let address = filled.from;
let signed_transaction = {
- let t = prepare_transaction(client, miner, request);
+ let t = Transaction {
+ nonce: filled.nonce
+ .or_else(|| miner
+ .last_nonce(&filled.from)
+ .map(|nonce| nonce + U256::one()))
+ .unwrap_or_else(|| client.latest_nonce(&filled.from)),
+
+ action: filled.to.map_or(Action::Create, Action::Call),
+ gas: filled.gas,
+ gas_price: filled.gas_price,
+ value: filled.value,
+ data: filled.data,
+ };
+
let hash = t.hash(network_id);
- let signature = try!(signature(accounts, address, password, hash));
+ let signature = try!(signature(accounts, address, hash, password));
t.with_signature(signature, network_id)
};
+ Ok(signed_transaction)
+}
- trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", ::rlp::encode(&signed_transaction).to_vec().pretty(), network_id);
+pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result
+ where C: MiningBlockChainClient, M: MinerService
+{
+
+ let network_id = client.signing_network_id();
+ let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password));
+
+ trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id);
dispatch_transaction(&*client, &*miner, signed_transaction)
}
-fn prepare_transaction(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
- Transaction {
- nonce: request.nonce
- .or_else(|| miner
- .last_nonce(&request.from)
- .map(|nonce| nonce + U256::one()))
- .unwrap_or_else(|| client.latest_nonce(&request.from)),
-
- action: request.to.map_or(Action::Create, Action::Call),
- gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
+pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest
+ where C: MiningBlockChainClient, M: MinerService
+{
+ FilledTransactionRequest {
+ from: request.from,
+ to: request.to,
+ nonce: request.nonce,
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
- value: request.value.unwrap_or_else(U256::zero),
- data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
+ gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
+ value: request.value.unwrap_or_else(|| 0.into()),
+ data: request.data.unwrap_or_else(Vec::new),
}
}
-pub fn default_gas_price(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
+pub fn default_gas_price(client: &C, miner: &M) -> U256
+ where C: MiningBlockChainClient, M: MinerService
+{
client.gas_price_median(100).unwrap_or_else(|| miner.sensible_gas_price())
}
+
+pub fn from_rpc(payload: RpcConfirmationPayload, client: &C, miner: &M) -> ConfirmationPayload
+ where C: MiningBlockChainClient, M: MinerService {
+
+ match payload {
+ RpcConfirmationPayload::SendTransaction(request) => {
+ ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), client, miner))
+ },
+ RpcConfirmationPayload::SignTransaction(request) => {
+ ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), client, miner))
+ },
+ RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
+ ConfirmationPayload::Decrypt(address.into(), msg.into())
+ },
+ RpcConfirmationPayload::Signature(RpcSignRequest { address, hash }) => {
+ ConfirmationPayload::Signature(address.into(), hash.into())
+ },
+ }
+}
diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs
index 5cb6108c1..7c5e89b87 100644
--- a/rpc/src/v1/helpers/requests.rs
+++ b/rpc/src/v1/helpers/requests.rs
@@ -100,9 +100,22 @@ pub struct ConfirmationRequest {
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum ConfirmationPayload {
/// Transaction
- Transaction(FilledTransactionRequest),
+ SendTransaction(FilledTransactionRequest),
+ /// Sign Transaction
+ SignTransaction(FilledTransactionRequest),
/// Sign request
- Sign(Address, H256),
+ Signature(Address, H256),
/// Decrypt request
Decrypt(Address, Bytes),
}
+
+impl ConfirmationPayload {
+ pub fn sender(&self) -> Address {
+ match *self {
+ ConfirmationPayload::SendTransaction(ref request) => request.from,
+ ConfirmationPayload::SignTransaction(ref request) => request.from,
+ ConfirmationPayload::Signature(ref address, _) => *address,
+ ConfirmationPayload::Decrypt(ref address, _) => *address,
+ }
+ }
+}
diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs
index 22e15d0d6..144e672c1 100644
--- a/rpc/src/v1/helpers/signing_queue.rs
+++ b/rpc/src/v1/helpers/signing_queue.rs
@@ -21,9 +21,10 @@ use std::collections::BTreeMap;
use jsonrpc_core;
use util::{Mutex, RwLock, U256};
use v1::helpers::{ConfirmationRequest, ConfirmationPayload};
+use v1::types::ConfirmationResponse;
/// Result that can be returned from JSON RPC.
-pub type RpcResult = Result;
+pub type RpcResult = Result;
/// Possible events happening in the queue that can be listened to.
#[derive(Debug, PartialEq)]
@@ -314,13 +315,12 @@ mod test {
use std::time::Duration;
use std::thread;
use std::sync::{mpsc, Arc};
- use util::{Address, U256, H256, Mutex};
+ use util::{Address, U256, Mutex};
use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, FilledTransactionRequest, ConfirmationPayload};
- use v1::types::H256 as NH256;
- use jsonrpc_core::to_value;
+ use v1::types::ConfirmationResponse;
fn request() -> ConfirmationPayload {
- ConfirmationPayload::Transaction(FilledTransactionRequest {
+ ConfirmationPayload::SendTransaction(FilledTransactionRequest {
from: Address::from(1),
to: Some(Address::from(2)),
gas_price: 0.into(),
@@ -353,10 +353,10 @@ mod test {
// Just wait for the other thread to start
thread::sleep(Duration::from_millis(100));
}
- queue.request_confirmed(id, Ok(to_value(&NH256::from(H256::from(1)))));
+ queue.request_confirmed(id, Ok(ConfirmationResponse::SendTransaction(1.into())));
// then
- assert_eq!(handle.join().expect("Thread should finish nicely"), Ok(to_value(&NH256::from(H256::from(1)))));
+ assert_eq!(handle.join().expect("Thread should finish nicely"), Ok(ConfirmationResponse::SendTransaction(1.into())));
}
#[test]
diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs
index 4e54078fb..6986cf0ed 100644
--- a/rpc/src/v1/impls/eth.rs
+++ b/rpc/src/v1/impls/eth.rs
@@ -49,7 +49,7 @@ use v1::types::{
H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256,
};
use v1::helpers::{CallRequest as CRequest, errors, limit_logs};
-use v1::helpers::dispatch::{default_gas_price, dispatch_transaction};
+use v1::helpers::dispatch::{dispatch_transaction, default_gas_price};
use v1::helpers::block_import::is_major_importing;
use v1::helpers::auto_args::Trailing;
@@ -610,7 +610,7 @@ impl Eth for EthClient where
let raw_transaction = raw.to_vec();
match UntrustedRlp::new(&raw_transaction).as_val() {
- Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction),
+ Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction).map(Into::into),
Err(e) => Err(errors::from_rlp_error(e)),
}
}
diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs
index ffb20610c..5c4a2ff51 100644
--- a/rpc/src/v1/impls/personal.rs
+++ b/rpc/src/v1/impls/personal.rs
@@ -26,7 +26,7 @@ use jsonrpc_core::Error;
use v1::traits::Personal;
use v1::types::{H160 as RpcH160, H256 as RpcH256, TransactionRequest};
use v1::helpers::errors;
-use v1::helpers::dispatch::sign_and_dispatch;
+use v1::helpers::dispatch::{self, sign_and_dispatch};
/// Account management (personal) rpc implementation.
pub struct PersonalClient where C: MiningBlockChainClient, M: MinerService {
@@ -92,13 +92,17 @@ impl Personal for PersonalClient where C: MiningBl
fn sign_and_send_transaction(&self, request: TransactionRequest, password: String) -> Result {
try!(self.active());
+ let client = take_weak!(self.client);
+ let miner = take_weak!(self.miner);
+ let accounts = take_weak!(self.accounts);
+ let request = dispatch::fill_optional_fields(request.into(), &*client, &*miner);
sign_and_dispatch(
- &*take_weak!(self.client),
- &*take_weak!(self.miner),
- &*take_weak!(self.accounts),
- request.into(),
+ &*client,
+ &*miner,
+ &*accounts,
+ request,
Some(password)
- )
+ ).map(Into::into)
}
}
diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs
index 61453e02b..0ee06b5c5 100644
--- a/rpc/src/v1/impls/signer.rs
+++ b/rpc/src/v1/impls/signer.rs
@@ -17,14 +17,15 @@
//! Transactions Confirmations rpc implementation
use std::sync::{Arc, Weak};
+
use jsonrpc_core::*;
use ethcore::account_provider::AccountProvider;
use ethcore::client::MiningBlockChainClient;
use ethcore::miner::MinerService;
use v1::traits::Signer;
-use v1::types::{TransactionModification, ConfirmationRequest, U256};
+use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, U256};
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
-use v1::helpers::dispatch::{sign_and_dispatch, sign, decrypt};
+use v1::helpers::dispatch;
/// Transactions confirmation (personal) rpc implementation.
pub struct SignerClient where C: MiningBlockChainClient, M: MinerService {
@@ -73,7 +74,7 @@ impl Signer for SignerClient where C: MiningBlockC
// TODO [ToDr] TransactionModification is redundant for some calls
// might be better to replace it in future
- fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result {
+ fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result {
try!(self.active());
let id = id.into();
@@ -83,21 +84,16 @@ impl Signer for SignerClient where C: MiningBlockC
let miner = take_weak!(self.miner);
signer.peek(&id).map(|confirmation| {
- let result = match confirmation.payload {
- ConfirmationPayload::Transaction(mut request) => {
- // apply modification
- if let Some(gas_price) = modification.gas_price {
- request.gas_price = gas_price.into();
- }
- sign_and_dispatch(&*client, &*miner, &*accounts, request.into(), Some(pass)).map(to_value)
+ let mut payload = confirmation.payload.clone();
+ // Modify payload
+ match (&mut payload, modification.gas_price) {
+ (&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => {
+ request.gas_price = gas_price.into();
},
- ConfirmationPayload::Sign(address, hash) => {
- sign(&*accounts, address, Some(pass), hash)
- },
- ConfirmationPayload::Decrypt(address, msg) => {
- decrypt(&*accounts, address, Some(pass), msg)
- },
- };
+ _ => {},
+ }
+ // Execute
+ let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass));
if let Ok(ref response) = result {
signer.request_confirmed(id, Ok(response.clone()));
}
diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs
index f54847757..cf20f1045 100644
--- a/rpc/src/v1/impls/signing.rs
+++ b/rpc/src/v1/impls/signing.rs
@@ -17,24 +17,33 @@
//! Signing RPC implementation.
use std::sync::{Arc, Weak};
+use transient_hashmap::TransientHashMap;
+use util::{U256, Mutex};
use ethcore::account_provider::AccountProvider;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
-use transient_hashmap::TransientHashMap;
-use util::{U256, Address, H256, Mutex};
-use jsonrpc_core::*;
-use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest, SignerService};
-use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch, sign, decrypt};
+use jsonrpc_core::Error;
+use v1::helpers::auto_args::Ready;
+use v1::helpers::{
+ errors, dispatch,
+ SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService
+};
use v1::traits::{EthSigning, ParitySigning};
-use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes};
+use v1::types::{
+ H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520,
+ Either as RpcEither,
+ TransactionRequest as RpcTransactionRequest,
+ ConfirmationPayload as RpcConfirmationPayload,
+ ConfirmationResponse as RpcConfirmationResponse
+};
const MAX_PENDING_DURATION: u64 = 60 * 60;
pub enum DispatchResult {
Promise(ConfirmationPromise),
- Value(Value),
+ Value(RpcConfirmationResponse),
}
/// Implementation of functions that require signing when no trusted signer is used.
@@ -68,59 +77,41 @@ impl SigningQueueClient where
Ok(())
}
- fn add_to_queue(&self, sender: Address, when_unlocked: WhenUnlocked, payload: Payload)
- -> Result where
- WhenUnlocked: Fn(&AccountProvider) -> Result,
- Payload: Fn() -> ConfirmationPayload, {
+ fn handle_dispatch(&self, res: Result, on_response: OnResponse)
+ where OnResponse: FnOnce(Result) + Send + 'static
+ {
+ match res {
+ Ok(DispatchResult::Value(result)) => on_response(Ok(result)),
+ Ok(DispatchResult::Promise(promise)) => {
+ promise.wait_for_result(move |result| {
+ on_response(result.unwrap_or_else(|| Err(errors::request_rejected())))
+ })
+ },
+ Err(e) => on_response(Err(e)),
+ }
+ }
+ fn add_to_queue(&self, payload: ConfirmationPayload) -> Result {
+ let client = take_weak!(self.client);
+ let miner = take_weak!(self.miner);
let accounts = take_weak!(self.accounts);
+
+ let sender = payload.sender();
if accounts.is_unlocked(sender) {
- return when_unlocked(&accounts).map(DispatchResult::Value);
+ return dispatch::execute(&*client, &*miner, &*accounts, payload, None).map(DispatchResult::Value);
}
- take_weak!(self.signer).add_request(payload())
+ take_weak!(self.signer).add_request(payload)
.map(DispatchResult::Promise)
.map_err(|_| errors::request_rejected_limit())
}
- fn handle_dispatch(&self, res: Result, ready: Ready) {
- match res {
- Ok(DispatchResult::Value(v)) => ready.ready(Ok(v)),
- Ok(DispatchResult::Promise(promise)) => {
- promise.wait_for_result(move |result| {
- ready.ready(result.unwrap_or_else(|| Err(errors::request_rejected())))
- })
- },
- Err(e) => ready.ready(Err(e)),
- }
- }
+ fn dispatch(&self, payload: RpcConfirmationPayload) -> Result {
+ let client = take_weak!(self.client);
+ let miner = take_weak!(self.miner);
- fn dispatch_sign(&self, params: Params) -> Result {
- from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| {
- let address: Address = address.into();
- let msg: H256 = msg.into();
-
- self.add_to_queue(
- address,
- |accounts| sign(accounts, address, None, msg.clone()),
- || ConfirmationPayload::Sign(address, msg.clone()),
- )
- })
- }
-
- fn dispatch_transaction(&self, params: Params) -> Result {
- from_params::<(TransactionRequest, )>(params).and_then(|(request, )| {
- let request: TRequest = request.into();
- let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
- self.add_to_queue(
- request.from,
- |accounts| sign_and_dispatch(&*client, &*miner, accounts, request.clone(), None).map(to_value),
- || {
- let request = fill_optional_fields(request.clone(), &*client, &*miner);
- ConfirmationPayload::Transaction(request)
- }
- )
- })
+ let payload = dispatch::from_rpc(payload, &*client, &*miner);
+ self.add_to_queue(payload)
}
}
@@ -128,62 +119,59 @@ impl ParitySigning for SigningQueueClient where
C: MiningBlockChainClient,
M: MinerService,
{
- fn post_sign(&self, params: Params) -> Result {
+ fn post_sign(&self, address: RpcH160, hash: RpcH256) -> Result, Error> {
try!(self.active());
- self.dispatch_sign(params).map(|result| match result {
- DispatchResult::Value(v) => v,
- DispatchResult::Promise(promise) => {
- let id = promise.id();
- self.pending.lock().insert(id, promise);
- to_value(&RpcU256::from(id))
- },
- })
+ self.dispatch(RpcConfirmationPayload::Signature((address, hash).into()))
+ .map(|result| match result {
+ DispatchResult::Value(v) => RpcEither::Or(v),
+ DispatchResult::Promise(promise) => {
+ let id = promise.id();
+ self.pending.lock().insert(id, promise);
+ RpcEither::Either(id.into())
+ },
+ })
}
- fn post_transaction(&self, params: Params) -> Result {
+ fn post_transaction(&self, request: RpcTransactionRequest) -> Result, Error> {
try!(self.active());
- self.dispatch_transaction(params).map(|result| match result {
- DispatchResult::Value(v) => v,
- DispatchResult::Promise(promise) => {
- let id = promise.id();
- self.pending.lock().insert(id, promise);
- to_value(&RpcU256::from(id))
- },
- })
+ self.dispatch(RpcConfirmationPayload::SendTransaction(request))
+ .map(|result| match result {
+ DispatchResult::Value(v) => RpcEither::Or(v),
+ DispatchResult::Promise(promise) => {
+ let id = promise.id();
+ self.pending.lock().insert(id, promise);
+ RpcEither::Either(id.into())
+ },
+ })
}
- fn check_request(&self, params: Params) -> Result {
+ fn check_request(&self, id: RpcU256) -> Result