diff --git a/CHANGELOG.md b/CHANGELOG.md index d15c4491d6..dce28e663b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Add new Ethreum RPC endpoints for transaction handling: eth_sendTransaction, eth_sendRawTransaction, eth_getTransactionByHash, eth_getTransactionByBlockHashAndIndex, eth_getTransactionByBlockNumberAndIndex, eth_getTransactionReceipt, eth_signTransaction, eth_estimateGas, eth_sign, eth_call - Add new Ethreum RPC endpoints for getting chain info: eth_chainId, eth_syncing, eth_gasPrice, eth_protocolVersion, eth_feeHistory - Add new Ethreum RPC endpoints for account handling: eth_accounts, eth_getTransactionCount, eth_getBalance, eth_getCode, eth_getStorageAt +- Add new Ethreum RPC filtering endpoints: eth_newFilter, eth_newBlockFilter, eth_newPendingTransactionFilter, eth_uninstallFilter, eth_getFilterChanges, eth_getFilterLogs, eth_getLogs - Add new Ethreum RPC mining endpoints: eth_mining, eth_coinbase, eth_hashrate, eth_getWork, eth_submitWork, eth_submitHashrate - Add chain-evm as optional dependency for jcli - Update gas price and block gas limit for EVM params diff --git a/jormungandr/src/jrpc/eth_filter/logic.rs b/jormungandr/src/jrpc/eth_filter/logic.rs new file mode 100644 index 0000000000..4b4af3589a --- /dev/null +++ b/jormungandr/src/jrpc/eth_filter/logic.rs @@ -0,0 +1,44 @@ +use super::Error; +use crate::{ + context::Context, + jrpc::eth_types::{ + filter::{Filter, FilterChanges}, + log::Log, + number::Number, + }, +}; + +pub fn new_filter(_filter: Filter, _context: &Context) -> Result { + // TODO implement + Ok(0.into()) +} + +pub fn new_block_filter(_context: &Context) -> Result { + // TODO implement + Ok(0.into()) +} + +pub fn new_pending_transaction_filter(_context: &Context) -> Result { + // TODO implement + Ok(0.into()) +} + +pub fn uninstall_filter(_filter_id: Number, _context: &Context) -> Result { + // TODO implement + Ok(true) +} + +pub fn get_filter_changes(_filter_id: Number, _context: &Context) -> Result { + // TODO implement + Ok(FilterChanges::Empty) +} + +pub fn get_filter_logs(_filter_id: Number, _context: &Context) -> Result, Error> { + // TODO implement + Ok(vec![Log::build()]) +} + +pub fn get_logs(_filter: Filter, _context: &Context) -> Result { + // TODO implement + Ok(FilterChanges::Empty) +} diff --git a/jormungandr/src/jrpc/eth_filter/mod.rs b/jormungandr/src/jrpc/eth_filter/mod.rs new file mode 100644 index 0000000000..4a87af9473 --- /dev/null +++ b/jormungandr/src/jrpc/eth_filter/mod.rs @@ -0,0 +1,73 @@ +use crate::context::ContextLock; +use jsonrpsee_http_server::RpcModule; + +mod logic; + +#[derive(Debug, thiserror::Error)] +pub enum Error {} + +pub fn eth_filter_module(context: ContextLock) -> RpcModule { + let mut module = RpcModule::new(context); + + module + .register_async_method("eth_newFilter", |params, context| async move { + let context = context.read().await; + let filter = params.parse()?; + logic::new_filter(filter, &context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_newBlockFilter", |_, context| async move { + let context = context.read().await; + logic::new_block_filter(&context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_newPendingTransactionFilter", |_, context| async move { + let context = context.read().await; + logic::new_pending_transaction_filter(&context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_uninstallFilter", |params, context| async move { + let context = context.read().await; + let filter_id = params.parse()?; + logic::uninstall_filter(filter_id, &context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_getFilterChanges", |params, context| async move { + let context = context.read().await; + let filter_id = params.parse()?; + logic::get_filter_changes(filter_id, &context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_getFilterLogs", |params, context| async move { + let context = context.read().await; + let filter_id = params.parse()?; + logic::get_filter_logs(filter_id, &context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + + module + .register_async_method("eth_getLogs", |params, context| async move { + let context = context.read().await; + let filter = params.parse()?; + logic::get_logs(filter, &context) + .map_err(|err| jsonrpsee_core::Error::Custom(err.to_string())) + }) + .unwrap(); + module +} diff --git a/jormungandr/src/jrpc/eth_types/filter.rs b/jormungandr/src/jrpc/eth_types/filter.rs new file mode 100644 index 0000000000..ed97956567 --- /dev/null +++ b/jormungandr/src/jrpc/eth_types/filter.rs @@ -0,0 +1,143 @@ +use super::{block_number::BlockNumber, log::Log}; +use chain_evm::ethereum_types::{H160, H256}; +use serde::{Deserialize, Serialize, Serializer}; + +/// Variadic value +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(untagged)] +pub enum VariadicValue { + /// Single + Single(T), + /// List + Multiple(Vec), + /// None + Null, +} + +/// Filter Address +pub type FilterAddress = VariadicValue; +/// Topic, supports `A` | `null` | `[A,B,C]` | `[A,[B,C]]` | [null,[B,C]] | [null,[null,C]] +pub type Topic = VariadicValue>; + +/// Filter +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] +pub struct Filter { + /// From Block + from_block: Option, + /// To Block + to_block: Option, + /// Address + address: FilterAddress, + /// Topics + topics: Topic, + /// Block hash + block_hash: Option, +} + +/// Results of the filter_changes RPC. +#[derive(Debug, PartialEq, Eq)] +pub enum FilterChanges { + #[allow(dead_code)] + /// New logs. + Logs(Vec), + #[allow(dead_code)] + /// New hashes (block or transactions) + Hashes(Vec), + /// Empty result, + Empty, +} + +impl Serialize for FilterChanges { + fn serialize(&self, s: S) -> Result + where + S: Serializer, + { + match *self { + FilterChanges::Logs(ref logs) => logs.serialize(s), + FilterChanges::Hashes(ref hashes) => hashes.serialize(s), + FilterChanges::Empty => (&[] as &[serde_json::Value]).serialize(s), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn filter_address_deserialize() { + let fa_single: FilterAddress = + serde_json::from_str(r#""0x0000000000000000000000000000000000000000""#).unwrap(); + let fa_multiple: FilterAddress = + serde_json::from_str(r#"["0x0000000000000000000000000000000000000000"]"#).unwrap(); + let fa_null: FilterAddress = serde_json::from_str(r#"null"#).unwrap(); + + assert_eq!(fa_single, FilterAddress::Single(H160::zero())); + assert_eq!(fa_multiple, FilterAddress::Multiple(vec![H160::zero()])); + assert_eq!(fa_null, FilterAddress::Null); + } + + #[test] + fn topic_deserialize() { + let t_single_single: Topic = serde_json::from_str( + r#""0x0000000000000000000000000000000000000000000000000000000000000000""#, + ) + .unwrap(); + let t_single_multiple: Topic = serde_json::from_str( + r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#, + ) + .unwrap(); + let t_multiple_multiple_1: Topic = serde_json::from_str( + r#"["0x0000000000000000000000000000000000000000000000000000000000000000",["0x0000000000000000000000000000000000000000000000000000000000000000"]]"#, + ) + .unwrap(); + let t_multiple_multiple_2: Topic = serde_json::from_str( + r#"[null,["0x0000000000000000000000000000000000000000000000000000000000000000"]]"#, + ) + .unwrap(); + let t_null: Topic = serde_json::from_str(r#"null"#).unwrap(); + + assert_eq!( + t_single_single, + Topic::Single(>::Single(H256::zero())) + ); + assert_eq!( + t_single_multiple, + Topic::Single(>::Multiple(vec![H256::zero()])) + ); + assert_eq!( + t_multiple_multiple_1, + Topic::Multiple(vec![ + >::Single(H256::zero()), + >::Multiple(vec![H256::zero()]) + ]) + ); + assert_eq!( + t_multiple_multiple_2, + Topic::Multiple(vec![ + >::Null, + >::Multiple(vec![H256::zero()]) + ]) + ); + assert_eq!(t_null, Topic::Single(>::Null)); + } + + #[test] + fn filter_changes_serialize() { + let fc_log = FilterChanges::Logs(vec![Log::build()]); + let fc_hashes = FilterChanges::Hashes(vec![H256::zero()]); + let fc_empty = FilterChanges::Empty; + + assert_eq!( + serde_json::to_string(&fc_log).unwrap(), + r#"[{"removed":true,"logIndex":"0x1","transactionIndex":"0x1","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x1","address":"0x0000000000000000000000000000000000000000","data":"0x","topics":[]}]"# + ); + assert_eq!( + serde_json::to_string(&fc_hashes).unwrap(), + r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"# + ); + assert_eq!(serde_json::to_string(&fc_empty).unwrap(), r#"[]"#); + } +} diff --git a/jormungandr/src/jrpc/eth_types/mod.rs b/jormungandr/src/jrpc/eth_types/mod.rs index a53e54550f..d1bddbc9db 100644 --- a/jormungandr/src/jrpc/eth_types/mod.rs +++ b/jormungandr/src/jrpc/eth_types/mod.rs @@ -2,6 +2,7 @@ pub mod block; pub mod block_number; pub mod bytes; pub mod fee; +pub mod filter; pub mod log; pub mod number; pub mod receipt; diff --git a/jormungandr/src/jrpc/eth_types/number.rs b/jormungandr/src/jrpc/eth_types/number.rs index cccf4b0f03..cea6bbad77 100644 --- a/jormungandr/src/jrpc/eth_types/number.rs +++ b/jormungandr/src/jrpc/eth_types/number.rs @@ -76,7 +76,7 @@ mod tests { use super::*; #[test] - fn index_deserialization() { + fn number_serde() { let s = r#"["0xa", "10", 42, "0x45"]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); assert_eq!( diff --git a/jormungandr/src/jrpc/mod.rs b/jormungandr/src/jrpc/mod.rs index 62f5133f17..25ce226559 100644 --- a/jormungandr/src/jrpc/mod.rs +++ b/jormungandr/src/jrpc/mod.rs @@ -5,6 +5,8 @@ mod eth_block_info; #[cfg(feature = "evm")] mod eth_chain_info; #[cfg(feature = "evm")] +mod eth_filter; +#[cfg(feature = "evm")] mod eth_miner; #[cfg(feature = "evm")] mod eth_transaction; @@ -46,6 +48,10 @@ pub async fn start_jrpc_server(config: Config, _context: ContextLock) { .merge(eth_account::eth_account_module(_context.clone())) .unwrap(); + modules + .merge(eth_filter::eth_filter_module(_context.clone())) + .unwrap(); + modules .merge(eth_miner::eth_miner_module(_context)) .unwrap();