From 270322d9d0080c4f4e1790324ba7289cfc46266c Mon Sep 17 00:00:00 2001 From: tgmichel Date: Tue, 9 Jun 2020 12:57:14 +0200 Subject: [PATCH 1/5] Wip implement transaction_by_hash for EthApi --- rpc/core/src/eth.rs | 2 +- rpc/src/lib.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/rpc/core/src/eth.rs b/rpc/core/src/eth.rs index ad8beef87b..e77258706c 100644 --- a/rpc/core/src/eth.rs +++ b/rpc/core/src/eth.rs @@ -129,7 +129,7 @@ pub trait EthApi { /// Get transaction by its hash. #[rpc(name = "eth_getTransactionByHash")] - fn transaction_by_hash(&self, _: H256) -> BoxFuture>; + fn transaction_by_hash(&self, _: H256) -> Result>; /// Returns transaction at given block hash and index. #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 5dd2752372..7d0a28c7ed 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -308,8 +308,31 @@ impl EthApiT for EthApi where unimplemented!("estimate_gas"); } - fn transaction_by_hash(&self, _: H256) -> BoxFuture> { - unimplemented!("transaction_by_hash"); + fn transaction_by_hash(&self, hash: H256) -> Result> { + Ok(Some( + Transaction { + hash: H256::default(), + nonce: U256::zero(), + block_hash: None, //Option, + block_number: None, //Option, + transaction_index: None, //Option, + from: H160::default(), + to: None, //Option, + value: U256::zero(), + gas_price: U256::zero(), + gas: U256::zero(), + input: Bytes(vec![]), + creates: None, //Option, + raw: Bytes(vec![]), + public_key: None, //Option, + chain_id: None, //Option, + standard_v: U256::zero(), + v: U256::zero(), + r: U256::zero(), + s: U256::zero(), + condition: None, //Option, + } + )) } fn transaction_by_block_hash_and_index( From 421ede1fce51c540064ef1fdb26c81782d13b817 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 10 Jun 2020 11:11:53 +0200 Subject: [PATCH 2/5] pallet-ethereum: add Transactions StorageMap --- frame/ethereum/src/lib.rs | 44 +++++++++++++++++++++++++++++++++++++-- rpc/primitives/src/lib.rs | 26 ++++++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 07e70952fc..296c39d2c2 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -32,7 +32,7 @@ use sp_runtime::{ }; use sha3::{Digest, Keccak256}; -pub use frontier_rpc_primitives::TransactionStatus; +pub use frontier_rpc_primitives::{TransactionStatus, Transaction as FullTransaction}; pub use ethereum::{Transaction, Log, Block}; /// A type alias for the balance type from this pallet's point of view. @@ -61,6 +61,7 @@ decl_storage! { BlockNumbers: map hasher(blake2_128_concat) T::BlockNumber => H256; PendingTransactionsAndReceipts: Vec<(ethereum::Transaction, ethereum::Receipt)>; TransactionStatuses: map hasher(blake2_128_concat) H256 => Option; + Transactions: map hasher(blake2_128_concat) H256 => Option; } } @@ -187,12 +188,51 @@ decl_module! { let block = ethereum::Block { header, - transactions, + transactions: transactions.clone(), ommers, }; BlocksAndReceipts::insert(hash, (block, receipts)); BlockNumbers::::insert(n, hash); + + for t in &transactions { + let transaction_hash = H256::from_slice( + Keccak256::digest(&rlp::encode(t)).as_slice() + ); + if let Some(transaction_status) = TransactionStatuses::get(transaction_hash) { + let full_transaction = FullTransaction { + hash: transaction_hash, + nonce: t.nonce, + block_hash: Some(hash), + block_number: Some(U256::from( + UniqueSaturatedInto::::unique_saturated_into(n) + )), + transaction_index: Some(U256::from( + UniqueSaturatedInto::::unique_saturated_into( + transaction_status.transaction_index + ) + )), + from: transaction_status.from, + to: transaction_status.to, + value: t.value, + gas_price: t.gas_price, + gas: t.gas_limit, + input: t.input.clone(), + creates: transaction_status.contract_address, + raw: vec![], // TODO, + public_key: None, // TODO, + chain_id: None, // TODO + standard_v: U256::zero(), // TODO + v: U256::zero(), // TODO + r: U256::zero(), // TODO + s: U256::zero(), // TODO + // Option, Not supported? By now all pending + // transactions are stored on chain on_finalize. + condition: None, + }; + Transactions::insert(transaction_hash, full_transaction); + }; + } } // A runtime code run after every block and have access to extended set of APIs. diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index ec399d7737..cf0770d4ae 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -16,7 +16,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_core::{H160, H256, U256}; +use sp_core::{H160, H256, H512, U256}; use ethereum::{Log, Block as EthereumBlock}; use ethereum_types::Bloom; use codec::{Encode, Decode}; @@ -33,6 +33,30 @@ pub struct TransactionStatus { pub logs_bloom: Bloom, } +#[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)] +pub struct Transaction { + pub hash: H256, + pub nonce: U256, + pub block_hash: Option, + pub block_number: Option, + pub transaction_index: Option, + pub from: H160, + pub to: Option, + pub value: U256, + pub gas_price: U256, + pub gas: U256, + pub input: Vec, + pub creates: Option, + pub raw: Vec, + pub public_key: Option, + pub chain_id: Option, + pub standard_v: U256, + pub v: U256, + pub r: U256, + pub s: U256, + pub condition: Option, +} + sp_api::decl_runtime_apis! { /// API necessary for Ethereum-compatibility layer. pub trait EthereumRuntimeApi { From 3c7126d5408e337b428c1a5ed033a3e491e67514 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 10 Jun 2020 12:09:28 +0200 Subject: [PATCH 3/5] Implement transaction_by_hash Runtime API --- frame/ethereum/src/lib.rs | 4 +++ rpc/primitives/src/lib.rs | 1 + rpc/src/lib.rs | 65 +++++++++++++++++++++++-------------- template/runtime/src/lib.rs | 5 +++ 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 296c39d2c2..4a30cd8eeb 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -265,6 +265,10 @@ impl Module { pub fn transaction_status(hash: H256) -> Option { TransactionStatuses::get(hash) } + + pub fn transaction_by_hash(hash: H256) -> Option { + Transactions::get(hash) + } pub fn block_by_number(number: T::BlockNumber) -> Option { if >::contains_key(number) { diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index cf0770d4ae..e757e3824d 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -67,6 +67,7 @@ sp_api::decl_runtime_apis! { fn account_code_at(address: H160) -> Vec; fn author() -> H160; fn block_by_number(number: u32) -> Option; + fn transaction_by_hash(hash: H256) -> Option; } } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 7d0a28c7ed..269396cc1d 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -31,7 +31,7 @@ use sp_runtime::traits::BlakeTwo256; use frontier_rpc_core::EthApi as EthApiT; use frontier_rpc_core::types::{ BlockNumber, Bytes, CallRequest, EthAccount, Filter, Index, Log, Receipt, RichBlock, - SyncStatus, Transaction, Work, Rich, Block, BlockTransactions + SyncStatus, Transaction, Work, Rich, Block, BlockTransactions, TransactionCondition }; use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction}; @@ -309,30 +309,45 @@ impl EthApiT for EthApi where } fn transaction_by_hash(&self, hash: H256) -> Result> { - Ok(Some( - Transaction { - hash: H256::default(), - nonce: U256::zero(), - block_hash: None, //Option, - block_number: None, //Option, - transaction_index: None, //Option, - from: H160::default(), - to: None, //Option, - value: U256::zero(), - gas_price: U256::zero(), - gas: U256::zero(), - input: Bytes(vec![]), - creates: None, //Option, - raw: Bytes(vec![]), - public_key: None, //Option, - chain_id: None, //Option, - standard_v: U256::zero(), - v: U256::zero(), - r: U256::zero(), - s: U256::zero(), - condition: None, //Option, - } - )) + let header = self + .select_chain + .best_chain() + .map_err(|_| internal_err("fetch header failed"))?; + + if let Ok(Some(transaction)) = self.client.runtime_api().transaction_by_hash( + &BlockId::Hash(header.hash()), hash) { + return Ok(Some( + Transaction { + hash: transaction.hash, + nonce: transaction.nonce, + block_hash: transaction.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.transaction_index, + from: transaction.from, + to: transaction.to, + value: transaction.value, + gas_price: transaction.gas_price, + gas: transaction.gas, + input: Bytes(transaction.input), + creates: transaction.creates, + raw: Bytes(transaction.raw), + public_key: transaction.public_key, + chain_id: match transaction.chain_id { + Some(chain_id) => Some(U64::from(chain_id)), + None => None + }, + standard_v: transaction.standard_v, + v: transaction.v, + r: transaction.r, + s: transaction.s, + condition: match transaction.chain_id { + Some(condition) => Some(TransactionCondition::Number(condition)), + None => None + } + } + )); + } + Ok(None) } fn transaction_by_block_hash_and_index( diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index d3de1bf9d6..1072725466 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -59,6 +59,7 @@ pub use frame_support::{ StorageValue, }; use ethereum::Block as EthereumBlock; +use frontier_rpc_primitives::Transaction as FullTransaction; #[cfg(any(feature = "std", test))] @@ -477,6 +478,10 @@ impl_runtime_apis! { fn block_by_number(number: u32) -> Option { >::block_by_number(number) } + + fn transaction_by_hash(hash: H256) -> Option { + >::transaction_by_hash(hash) + } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< From 6e353d81065822cbbac8aaca2de49c83f9228f38 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 10 Jun 2020 17:03:12 +0200 Subject: [PATCH 4/5] pallet-ethereum: change Transactions to store references only * paritytech/frontier/pull/35#discussion_r438019271 --- frame/ethereum/src/lib.rs | 89 +++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 4608ea2204..a039a7328c 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -61,7 +61,7 @@ decl_storage! { BlockNumbers: map hasher(blake2_128_concat) T::BlockNumber => H256; PendingTransactionsAndReceipts: Vec<(ethereum::Transaction, ethereum::Receipt)>; TransactionStatuses: map hasher(blake2_128_concat) H256 => Option; - Transactions: map hasher(blake2_128_concat) H256 => Option; + Transactions: map hasher(blake2_128_concat) H256 => Option<(H256,u32)>; } } @@ -195,43 +195,16 @@ decl_module! { BlocksAndReceipts::insert(hash, (block, receipts)); BlockNumbers::::insert(n, hash); - for t in &transactions { + for t in &transactions.clone() { let transaction_hash = H256::from_slice( Keccak256::digest(&rlp::encode(t)).as_slice() ); - if let Some(transaction_status) = TransactionStatuses::get(transaction_hash) { - let full_transaction = FullTransaction { - hash: transaction_hash, - nonce: t.nonce, - block_hash: Some(hash), - block_number: Some(U256::from( - UniqueSaturatedInto::::unique_saturated_into(n) - )), - transaction_index: Some(U256::from( - UniqueSaturatedInto::::unique_saturated_into( - transaction_status.transaction_index - ) - )), - from: transaction_status.from, - to: transaction_status.to, - value: t.value, - gas_price: t.gas_price, - gas: t.gas_limit, - input: t.input.clone(), - creates: transaction_status.contract_address, - raw: vec![], // TODO, - public_key: None, // TODO, - chain_id: None, // TODO - standard_v: U256::zero(), // TODO - v: U256::zero(), // TODO - r: U256::zero(), // TODO - s: U256::zero(), // TODO - // Option, Not supported? By now all pending - // transactions are stored on chain on_finalize. - condition: None, - }; - Transactions::insert(transaction_hash, full_transaction); - }; + if let Some(status) = TransactionStatuses::get(transaction_hash) { + Transactions::insert( + transaction_hash, + (hash, status.transaction_index) + ); + } } } @@ -267,7 +240,51 @@ impl Module { } pub fn transaction_by_hash(hash: H256) -> Option { - Transactions::get(hash) + let (block_hash, transaction_index) = match Transactions::get(hash) { + Some(a) => a, + _ => return None, + }; + + let transaction_status = match TransactionStatuses::get(hash) { + Some(a) => a, + _ => return None, + }; + + let (block,_receipt) = match BlocksAndReceipts::get(block_hash) { + Some(a) => a, + _ => return None, + }; + + let transaction = &block.transactions[transaction_index as usize]; + + Some(FullTransaction { + hash: hash, + nonce: transaction.nonce, + block_hash: Some(block_hash), + block_number: Some(block.header.number), + transaction_index: Some(U256::from( + UniqueSaturatedInto::::unique_saturated_into( + transaction_status.transaction_index + ) + )), + from: transaction_status.from, + to: transaction_status.to, + value: transaction.value, + gas_price: transaction.gas_price, + gas: transaction.gas_limit, + input: transaction.input.clone(), + creates: transaction_status.contract_address, + raw: vec![], // TODO, + public_key: None, // TODO, + chain_id: None, // TODO + standard_v: U256::zero(), // TODO + v: U256::zero(), // TODO + r: U256::zero(), // TODO + s: U256::zero(), // TODO + // Option, Not supported? By now all pending + // transactions are stored on chain on_finalize. + condition: None, + }) } pub fn block_by_number(number: T::BlockNumber) -> Option { From 868f87a85c5345f72ea2bb22c61a309ee2d67a7f Mon Sep 17 00:00:00 2001 From: tgmichel Date: Mon, 15 Jun 2020 10:03:58 +0200 Subject: [PATCH 5/5] Move Transaction build logic to Rpc module --- frame/ethereum/src/lib.rs | 66 +++++++++---------------------------- rpc/primitives/src/lib.rs | 34 ++++--------------- rpc/src/lib.rs | 53 ++++++++++++++--------------- template/runtime/src/lib.rs | 9 +++-- 4 files changed, 55 insertions(+), 107 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index a039a7328c..562cb83064 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -32,7 +32,7 @@ use sp_runtime::{ }; use sha3::{Digest, Keccak256}; -pub use frontier_rpc_primitives::{TransactionStatus, Transaction as FullTransaction}; +pub use frontier_rpc_primitives::TransactionStatus; pub use ethereum::{Transaction, Log, Block}; /// A type alias for the balance type from this pallet's point of view. @@ -61,7 +61,7 @@ decl_storage! { BlockNumbers: map hasher(blake2_128_concat) T::BlockNumber => H256; PendingTransactionsAndReceipts: Vec<(ethereum::Transaction, ethereum::Receipt)>; TransactionStatuses: map hasher(blake2_128_concat) H256 => Option; - Transactions: map hasher(blake2_128_concat) H256 => Option<(H256,u32)>; + Transactions: map hasher(blake2_128_concat) H256 => Option<(H256, u32)>; } } @@ -192,10 +192,7 @@ decl_module! { ommers, }; - BlocksAndReceipts::insert(hash, (block, receipts)); - BlockNumbers::::insert(n, hash); - - for t in &transactions.clone() { + for t in &transactions { let transaction_hash = H256::from_slice( Keccak256::digest(&rlp::encode(t)).as_slice() ); @@ -206,6 +203,9 @@ decl_module! { ); } } + + BlocksAndReceipts::insert(hash, (block, receipts)); + BlockNumbers::::insert(n, hash); } // A runtime code run after every block and have access to extended set of APIs. @@ -239,52 +239,16 @@ impl Module { TransactionStatuses::get(hash) } - pub fn transaction_by_hash(hash: H256) -> Option { - let (block_hash, transaction_index) = match Transactions::get(hash) { - Some(a) => a, - _ => return None, - }; - - let transaction_status = match TransactionStatuses::get(hash) { - Some(a) => a, - _ => return None, - }; - - let (block,_receipt) = match BlocksAndReceipts::get(block_hash) { - Some(a) => a, - _ => return None, - }; - + pub fn transaction_by_hash(hash: H256) -> Option<( + ethereum::Transaction, + ethereum::Block, + TransactionStatus + )> { + let (block_hash, transaction_index) = Transactions::get(hash)?; + let transaction_status = TransactionStatuses::get(hash)?; + let (block,_receipt) = BlocksAndReceipts::get(block_hash)?; let transaction = &block.transactions[transaction_index as usize]; - - Some(FullTransaction { - hash: hash, - nonce: transaction.nonce, - block_hash: Some(block_hash), - block_number: Some(block.header.number), - transaction_index: Some(U256::from( - UniqueSaturatedInto::::unique_saturated_into( - transaction_status.transaction_index - ) - )), - from: transaction_status.from, - to: transaction_status.to, - value: transaction.value, - gas_price: transaction.gas_price, - gas: transaction.gas_limit, - input: transaction.input.clone(), - creates: transaction_status.contract_address, - raw: vec![], // TODO, - public_key: None, // TODO, - chain_id: None, // TODO - standard_v: U256::zero(), // TODO - v: U256::zero(), // TODO - r: U256::zero(), // TODO - s: U256::zero(), // TODO - // Option, Not supported? By now all pending - // transactions are stored on chain on_finalize. - condition: None, - }) + Some((transaction.clone(), block, transaction_status)) } pub fn block_by_number(number: T::BlockNumber) -> Option { diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index 6dea32335f..b06b7a47b8 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -16,8 +16,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_core::{H160, H256, H512, U256}; -use ethereum::{Log, Block as EthereumBlock}; +use sp_core::{H160, H256, U256}; +use ethereum::{Log, Block as EthereumBlock, Transaction as EthereumTransaction}; use ethereum_types::Bloom; use codec::{Encode, Decode}; use sp_std::vec::Vec; @@ -33,30 +33,6 @@ pub struct TransactionStatus { pub logs_bloom: Bloom, } -#[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)] -pub struct Transaction { - pub hash: H256, - pub nonce: U256, - pub block_hash: Option, - pub block_number: Option, - pub transaction_index: Option, - pub from: H160, - pub to: Option, - pub value: U256, - pub gas_price: U256, - pub gas: U256, - pub input: Vec, - pub creates: Option, - pub raw: Vec, - pub public_key: Option, - pub chain_id: Option, - pub standard_v: U256, - pub v: U256, - pub r: U256, - pub s: U256, - pub condition: Option, -} - sp_api::decl_runtime_apis! { /// API necessary for Ethereum-compatibility layer. pub trait EthereumRuntimeApi { @@ -69,7 +45,11 @@ sp_api::decl_runtime_apis! { fn block_by_number(number: u32) -> Option; fn block_transaction_count_by_number(number: u32) -> Option; fn block_by_hash(hash: H256) -> Option; - fn transaction_by_hash(hash: H256) -> Option; + fn transaction_by_hash(hash: H256) -> Option<( + EthereumTransaction, + EthereumBlock, + TransactionStatus + )>; } } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 16a40d971d..b4bc176830 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -31,7 +31,7 @@ use sp_runtime::traits::BlakeTwo256; use frontier_rpc_core::EthApi as EthApiT; use frontier_rpc_core::types::{ BlockNumber, Bytes, CallRequest, EthAccount, Filter, Index, Log, Receipt, RichBlock, - SyncStatus, Transaction, Work, Rich, Block, BlockTransactions, TransactionCondition + SyncStatus, Transaction, Work, Rich, Block, BlockTransactions }; use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction}; @@ -346,37 +346,38 @@ impl EthApiT for EthApi where .select_chain .best_chain() .map_err(|_| internal_err("fetch header failed"))?; - - if let Ok(Some(transaction)) = self.client.runtime_api().transaction_by_hash( - &BlockId::Hash(header.hash()), hash) { + + if let Ok(Some((transaction, block, status))) = self.client.runtime_api() + .transaction_by_hash(&BlockId::Hash(header.hash()), hash) { + return Ok(Some( Transaction { - hash: transaction.hash, + hash: hash, nonce: transaction.nonce, - block_hash: transaction.block_hash, - block_number: transaction.block_number, - transaction_index: transaction.transaction_index, - from: transaction.from, - to: transaction.to, + block_hash: Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.header)).as_slice() + )), + block_number: Some(block.header.number), + transaction_index: Some(U256::from( + UniqueSaturatedInto::::unique_saturated_into( + status.transaction_index + ) + )), + from: status.from, + to: status.to, value: transaction.value, gas_price: transaction.gas_price, - gas: transaction.gas, + gas: transaction.gas_limit, input: Bytes(transaction.input), - creates: transaction.creates, - raw: Bytes(transaction.raw), - public_key: transaction.public_key, - chain_id: match transaction.chain_id { - Some(chain_id) => Some(U64::from(chain_id)), - None => None - }, - standard_v: transaction.standard_v, - v: transaction.v, - r: transaction.r, - s: transaction.s, - condition: match transaction.chain_id { - Some(condition) => Some(TransactionCondition::Number(condition)), - None => None - } + creates: status.contract_address, + raw: Bytes(vec![]), // TODO + public_key: None, // TODO + chain_id: None, // TODO + standard_v: U256::zero(), // TODO + v: U256::zero(), // TODO + r: U256::zero(), // TODO + s: U256::zero(), // TODO + condition: None // TODO } )); } diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 0f42cf42cf..c0417ffb96 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -58,8 +58,8 @@ pub use frame_support::{ }, StorageValue, }; -use ethereum::Block as EthereumBlock; -use frontier_rpc_primitives::Transaction as FullTransaction; +use ethereum::{Block as EthereumBlock, Transaction as EthereumTransaction}; +use frontier_rpc_primitives::{TransactionStatus}; #[cfg(any(feature = "std", test))] @@ -490,7 +490,10 @@ impl_runtime_apis! { >::block_by_hash(hash) } - fn transaction_by_hash(hash: H256) -> Option { + fn transaction_by_hash(hash: H256) -> Option<( + EthereumTransaction, + EthereumBlock, + TransactionStatus)> { >::transaction_by_hash(hash) } }