Skip to content

Commit

Permalink
Add support and Runtime API implementation for eth_getLogs (#85)
Browse files Browse the repository at this point in the history
* Implement logs Runtime API (wip)

* Add support for remaining fields

* Cleanup

* Styling fixes
  • Loading branch information
tgmichel committed Jul 23, 2020
1 parent e1e984b commit aa6fbc9
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 8 deletions.
108 changes: 108 additions & 0 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,114 @@ impl<T: Trait> Module<T> {
}).collect()
}

fn block_logs(
block_hash: H256,
address: Option<H160>,
topic: Option<Vec<H256>>
) -> Option<Vec<(
H160, // address
Vec<H256>, // topics
Vec<u8>, // data
Option<H256>, // block_hash
Option<U256>, // block_number
Option<H256>, // transaction_hash
Option<U256>, // transaction_index
Option<U256>, // log index in block
Option<U256>, // 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<u32>,
to_block: Option<u32>,
block_hash: Option<H256>,
address: Option<H160>,
topic: Option<Vec<H256>>,
) -> Option<Vec<(
H160, // address
Vec<H256>, // topics
Vec<u8>, // data
Option<H256>, // block_hash
Option<U256>, // block_number
Option<H256>, // transaction_hash
Option<U256>, // transaction_index
Option<U256>, // log index in block
Option<U256>, // log index in transaction
)>> {
if let Some(block_hash) = block_hash {
<Module<T>>::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 <BlockNumbers<T>>::contains_key(block_number) {
let hash = <BlockNumbers<T>>::get(block_number);
output.extend(<Module<T>>::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(
Expand Down
2 changes: 1 addition & 1 deletion rpc/core/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ pub trait EthApi {

/// Returns logs matching given filter object.
#[rpc(name = "eth_getLogs")]
fn logs(&self, _: Filter) -> BoxFuture<Vec<Log>>;
fn logs(&self, _: Filter) -> Result<Vec<Log>>;

/// Returns the hash of the current block, the seedHash, and the boundary condition to be met.
#[rpc(name = "eth_getWork")]
Expand Down
4 changes: 1 addition & 3 deletions rpc/core/src/types/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ pub struct Filter {
/// Address
pub address: Option<FilterAddress>,
/// Topics
pub topics: Option<Vec<Topic>>,
/// Limit
pub limit: Option<usize>,
pub topics: Option<Topic>,
}

/// Results of the filter_changes RPC.
Expand Down
2 changes: 1 addition & 1 deletion rpc/core/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 18 additions & 0 deletions rpc/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>,
to_block: Option<u32>,
block_hash: Option<H256>,
address: Option<H160>,
topic: Option<Vec<H256>>
) -> Vec<(
H160, // address
Vec<H256>, // topics
Vec<u8>, // data
Option<H256>, // block_hash
Option<U256>, // block_number
Option<H256>, // transaction_hash
Option<U256>, // transaction_index
Option<U256>, // log index in block
Option<U256>, // log index in transaction
)>;
}
}

Expand Down
74 changes: 71 additions & 3 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -671,8 +671,76 @@ impl<B, C, SC, P, CT, BE> EthApiT for EthApi<B, C, SC, P, CT, BE> where
Err(not_supported_err("Method eth_compileSerpent not supported."))
}

fn logs(&self, _: Filter) -> BoxFuture<Vec<Log>> {
unimplemented!("logs");
fn logs(&self, filter: Filter) -> Result<Vec<Log>> {
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<Work> {
Expand Down
30 changes: 30 additions & 0 deletions template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,36 @@ impl_runtime_apis! {
index
)
}

fn logs(
from_block: Option<u32>,
to_block: Option<u32>,
block_hash: Option<H256>,
address: Option<H160>,
topic: Option<Vec<H256>>
) -> Vec<(
H160, // address
Vec<H256>, // topics
Vec<u8>, // data
Option<H256>, // block_hash
Option<U256>, // block_number
Option<H256>, // transaction_hash
Option<U256>, // transaction_index
Option<U256>, // log index in block
Option<U256>, // log index in transaction
)> {
let output = <ethereum::Module<Runtime>>::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<
Expand Down

0 comments on commit aa6fbc9

Please sign in to comment.