diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 06faf057e..74c8e1a1a 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -105,6 +105,9 @@ pub trait MinerService : Send + Sync { /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error>; + + /// Query pending transactions for hash + fn transaction(&self, hash: &H256) -> Option; } /// Mining status diff --git a/miner/src/miner.rs b/miner/src/miner.rs index e989fa495..529e86bae 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -164,6 +164,11 @@ impl MinerService for Miner { transaction_queue.pending_hashes() } + fn transaction(&self, hash: &H256) -> Option { + let queue = self.transaction_queue.lock().unwrap(); + queue.find(hash) + } + fn update_sealing(&self, chain: &BlockChainClient) { if self.sealing_enabled.load(atomic::Ordering::Relaxed) { let current_no = chain.chain_info().best_block_number; diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs index 9abe80f1c..8f2855836 100644 --- a/miner/src/transaction_queue.rs +++ b/miner/src/transaction_queue.rs @@ -504,6 +504,11 @@ impl TransactionQueue { .collect() } + /// Finds transaction in the queue by hash (if any) + pub fn find(&self, hash: &H256) -> Option { + match self.by_hash.get(hash) { Some(transaction_ref) => Some(transaction_ref.transaction.clone()), None => None } + } + /// Removes all elements (in any state) from the queue pub fn clear(&mut self) { self.current.clear(); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 2fe887654..1c417dcbc 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -348,7 +348,13 @@ impl Eth for EthClient fn transaction_by_hash(&self, params: Params) -> Result { from_params::<(H256,)>(params) - .and_then(|(hash,)| self.transaction(TransactionId::Hash(hash))) + .and_then(|(hash,)| { + let miner = take_weak!(self.miner); + match miner.transaction(&hash) { + Some(pending_tx) => to_value(&Transaction::from(pending_tx)), + None => self.transaction(TransactionId::Hash(hash)) + } + }) } fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 8b40ba65c..6d7d0e6ba 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -53,7 +53,7 @@ struct EthTester { pub client: Arc, pub sync: Arc, _accounts_provider: Arc, - _miner: Arc, + miner: Arc, hashrates: Arc>>, pub io: IoHandler, } @@ -73,7 +73,7 @@ impl Default for EthTester { client: client, sync: sync, _accounts_provider: ap, - _miner: miner, + miner: miner, io: io, hashrates: hashrates, } @@ -258,6 +258,27 @@ fn rpc_eth_transaction_count_by_number_pending() { assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned())); } +#[test] +fn rpc_eth_pending_transaction_by_hash() { + use util::*; + use ethcore::transaction::*; + + let tester = EthTester::default(); + { + let tx: SignedTransaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + tester.miner.pending_transactions.lock().unwrap().insert(H256::zero(), tx); + } + + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x01","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x00","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"value":"0x0a"},"id":1}"#; + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionByHash", + "params": ["0x0000000000000000000000000000000000000000000000000000000000000000"], + "id": 1 + }"#; + assert_eq!(tester.io.handle_request(request), Some(response.to_owned())); +} + #[test] fn rpc_eth_uncle_count_by_block_hash() { diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 7f07341bf..d00b0dd1e 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -30,6 +30,8 @@ pub struct TestMinerService { pub imported_transactions: RwLock>, /// Latest closed block. pub latest_closed_block: Mutex>, + /// Pre-existed pending transactions + pub pending_transactions: Mutex>, } impl Default for TestMinerService { @@ -37,6 +39,7 @@ impl Default for TestMinerService { TestMinerService { imported_transactions: RwLock::new(Vec::new()), latest_closed_block: Mutex::new(None), + pending_transactions: Mutex::new(HashMap::new()), } } } @@ -73,6 +76,10 @@ impl MinerService for TestMinerService { &self.latest_closed_block } + fn transaction(&self, hash: &H256) -> Option { + self.pending_transactions.lock().unwrap().get(hash).and_then(|tx_ref| Some(tx_ref.clone())) + } + /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec) -> Result<(), Error> { unimplemented!(); } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index d809d19b4..8a46d5e15 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use util::numbers::*; -use ethcore::transaction::{LocalizedTransaction, Action}; +use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, OptionalValue}; #[derive(Debug, Default, Serialize)] @@ -58,6 +58,27 @@ impl From for Transaction { } } +impl From for Transaction { + fn from(t: SignedTransaction) -> Transaction { + Transaction { + hash: t.hash(), + nonce: t.nonce, + block_hash: OptionalValue::Null, + block_number: OptionalValue::Null, + transaction_index: OptionalValue::Null, + from: t.sender().unwrap(), + to: match t.action { + Action::Create => OptionalValue::Null, + Action::Call(ref address) => OptionalValue::Value(address.clone()) + }, + value: t.value, + gas_price: t.gas_price, + gas: t.gas, + input: Bytes::new(t.data.clone()) + } + } +} + #[cfg(test)] mod tests { use super::*;