Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

evm: eth_getLogs #2062

Merged
merged 13 commits into from
Jun 14, 2023
6 changes: 6 additions & 0 deletions lib/ain-evm/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::backend::{EVMBackend, Vicinity};
use crate::block::BlockHandler;
use crate::evm::EVMHandler;
use crate::executor::{AinExecutor, TxResponse};
use crate::log::LogHandler;
use crate::receipt::ReceiptHandler;
use crate::storage::traits::BlockStorage;
use crate::storage::Storage;
Expand All @@ -23,6 +24,7 @@ pub struct Handlers {
pub evm: EVMHandler,
pub block: BlockHandler,
pub receipt: ReceiptHandler,
pub logs: LogHandler,
pub storage: Arc<Storage>,
}

Expand Down Expand Up @@ -54,6 +56,7 @@ impl Handlers {
evm: EVMHandler::new_from_json(Arc::clone(&storage), PathBuf::from(path)),
block: BlockHandler::new(Arc::clone(&storage)),
receipt: ReceiptHandler::new(Arc::clone(&storage)),
logs: LogHandler::new(Arc::clone(&storage)),
storage,
})
} else {
Expand All @@ -62,6 +65,7 @@ impl Handlers {
evm: EVMHandler::restore(Arc::clone(&storage)),
block: BlockHandler::new(Arc::clone(&storage)),
receipt: ReceiptHandler::new(Arc::clone(&storage)),
logs: LogHandler::new(Arc::clone(&storage)),
storage,
})
}
Expand Down Expand Up @@ -221,6 +225,8 @@ impl Handlers {
let base_fee = self.block.calculate_base_fee(parent_hash);

self.block.connect_block(block.clone(), base_fee);
self.logs
.generate_logs_from_receipts(&receipts, block.header.number);
self.receipt.put_receipts(receipts);
}

Expand Down
1 change: 1 addition & 0 deletions lib/ain-evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod executor;
mod fee;
mod genesis;
pub mod handler;
pub mod log;
pub mod receipt;
pub mod runtime;
pub mod storage;
Expand Down
98 changes: 98 additions & 0 deletions lib/ain-evm/src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::receipt::Receipt;
use crate::storage::traits::LogStorage;
use crate::storage::Storage;
use ethereum::ReceiptV3;
use log::debug;
use primitive_types::{H160, H256, U256};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LogIndex {
pub block_hash: H256,
pub topics: Vec<H256>,
pub data: Vec<u8>,
pub log_index: U256,
pub address: H160,
pub removed: bool,
pub transaction_hash: H256,
pub transaction_index: usize,
}

pub struct LogHandler {
storage: Arc<Storage>,
}

impl LogHandler {
pub fn new(storage: Arc<Storage>) -> Self {
Self { storage }
}

pub fn generate_logs_from_receipts(&self, receipts: &Vec<Receipt>, block_number: U256) {
let mut logs_map: HashMap<H160, Vec<LogIndex>> = HashMap::new();
let mut log_index = 0; // log index is a block level index
for receipt in receipts {
let logs = match &receipt.receipt {
ReceiptV3::Legacy(r) => &r.logs,
ReceiptV3::EIP2930(r) => &r.logs,
ReceiptV3::EIP1559(r) => &r.logs,
};

for log in logs {
let map = logs_map.entry(log.address).or_insert(Vec::new());

map.push(LogIndex {
block_hash: receipt.block_hash,
topics: log.clone().topics,
data: log.clone().data,
log_index: U256::from(log_index),
address: log.clone().address,
removed: false, // hardcoded as no reorgs on DeFiChain
transaction_hash: receipt.tx_hash,
transaction_index: receipt.tx_index,
});

log_index += 1;
}
}

logs_map
.into_iter()
.for_each(|(address, logs)| self.storage.put_logs(address, logs, block_number));
}

// get logs at a block height and filter for topics
pub fn get_logs(
&self,
address: Option<Vec<H160>>,
topics: Option<Vec<H256>>,
block_number: U256,
) -> Vec<LogIndex> {
debug!("Getting logs for block {:#x?}", block_number);
let logs = self.storage.get_logs(&block_number).unwrap_or_default();

let logs = match address {
None => logs.into_iter().flat_map(|(_, log)| log).collect(),
Some(addresses) => {
// filter by addresses
logs.into_iter()
.filter(|(address, _)| addresses.contains(address))
.into_iter()
.flat_map(|(_, log)| log)
.collect()
}
};

match topics {
None => logs,
Some(topics) => logs
.into_iter()
.filter(|log| {
let set: HashSet<_> = log.topics.iter().copied().collect();
topics.iter().any(|item| set.contains(item))
})
.collect(),
}
}
}
37 changes: 35 additions & 2 deletions lib/ain-evm/src/storage/data_handler.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::{collections::HashMap, sync::RwLock};

use crate::log::LogIndex;
use ethereum::{BlockAny, TransactionV2};
use primitive_types::{H256, U256};
use primitive_types::{H160, H256, U256};
use std::borrow::ToOwned;

use crate::receipt::Receipt;
use crate::storage::traits::LogStorage;

use super::{
code::CodeHistory,
Expand All @@ -21,19 +23,22 @@ pub static RECEIPT_MAP_PATH: &str = "receipt_map.bin";
pub static CODE_MAP_PATH: &str = "code_map.bin";
pub static TRANSACTION_DATA_PATH: &str = "transaction_data.bin";
pub static BASE_FEE_MAP_PATH: &str = "base_fee_map.bin";
pub static ADDRESS_LOGS_MAP_PATH: &str = "address_logs_map.bin";

type BlockHashtoBlock = HashMap<H256, U256>;
type Blocks = HashMap<U256, BlockAny>;
type TxHashToTx = HashMap<H256, TransactionV2>;
type LatestBlockNumber = U256;
type TransactionHashToReceipt = HashMap<H256, Receipt>;
type BlockHashtoBaseFee = HashMap<H256, U256>;
type AddressToLogs = HashMap<U256, HashMap<H160, Vec<LogIndex>>>;

impl PersistentState for BlockHashtoBlock {}
impl PersistentState for Blocks {}
impl PersistentState for LatestBlockNumber {}
impl PersistentState for TransactionHashToReceipt {}
impl PersistentState for TxHashToTx {}
impl PersistentState for AddressToLogs {}

#[derive(Debug, Default)]
pub struct BlockchainDataHandler {
Expand All @@ -48,6 +53,8 @@ pub struct BlockchainDataHandler {
base_fee_map: RwLock<BlockHashtoBaseFee>,

code_map: RwLock<CodeHistory>,

address_logs_map: RwLock<AddressToLogs>,
}

impl BlockchainDataHandler {
Expand Down Expand Up @@ -75,6 +82,9 @@ impl BlockchainDataHandler {
BlockHashtoBaseFee::load_from_disk(BASE_FEE_MAP_PATH).unwrap_or_default(),
),
code_map: RwLock::new(CodeHistory::load_from_disk(CODE_MAP_PATH).unwrap_or_default()),
address_logs_map: RwLock::new(
AddressToLogs::load_from_disk(ADDRESS_LOGS_MAP_PATH).unwrap_or_default(),
),
}
}
}
Expand Down Expand Up @@ -203,6 +213,25 @@ impl ReceiptStorage for BlockchainDataHandler {
}
}

impl LogStorage for BlockchainDataHandler {
fn get_logs(&self, block_number: &U256) -> Option<HashMap<H160, Vec<LogIndex>>> {
self.address_logs_map
.read()
.unwrap()
.get(block_number)
.map(ToOwned::to_owned)
}

fn put_logs(&self, address: H160, logs: Vec<LogIndex>, block_number: U256) {
let mut address_logs_map = self.address_logs_map.write().unwrap();

let address_map = address_logs_map
.entry(block_number)
.or_insert(HashMap::new());
address_map.insert(address, logs);
}
}

impl FlushableStorage for BlockchainDataHandler {
fn flush(&self) -> Result<(), PersistentStateError> {
self.block_map
Expand All @@ -227,7 +256,11 @@ impl FlushableStorage for BlockchainDataHandler {
self.base_fee_map
.write()
.unwrap()
.save_to_disk(BASE_FEE_MAP_PATH)
.save_to_disk(BASE_FEE_MAP_PATH)?;
self.address_logs_map
.write()
.unwrap()
.save_to_disk(ADDRESS_LOGS_MAP_PATH)
}
}

Expand Down
16 changes: 15 additions & 1 deletion lib/ain-evm/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ mod code;
mod data_handler;
pub mod traits;

use crate::log::LogIndex;
use ethereum::{BlockAny, TransactionV2};
use primitive_types::{H256, U256};
use primitive_types::{H160, H256, U256};
use std::collections::HashMap;

use crate::receipt::Receipt;
use crate::storage::traits::LogStorage;

use self::{
cache::Cache,
Expand Down Expand Up @@ -174,6 +177,17 @@ impl ReceiptStorage for Storage {
}
}

impl LogStorage for Storage {
fn get_logs(&self, block_number: &U256) -> Option<HashMap<H160, Vec<LogIndex>>> {
self.blockchain_data_handler.get_logs(block_number)
}

fn put_logs(&self, address: H160, logs: Vec<LogIndex>, block_number: U256) {
self.blockchain_data_handler
.put_logs(address, logs, block_number)
}
}

impl FlushableStorage for Storage {
fn flush(&self) -> Result<(), PersistentStateError> {
self.blockchain_data_handler.flush()
Expand Down
9 changes: 8 additions & 1 deletion lib/ain-evm/src/storage/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ use ethereum::BlockAny;
use ethereum::TransactionV2;
use keccak_hash::H256;
use log::debug;
use primitive_types::U256;
use primitive_types::{H160, U256};
use std::collections::HashMap;
use std::fs::File;

use crate::log::LogIndex;
use std::fmt;
use std::io;
use std::io::Write;
Expand Down Expand Up @@ -44,6 +46,11 @@ pub trait ReceiptStorage {
fn put_receipts(&self, receipts: Vec<Receipt>);
}

pub trait LogStorage {
fn get_logs(&self, block_number: &U256) -> Option<HashMap<H160, Vec<LogIndex>>>;
fn put_logs(&self, address: H160, logs: Vec<LogIndex>, block_number: U256);
}

pub trait FlushableStorage {
fn flush(&self) -> Result<(), PersistentStateError>;
}
Expand Down
1 change: 1 addition & 0 deletions lib/ain-grpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod impls;
mod receipt;
pub mod rpc;
mod transaction;
mod transaction_log;
mod transaction_request;
mod utils;

Expand Down
57 changes: 57 additions & 0 deletions lib/ain-grpc/src/rpc/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use ain_cpp_imports::get_eth_priv_key;
use ain_evm::executor::TxResponse;
use ain_evm::handler::Handlers;

use crate::transaction_log::{GetLogsRequest, LogResult};
use ain_evm::storage::traits::{BlockStorage, ReceiptStorage, TransactionStorage};
use ain_evm::transaction::{SignedTx, TransactionError};
use ethereum::{EnvelopedEncodable, TransactionV2};
Expand Down Expand Up @@ -204,6 +205,9 @@ pub trait MetachainRPC {

#[method(name = "maxPriorityFeePerGas")]
fn max_priority_fee_per_gas(&self) -> RpcResult<U256>;

#[method(name = "getLogs")]
fn get_logs(&self, input: GetLogsRequest) -> RpcResult<Vec<LogResult>>;
}

pub struct MetachainRPCModule {
Expand Down Expand Up @@ -721,6 +725,59 @@ impl MetachainRPCServer for MetachainRPCModule {
fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
Ok(self.handler.block.suggested_priority_fee())
}

fn get_logs(&self, input: GetLogsRequest) -> RpcResult<Vec<LogResult>> {
if let (Some(_), Some(_)) = (input.block_hash, input.to_block.or(input.from_block)) {
return Err(Error::Custom(String::from(
"cannot specify both blockHash and fromBlock/toBlock, choose one or the other",
)));
}

let block_numbers = match input.block_hash {
None => {
// use fromBlock-toBlock
let mut block_number = self.block_number_to_u256(input.from_block);
let to_block_number = self.block_number_to_u256(input.to_block);
let mut block_numbers = Vec::new();

if block_number > to_block_number {
return Err(Error::Custom(format!(
"fromBlock ({}) > toBlock ({})",
format_u256(block_number),
format_u256(to_block_number)
)));
}

while block_number <= to_block_number {
block_numbers.push(block_number);
block_number += U256::one();
}

block_numbers
}
Some(block_hash) => {
vec![
self.handler
.storage
.get_block_by_hash(&block_hash)
.ok_or_else(|| Error::Custom(String::from("Unable to find block hash")))?
.header
.number,
]
}
};

Ok(block_numbers
.into_iter()
.flat_map(|block_number| {
self.handler
.logs
.get_logs(input.clone().address, input.clone().topics, block_number)
.into_iter()
.map(LogResult::from)
})
.collect())
}
}

fn sign(
Expand Down
Loading