Merge pull request #798 from ethcore/eth_rpc

another batch of rpc improvements
This commit is contained in:
Gav Wood 2016-03-22 18:38:55 +01:00
commit d7fb464fa9
14 changed files with 390 additions and 123 deletions

View File

@ -21,7 +21,7 @@ use util::*;
use util::panics::*; use util::panics::*;
use views::BlockView; use views::BlockView;
use error::*; use error::*;
use header::{BlockNumber}; use header::{BlockNumber, Header};
use state::State; use state::State;
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
@ -36,7 +36,7 @@ use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient}; use client::{BlockId, TransactionId, UncleId, ClientConfig, BlockChainClient};
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed}; use executive::{Executive, Executed};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
@ -549,6 +549,11 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
self.transaction_address(id).and_then(|address| self.chain.transaction(&address)) self.transaction_address(id).and_then(|address| self.chain.transaction(&address))
} }
fn uncle(&self, id: UncleId) -> Option<Header> {
let index = id.1;
self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index))
}
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> { fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
self.transaction_address(id).and_then(|address| { self.transaction_address(id).and_then(|address| {
let t = self.chain.block(&address.block_hash) let t = self.chain.block(&address.block_hash)

View File

@ -20,7 +20,7 @@ use util::hash::H256;
use header::BlockNumber; use header::BlockNumber;
/// Uniquely identifies block. /// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum BlockId { pub enum BlockId {
/// Block's sha3. /// Block's sha3.
/// Querying by hash is always faster. /// Querying by hash is always faster.
@ -34,7 +34,7 @@ pub enum BlockId {
} }
/// Uniquely identifies transaction. /// Uniquely identifies transaction.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum TransactionId { pub enum TransactionId {
/// Transaction's sha3. /// Transaction's sha3.
Hash(H256), Hash(H256),
@ -42,3 +42,11 @@ pub enum TransactionId {
/// Querying by block position is always faster. /// Querying by block position is always faster.
Location(BlockId, usize) Location(BlockId, usize)
} }
/// Uniquely identifies Uncle.
pub struct UncleId (
/// Block id.
pub BlockId,
/// Position in block.
pub usize
);

View File

@ -23,7 +23,7 @@ mod test_client;
pub use self::client::*; pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig}; pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
pub use self::ids::{BlockId, TransactionId}; pub use self::ids::{BlockId, TransactionId, UncleId};
pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use executive::Executed; pub use executive::Executed;
@ -34,7 +34,7 @@ use util::numbers::U256;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{ClosedBlock, SealedBlock}; use block::{ClosedBlock, SealedBlock};
use header::BlockNumber; use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use filter::Filter; use filter::Filter;
@ -77,6 +77,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get transaction with given hash. /// Get transaction with given hash.
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>; fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
/// Get uncle with given id.
fn uncle(&self, id: UncleId) -> Option<Header>;
/// Get transaction receipt with given hash. /// Get transaction receipt with given hash.
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>; fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;

View File

@ -19,7 +19,7 @@
use util::*; use util::*;
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute; use blockchain::TreeRoute;
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId}; use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId};
use header::{Header as BlockHeader, BlockNumber}; use header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -52,6 +52,8 @@ pub struct TestBlockChainClient {
pub code: RwLock<HashMap<Address, Bytes>>, pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result. /// Execution result.
pub execution_result: RwLock<Option<Executed>>, pub execution_result: RwLock<Option<Executed>>,
/// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionId, LocalizedReceipt>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -87,12 +89,18 @@ impl TestBlockChainClient {
storage: RwLock::new(HashMap::new()), storage: RwLock::new(HashMap::new()),
code: RwLock::new(HashMap::new()), code: RwLock::new(HashMap::new()),
execution_result: RwLock::new(None), execution_result: RwLock::new(None),
receipts: RwLock::new(HashMap::new()),
}; };
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().unwrap().clone(); client.genesis_hash = client.last_hash.read().unwrap().clone();
client client
} }
/// Set the transaction receipt result
pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) {
self.receipts.write().unwrap().insert(id, receipt);
}
/// Set the execution result. /// Set the execution result.
pub fn set_execution_result(&self, result: Executed) { pub fn set_execution_result(&self, result: Executed) {
*self.execution_result.write().unwrap() = Some(result); *self.execution_result.write().unwrap() = Some(result);
@ -224,10 +232,14 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn transaction_receipt(&self, _id: TransactionId) -> Option<LocalizedReceipt> { fn uncle(&self, _id: UncleId) -> Option<BlockHeader> {
unimplemented!(); unimplemented!();
} }
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
self.receipts.read().unwrap().get(&id).cloned()
}
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> { fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
unimplemented!(); unimplemented!();
} }

View File

@ -78,7 +78,7 @@ impl FromJson for LogEntry {
} }
/// Log localized in a blockchain. /// Log localized in a blockchain.
#[derive(Default, Debug, PartialEq)] #[derive(Default, Debug, PartialEq, Clone)]
pub struct LocalizedLogEntry { pub struct LocalizedLogEntry {
/// Plain log entry. /// Plain log entry.
pub entry: LogEntry, pub entry: LogEntry,

View File

@ -76,6 +76,7 @@ impl HeapSizeOf for Receipt {
} }
/// Receipt with additional info. /// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt { pub struct LocalizedReceipt {
/// Transaction hash. /// Transaction hash.
pub transaction_hash: H256, pub transaction_hash: H256,

View File

@ -23,18 +23,27 @@ use ethminer::{MinerService, AccountDetails};
use jsonrpc_core::*; use jsonrpc_core::*;
use util::numbers::*; use util::numbers::*;
use util::sha3::*; use util::sha3::*;
use util::rlp::encode; use util::rlp::{encode, UntrustedRlp, View};
use util::crypto::KeyPair;
use ethcore::client::*; use ethcore::client::*;
use ethcore::block::IsBlock; use ethcore::block::IsBlock;
use ethcore::views::*; use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
use ethcore::ethereum::denominations::shannon; use ethcore::ethereum::denominations::shannon;
use ethcore::transaction::Transaction as EthTransaction; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use v1::traits::{Eth, EthFilter}; 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 v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner};
use util::keys::store::AccountProvider; 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. /// Eth rpc implementation.
pub struct EthClient<C, S, A, M, EM = ExternalMiner> pub struct EthClient<C, S, A, M, EM = ExternalMiner>
where C: BlockChainClient, where C: BlockChainClient,
@ -61,7 +70,6 @@ impl<C, S, A, M> EthClient<C, S, A, M, ExternalMiner>
} }
} }
impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
where C: BlockChainClient, where C: BlockChainClient,
S: SyncProvider, S: SyncProvider,
@ -127,9 +135,65 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
} }
} }
fn uncle(&self, _block: BlockId, _index: usize) -> Result<Value, Error> { fn uncle(&self, id: UncleId) -> Result<Value, Error> {
// TODO: implement! let client = take_weak!(self.client);
Ok(Value::Null) match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockId::Hash(u.hash())).map(|diff| (diff, u))) {
Some((difficulty, uncle)) => {
let block = Block {
hash: OptionalValue::Value(uncle.hash()),
parent_hash: uncle.parent_hash,
uncles_hash: uncle.uncles_hash,
author: uncle.author,
miner: uncle.author,
state_root: uncle.state_root,
transactions_root: uncle.transactions_root,
number: OptionalValue::Value(U256::from(uncle.number)),
gas_used: uncle.gas_used,
gas_limit: uncle.gas_limit,
logs_bloom: uncle.log_bloom,
timestamp: U256::from(uncle.timestamp),
difficulty: uncle.difficulty,
total_difficulty: difficulty,
receipts_root: uncle.receipts_root,
extra_data: Bytes::new(uncle.extra_data),
// todo:
nonce: H64::from(0),
uncles: vec![],
transactions: BlockTransactions::Hashes(vec![]),
};
to_value(&block)
},
None => Ok(Value::Null)
}
}
fn sign_call(client: &Arc<C>, accounts: &Arc<A>, request: CallRequest) -> Option<SignedTransaction> {
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()))
}
}
} }
} }
@ -191,7 +255,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn gas_price(&self, params: Params) -> Result<Value, Error> { fn gas_price(&self, params: Params) -> Result<Value, Error> {
match params { match params {
Params::None => to_value(&(shannon() * U256::from(50))), Params::None => to_value(&default_gas_price()),
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())
} }
} }
@ -304,12 +368,12 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> { fn uncle_by_block_hash_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(H256, Index)>(params) from_params::<(H256, Index)>(params)
.and_then(|(hash, index)| self.uncle(BlockId::Hash(hash), index.value())) .and_then(|(hash, index)| self.uncle(UncleId(BlockId::Hash(hash), index.value())))
} }
fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> { fn uncle_by_block_number_and_index(&self, params: Params) -> Result<Value, Error> {
from_params::<(BlockNumber, Index)>(params) from_params::<(BlockNumber, Index)>(params)
.and_then(|(number, index)| self.uncle(number.into(), index.value())) .and_then(|(number, index)| self.uncle(UncleId(number.into(), index.value())))
} }
fn compilers(&self, params: Params) -> Result<Value, Error> { fn compilers(&self, params: Params) -> Result<Value, Error> {
@ -371,7 +435,6 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
} }
fn submit_hashrate(&self, params: Params) -> Result<Value, Error> { fn submit_hashrate(&self, params: Params) -> Result<Value, Error> {
// TODO: Index should be U256.
from_params::<(U256, H256)>(params).and_then(|(rate, id)| { from_params::<(U256, H256)>(params).and_then(|(rate, id)| {
self.external_miner.submit_hashrate(rate, id); self.external_miner.submit_hashrate(rate, id);
to_value(&true) to_value(&true)
@ -380,14 +443,22 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn send_transaction(&self, params: Params) -> Result<Value, Error> { fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params) from_params::<(TransactionRequest, )>(params)
.and_then(|(transaction_request, )| { .and_then(|(request, )| {
let accounts = take_weak!(self.accounts); let accounts = take_weak!(self.accounts);
match accounts.account_secret(&transaction_request.from) { match accounts.account_secret(&request.from) {
Ok(secret) => { Ok(secret) => {
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
let client = take_weak!(self.client); 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 signed_transaction = transaction.sign(&secret);
let hash = signed_transaction.hash(); let hash = signed_transaction.hash();
@ -408,47 +479,58 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
}) })
} }
fn call(&self, params: Params) -> Result<Value, Error> { fn send_raw_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, BlockNumber)>(params) from_params::<(Bytes, )>(params)
.and_then(|(transaction_request, _block_number)| { .and_then(|(raw_transaction, )| {
let accounts = take_weak!(self.accounts); let decoded: Result<SignedTransaction, _> = UntrustedRlp::new(&raw_transaction.to_vec()).as_val();
match accounts.account_secret(&transaction_request.from) { match decoded {
Ok(secret) => { Ok(signed_tx) => {
let miner = take_weak!(self.miner);
let client = take_weak!(self.client); let client = take_weak!(self.client);
let transaction: EthTransaction = transaction_request.into(); let hash = signed_tx.hash();
let signed_transaction = transaction.sign(&secret); let import = miner.import_transactions(vec![signed_tx], |a: &Address| AccountDetails {
nonce: client.nonce(a),
let output = client.call(&signed_transaction) balance: client.balance(a),
.map(|e| Bytes::new(e.output)) });
.unwrap_or(Bytes::default()); match import.into_iter().collect::<Result<Vec<_>, _>>() {
Ok(_) => to_value(&hash),
to_value(&output) Err(e) => {
warn!("Error sending transaction: {:?}", e);
to_value(&U256::zero())
}
}
}, },
Err(_) => { to_value(&Bytes::default()) } Err(_) => { to_value(&U256::zero()) }
} }
}) })
} }
fn estimate_gas(&self, params: Params) -> Result<Value, Error> { fn call(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, BlockNumber)>(params) from_params::<(CallRequest, BlockNumber)>(params)
.and_then(|(transaction_request, _block_number)| { .and_then(|(request, _block_number)| {
let accounts = take_weak!(self.accounts);
match accounts.account_secret(&transaction_request.from) {
Ok(secret) => {
let client = take_weak!(self.client); let client = take_weak!(self.client);
let accounts = take_weak!(self.accounts);
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(); to_value(&output)
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()) }
} }
fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
from_params::<(CallRequest, BlockNumber)>(params)
.and_then(|(request, _block_number)| {
let client = take_weak!(self.client);
let accounts = take_weak!(self.accounts);
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()));
to_value(&output)
}) })
} }
} }

View File

@ -14,12 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use util::hash::{Address, H256}; use util::hash::{Address, H256, FixedHash};
use util::numbers::{Uint, U256}; use util::numbers::{Uint, U256};
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed}; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt;
use v1::{Eth, EthClient}; use v1::{Eth, EthClient};
use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService, TestExternalMiner}; use v1::tests::helpers::{TestAccount, TestAccountProvider, TestSyncProvider, Config, TestMinerService, TestExternalMiner};
@ -382,6 +385,63 @@ fn rpc_eth_sign() {
unimplemented!() unimplemented!()
} }
#[test]
fn rpc_eth_transaction_receipt() {
let receipt = LocalizedReceipt {
transaction_hash: H256::zero(),
transaction_index: 0,
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: 0x4510c,
cumulative_gas_used: U256::from(0x20),
gas_used: U256::from(0x10),
contract_address: None,
logs: vec![LocalizedLogEntry {
entry: LogEntry {
address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(),
topics: vec![
H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(),
H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap()
],
data: vec![],
},
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: 0x4510c,
transaction_hash: H256::new(),
transaction_index: 0,
log_index: 1,
}]
};
let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap();
let tester = EthTester::default();
tester.client.set_transaction_receipt(TransactionId::Hash(hash), receipt);
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","data":"0x","logIndex":"0x01","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00"}],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00"},"id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test]
fn rpc_eth_transaction_receipt_null() {
let tester = EthTester::default();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test] #[test]
fn rpc_eth_compilers() { fn rpc_eth_compilers() {
let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#; let request = r#"{"jsonrpc": "2.0", "method": "eth_getCompilers", "params": [], "id": 1}"#;

View File

@ -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 <http://www.gnu.org/licenses/>.
use util::hash::Address;
use util::numbers::U256;
use v1::types::Bytes;
#[derive(Debug, Default, PartialEq, Deserialize)]
pub struct CallRequest {
pub from: Option<Address>,
pub to: Option<Address>,
#[serde(rename="gasPrice")]
pub gas_price: Option<U256>,
pub gas: Option<U256>,
pub value: Option<U256>,
pub data: Option<Bytes>,
pub nonce: Option<U256>,
}
#[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,
});
}
}

View File

@ -20,19 +20,19 @@ use v1::types::Bytes;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Log { pub struct Log {
address: Address, pub address: Address,
topics: Vec<H256>, pub topics: Vec<H256>,
data: Bytes, pub data: Bytes,
#[serde(rename="blockHash")] #[serde(rename="blockHash")]
block_hash: H256, pub block_hash: H256,
#[serde(rename="blockNumber")] #[serde(rename="blockNumber")]
block_number: U256, pub block_number: U256,
#[serde(rename="transactionHash")] #[serde(rename="transactionHash")]
transaction_hash: H256, pub transaction_hash: H256,
#[serde(rename="transactionIndex")] #[serde(rename="transactionIndex")]
transaction_index: U256, pub transaction_index: U256,
#[serde(rename="logIndex")] #[serde(rename="logIndex")]
log_index: U256, pub log_index: U256,
} }
impl From<LocalizedLogEntry> for Log { impl From<LocalizedLogEntry> for Log {

View File

@ -24,6 +24,7 @@ mod optionals;
mod sync; mod sync;
mod transaction; mod transaction;
mod transaction_request; mod transaction_request;
mod call_request;
mod receipt; mod receipt;
pub use self::block::{Block, BlockTransactions}; pub use self::block::{Block, BlockTransactions};
@ -36,5 +37,6 @@ pub use self::optionals::OptionalValue;
pub use self::sync::{SyncStatus, SyncInfo}; pub use self::sync::{SyncStatus, SyncInfo};
pub use self::transaction::Transaction; pub use self::transaction::Transaction;
pub use self::transaction_request::TransactionRequest; pub use self::transaction_request::TransactionRequest;
pub use self::call_request::CallRequest;
pub use self::receipt::Receipt; pub use self::receipt::Receipt;

View File

@ -53,4 +53,42 @@ impl From<LocalizedReceipt> for Receipt {
} }
} }
#[cfg(test)]
mod tests {
use serde_json;
use std::str::FromStr;
use util::numbers::*;
use v1::types::{Bytes, Log, Receipt};
#[test]
fn receipt_serialization() {
let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01"}]}"#;
let receipt = Receipt {
transaction_hash: H256::zero(),
transaction_index: U256::zero(),
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: U256::from(0x4510c),
cumulative_gas_used: U256::from(0x20),
gas_used: U256::from(0x10),
contract_address: None,
logs: vec![Log {
address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(),
topics: vec![
H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(),
H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap()
],
data: Bytes::new(vec![]),
block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(),
block_number: U256::from(0x4510c),
transaction_hash: H256::new(),
transaction_index: U256::zero(),
log_index: U256::one()
}]
};
let serialized = serde_json::to_string(&receipt).unwrap();
assert_eq!(serialized, s);
}
}

View File

@ -15,8 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::hash::Address; use util::hash::Address;
use util::numbers::{Uint, U256}; use util::numbers::U256;
use ethcore::transaction::{Action, Transaction};
use v1::types::Bytes; use v1::types::Bytes;
#[derive(Debug, Default, PartialEq, Deserialize)] #[derive(Debug, Default, PartialEq, Deserialize)]
@ -31,19 +30,6 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
} }
impl Into<Transaction> 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)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
@ -55,50 +41,6 @@ mod tests {
use v1::types::Bytes; use v1::types::Bytes;
use super::*; 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] #[test]
fn transaction_request_deserialize() { fn transaction_request_deserialize() {
let s = r#"{ let s = r#"{

View File

@ -20,6 +20,7 @@ use numbers::*;
use bytes::*; use bytes::*;
use secp256k1::{key, Secp256k1}; use secp256k1::{key, Secp256k1};
use rand::os::OsRng; use rand::os::OsRng;
use sha3::Hashable;
/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data. /// Secret key for secp256k1 EC operations. 256 bit generic "hash" data.
pub type Secret = H256; pub type Secret = H256;
@ -135,15 +136,22 @@ impl KeyPair {
public: p, public: p,
}) })
} }
/// Returns public key /// Returns public key
pub fn public(&self) -> &Public { pub fn public(&self) -> &Public {
&self.public &self.public
} }
/// Returns private key /// Returns private key
pub fn secret(&self) -> &Secret { pub fn secret(&self) -> &Secret {
&self.secret &self.secret
} }
/// Returns address.
pub fn address(&self) -> Address {
Address::from(self.public.sha3())
}
/// Sign a message with our secret key. /// Sign a message with our secret key.
pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) } pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) }
} }