Refactoring Signer to auto_args + eth_signTransaction (#3261)

* Sign transaction initial

* Refactoring signer to auto_args
This commit is contained in:
Tomasz Drwięga 2016-11-09 13:13:35 +01:00 committed by Gav Wood
parent 3c6f148a16
commit b33b237f76
17 changed files with 560 additions and 283 deletions

View File

@ -14,84 +14,153 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<RpcH256, Error>
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<C, M>(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: Option<String>) -> Result<ConfirmationResponse, Error>
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<String>, hash: H256) -> Result<Signature, Error> {
fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option<String>) -> Result<Signature, Error> {
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<String>, hash: H256) -> Result<Value, Error> {
signature(accounts, address, password, hash)
.map(RpcH520::from)
.map(to_value)
}
pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option<String>, msg: Bytes) -> Result<Value, Error> {
fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: Option<String>) -> Result<Bytes, Error> {
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<C, M>(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option<String>) -> Result<RpcH256, Error>
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<H256, Error>
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<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option<String>) -> Result<SignedTransaction, Error>
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<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option<String>) -> Result<H256, Error>
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<C, M>(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<C, M>(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<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
pub fn default_gas_price<C, M>(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<C, M>(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())
},
}
}

View File

@ -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,
}
}
}

View File

@ -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<jsonrpc_core::Value, jsonrpc_core::Error>;
pub type RpcResult = Result<ConfirmationResponse, jsonrpc_core::Error>;
/// 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]

View File

@ -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<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> 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)),
}
}

View File

@ -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<C, M> where C: MiningBlockChainClient, M: MinerService {
@ -92,13 +92,17 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
fn sign_and_send_transaction(&self, request: TransactionRequest, password: String) -> Result<RpcH256, Error> {
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)
}
}

View File

@ -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<C, M> where C: MiningBlockChainClient, M: MinerService {
@ -73,7 +74,7 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> 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<Value, Error> {
fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result<ConfirmationResponse, Error> {
try!(self.active());
let id = id.into();
@ -83,21 +84,16 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> 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()));
}

View File

@ -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<C, M> SigningQueueClient<C, M> where
Ok(())
}
fn add_to_queue<WhenUnlocked, Payload>(&self, sender: Address, when_unlocked: WhenUnlocked, payload: Payload)
-> Result<DispatchResult, Error> where
WhenUnlocked: Fn(&AccountProvider) -> Result<Value, Error>,
Payload: Fn() -> ConfirmationPayload, {
fn handle_dispatch<OnResponse>(&self, res: Result<DispatchResult, Error>, on_response: OnResponse)
where OnResponse: FnOnce(Result<RpcConfirmationResponse, Error>) + 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<DispatchResult, Error> {
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<DispatchResult, Error>, 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<DispatchResult, Error> {
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
fn dispatch_sign(&self, params: Params) -> Result<DispatchResult, Error> {
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<DispatchResult, Error> {
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<C: 'static, M: 'static> ParitySigning for SigningQueueClient<C, M> where
C: MiningBlockChainClient,
M: MinerService,
{
fn post_sign(&self, params: Params) -> Result<Value, Error> {
fn post_sign(&self, address: RpcH160, hash: RpcH256) -> Result<RpcEither<RpcU256, RpcConfirmationResponse>, 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<Value, Error> {
fn post_transaction(&self, request: RpcTransactionRequest) -> Result<RpcEither<RpcU256, RpcConfirmationResponse>, 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<Value, Error> {
fn check_request(&self, id: RpcU256) -> Result<Option<RpcConfirmationResponse>, Error> {
try!(self.active());
let mut pending = self.pending.lock();
from_params::<(RpcU256, )>(params).and_then(|(id, )| {
let id: U256 = id.into();
let res = match pending.get(&id) {
Some(ref promise) => match promise.result() {
ConfirmationResult::Waiting => { return Ok(Value::Null); }
ConfirmationResult::Rejected => Err(errors::request_rejected()),
ConfirmationResult::Confirmed(rpc_response) => rpc_response,
},
_ => { return Err(errors::request_not_found()); }
};
pending.remove(&id);
res
})
let id: U256 = id.into();
let res = match pending.get(&id) {
Some(ref promise) => match promise.result() {
ConfirmationResult::Waiting => { return Ok(None); }
ConfirmationResult::Rejected => Err(errors::request_rejected()),
ConfirmationResult::Confirmed(rpc_response) => rpc_response.map(Some),
},
_ => { return Err(errors::request_not_found()); }
};
pending.remove(&id);
res
}
fn decrypt_message(&self, params: Params, ready: Ready) {
fn decrypt_message(&self, ready: Ready<RpcBytes>, address: RpcH160, data: RpcBytes) {
let res = self.active()
.and_then(|_| from_params::<(RpcH160, RpcBytes)>(params))
.and_then(|(address, msg)| {
let address: Address = address.into();
self.add_to_queue(
address,
|accounts| decrypt(accounts, address, None, msg.clone().into()),
|| ConfirmationPayload::Decrypt(address, msg.clone().into())
)
});
self.handle_dispatch(res, ready);
.and_then(|_| self.dispatch(RpcConfirmationPayload::Decrypt((address, data).into())));
// TODO [todr] typed handle_dispatch
self.handle_dispatch(res, |response| {
match response {
Ok(RpcConfirmationResponse::Decrypt(data)) => ready.ready(Ok(data)),
Err(e) => ready.ready(Err(e)),
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
}
});
}
}
@ -191,26 +179,36 @@ impl<C: 'static, M: 'static> EthSigning for SigningQueueClient<C, M> where
C: MiningBlockChainClient,
M: MinerService,
{
fn sign(&self, params: Params, ready: Ready) {
let res = self.active().and_then(|_| self.dispatch_sign(params));
self.handle_dispatch(res, ready);
fn sign(&self, ready: Ready<RpcH520>, address: RpcH160, hash: RpcH256) {
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::Signature((address, hash).into())));
self.handle_dispatch(res, |response| {
match response {
Ok(RpcConfirmationResponse::Signature(signature)) => ready.ready(Ok(signature)),
Err(e) => ready.ready(Err(e)),
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
}
});
}
fn send_transaction(&self, params: Params, ready: Ready) {
let res = self.active().and_then(|_| self.dispatch_transaction(params));
self.handle_dispatch(res, ready);
fn send_transaction(&self, ready: Ready<RpcH256>, request: RpcTransactionRequest) {
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SendTransaction(request)));
self.handle_dispatch(res, |response| {
match response {
Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.ready(Ok(hash)),
Err(e) => ready.ready(Err(e)),
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
}
});
}
}
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
where C: MiningBlockChainClient, M: MinerService {
FilledRequest {
from: request.from,
to: request.to,
nonce: request.nonce,
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
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),
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) {
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request)));
self.handle_dispatch(res, |response| {
match response {
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => ready.ready(Ok(rlp)),
Err(e) => ready.ready(Err(e)),
e => ready.ready(Err(errors::internal("Unexpected result.", e))),
}
});
}
}

View File

@ -22,11 +22,19 @@ use ethcore::account_provider::AccountProvider;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use jsonrpc_core::*;
use jsonrpc_core::Error;
use v1::helpers::auto_args::Ready;
use v1::helpers::errors;
use v1::helpers::dispatch::{sign_and_dispatch, sign, decrypt};
use v1::helpers::dispatch;
use v1::traits::{EthSigning, ParitySigning};
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, Bytes as RpcBytes};
use v1::types::{
U256 as RpcU256,
H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
Either as RpcEither,
TransactionRequest as RpcTransactionRequest,
ConfirmationPayload as RpcConfirmationPayload,
ConfirmationResponse as RpcConfirmationResponse,
};
/// Implementation of functions that require signing when no trusted signer is used.
pub struct SigningUnsafeClient<C, M> where
@ -58,26 +66,47 @@ impl<C, M> SigningUnsafeClient<C, M> where
take_weak!(self.client).keep_alive();
Ok(())
}
fn handle(&self, payload: RpcConfirmationPayload) -> Result<RpcConfirmationResponse, Error> {
try!(self.active());
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
let accounts = take_weak!(self.accounts);
let payload = dispatch::from_rpc(payload, &*client, &*miner);
dispatch::execute(&*client, &*miner, &*accounts, payload, None)
}
}
impl<C: 'static, M: 'static> EthSigning for SigningUnsafeClient<C, M> where
C: MiningBlockChainClient,
M: MinerService,
{
fn sign(&self, params: Params, ready: Ready) {
ready.ready(self.active()
.and_then(|_| from_params::<(RpcH160, RpcH256)>(params))
.and_then(|(address, msg)| {
sign(&*take_weak!(self.accounts), address.into(), None, msg.into())
}))
fn sign(&self, ready: Ready<RpcH520>, address: RpcH160, hash: RpcH256) {
let result = match self.handle(RpcConfirmationPayload::Signature((address, hash).into())) {
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)),
};
ready.ready(result);
}
fn send_transaction(&self, params: Params, ready: Ready) {
ready.ready(self.active()
.and_then(|_| from_params::<(TransactionRequest, )>(params))
.and_then(|(request, )| {
sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), &*take_weak!(self.accounts), request.into(), None).map(to_value)
}))
fn send_transaction(&self, ready: Ready<RpcH256>, request: RpcTransactionRequest) {
let result = match self.handle(RpcConfirmationPayload::SendTransaction(request)) {
Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash),
Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)),
};
ready.ready(result);
}
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) {
let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) {
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp),
Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)),
};
ready.ready(result);
}
}
@ -85,25 +114,26 @@ impl<C: 'static, M: 'static> ParitySigning for SigningUnsafeClient<C, M> where
C: MiningBlockChainClient,
M: MinerService,
{
fn decrypt_message(&self, params: Params, ready: Ready) {
ready.ready(self.active()
.and_then(|_| from_params::<(RpcH160, RpcBytes)>(params))
.and_then(|(address, ciphertext)| {
decrypt(&*take_weak!(self.accounts), address.into(), None, ciphertext.0)
}))
fn decrypt_message(&self, ready: Ready<RpcBytes>, address: RpcH160, data: RpcBytes) {
let result = match self.handle(RpcConfirmationPayload::Decrypt((address, data).into())) {
Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data),
Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)),
};
ready.ready(result);
}
fn post_sign(&self, _: Params) -> Result<Value, Error> {
fn post_sign(&self, _: RpcH160, _: RpcH256) -> Result<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
// We don't support this in non-signer mode.
Err(errors::signer_disabled())
}
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
fn post_transaction(&self, _: RpcTransactionRequest) -> Result<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
// We don't support this in non-signer mode.
Err(errors::signer_disabled())
}
fn check_request(&self, _: Params) -> Result<Value, Error> {
fn check_request(&self, _: RpcU256) -> Result<Option<RpcConfirmationResponse>, Error> {
// We don't support this in non-signer mode.
Err(errors::signer_disabled())
}

View File

@ -18,6 +18,7 @@ use std::str::FromStr;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Instant, Duration};
use rlp;
use jsonrpc_core::IoHandler;
use util::{Uint, U256, Address, H256, FixedHash, Mutex};
use ethcore::account_provider::AccountProvider;
@ -761,6 +762,44 @@ fn rpc_eth_send_transaction() {
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
}
#[test]
fn rpc_eth_sign_transaction() {
let tester = EthTester::default();
let address = tester.accounts_provider.new_account("").unwrap();
tester.accounts_provider.unlock_account_permanently(address, "".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_signTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let t = Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
value: U256::from(0x9184e72au64),
data: vec![]
};
let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
let t = t.with_signature(signature, None);
let rlp = rlp::encode(&t);
let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#;
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
}
#[test]
fn rpc_eth_send_transaction_with_bad_to() {
let tester = EthTester::default();
@ -839,7 +878,7 @@ fn rpc_eth_send_raw_transaction() {
let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
let t = t.with_signature(signature, None);
let rlp = ::rlp::encode(&t).to_vec().to_hex();
let rlp = rlp::encode(&t).to_vec().to_hex();
let req = r#"{
"jsonrpc": "2.0",

View File

@ -71,7 +71,7 @@ fn signer_tester() -> SignerTester {
fn should_return_list_of_items_to_confirm() {
// given
let tester = signer_tester();
tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
@ -80,7 +80,7 @@ fn should_return_list_of_items_to_confirm() {
data: vec![],
nonce: None,
})).unwrap();
tester.signer.add_request(ConfirmationPayload::Sign(1.into(), 5.into())).unwrap();
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), 5.into())).unwrap();
// when
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
@ -100,7 +100,7 @@ fn should_return_list_of_items_to_confirm() {
fn should_reject_transaction_from_queue_without_dispatching() {
// given
let tester = signer_tester();
tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
@ -125,7 +125,7 @@ fn should_reject_transaction_from_queue_without_dispatching() {
fn should_not_remove_transaction_if_password_is_invalid() {
// given
let tester = signer_tester();
tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
@ -149,7 +149,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
fn should_not_remove_sign_if_password_is_invalid() {
// given
let tester = signer_tester();
tester.signer.add_request(ConfirmationPayload::Sign(0.into(), 5.into())).unwrap();
tester.signer.add_request(ConfirmationPayload::Signature(0.into(), 5.into())).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// when
@ -167,7 +167,7 @@ fn should_confirm_transaction_and_dispatch() {
let tester = signer_tester();
let address = tester.accounts.new_account("test").unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest {
from: address,
to: Some(recipient),
gas_price: U256::from(10_000),

View File

@ -16,15 +16,17 @@
use std::str::FromStr;
use std::sync::Arc;
use jsonrpc_core::{IoHandler, to_value, Success};
use rlp;
use jsonrpc_core::{IoHandler, Success};
use v1::impls::SigningQueueClient;
use v1::traits::{EthSigning, ParitySigning, Parity};
use v1::helpers::{SignerService, SigningQueue};
use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes};
use v1::types::ConfirmationResponse;
use v1::tests::helpers::TestMinerService;
use v1::tests::mocked::parity;
use util::{Address, FixedHash, Uint, U256, H256, H520};
use util::{Address, FixedHash, Uint, U256, H256, ToPretty};
use ethcore::account_provider::AccountProvider;
use ethcore::client::TestBlockChainClient;
use ethcore::transaction::{Transaction, Action};
@ -88,7 +90,7 @@ fn should_add_sign_to_queue() {
let async_result = tester.io.handle_request(&request).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// respond
tester.signer.request_confirmed(U256::from(1), Ok(to_value(&RpcH520::from(H520::default()))));
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into())));
assert!(async_result.on_result(move |res| {
assert_eq!(res, response.to_owned());
}));
@ -162,7 +164,7 @@ fn should_check_status_of_request_when_its_resolved() {
"id": 1
}"#;
tester.io.handle_request_sync(&request).expect("Sent");
tester.signer.request_confirmed(U256::from(1), Ok(to_value(&"Hello World!")));
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(1.into())));
// when
let request = r#"{
@ -171,7 +173,7 @@ fn should_check_status_of_request_when_its_resolved() {
"params": ["0x1"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","id":1}"#;
// then
assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned()));
@ -228,7 +230,54 @@ fn should_add_transaction_to_queue() {
let async_result = tester.io.handle_request(&request).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// respond
tester.signer.request_confirmed(U256::from(1), Ok(to_value(&RpcH256::from(H256::default()))));
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into())));
assert!(async_result.on_result(move |res| {
assert_eq!(res, response.to_owned());
}));
}
#[test]
fn should_add_sign_transaction_to_the_queue() {
// given
let tester = eth_signing();
let address = tester.accounts.new_account("test").unwrap();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_signTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let t = Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
value: U256::from(0x9184e72au64),
data: vec![]
};
let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap();
let t = t.with_signature(signature, None);
let rlp = rlp::encode(&t);
let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#;
// then
tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
let async_result = tester.io.handle_request(&request).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// respond
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(rlp.to_vec().into())));
assert!(async_result.on_result(move |res| {
assert_eq!(res, response.to_owned());
}));
@ -325,7 +374,7 @@ fn should_add_decryption_to_the_queue() {
let async_result = tester.io.handle_request(&request).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// respond
tester.signer.request_confirmed(U256::from(1), Ok(to_value(Bytes(vec![0x1, 0x2]))));
tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into())));
assert!(async_result.on_result(move |res| {
assert_eq!(res, response.to_owned());
}));

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Eth rpc interface.
use jsonrpc_core::*;
use jsonrpc_core::Error;
use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index};
use v1::types::{Log, Receipt, SyncStatus, Transaction, Work};

View File

@ -15,26 +15,27 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Eth rpc interface.
use std::sync::Arc;
use jsonrpc_core::*;
/// 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, _: Ready);
use v1::helpers::auto_args::{WrapAsync, Ready};
use v1::types::{H160, H256, H520, TransactionRequest, Bytes};
/// Sends transaction; will block for 20s to try to return the
/// transaction hash.
/// If it cannot yet be signed, it will return a transaction ID for
/// later use with check_transaction.
fn send_transaction(&self, _: Params, _: Ready);
build_rpc_trait! {
/// Signing methods implementation relying on unlocked accounts.
pub trait EthSigning {
/// Signs the data with given address signature.
#[rpc(async, name = "eth_sign")]
fn sign(&self, Ready<H520>, H160, H256);
/// 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_async_method("eth_sign", EthSigning::sign);
delegate.add_async_method("eth_sendTransaction", EthSigning::send_transaction);
/// Sends transaction; will block waiting for signer to return the
/// transaction hash.
/// If Signer is disable it will require the account to be unlocked.
#[rpc(async, name = "eth_sendTransaction")]
fn send_transaction(&self, Ready<H256>, TransactionRequest);
delegate
/// Signs transactions without dispatching it to the network.
/// Returns signed transaction RLP representation.
/// It can be later submitted using `eth_sendRawTransaction`.
#[rpc(async, name = "eth_signTransaction")]
fn sign_transaction(&self, Ready<Bytes>, TransactionRequest);
}
}

View File

@ -15,38 +15,32 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! ParitySigning rpc interface.
use std::sync::Arc;
use jsonrpc_core::*;
use jsonrpc_core::Error;
/// Signing methods implementation relying on unlocked accounts.
pub trait ParitySigning: Sized + Send + Sync + 'static {
/// Posts sign request asynchronously.
/// Will return a confirmation ID for later use with check_transaction.
fn post_sign(&self, _: Params) -> Result<Value, Error>;
use v1::helpers::auto_args::{Wrap, WrapAsync, Ready};
use v1::types::{U256, H160, H256, Bytes, ConfirmationResponse, TransactionRequest, Either};
/// Posts transaction asynchronously.
/// Will return a transaction ID for later use with check_transaction.
fn post_transaction(&self, _: Params) -> Result<Value, Error>;
build_rpc_trait! {
/// Signing methods implementation.
pub trait ParitySigning {
/// Posts sign request asynchronously.
/// Will return a confirmation ID for later use with check_transaction.
#[rpc(name = "parity_postSign")]
fn post_sign(&self, H160, H256) -> Result<Either<U256, ConfirmationResponse>, Error>;
/// Checks the progress of a previously posted request (transaction/sign).
/// Should be given a valid send_transaction ID.
/// Returns the transaction hash, the zero hash (not yet available),
/// or the signature,
/// or an error.
fn check_request(&self, _: Params) -> Result<Value, Error>;
/// Posts transaction asynchronously.
/// Will return a transaction ID for later use with check_transaction.
#[rpc(name = "parity_postTransaction")]
fn post_transaction(&self, TransactionRequest) -> Result<Either<U256, ConfirmationResponse>, Error>;
/// Decrypt some ECIES-encrypted message.
/// First parameter is the address with which it is encrypted, second is the ciphertext.
fn decrypt_message(&self, _: Params, _: Ready);
/// Checks the progress of a previously posted request (transaction/sign).
/// Should be given a valid send_transaction ID.
#[rpc(name = "parity_checkRequest")]
fn check_request(&self, U256) -> Result<Option<ConfirmationResponse>, 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("parity_postSign", ParitySigning::post_sign);
delegate.add_method("parity_postTransaction", ParitySigning::post_transaction);
delegate.add_method("parity_checkRequest", ParitySigning::check_request);
delegate.add_async_method("parity_decryptMessage", ParitySigning::decrypt_message);
delegate
/// Decrypt some ECIES-encrypted message.
/// First parameter is the address with which it is encrypted, second is the ciphertext.
#[rpc(async, name = "parity_decryptMessage")]
fn decrypt_message(&self, Ready<Bytes>, H160, Bytes);
}
}

View File

@ -15,10 +15,10 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Parity Signer-related rpc interface.
use jsonrpc_core::{Value, Error};
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
use v1::types::{U256, TransactionModification, ConfirmationRequest};
use v1::types::{U256, TransactionModification, ConfirmationRequest, ConfirmationResponse};
build_rpc_trait! {
@ -31,7 +31,7 @@ build_rpc_trait! {
/// Confirm specific request.
#[rpc(name = "signer_confirmRequest")]
fn confirm_request(&self, U256, TransactionModification, String) -> Result<Value, Error>;
fn confirm_request(&self, U256, TransactionModification, String) -> Result<ConfirmationResponse, Error>;
/// Reject the confirmation request.
#[rpc(name = "signer_rejectRequest")]

View File

@ -16,10 +16,11 @@
//! Types used in Confirmations queue (Trusted Signer)
use v1::types::{U256, TransactionRequest, H160, H256, Bytes};
use std::fmt;
use serde::{Serialize, Serializer};
use v1::types::{U256, TransactionRequest, H160, H256, H520, Bytes};
use v1::helpers;
/// Confirmation waiting in a queue
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
pub struct ConfirmationRequest {
@ -47,6 +48,15 @@ pub struct SignRequest {
pub hash: H256,
}
impl From<(H160, H256)> for SignRequest {
fn from(tuple: (H160, H256)) -> Self {
SignRequest {
address: tuple.0,
hash: tuple.1,
}
}
}
/// Decrypt request
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
pub struct DecryptRequest {
@ -56,15 +66,53 @@ pub struct DecryptRequest {
pub msg: Bytes,
}
impl From<(H160, Bytes)> for DecryptRequest {
fn from(tuple: (H160, Bytes)) -> Self {
DecryptRequest {
address: tuple.0,
msg: tuple.1,
}
}
}
/// Confirmation response for particular payload
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum ConfirmationResponse {
/// Transaction Hash
SendTransaction(H256),
/// Transaction RLP
SignTransaction(Bytes),
/// Signature
Signature(H520),
/// Decrypted data
Decrypt(Bytes),
}
impl Serialize for ConfirmationResponse {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer
{
match *self {
ConfirmationResponse::SendTransaction(ref hash) => hash.serialize(serializer),
ConfirmationResponse::SignTransaction(ref rlp) => rlp.serialize(serializer),
ConfirmationResponse::Signature(ref signature) => signature.serialize(serializer),
ConfirmationResponse::Decrypt(ref data) => data.serialize(serializer),
}
}
}
/// Confirmation payload, i.e. the thing to be confirmed
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
pub enum ConfirmationPayload {
/// Transaction
/// Send Transaction
#[serde(rename="transaction")]
Transaction(TransactionRequest),
SendTransaction(TransactionRequest),
/// Sign Transaction
#[serde(rename="transaction")]
SignTransaction(TransactionRequest),
/// Signature
#[serde(rename="sign")]
Sign(SignRequest),
Signature(SignRequest),
/// Decryption
#[serde(rename="decrypt")]
Decrypt(DecryptRequest),
@ -73,8 +121,9 @@ pub enum ConfirmationPayload {
impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
fn from(c: helpers::ConfirmationPayload) -> Self {
match c {
helpers::ConfirmationPayload::Transaction(t) => ConfirmationPayload::Transaction(t.into()),
helpers::ConfirmationPayload::Sign(address, hash) => ConfirmationPayload::Sign(SignRequest {
helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()),
helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()),
helpers::ConfirmationPayload::Signature(address, hash) => ConfirmationPayload::Signature(SignRequest {
address: address.into(),
hash: hash.into(),
}),
@ -94,6 +143,41 @@ pub struct TransactionModification {
pub gas_price: Option<U256>,
}
/// Represents two possible return values.
#[derive(Debug, Clone)]
pub enum Either<A, B> where
A: fmt::Debug + Clone,
B: fmt::Debug + Clone,
{
/// Primary value
Either(A),
/// Secondary value
Or(B),
}
impl<A, B> From<A> for Either<A, B> where
A: fmt::Debug + Clone,
B: fmt::Debug + Clone,
{
fn from(a: A) -> Self {
Either::Either(a)
}
}
impl<A, B> Serialize for Either<A, B> where
A: Serialize + fmt::Debug + Clone,
B: Serialize + fmt::Debug + Clone,
{
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer
{
match *self {
Either::Either(ref a) => a.serialize(serializer),
Either::Or(ref b) => b.serialize(serializer),
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
@ -107,7 +191,7 @@ mod tests {
// given
let request = helpers::ConfirmationRequest {
id: 15.into(),
payload: helpers::ConfirmationPayload::Sign(1.into(), 5.into()),
payload: helpers::ConfirmationPayload::Signature(1.into(), 5.into()),
};
// when
@ -123,7 +207,7 @@ mod tests {
// given
let request = helpers::ConfirmationRequest {
id: 15.into(),
payload: helpers::ConfirmationPayload::Transaction(helpers::FilledTransactionRequest {
payload: helpers::ConfirmationPayload::SendTransaction(helpers::FilledTransactionRequest {
from: 0.into(),
to: None,
gas: 15_000.into(),

View File

@ -38,7 +38,7 @@ pub use self::bytes::Bytes;
pub use self::block::{RichBlock, Block, BlockTransactions};
pub use self::block_number::BlockNumber;
pub use self::call_request::CallRequest;
pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, TransactionModification};
pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, TransactionModification, SignRequest, DecryptRequest, Either};
pub use self::filter::{Filter, FilterChanges};
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
pub use self::index::Index;