-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(#528): deconstructs middleware module into components
- Loading branch information
1 parent
508fe42
commit b30f727
Showing
7 changed files
with
339 additions
and
297 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
///! Utility functions for casting between revm and ethers-rs types. | ||
use ethers::types::Address; | ||
use revm::primitives::B160; | ||
|
||
/// Converts logs from the Revm format to the Ethers format. | ||
/// | ||
/// This function iterates over a list of logs as they appear in the `revm` and | ||
/// converts each log entry to the corresponding format used by the `ethers-rs` | ||
/// library. | ||
#[inline] | ||
pub fn revm_logs_to_ethers_logs( | ||
revm_logs: Vec<revm::primitives::Log>, | ||
) -> Vec<ethers::core::types::Log> { | ||
let mut logs: Vec<ethers::core::types::Log> = vec![]; | ||
for revm_log in revm_logs { | ||
let topics = revm_log.topics.into_iter().map(recast_b256).collect(); | ||
let log = ethers::core::types::Log { | ||
address: recast_address(revm_log.address), | ||
topics, | ||
data: ethers::core::types::Bytes::from(revm_log.data), | ||
block_hash: None, | ||
block_number: None, | ||
transaction_hash: None, | ||
transaction_index: None, | ||
log_index: None, | ||
transaction_log_index: None, | ||
log_type: None, | ||
removed: None, | ||
}; | ||
logs.push(log); | ||
} | ||
logs | ||
} | ||
|
||
// Certainly will go away with alloy-types | ||
/// Recast a B160 into an Address type | ||
/// # Arguments | ||
/// * `address` - B160 to recast. (B160) | ||
/// # Returns | ||
/// * `Address` - Recasted Address. | ||
#[inline] | ||
pub fn recast_address(address: B160) -> Address { | ||
let temp: [u8; 20] = address.as_bytes().try_into().unwrap(); | ||
Address::from(temp) | ||
} | ||
|
||
/// Recast a B256 into an H256 type | ||
/// # Arguments | ||
/// * `input` - B256 to recast. (B256) | ||
/// # Returns | ||
/// * `H256` - Recasted H256. | ||
#[inline] | ||
pub fn recast_b256(input: revm::primitives::B256) -> ethers::types::H256 { | ||
let temp: [u8; 32] = input.as_bytes().try_into().unwrap(); | ||
ethers::types::H256::from(temp) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
///! Messengers/connections to the underlying EVM in the environment. | ||
use std::{ | ||
collections::HashMap, | ||
fmt::Debug, | ||
sync::{Arc, Mutex, Weak}, | ||
}; | ||
|
||
use ethers::{prelude::ProviderError, providers::JsonRpcClient, types::FilteredParams}; | ||
use serde::{de::DeserializeOwned, Serialize}; | ||
|
||
use super::cast::revm_logs_to_ethers_logs; | ||
use super::events::FilterReceiver; | ||
|
||
use crate::environment::{EventBroadcaster, InstructionSender, OutcomeReceiver, OutcomeSender}; | ||
|
||
/// Represents a connection to the EVM contained in the corresponding | ||
/// [`Environment`]. | ||
#[derive(Debug)] | ||
pub struct Connection { | ||
/// Used to send calls and transactions to the [`Environment`] to be | ||
/// executed by `revm`. | ||
pub(crate) instruction_sender: Weak<InstructionSender>, | ||
|
||
/// Used to send results back to a client that made a call/transaction with | ||
/// the [`Environment`]. This [`ResultSender`] is passed along with a | ||
/// call/transaction so the [`Environment`] can reply back with the | ||
/// [`ExecutionResult`]. | ||
pub(crate) outcome_sender: OutcomeSender, | ||
|
||
/// Used to receive the [`ExecutionResult`] from the [`Environment`] upon | ||
/// call/transact. | ||
pub(crate) outcome_receiver: OutcomeReceiver, | ||
|
||
/// A reference to the [`EventBroadcaster`] so that more receivers of the | ||
/// broadcast can be taken from it. | ||
pub(crate) event_broadcaster: Arc<Mutex<EventBroadcaster>>, | ||
|
||
/// A collection of `FilterReceiver`s that will receive outgoing logs | ||
/// generated by `revm` and output by the [`Environment`]. | ||
pub(crate) filter_receivers: | ||
Arc<tokio::sync::Mutex<HashMap<ethers::types::U256, FilterReceiver>>>, | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl JsonRpcClient for Connection { | ||
type Error = ProviderError; | ||
|
||
/// Processes a JSON-RPC request and returns the response. | ||
/// Currently only handles the `eth_getFilterChanges` call since this is | ||
/// used for polling events emitted from the [`Environment`]. | ||
async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>( | ||
&self, | ||
method: &str, | ||
params: T, | ||
) -> Result<R, ProviderError> { | ||
match method { | ||
"eth_getFilterChanges" => { | ||
// TODO: The extra json serialization/deserialization can probably be avoided | ||
// somehow | ||
|
||
// Get the `Filter` ID from the params `T` | ||
// First convert it into a JSON `Value` | ||
let value = serde_json::to_value(¶ms)?; | ||
|
||
// Take this value as an array then cast it to a string | ||
let str = value.as_array().ok_or(ProviderError::CustomError( | ||
"The params value passed to the `Connection` via a `request` was empty. | ||
This is likely due to not specifying a specific `Filter` ID!".to_string() | ||
))?[0] | ||
.as_str().ok_or(ProviderError::CustomError( | ||
"The params value passed to the `Connection` via a `request` could not be later cast to `str`!".to_string() | ||
))?; | ||
|
||
// Now get the `U256` ID via the string decoded from hex radix. | ||
let id = ethers::types::U256::from_str_radix(str, 16) | ||
.map_err(|e| ProviderError::CustomError( | ||
format!("The `str` representation of the filter ID could not be cast into `U256` due to: {:?}!", | ||
e)))?; | ||
|
||
// Get the corresponding `filter_receiver` and await for logs to appear. | ||
let mut filter_receivers = self.filter_receivers.lock().await; | ||
let filter_receiver = | ||
filter_receivers | ||
.get_mut(&id) | ||
.ok_or(ProviderError::CustomError( | ||
"The filter ID does not seem to match any that this client owns!" | ||
.to_string(), | ||
))?; | ||
let mut logs = vec![]; | ||
let filtered_params = FilteredParams::new(Some(filter_receiver.filter.clone())); | ||
if let Ok(received_logs) = filter_receiver.receiver.try_recv() { | ||
let ethers_logs = revm_logs_to_ethers_logs(received_logs); | ||
for log in ethers_logs { | ||
if filtered_params.filter_address(&log) | ||
&& filtered_params.filter_topics(&log) | ||
{ | ||
logs.push(log); | ||
} | ||
} | ||
} | ||
// Take the logs and Stringify then JSONify to cast into `R`. | ||
let logs_str = serde_json::to_string(&logs)?; | ||
let logs_deserializeowned: R = serde_json::from_str(&logs_str)?; | ||
Ok(logs_deserializeowned) | ||
} | ||
_ => Err(ProviderError::UnsupportedRPC), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
///! Possible errors thrown by interacting with the revm middleware client. | ||
use ethers::{prelude::ProviderError, providers::MiddlewareError}; | ||
use std::fmt::Debug; | ||
use thiserror::Error; | ||
|
||
/// Errors that can occur while using the [`RevmMiddleware`]. | ||
/// These errors are likely to be more common than other errors in | ||
/// `arbiter-core` as they can come from simple issues such as contract reverts | ||
/// or halts. Certain errors such as [`RevmMiddlewareError::Send`], | ||
/// [`RevmMiddlewareError::Receive`], [`RevmMiddlewareError::Conversion`], | ||
/// [`RevmMiddlewareError::Json`], and [`RevmMiddlewareError::EventBroadcaster`] | ||
/// are considered more worrying. If these are achieved, please feel free to | ||
/// contact our team via the [Telegram group](https://t.me/arbiter_rs) or on | ||
/// [GitHub](https://github.com/primitivefinance/arbiter/). | ||
#[derive(Error, Debug)] | ||
pub enum RevmMiddlewareError { | ||
/// An error occurred while attempting to interact with the [`Environment`]. | ||
#[error("an error came from the environment! due to: {0}")] | ||
Environment(#[from] crate::environment::errors::EnvironmentError), | ||
|
||
/// An error occurred while attempting to interact with the provider: | ||
/// [`Connection`]. | ||
#[error("an error came from the provider! due to: {0}")] | ||
Provider(#[from] ProviderError), | ||
|
||
/// An error occurred while attempting to send a transaction. | ||
#[error("failed to send transaction! due to: {0}")] | ||
Send(String), | ||
|
||
/// There was an issue receiving an [`ExecutionResult`], possibly from | ||
/// another service or module. | ||
#[error("failed to receive `ExecutionResult`! due to: {0}")] | ||
Receive(#[from] crossbeam_channel::RecvError), | ||
|
||
/// There was a failure trying to obtain a lock on the [`EventBroadcaster`], | ||
/// possibly due to concurrency issues. | ||
#[error("failed to gain event broadcaster lock! due to: {0}")] | ||
EventBroadcaster(String), | ||
|
||
/// The required data or functionality for an instruction was missing or | ||
/// incomplete. | ||
#[error("missing data! due to: {0}")] | ||
MissingData(String), | ||
|
||
/// An error occurred during type conversion, possibly when translating | ||
/// between domain-specific types. | ||
#[error("failed to convert types! due to: {0}")] | ||
Conversion(String), | ||
|
||
/// An error occurred while trying to serialize or deserialize JSON data. | ||
#[error("failed to handle with JSON data! due to: {0:?}")] | ||
Json(serde_json::Error), | ||
|
||
/// The execution of a transaction was reverted, indicating that the | ||
/// transaction was not successful. | ||
#[error("execution failed to succeed due to revert!\n gas used is: {gas_used}\n output is {output:?}")] | ||
ExecutionRevert { | ||
/// Provides the amount of gas used by the transaction. | ||
gas_used: u64, | ||
|
||
/// Provides the output or reason why the transaction was reverted. | ||
output: revm::primitives::Bytes, | ||
}, | ||
|
||
/// The execution of a transaction halted unexpectedly. | ||
#[error("execution failed to succeed due to halt!\n reason is: {reason:?}\n gas used is: {gas_used}")] | ||
ExecutionHalt { | ||
/// Provides the reason for the halt. | ||
reason: revm::primitives::Halt, | ||
|
||
/// Provides the amount of gas used by the transaction. | ||
gas_used: u64, | ||
}, | ||
} | ||
|
||
impl MiddlewareError for RevmMiddlewareError { | ||
type Inner = ProviderError; | ||
|
||
fn from_err(e: Self::Inner) -> Self { | ||
RevmMiddlewareError::Provider(e) | ||
} | ||
|
||
fn as_inner(&self) -> Option<&Self::Inner> { | ||
None | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
///! Specific event types that can be emitted by the [`Environment`]. | ||
/// | ||
use ethers::types::Filter; | ||
use std::fmt::Debug; | ||
|
||
/// Packages together a [`crossbeam_channel::Receiver<Vec<Log>>`] along with a | ||
/// [`Filter`] for events. Allows the client to have a stream of filtered | ||
/// events. | ||
#[derive(Debug)] | ||
pub(crate) struct FilterReceiver { | ||
/// The filter definition used for this receiver. | ||
/// Comes from the `ethers-rs` crate. | ||
pub(crate) filter: Filter, | ||
|
||
/// The receiver for the channel that receives logs from the broadcaster. | ||
/// These are filtered upon reception. | ||
pub(crate) receiver: crossbeam_channel::Receiver<Vec<revm::primitives::Log>>, | ||
} |
Oops, something went wrong.