Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

jsonrpc #374

Merged
merged 15 commits into from
Feb 9, 2016
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 43 additions & 0 deletions ethcore/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ pub trait BlockProvider {
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256>;

/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress>;

/// Get the partial-header of a block.
fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
Expand All @@ -107,6 +110,16 @@ pub trait BlockProvider {
self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
}

/// Get transaction with given transaction hash.
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
self.transaction_address(hash).and_then(|address| self.transaction_at(&address))
}

/// Get transaction at given address.
fn transaction_at(&self, address: &TransactionAddress) -> Option<SignedTransaction> {
self.block(&address.block_hash).map(|bytes| BlockView::new(&bytes).transactions()).and_then(|t| t.into_iter().nth(address.index))
}

/// Get a list of transactions for a given block.
/// Returns None if block deos not exist.
fn transactions(&self, hash: &H256) -> Option<Vec<SignedTransaction>> {
Expand Down Expand Up @@ -201,6 +214,11 @@ impl BlockProvider for BlockChain {
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.query_extras(&index, &self.block_hashes)
}

/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress> {
self.query_extras(hash, &self.transaction_addresses)
}
}

const COLLECTION_QUEUE_SIZE: usize = 8;
Expand Down Expand Up @@ -474,6 +492,14 @@ impl BlockChain {
parent_details.children.push(hash.clone());
batch.put_extras(&parent_hash, &parent_details);

// update transaction addresses
for (i, tx_hash) in block.transaction_hashes().iter().enumerate() {
batch.put_extras(tx_hash, &TransactionAddress {
block_hash: hash.clone(),
index: i
});
}

// if it's not new best block, just return
if !is_new_best {
return (batch, None, details);
Expand Down Expand Up @@ -824,4 +850,21 @@ mod tests {
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 0);
}

#[test]
fn find_transaction_by_hash() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();

let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
bc.insert_block(&b1);

let transactions = bc.transactions(&b1_hash).unwrap();
assert_eq!(transactions.len(), 7);
for t in transactions {
assert_eq!(bc.transaction(&t.hash()).unwrap(), t);
}
}
}
7 changes: 7 additions & 0 deletions ethcore/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block total difficulty.
fn block_total_difficulty(&self, hash: &H256) -> Option<U256>;

/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;

/// Get raw block header data by block number.
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;

Expand Down Expand Up @@ -358,6 +361,10 @@ impl BlockChainClient for Client {
self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty)
}

fn code(&self, address: &Address) -> Option<Bytes> {
self.state().code(address)
}

fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
}
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/extras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ pub struct TransactionAddress {
/// Block hash
pub block_hash: H256,
/// Transaction index within the block
pub index: u64
pub index: usize
}

impl ExtrasIndexable for TransactionAddress {
Expand Down
12 changes: 9 additions & 3 deletions ethcore/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use util::*;
use error::*;
use evm::Schedule;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
/// Transaction action type.
pub enum Action {
/// Create creates new contract.
Expand All @@ -45,7 +45,7 @@ impl Decodable for Action {

/// A set of information describing an externally-originating message call
/// or contract creation operation.
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Transaction {
/// Nonce.
pub nonce: U256,
Expand Down Expand Up @@ -158,7 +158,7 @@ impl Transaction {



#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq)]
pub struct SignedTransaction {
/// Plain Transaction.
unsigned: Transaction,
Expand All @@ -174,6 +174,12 @@ pub struct SignedTransaction {
sender: RefCell<Option<Address>>
}

impl PartialEq for SignedTransaction {
fn eq(&self, other: &SignedTransaction) -> bool {
self.unsigned == other.unsigned && self.v == other.v && self.r == other.r && self.s == other.s
}
}

impl Deref for SignedTransaction {
type Target = Transaction;

Expand Down
4 changes: 4 additions & 0 deletions ethcore/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ mod tests {
})
}

fn transaction_address(&self, _hash: &H256) -> Option<TransactionAddress> {
unimplemented!()
}

/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.numbers.get(&index).cloned()
Expand Down
10 changes: 10 additions & 0 deletions ethcore/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ impl<'a> BlockView<'a> {
self.rlp.val_at(1)
}

/// Return number of transactions in given block, without deserializing them.
pub fn transactions_count(&self) -> usize {
self.rlp.at(1).iter().count()
}

/// Return List of transactions in given block.
pub fn transaction_views(&self) -> Vec<TransactionView> {
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
Expand All @@ -170,6 +175,11 @@ impl<'a> BlockView<'a> {
self.rlp.val_at(2)
}

/// Return number of uncles in given block, without deserializing them.
pub fn uncles_count(&self) -> usize {
self.rlp.at(2).iter().count()
}

/// Return List of transactions in given block.
pub fn uncle_views(&self) -> Vec<HeaderView> {
self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect()
Expand Down
2 changes: 1 addition & 1 deletion rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ ethcore = { path = "../ethcore" }
ethsync = { path = "../sync" }
clippy = "0.0.37"
target_info = "0.1.0"

rustc-serialize = "0.3"
1 change: 1 addition & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#![plugin(serde_macros)]
#![plugin(clippy)]

extern crate rustc_serialize;
extern crate target_info;
extern crate serde;
extern crate serde_json;
Expand Down
70 changes: 57 additions & 13 deletions rpc/src/v1/impls/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use util::sha3::*;
use ethcore::client::*;
use ethcore::views::*;
use v1::traits::{Eth, EthFilter};
use v1::types::Block;
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus};

/// Eth rpc implementation.
pub struct EthClient {
Expand All @@ -40,55 +40,92 @@ impl EthClient {
}

impl Eth for EthClient {
// TODO: do not hardcode protocol version
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::U64(63)),
_ => Err(Error::invalid_params())
}
}

// TODO: do no hardcode default sync status
fn syncing(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => to_value(&SyncStatus::default()),
_ => Err(Error::invalid_params())
}
}

// TODO: do not hardcode author.
fn author(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => to_value(&Address::new()),
_ => Err(Error::invalid_params())
}
}

fn gas_price(&self, params: Params) -> Result<Value, Error> {
// TODO: return real value of mining once it's implemented.
fn is_mining(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::U64(0)),
Params::None => Ok(Value::Bool(false)),
_ => Err(Error::invalid_params())
}
}

fn block_number(&self, params: Params) -> Result<Value, Error> {
// TODO: return real hashrate once we have mining
fn hashrate(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::U64(self.client.chain_info().best_block_number)),
Params::None => Ok(Value::U64(0)),
_ => Err(Error::invalid_params())
}
}

fn is_mining(&self, params: Params) -> Result<Value, Error> {
// TODO: do not hardode gas_price
fn gas_price(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::Bool(false)),
Params::None => Ok(Value::U64(0)),
_ => Err(Error::invalid_params())
}
}

fn hashrate(&self, params: Params) -> Result<Value, Error> {
fn block_number(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::U64(0)),
Params::None => Ok(Value::U64(self.client.chain_info().best_block_number)),
_ => Err(Error::invalid_params())
}
}

fn block_transaction_count(&self, _: Params) -> Result<Value, Error> {
Ok(Value::U64(0))
fn block_transaction_count(&self, params: Params) -> Result<Value, Error> {
match from_params::<H256>(params) {
Ok(hash) => match self.client.block(&hash) {
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
None => Ok(Value::Null)
},
Err(err) => Err(err)
}
}

fn block_uncles_count(&self, params: Params) -> Result<Value, Error> {
match from_params::<H256>(params) {
Ok(hash) => match self.client.block(&hash) {
Some(bytes) => to_value(&BlockView::new(&bytes).uncles_count()),
None => Ok(Value::Null)
},
Err(err) => Err(err)
}
}

// TODO: do not ignore block number param
fn code_at(&self, params: Params) -> Result<Value, Error> {
match from_params::<(Address, BlockNumber)>(params) {
Ok((address, _block_number)) => to_value(&self.client.code(&address).map_or_else(Bytes::default, Bytes::new)),
Err(err) => Err(err)
}
}

fn block(&self, params: Params) -> Result<Value, Error> {
match from_params::<(H256, bool)>(params) {
Ok((hash, _include_txs)) => match (self.client.block_header(&hash), self.client.block_total_difficulty(&hash)) {
Ok((hash, include_txs)) => match (self.client.block_header(&hash), self.client.block_total_difficulty(&hash)) {
(Some(bytes), Some(total_difficulty)) => {
let view = HeaderView::new(&bytes);
let block = Block {
Expand All @@ -108,7 +145,14 @@ impl Eth for EthClient {
difficulty: view.difficulty(),
total_difficulty: total_difficulty,
uncles: vec![],
transactions: vec![]
transactions: {
if include_txs {
BlockTransactions::Hashes(vec![])
} else {
BlockTransactions::Full(vec![])
}
},
extra_data: Bytes::default()
};
to_value(&block)
},
Expand Down
2 changes: 2 additions & 0 deletions rpc/src/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
pub mod traits;
mod impls;
mod types;
#[cfg(test)]
mod tests;

pub use self::traits::{Web3, Eth, EthFilter, Net};
pub use self::impls::*;
1 change: 1 addition & 0 deletions rpc/src/v1/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//TODO: load custom blockchain state and test
4 changes: 4 additions & 0 deletions rpc/src/v1/traits/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub trait Eth: Sized + Send + Sync + 'static {
/// Returns protocol version.
fn protocol_version(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }

/// Returns an object with data about the sync status or false. (wtf?)
fn syncing(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }

/// Returns the number of hashes per second that the node is mining with.
fn hashrate(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }

Expand Down Expand Up @@ -108,6 +111,7 @@ pub trait Eth: Sized + Send + Sync + 'static {
fn to_delegate(self) -> IoDelegate<Self> {
let mut delegate = IoDelegate::new(Arc::new(self));
delegate.add_method("eth_protocolVersion", Eth::protocol_version);
delegate.add_method("eth_syncing", Eth::syncing);
delegate.add_method("eth_hashrate", Eth::hashrate);
delegate.add_method("eth_coinbase", Eth::author);
delegate.add_method("eth_mining", Eth::is_mining);
Expand Down