From aa6fbc91a90493d0969e9b4960893510ad7a16ed Mon Sep 17 00:00:00 2001 From: tgmichel Date: Thu, 23 Jul 2020 15:35:22 +0200 Subject: [PATCH] Add support and Runtime API implementation for eth_getLogs (#85) * Implement logs Runtime API (wip) * Add support for remaining fields * Cleanup * Styling fixes --- frame/ethereum/src/lib.rs | 108 +++++++++++++++++++++++++++++++++++ rpc/core/src/eth.rs | 2 +- rpc/core/src/types/filter.rs | 4 +- rpc/core/src/types/mod.rs | 2 +- rpc/primitives/src/lib.rs | 18 ++++++ rpc/src/lib.rs | 74 +++++++++++++++++++++++- template/runtime/src/lib.rs | 30 ++++++++++ 7 files changed, 230 insertions(+), 8 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 5784bf24f..a113ba0dd 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -365,6 +365,114 @@ impl Module { }).collect() } + fn block_logs( + block_hash: H256, + address: Option, + topic: Option> + ) -> Option, // topics + Vec, // data + Option, // block_hash + Option, // block_number + Option, // transaction_hash + Option, // transaction_index + Option, // log index in block + Option, // log index in transaction + )>> { + let mut output = vec![]; + let (block, receipts) = BlocksAndReceipts::get(block_hash)?; + let mut block_log_index: u32 = 0; + for (index, receipt) in receipts.iter().enumerate() { + let logs = receipt.logs.clone(); + let mut transaction_log_index: u32 = 0; + let transaction = &block.transactions[index as usize]; + let transaction_hash = H256::from_slice( + Keccak256::digest(&rlp::encode(transaction)).as_slice() + ); + for log in logs { + let mut add: bool = false; + if let (Some(address), Some(topics)) = (address.clone(), topic.clone()) { + if address == log.address && log.topics.starts_with(&topics) { + add = true; + } + } else if let Some(address) = address { + if address == log.address { + add = true; + } + } else if let Some(topics) = &topic { + if log.topics.starts_with(&topics) { + add = true; + } + } + if add { + output.push(( + log.address.clone(), + log.topics.clone(), + log.data.clone(), + Some(H256::from_slice( + Keccak256::digest(&rlp::encode(&block.header)).as_slice() + )), + Some(block.header.number.clone()), + Some(transaction_hash), + Some(U256::from(index)), + Some(U256::from(block_log_index)), + Some(U256::from(transaction_log_index)) + )); + } + transaction_log_index += 1; + block_log_index += 1; + } + } + Some(output) + } + + pub fn filtered_logs( + from_block: Option, + to_block: Option, + block_hash: Option, + address: Option, + topic: Option>, + ) -> Option, // topics + Vec, // data + Option, // block_hash + Option, // block_number + Option, // transaction_hash + Option, // transaction_index + Option, // log index in block + Option, // log index in transaction + )>> { + if let Some(block_hash) = block_hash { + >::block_logs( + block_hash, + address, + topic + ) + } else if let (Some(from_block), Some(to_block)) = (from_block, to_block) { + let mut output = vec![]; + if from_block >= to_block { + for number in from_block..to_block { + let block_number = T::BlockNumber::from(number); + if >::contains_key(block_number) { + let hash = >::get(block_number); + output.extend(>::block_logs( + hash, + address.clone(), + topic.clone() + ).unwrap()) + } + } + Some(output) + } else { + None + } + } else { + None + } + } + /// Execute an Ethereum transaction, ignoring transaction signatures. pub fn execute(source: H160, transaction: ethereum::Transaction) { let transaction_hash = H256::from_slice( diff --git a/rpc/core/src/eth.rs b/rpc/core/src/eth.rs index b9bff452c..f5790e407 100644 --- a/rpc/core/src/eth.rs +++ b/rpc/core/src/eth.rs @@ -185,7 +185,7 @@ pub trait EthApi { /// Returns logs matching given filter object. #[rpc(name = "eth_getLogs")] - fn logs(&self, _: Filter) -> BoxFuture>; + fn logs(&self, _: Filter) -> Result>; /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. #[rpc(name = "eth_getWork")] diff --git a/rpc/core/src/types/filter.rs b/rpc/core/src/types/filter.rs index 84bddc45c..3c372660c 100644 --- a/rpc/core/src/types/filter.rs +++ b/rpc/core/src/types/filter.rs @@ -66,9 +66,7 @@ pub struct Filter { /// Address pub address: Option, /// Topics - pub topics: Option>, - /// Limit - pub limit: Option, + pub topics: Option, } /// Results of the filter_changes RPC. diff --git a/rpc/core/src/types/mod.rs b/rpc/core/src/types/mod.rs index fed392874..a9ee007cc 100644 --- a/rpc/core/src/types/mod.rs +++ b/rpc/core/src/types/mod.rs @@ -38,7 +38,7 @@ pub use self::bytes::Bytes; pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich}; pub use self::block_number::BlockNumber; pub use self::call_request::CallRequest; -pub use self::filter::{Filter, FilterChanges}; +pub use self::filter::{Filter, FilterChanges, VariadicValue}; pub use self::index::Index; pub use self::log::Log; pub use self::receipt::Receipt; diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index f9b60b95b..f9dd171f7 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -122,6 +122,24 @@ sp_api::decl_runtime_apis! { EthereumBlock, TransactionStatus )>; + /// For given filter arguments, return data necessary to build Logs + fn logs( + from_block: Option, + to_block: Option, + block_hash: Option, + address: Option, + topic: Option> + ) -> Vec<( + H160, // address + Vec, // topics + Vec, // data + Option, // block_hash + Option, // block_number + Option, // transaction_hash + Option, // transaction_index + Option, // log index in block + Option, // log index in transaction + )>; } } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 8eaebd921..3be37fca5 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, VariadicValue }; use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction, TransactionStatus}; @@ -671,8 +671,76 @@ impl EthApiT for EthApi where Err(not_supported_err("Method eth_compileSerpent not supported.")) } - fn logs(&self, _: Filter) -> BoxFuture> { - unimplemented!("logs"); + fn logs(&self, filter: Filter) -> Result> { + let header = self.select_chain.best_chain() + .map_err(|_| internal_err("fetch header failed"))?; + + let mut from_block = None; + if let Some(from_block_input) = filter.from_block { + if let Ok(Some(block_number)) = self.native_block_number(Some(from_block_input)) { + from_block = Some(block_number); + } + } + + let mut to_block = None; + if let Some(to_block_input) = filter.to_block { + if let Ok(Some(block_number)) = self.native_block_number(Some(to_block_input)) { + to_block = Some(block_number); + } + } + + let mut address = None; + if let Some(address_input) = filter.address { + match address_input { + VariadicValue::Single(x) => { address = Some(x); }, + _ => { address = None; } + } + } + + let mut topics = None; + if let Some(topics_input) = filter.topics { + match topics_input { + VariadicValue::Multiple(x) => { topics = Some(x); }, + _ => { topics = None; } + } + } + + if let Ok(logs) = self.client.runtime_api() + .logs( + &BlockId::Hash(header.hash()), + from_block, + to_block, + filter.block_hash, + address, + topics + ) { + let mut output = vec![]; + for log in logs { + let address = log.0; + let topics = log.1; + let data = log.2; + let block_hash = log.3; + let block_number = log.4; + let transaction_hash = log.5; + let transaction_index = log.6; + let log_index = log.7; + let transaction_log_index = log.8; + output.push(Log { + address, + topics, + data: Bytes(data), + block_hash, + block_number, + transaction_hash, + transaction_index, + log_index, + transaction_log_index, + removed: false + }); + } + return Ok(output); + } + Ok(vec![]) } fn work(&self) -> Result { diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index aec7943ef..e63a4a620 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -583,6 +583,36 @@ impl_runtime_apis! { index ) } + + fn logs( + from_block: Option, + to_block: Option, + block_hash: Option, + address: Option, + topic: Option> + ) -> Vec<( + H160, // address + Vec, // topics + Vec, // data + Option, // block_hash + Option, // block_number + Option, // transaction_hash + Option, // transaction_index + Option, // log index in block + Option, // log index in transaction + )> { + let output = >::filtered_logs( + from_block, + to_block, + block_hash, + address, + topic + ); + if let Some(output) = output { + return output; + } + return vec![]; + } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<