Skip to content

Commit

Permalink
doc: added rustdoc for uopool crate
Browse files Browse the repository at this point in the history
  • Loading branch information
da-bao-jian committed Aug 13, 2023
1 parent 33dc6e6 commit 97d6c04
Show file tree
Hide file tree
Showing 24 changed files with 419 additions and 0 deletions.
15 changes: 15 additions & 0 deletions crates/uopool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Silius UserOperation Pool

The UserOperation alternative mempool implementation according to the [ERC-4337 specifications](https://eips.ethereum.org/EIPS/eip-4337#Alternative%20Mempools).

## Components Breakdown
### `uopool` module
* `uopool` module provides the `UoPool` type that encapsulates `validator`, `mempool` and `reputation` trait implementations for a customizable alternative mempool.
### `validate` module
* `validate` module provides the `Validator` trait that defines the interface for constructing flexible alternative memppool validation rules.
### `mempool` module
* `mempool` module provides the `Mempool` trait that defines the interface for mempool-related operations.
### `reputation` module
* `reputation` module provides the `Reputation` trait that defines the interface for reputation-realted operations.
### `database` and `memory` modules
* `database` and `memory` modules provide different implementations of the `Mempool` trait, depending on the use case.
1 change: 1 addition & 0 deletions crates/uopool/src/database/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
//! The database implementation of the [Mempool](crate::mempool::Mempool) trait. Primarily used for storing mempool information in a local database.
pub mod mempool;
mod utils;
1 change: 1 addition & 0 deletions crates/uopool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! The UserOperation alternative mempool implementation according to the [ERC-4337 specifications](https://eips.ethereum.org/EIPS/eip-4337#Alternative%20Mempools).
#![allow(dead_code)]

mod database;
Expand Down
1 change: 1 addition & 0 deletions crates/uopool/src/memory/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
//! The in-memory implementation of the [Mempool](crate::mempool::Mempool) trait.
pub mod mempool;
pub mod reputation;
2 changes: 2 additions & 0 deletions crates/uopool/src/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub fn mempool_id(ep: &Address, chain_id: &U256) -> MempoolId {
)
}

/// Mempool trait that's implemented by [DatabaseMempool](DatabaseMempool) and [MemoryMempool](MemoryMempool)
/// See [DatabaseMempool](DatabaseMempool) and [MemoryMempool](MemoryMempool) for implementation details
pub trait Mempool: Debug {
type UserOperations: IntoIterator<Item = UserOperation>;
type CodeHashes: IntoIterator<Item = CodeHash>;
Expand Down
3 changes: 3 additions & 0 deletions crates/uopool/src/reputation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use std::{fmt::Debug, ops::Deref};

pub type ReputationBox<T> = Box<dyn Reputation<ReputationEntries = T> + Send + Sync>;

/// Reputation trait is imeplemented by [DatabaseMempool](DatabaseMempool) and [MemoryMempool](MemoryMempool) according to [Reputation scoring and throttling/banning for global entities](https://eips.ethereum.org/EIPS/eip-4337#reputation-scoring-and-throttlingbanning-for-global-entities) requirements.
/// [UserOperation’s](UserOperation) storage access rules prevent them from interfere with each other. But “global” entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously-valid UserOperations.
/// To prevent abuse, we need to throttle down (or completely ban for a period of time) an entity that causes invalidation of large number of UserOperations in the mempool. To prevent such entities from “sybil-attack”, we require them to stake with the system, and thus make such DoS attack very expensive.
pub trait Reputation: Debug {
type ReputationEntries: IntoIterator<Item = ReputationEntry>;

Expand Down
129 changes: 129 additions & 0 deletions crates/uopool/src/uopool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,41 @@ pub type VecCh = Vec<CodeHash>;

const LATEST_SCAN_DEPTH: u64 = 1000;

/// The alternative mempool pool implementation that provides functionalities to add, remove, validate, and serves data requests from the [RPC API](EthApiServer).
/// Architecturally, the [UoPool](UoPool) is the backend service managed by the [UoPoolService](UoPoolService) and serves requests from the [RPC API](EthApiServer).
pub struct UoPool<M: Middleware + 'static, V: UserOperationValidator> {
/// The unique ID of the mempool
pub id: MempoolId,
/// The [EntryPoint](EntryPoint) contract object
pub entry_point: EntryPoint<M>,
/// The [UserOperationValidator](UserOperationValidator) object
pub validator: V,
/// The [MempoolBox](MempoolBox) is a [Boxed pointer](https://doc.rust-lang.org/std/boxed/struct.Box.html) to a [Mempool](Mempool) object
pub mempool: MempoolBox<VecUo, VecCh>,
/// The [ReputationBox](ReputationBox) is a [Boxed pointer](https://doc.rust-lang.org/std/boxed/struct.Box.html) to a [ReputationEntry](ReputationEntry) object
pub reputation: ReputationBox<Vec<ReputationEntry>>,
/// The Ethereum client [Middleware](ethers::providers::Middleware)
pub eth_client: Arc<M>,
// The maximum gas limit for [UserOperation](UserOperation) gas verification.
pub max_verification_gas: U256,
// The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID
pub chain: Chain,
}

impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
/// Creates a new [UoPool](UoPool) object
///
/// # Arguments
/// `entry_point` - The [EntryPoint](EntryPoint) contract object
/// `validator` - The [UserOperationValidator](UserOperationValidator) object
/// `mempool` - The [MempoolBox](MempoolBox) is a [Boxed pointer](https://doc.rust-lang.org/std/boxed/struct.Box.html) to a [Mempool](Mempool) object
/// `reputation` - The [ReputationBox](ReputationBox) is a [Boxed pointer](https://doc.rust-lang.org/std/boxed/struct.Box.html) to a [ReputationEntry](ReputationEntry) object
/// `eth_client` - The Ethereum client [Middleware](ethers::providers::Middleware)
/// `max_verification_gas` - The maximum gas limit for [UserOperation](UserOperation) gas verification.
/// `chain` - The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID
///
/// # Returns
/// `Self` - The [UoPool](UoPool) object
#[allow(clippy::too_many_arguments)]
pub fn new(
entry_point: EntryPoint<M>,
Expand All @@ -72,27 +95,57 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
}
}

/// Returns the [EntryPoint](EntryPoint) contract address
///
/// # Returns
/// `Address` - The [EntryPoint](EntryPoint) contract address
pub fn entry_point_address(&self) -> Address {
self.entry_point.address()
}

/// Returns all of the [UserOperations](UserOperation) in the mempool
///
/// # Returns
/// `Vec<UserOperation>` - An array of [UserOperations](UserOperation)
pub fn get_all(&self) -> Vec<UserOperation> {
self.mempool.get_all()
}

/// Returns an array of [ReputationEntry](ReputationEntry) for entities.
///
/// # Returns
/// `Vec<ReputationEntry>` - An array of [ReputationEntry](ReputationEntry)
pub fn get_reputation(&self) -> Vec<ReputationEntry> {
self.reputation.get_all()
}

/// Sets the [ReputationEntry](ReputationEntry) for entities
///
/// # Arguments
/// `reputation` - An array of [ReputationEntry](ReputationEntry)
///
/// # Returns
/// `()` - Returns nothing
pub fn set_reputation(&mut self, reputation: Vec<ReputationEntry>) {
self.reputation.set(reputation);
}

/// Batch clears the [Mempool](Mempool) and [Reputation](Reputation).
///
/// # Returns
/// `()` - Returns nothing
pub fn clear(&mut self) {
self.mempool.clear();
self.reputation.clear();
}

/// Validates a single [UserOperation](UserOperation) and returns the validation outcome by calling [UserOperationValidator::validate_user_operation](UserOperationValidator::validate_user_operation)
///
/// # Arguments
/// `uo` - The [UserOperation](UserOperation) to validate
///
/// # Returns
/// `Result<UserOperationValidationOutcome, ValidationError>` - The validation outcome
pub async fn validate_user_operation(
&self,
uo: &UserOperation,
Expand All @@ -110,6 +163,15 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
}

/// Adds a single validated user operation into the pool
/// Indirectly invoked by [EthApiServer::send_user_operation](EthApiServer::send_user_operation) via [UoPoolService::add](UoPoolService::add) to add a [UserOperation](UserOperation) into the mempool
/// The function first validates the [UserOperation](UserOperation) by calling [UoPool::validate_user_operation](UoPool::validate_user_operation). If [UserOperation](UserOperation) passes the validation, then adds it into the mempool by calling [Mempool::add](Mempool::add).
///
/// # Arguments
/// `uo` - The [UserOperation](UserOperation) to add
/// `res` - The [UserOperationValidationOutcome](UserOperationValidationOutcome) of the validation
///
/// # Returns
/// `Result<UserOperationHash, AddError>` - The hash of the added [UserOperation](UserOperation)
pub async fn add_user_operation(
&mut self,
uo: UserOperation,
Expand Down Expand Up @@ -151,10 +213,23 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
}
}

/// Sorts the [UserOperations](UserOperation) in the mempool by calling the [Mempool::get_sorted](Mempool::get_sorted) function
///
/// # Returns
/// `Result<Vec<UserOperation>, anyhow::Error>` - The sorted [UserOperations](UserOperation)
pub fn get_sorted_user_operations(&self) -> anyhow::Result<Vec<UserOperation>> {
self.mempool.get_sorted()
}

/// Bundles an array of [UserOperations](UserOperation)
/// The function first checks the reputations of the entiries, then validate each [UserOperation](UserOperation) by calling [UoPool::validate_user_operation](UoPool::validate_user_operation).
/// If the [UserOperations](UserOperation) passes the validation, push it into the `uos_valid` array.
///
/// # Arguments
/// `uos` - An array of [UserOperations](UserOperation) to bundle
///
/// # Returns
/// `Result<Vec<UserOperation>, anyhow::Error>` - The bundled [UserOperations](UserOperation).
pub async fn bundle_user_operations(
&mut self,
uos: Vec<UserOperation>,
Expand Down Expand Up @@ -290,6 +365,10 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
Ok(uos_valid)
}

/// Gets the block base fee per gas
///
/// # Returns
/// `Result<U256, anyhow::Error>` - The block base fee per gas.
pub async fn base_fee_per_gas(&self) -> anyhow::Result<U256> {
let block = self
.eth_client
Expand All @@ -301,6 +380,14 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
.ok_or(format_err!("No base fee found"))
}

/// Estimates the `verification_gas_limit`, `call_gas_limit` and `pre_verification_gas` for a user operation.
/// The function is indirectly invoked by the `estimate_user_operation_gas` JSON RPC method.
///
/// # Arguments
/// * `uo` - The [UserOperation](UserOperation) to estimate the gas for.
///
/// # Returns
/// `Result<UserOperationGasEstimation, SimulationCheckError>` - The gas estimation result, which includes the `verification_gas_limit`, `call_gas_limit` and `pre_verification_gas`.
pub async fn estimate_user_operation_gas(
&self,
uo: &UserOperation,
Expand Down Expand Up @@ -369,6 +456,13 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
})
}

/// Filters the events logged from the [EntryPoint](EntryPoint) contract for a given user operation hash.
///
/// # Arguments
/// * `uo_hash` - The [UserOperationHash](UserOperationHash) to filter the events for.
///
/// # Returns
/// `Result<Option<(UserOperationEventFilter, LogMeta)>, anyhow::Error>` - The filtered event, if any.
pub async fn get_user_operation_event_meta(
&self,
uo_hash: &UserOperationHash,
Expand All @@ -388,6 +482,14 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
Ok(event)
}

/// Gets the user operation by hash.
/// The function is indirectly invoked by the `get_user_operation_by_hash` JSON RPC method.
///
/// # Arguments
/// * `uo_hash` - The [UserOperationHash](UserOperationHash) to get the user operation for.
///
/// # Returns
/// `Result<UserOperationByHash, anyhow::Error>` - The user operation, if any.
pub async fn get_user_operation_by_hash(
&self,
uo_hash: &UserOperationHash,
Expand Down Expand Up @@ -420,6 +522,14 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
Err(format_err!("No user operation found"))
}

/// Gets the [UserOperationReceipt](UserOperationReceipt) by hash.
/// The function is indirectly invoked by the `get_user_operation_receipt` JSON RPC method.
///
/// # Arguments
/// * `uo_hash` - The [UserOperationHash](UserOperationHash) to get the user operation receipt for.
///
/// # Returns
/// `Result<UserOperationReceipt, anyhow::Error>` - The user operation receipt, if any.
pub async fn get_user_operation_receipt(
&self,
uo_hash: &UserOperationHash,
Expand Down Expand Up @@ -451,6 +561,11 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
Err(format_err!("No user operation found"))
}

/// Queries the [EntryPoint](EntryPoint) contract for the past events logged that are included in the current block.
/// If [UserOperation](UserOperation) is found, it is removed from the [UserOperationQueue](UserOperationQueue), while simultaneously incrementing the reputation of the sender and paymaster.
///
/// # Returns
/// `Result<(), anyhow::Error>` - None if the query was successful.
pub async fn handle_past_events(&mut self) -> anyhow::Result<()> {
let block_num = self.eth_client.get_block_number().await?;
let block_st = std::cmp::max(
Expand Down Expand Up @@ -482,11 +597,25 @@ impl<M: Middleware + 'static, V: UserOperationValidator> UoPool<M, V> {
Ok(())
}

/// Removes the [UserOperation](UserOperation) from the [UserOperationQueue](UserOperationQueue) given the [UserOperationHash](UserOperationHash).
///
/// # Arguments
/// * `uo_hash` - The [UserOperationHash](UserOperationHash) to remove the user operation for.
///
/// # Returns
/// `Option<()>` - None if the user operation was successfully removed.
pub fn remove_user_operation(&mut self, uo_hash: &UserOperationHash) -> Option<()> {
self.mempool.remove(uo_hash).ok();
None
}

/// Removes multiple [UserOperations](UserOperation) from the [UserOperationQueue](UserOperationQueue) given an array of [UserOperationHash](UserOperationHash).
///
/// # Arguments
/// * `uo_hashes` - The array of [UserOperationHash](UserOperationHash) to remove the user operations for.
///
/// # Returns
/// `Option<()>` - None
pub fn remove_user_operations(&mut self, uo_hashes: Vec<UserOperationHash>) {
for uo_hash in uo_hashes {
self.remove_user_operation(&uo_hash);
Expand Down
28 changes: 28 additions & 0 deletions crates/uopool/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn equal_code_hashes(hashes: &Vec<CodeHash>, hashes_prev: &Vec<CodeHash>) ->
true
}

/// Struct to calculate the pre-verification gas of a [UserOperation](UserOperation)
// https://github.com/eth-infinitism/bundler/blob/main/packages/sdk/src/calcPreVerificationGas.ts#L44-L52
pub struct Overhead {
pub fixed: U256,
Expand All @@ -51,6 +52,14 @@ impl Default for Overhead {
}

impl Overhead {
/// Calculates the pre-verification gas of a [UserOperation](UserOperation)
/// The function first packs the [UserOperation](UserOperation) by calling the [pack](UserOperation::pack) method, then extracts the call data for gas calculation.
///
/// # Arguments
/// `uo` - The [UserOperation](UserOperation) to calculate the pre-verification gas for
///
/// # Returns
/// The pre-verification gas of the [UserOperation](UserOperation)
pub fn calculate_pre_verification_gas(&self, uo: &UserOperation) -> U256 {
let uo_pack = uo.pack();
let call_data: U256 = U256::from(
Expand All @@ -75,12 +84,31 @@ impl Overhead {
}
}

/// Helper function to calculate the valid gas of a [UserOperation](UserOperation)
/// The function is invoked by the [check_valid_gas](crates::uopool::validate::sanity::check_valid_gas) method.
///
/// # Arguments
/// `gas_price` - The gas price
/// `gas_incr_perc` - The gas increase percentage
///
/// # Returns
/// The valid gas of the [UserOperation](UserOperation)
pub fn calculate_valid_gas(gas_price: U256, gas_incr_perc: U256) -> U256 {
let gas_price = gas_price.as_u64() as f64;
let gas_incr_perc = gas_incr_perc.as_u64() as f64;
((gas_price * (1.0 + gas_incr_perc / 100.0)).ceil() as u64).into()
}

/// Helper function to calculate the call gas limit of a [UserOperation](UserOperation)
/// The function is invoked by the [estimate_user_operation_gas](crates::uopool::estimate::estimate_user_operation_gas) method.
///
/// # Arguments
/// `paid` - The paid gas
/// `pre_op_gas` - The pre-operation gas
/// `fee_per_gas` - The fee per gas
///
/// # Returns
/// The call gas limit of the [UserOperation](UserOperation)
pub fn calculate_call_gas_limit(paid: U256, pre_op_gas: U256, fee_per_gas: U256) -> U256 {
paid / fee_per_gas - pre_op_gas + Overhead::default().fixed
}
Expand Down
Loading

0 comments on commit 97d6c04

Please sign in to comment.