diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 6d8bd4b07..963afbecb 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -24,17 +24,26 @@ use jsonrpc_core::*; use util::numbers::*; use util::sha3::*; use util::rlp::{encode, UntrustedRlp, View}; +use util::crypto::KeyPair; use ethcore::client::*; use ethcore::block::IsBlock; use ethcore::views::*; use ethcore::ethereum::Ethash; use ethcore::ethereum::denominations::shannon; -use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction}; +use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log, Receipt}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner}; use util::keys::store::AccountProvider; +fn default_gas() -> U256 { + U256::from(21_000) +} + +fn default_gas_price() -> U256 { + shannon() * U256::from(50) +} + /// Eth rpc implementation. pub struct EthClient where C: BlockChainClient, @@ -157,6 +166,35 @@ impl EthClient None => Ok(Value::Null) } } + + fn sign_call(client: &Arc, accounts: &Arc, request: CallRequest) -> Option { + match request.from { + Some(ref from) => { + let transaction = EthTransaction { + nonce: request.nonce.unwrap_or_else(|| client.nonce(from)), + action: request.to.map_or(Action::Create, Action::Call), + gas: request.gas.unwrap_or_else(default_gas), + gas_price: request.gas_price.unwrap_or_else(default_gas_price), + value: request.value.unwrap_or_else(U256::zero), + data: request.data.map_or_else(Vec::new, |d| d.to_vec()) + }; + + accounts.account_secret(from).ok().map(|secret| transaction.sign(&secret)) + }, + None => { + let transaction = EthTransaction { + nonce: request.nonce.unwrap_or_else(U256::zero), + action: request.to.map_or(Action::Create, Action::Call), + gas: request.gas.unwrap_or_else(default_gas), + gas_price: request.gas_price.unwrap_or_else(default_gas_price), + value: request.value.unwrap_or_else(U256::zero), + data: request.data.map_or_else(Vec::new, |d| d.to_vec()) + }; + + KeyPair::create().ok().map(|kp| transaction.sign(kp.secret())) + } + } + } } impl Eth for EthClient @@ -217,7 +255,7 @@ impl Eth for EthClient fn gas_price(&self, params: Params) -> Result { match params { - Params::None => to_value(&(shannon() * U256::from(50))), + Params::None => to_value(&default_gas_price()), _ => Err(Error::invalid_params()) } } @@ -405,14 +443,22 @@ impl Eth for EthClient fn send_transaction(&self, params: Params) -> Result { from_params::<(TransactionRequest, )>(params) - .and_then(|(transaction_request, )| { + .and_then(|(request, )| { let accounts = take_weak!(self.accounts); - match accounts.account_secret(&transaction_request.from) { + match accounts.account_secret(&request.from) { Ok(secret) => { let miner = take_weak!(self.miner); let client = take_weak!(self.client); - let transaction: EthTransaction = transaction_request.into(); + let transaction = EthTransaction { + nonce: request.nonce.unwrap_or_else(|| client.nonce(&request.from)), + action: request.to.map_or(Action::Create, Action::Call), + gas: request.gas.unwrap_or_else(default_gas), + gas_price: request.gas_price.unwrap_or_else(default_gas_price), + value: request.value.unwrap_or_else(U256::zero), + data: request.data.map_or_else(Vec::new, |d| d.to_vec()) + }; + let signed_transaction = transaction.sign(&secret); let hash = signed_transaction.hash(); @@ -461,47 +507,30 @@ impl Eth for EthClient } fn call(&self, params: Params) -> Result { - println!("params: {:?}", params); - from_params::<(TransactionRequest, BlockNumber)>(params) - .and_then(|(transaction_request, _block_number)| { + from_params::<(CallRequest, BlockNumber)>(params) + .and_then(|(request, _block_number)| { + let client = take_weak!(self.client); let accounts = take_weak!(self.accounts); - match accounts.account_secret(&transaction_request.from) { - Ok(secret) => { - let client = take_weak!(self.client); + let signed = Self::sign_call(&client, &accounts, request); + let output = signed.map(|tx| client.call(&tx) + .map(|e| Bytes::new(e.output)) + .unwrap_or(Bytes::default())); - let transaction: EthTransaction = transaction_request.into(); - let signed_transaction = transaction.sign(&secret); - - let output = client.call(&signed_transaction) - .map(|e| Bytes::new(e.output)) - .unwrap_or(Bytes::default()); - - to_value(&output) - }, - Err(_) => { to_value(&Bytes::default()) } - } + to_value(&output) }) } fn estimate_gas(&self, params: Params) -> Result { - from_params::<(TransactionRequest, BlockNumber)>(params) - .and_then(|(transaction_request, _block_number)| { + from_params::<(CallRequest, BlockNumber)>(params) + .and_then(|(request, _block_number)| { + let client = take_weak!(self.client); let accounts = take_weak!(self.accounts); - match accounts.account_secret(&transaction_request.from) { - Ok(secret) => { - let client = take_weak!(self.client); + let signed = Self::sign_call(&client, &accounts, request); + let output = signed.map(|tx| client.call(&tx) + .map(|e| e.gas_used + e.refunded) + .unwrap_or(U256::zero())); - let transaction: EthTransaction = transaction_request.into(); - let signed_transaction = transaction.sign(&secret); - - let gas_used = client.call(&signed_transaction) - .map(|e| e.gas_used + e.refunded) - .unwrap_or(U256::zero()); - - to_value(&gas_used) - }, - Err(_) => { to_value(&U256::zero()) } - } + to_value(&output) }) } } diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs new file mode 100644 index 000000000..a47d40eb3 --- /dev/null +++ b/rpc/src/v1/types/call_request.rs @@ -0,0 +1,106 @@ +// 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 . + +use util::hash::Address; +use util::numbers::U256; +use v1::types::Bytes; + +#[derive(Debug, Default, PartialEq, Deserialize)] +pub struct CallRequest { + pub from: Option
, + pub to: Option
, + #[serde(rename="gasPrice")] + pub gas_price: Option, + pub gas: Option, + pub value: Option, + pub data: Option, + pub nonce: Option, +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use serde_json; + use util::numbers::{Uint, U256}; + use util::hash::Address; + use ethcore::transaction::{Transaction, Action}; + use v1::types::Bytes; + use super::*; + + #[test] + fn transaction_request_deserialize() { + let s = r#"{ + "from":"0x0000000000000000000000000000000000000001", + "to":"0x0000000000000000000000000000000000000002", + "gasPrice":"0x1", + "gas":"0x2", + "value":"0x3", + "data":"0x123456", + "nonce":"0x4" + }"#; + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + + assert_eq!(deserialized, CallRequest { + from: Some(Address::from(1)), + to: Some(Address::from(2)), + gas_price: Some(U256::from(1)), + gas: Some(U256::from(2)), + value: Some(U256::from(3)), + data: Some(Bytes::new(vec![0x12, 0x34, 0x56])), + nonce: Some(U256::from(4)), + }); + } + + #[test] + fn transaction_request_deserialize2() { + let s = r#"{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }"#; + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + + assert_eq!(deserialized, CallRequest { + from: Some(Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), + to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + gas_price: Some(U256::from_str("9184e72a000").unwrap()), + gas: Some(U256::from_str("76c0").unwrap()), + value: Some(U256::from_str("9184e72a").unwrap()), + data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())), + nonce: None + }); + } + + #[test] + fn transaction_request_deserialize_empty() { + let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; + let deserialized: CallRequest = serde_json::from_str(s).unwrap(); + + assert_eq!(deserialized, CallRequest { + from: Some(Address::from(1)), + to: None, + gas_price: None, + gas: None, + value: None, + data: None, + nonce: None, + }); + } +} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 0121e4aea..06b07b146 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -24,6 +24,7 @@ mod optionals; mod sync; mod transaction; mod transaction_request; +mod call_request; mod receipt; pub use self::block::{Block, BlockTransactions}; @@ -36,5 +37,6 @@ 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::call_request::CallRequest; pub use self::receipt::Receipt; diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index ed4dc19a2..a9ed8a3f4 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -15,8 +15,7 @@ // along with Parity. If not, see . use util::hash::Address; -use util::numbers::{Uint, U256}; -use ethcore::transaction::{Action, Transaction}; +use util::numbers::U256; use v1::types::Bytes; #[derive(Debug, Default, PartialEq, Deserialize)] @@ -31,19 +30,6 @@ pub struct TransactionRequest { pub nonce: Option, } -impl Into for TransactionRequest { - fn into(self) -> Transaction { - Transaction { - nonce: self.nonce.unwrap_or_else(U256::zero), - action: self.to.map_or(Action::Create, Action::Call), - gas: self.gas.unwrap_or_else(U256::zero), - gas_price: self.gas_price.unwrap_or_else(U256::zero), - value: self.value.unwrap_or_else(U256::zero), - data: self.data.map_or_else(Vec::new, |d| d.to_vec()), - } - } -} - #[cfg(test)] mod tests { use std::str::FromStr; @@ -55,50 +41,6 @@ mod tests { use v1::types::Bytes; use super::*; - #[test] - fn transaction_request_into_transaction() { - let tr = TransactionRequest { - from: Address::default(), - to: Some(Address::from(10)), - gas_price: Some(U256::from(20)), - gas: Some(U256::from(10_000)), - value: Some(U256::from(1)), - data: Some(Bytes::new(vec![10, 20])), - nonce: Some(U256::from(12)), - }; - - assert_eq!(Transaction { - nonce: U256::from(12), - action: Action::Call(Address::from(10)), - gas: U256::from(10_000), - gas_price: U256::from(20), - value: U256::from(1), - data: vec![10, 20], - }, tr.into()); - } - - #[test] - fn empty_transaction_request_into_transaction() { - let tr = TransactionRequest { - from: Address::default(), - to: None, - gas_price: None, - gas: None, - value: None, - data: None, - nonce: None, - }; - - assert_eq!(Transaction { - nonce: U256::zero(), - action: Action::Create, - gas: U256::zero(), - gas_price: U256::zero(), - value: U256::zero(), - data: vec![], - }, tr.into()); - } - #[test] fn transaction_request_deserialize() { let s = r#"{ diff --git a/util/src/crypto.rs b/util/src/crypto.rs index 66e9f2edb..e5096a317 100644 --- a/util/src/crypto.rs +++ b/util/src/crypto.rs @@ -20,6 +20,7 @@ use numbers::*; use bytes::*; use secp256k1::{key, Secp256k1}; use rand::os::OsRng; +use sha3::Hashable; /// Secret key for secp256k1 EC operations. 256 bit generic "hash" data. pub type Secret = H256; @@ -135,15 +136,22 @@ impl KeyPair { public: p, }) } + /// Returns public key pub fn public(&self) -> &Public { &self.public } + /// Returns private key pub fn secret(&self) -> &Secret { &self.secret } + /// Returns address. + pub fn address(&self) -> Address { + Address::from(self.public.sha3()) + } + /// Sign a message with our secret key. pub fn sign(&self, message: &H256) -> Result { ec::sign(&self.secret, message) } }