From 646338b674837a2446271df04dbf43e183009290 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Mon, 15 Jun 2020 16:08:31 +0200 Subject: [PATCH] Implement transaction_by_block_hash_and_index for Runtime API (#39) * Wip implement transaction_by_hash for EthApi * pallet-ethereum: add Transactions StorageMap * Implement transaction_by_hash Runtime API * pallet-ethereum: change Transactions to store references only * paritytech/frontier/pull/35#discussion_r438019271 * Move Transaction build logic to Rpc module * Implement transaction_by_block_hash_and_index for Runtime API --- frame/ethereum/src/lib.rs | 21 ++++++++ rpc/core/src/eth.rs | 2 +- rpc/primitives/src/lib.rs | 8 +++ rpc/src/lib.rs | 99 +++++++++++++++++++++++-------------- template/runtime/src/lib.rs | 7 +++ 5 files changed, 100 insertions(+), 37 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index afb28a211..12d8d2cd4 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -257,6 +257,27 @@ impl Module { Some((transaction.clone(), block, transaction_status)) } + pub fn transaction_by_block_hash_and_index( + hash: H256, + index: u32 + ) -> Option<( + ethereum::Transaction, + ethereum::Block, + TransactionStatus + )> { + let (block,_receipt) = BlocksAndReceipts::get(hash)?; + if index < block.transactions.len() as u32 { + let transaction = &block.transactions[index as usize]; + let transaction_hash = H256::from_slice( + Keccak256::digest(&rlp::encode(transaction)).as_slice() + ); + let transaction_status = TransactionStatuses::get(transaction_hash)?; + Some((transaction.clone(), block, transaction_status)) + } else { + None + } + } + pub fn block_by_number(number: T::BlockNumber) -> Option { if >::contains_key(number) { let hash = >::get(number); diff --git a/rpc/core/src/eth.rs b/rpc/core/src/eth.rs index 12bc8f08f..81d10af56 100644 --- a/rpc/core/src/eth.rs +++ b/rpc/core/src/eth.rs @@ -137,7 +137,7 @@ pub trait EthApi { &self, _: H256, _: Index, - ) -> BoxFuture>; + ) -> Result>; /// Returns transaction by given block number and index. #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index 9c4cf8304..4ea881fd5 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -51,6 +51,14 @@ sp_api::decl_runtime_apis! { EthereumBlock, TransactionStatus )>; + fn transaction_by_block_hash_and_index( + hash: H256, + index: u32 + ) -> Option<( + EthereumTransaction, + EthereumBlock, + TransactionStatus + )>; } } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 6856ba088..6c011253d 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -16,6 +16,7 @@ use std::{marker::PhantomData, sync::Arc}; use std::collections::BTreeMap; +use ethereum::{Block as EthereumBlock, Transaction as EthereumTransaction}; use ethereum_types::{H160, H256, H64, U256, U64}; use jsonrpc_core::{BoxFuture, Result, ErrorCode, Error, futures::future::{self, Future}}; use futures::future::TryFutureExt; @@ -32,7 +33,7 @@ use frontier_rpc_core::types::{ BlockNumber, Bytes, CallRequest, EthAccount, Filter, Index, Log, Receipt, RichBlock, SyncStatus, Transaction, Work, Rich, Block, BlockTransactions }; -use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction}; +use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction, TransactionStatus}; pub use frontier_rpc_core::EthApiServer; @@ -93,6 +94,43 @@ fn rich_block_build(block: ethereum::Block) -> RichBlock { } } +fn transaction_build( + transaction: EthereumTransaction, + block: EthereumBlock, + status: TransactionStatus +) -> Transaction { + Transaction { + hash: H256::from_slice( + Keccak256::digest(&rlp::encode(&transaction)).as_slice() + ), + nonce: transaction.nonce, + 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_limit, + input: Bytes(transaction.input), + 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 + } +} + impl EthApiT for EthApi where C: ProvideRuntimeApi + StorageProvider, C::Api: EthereumRuntimeApi, @@ -356,47 +394,36 @@ impl EthApiT for EthApi where if let Ok(Some((transaction, block, status))) = self.client.runtime_api() .transaction_by_hash(&BlockId::Hash(header.hash()), hash) { - - return Ok(Some( - Transaction { - hash: hash, - nonce: transaction.nonce, - 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_limit, - input: Bytes(transaction.input), - 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 - } - )); + return Ok(Some(transaction_build( + transaction, + block, + status + ))); } Ok(None) } fn transaction_by_block_hash_and_index( &self, - _: H256, - _: Index, - ) -> BoxFuture> { - unimplemented!("transaction_by_block_hash_and_index"); + hash: H256, + index: Index, + ) -> Result> { + let header = self + .select_chain + .best_chain() + .map_err(|_| internal_err("fetch header failed"))?; + + let index_param = index.value() as u32; + + if let Ok(Some((transaction, block, status))) = self.client.runtime_api() + .transaction_by_block_hash_and_index(&BlockId::Hash(header.hash()), hash, index_param) { + return Ok(Some(transaction_build( + transaction, + block, + status + ))); + } + Ok(None) } fn transaction_by_block_number_and_index( diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index d4a6ef52d..7feb3bbaa 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -503,6 +503,13 @@ impl_runtime_apis! { TransactionStatus)> { >::transaction_by_hash(hash) } + + fn transaction_by_block_hash_and_index(hash: H256, index: u32) -> Option<( + EthereumTransaction, + EthereumBlock, + TransactionStatus)> { + >::transaction_by_block_hash_and_index(hash, index) + } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<