Skip to content

Commit

Permalink
fix(builder): fix builder state machine replacement tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
dancoombs committed Jun 24, 2024
1 parent 3de6345 commit e8d5e75
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 106 deletions.
26 changes: 18 additions & 8 deletions bin/rundler/src/cli/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,25 @@ pub struct BuilderArgs {
)]
replacement_fee_percent_increase: u64,

/// Maximum number of times to increase gas fees when retrying a transaction
/// Maximum number of times to increase gas fees when retrying a cancellation transaction
/// before giving up.
#[arg(
long = "builder.max_fee_increases",
name = "builder.max_fee_increases",
env = "BUILDER_MAX_FEE_INCREASES",
// Seven increases of 10% is roughly 2x the initial fees.
default_value = "7"
long = "builder.max_cancellation_fee_increases",
name = "builder.max_cancellation_fee_increases",
env = "BUILDER_MAX_CANCELLATION_FEE_INCREASES",
default_value = "15"
)]
max_fee_increases: u64,
max_cancellation_fee_increases: u64,

/// The maximum number of blocks to wait in a replacement underpriced state before issuing
/// a cancellation transaction.
#[arg(
long = "builder.max_replacement_underpriced_blocks",
name = "builder.max_replacement_underpriced_blocks",
env = "BUILDER_MAX_REPLACEMENT_UNDERPRICED_BLOCKS",
default_value = "20"
)]
max_replacement_underpriced_blocks: u64,

/// The index offset to apply to the builder index
#[arg(
Expand Down Expand Up @@ -350,7 +359,8 @@ impl BuilderArgs {
sim_settings: common.try_into()?,
max_blocks_to_wait_for_mine: self.max_blocks_to_wait_for_mine,
replacement_fee_percent_increase: self.replacement_fee_percent_increase,
max_fee_increases: self.max_fee_increases,
max_cancellation_fee_increases: self.max_cancellation_fee_increases,
max_replacement_underpriced_blocks: self.max_replacement_underpriced_blocks,
remote_address,
})
}
Expand Down
44 changes: 32 additions & 12 deletions crates/builder/src/bundle_proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,35 @@ pub(crate) trait BundleProposer: Send + Sync + 'static {
&mut self,
min_fees: Option<GasFees>,
is_replacement: bool,
) -> anyhow::Result<Bundle<Self::UO>>;
) -> BundleProposerResult<Bundle<Self::UO>>;

/// Gets the current gas fees
///
/// If `min_fees` is `Some`, the proposer will ensure the gas fees returned are at least `min_fees`.
async fn estimate_gas_fees(&self, min_fees: Option<GasFees>)
-> anyhow::Result<(GasFees, U256)>;
async fn estimate_gas_fees(
&self,
min_fees: Option<GasFees>,
) -> BundleProposerResult<(GasFees, U256)>;

/// Notifies the proposer that a condition was not met during the last bundle proposal
fn notify_condition_not_met(&mut self);
}

pub(crate) type BundleProposerResult<T> = std::result::Result<T, BundleProposerError>;

#[derive(Debug, thiserror::Error)]
pub(crate) enum BundleProposerError {
#[error("No operations initially")]
NoOperationsInitially,
#[error("No operations after fee filtering")]
NoOperationsAfterFeeFilter,
#[error(transparent)]
ProviderError(#[from] rundler_provider::ProviderError),
/// All other errors
#[error(transparent)]
Other(#[from] anyhow::Error),
}

#[derive(Debug)]
pub(crate) struct BundleProposerImpl<UO, S, E, P, M> {
builder_index: u64,
Expand Down Expand Up @@ -155,8 +172,11 @@ where
async fn estimate_gas_fees(
&self,
required_fees: Option<GasFees>,
) -> anyhow::Result<(GasFees, U256)> {
self.fee_estimator.required_bundle_fees(required_fees).await
) -> BundleProposerResult<(GasFees, U256)> {
Ok(self
.fee_estimator
.required_bundle_fees(required_fees)
.await?)
}

fn notify_condition_not_met(&mut self) {
Expand All @@ -167,16 +187,16 @@ where
&mut self,
required_fees: Option<GasFees>,
is_replacement: bool,
) -> anyhow::Result<Bundle<UO>> {
) -> BundleProposerResult<Bundle<UO>> {
let (ops, (block_hash, _), (bundle_fees, base_fee)) = try_join!(
self.get_ops_from_pool(),
self.provider
.get_latest_block_hash_and_number()
.map_err(anyhow::Error::from),
.map_err(BundleProposerError::from),
self.estimate_gas_fees(required_fees)
)?;
if ops.is_empty() {
return Ok(Bundle::default());
return Err(BundleProposerError::NoOperationsInitially);
}

tracing::debug!("Starting bundle proposal with {} ops", ops.len());
Expand Down Expand Up @@ -206,7 +226,7 @@ where

tracing::debug!("Bundle proposal after fee limit had {} ops", ops.len());
if ops.is_empty() {
return Ok(Bundle::default());
return Err(BundleProposerError::NoOperationsAfterFeeFilter);
}

// (2) Limit the amount of operations for simulation
Expand Down Expand Up @@ -686,7 +706,7 @@ where
async fn estimate_gas_rejecting_failed_ops(
&self,
context: &mut ProposalContext<UO>,
) -> anyhow::Result<Option<U256>> {
) -> BundleProposerResult<Option<U256>> {
// sum up the gas needed for all the ops in the bundle
// and apply an overhead multiplier
let gas = math::increase_by_percent(
Expand Down Expand Up @@ -731,7 +751,7 @@ where
}
}

async fn get_ops_from_pool(&self) -> anyhow::Result<Vec<PoolOperation>> {
async fn get_ops_from_pool(&self) -> BundleProposerResult<Vec<PoolOperation>> {
// Use builder's index as the shard index to ensure that two builders don't
// attempt to bundle the same operations.
//
Expand All @@ -754,7 +774,7 @@ where
&self,
addresses: impl IntoIterator<Item = Address>,
block_hash: H256,
) -> anyhow::Result<HashMap<Address, U256>> {
) -> BundleProposerResult<HashMap<Address, U256>> {
let futures = addresses.into_iter().map(|address| async move {
let deposit = self
.entry_point
Expand Down
Loading

0 comments on commit e8d5e75

Please sign in to comment.