Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/steps/ordering/destination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use {
super::{OrderBy, OrderScore},
crate::{
alloy::{consensus::Transaction, primitives::Address},
prelude::*,
},
core::{convert::Infallible, marker::PhantomData},
};

#[derive(Debug, Clone)]
pub struct DestinationAndPriorityFeeScore<P: Platform> {
destination_address: Address,
_phantom: PhantomData<P>,
}

impl<P: Platform> Default for DestinationAndPriorityFeeScore<P> {
fn default() -> Self {
Self {
destination_address: Address::ZERO,
_phantom: PhantomData,
}
}
}

impl<P: Platform> DestinationAndPriorityFeeScore<P> {
pub fn new(destination_address: Address) -> Self {
Self {
destination_address,
_phantom: PhantomData,
}
}
}

impl<P: Platform> OrderScore<P> for DestinationAndPriorityFeeScore<P> {
type Error = Infallible;
type Score = (u8, u128);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe instead of (u8, u128) we could simply have (bool, u128)?


// Since this implementation of score() returns a tuple, this creates a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that you could also do the following:
use u256, for transaction that are pointing to dest addr - place their effective_tip_per_gas in highest 128 bits, for the rest - in the lowest 128 bits, this way you could just compare the values with any extra logic.

// two-tier priority system which we want: Transactions going to the
// destination address are always prioritized over transactions that aren't.
// Within each tier, they're ordered by effective tip.
fn score(
&self,
checkpoint: &Checkpoint<P>,
) -> Result<Self::Score, Self::Error> {
let all_destination = checkpoint
.transactions()
.iter()
.all(|tx| tx.to() == Some(self.destination_address));
let tip_sum = CheckpointExt::effective_tip_per_gas(checkpoint);
Ok((u8::from(all_destination), tip_sum))
}
}

pub type OrderByDestinationAndPriorityFee<P> =
OrderBy<P, DestinationAndPriorityFeeScore<P>>;
51 changes: 43 additions & 8 deletions src/steps/ordering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ use {
},
};

mod destination;
mod profit;
mod tip;

pub use {profit::OrderByCoinbaseProfit, tip::OrderByPriorityFee};
pub use {
destination::{
DestinationAndPriorityFeeScore,
OrderByDestinationAndPriorityFee,
},
profit::OrderByCoinbaseProfit,
tip::OrderByPriorityFee,
};

/// A trait that implements logic for assigning a score to an order.
/// Different implementations of this trait provide different ordering
Expand All @@ -25,7 +33,10 @@ pub trait OrderScore<P: Platform>:
type Score: Clone + Ord + Eq + core::hash::Hash;
type Error: core::error::Error + Send + Sync + 'static;

fn score(_: &Checkpoint<P>) -> Result<Self::Score, Self::Error>;
fn score(
&self,
checkpoint: &Checkpoint<P>,
) -> Result<Self::Score, Self::Error>;
}

/// A generic implementation of a step that will order checkpoints based on a
Expand All @@ -36,8 +47,29 @@ pub trait OrderScore<P: Platform>:
/// Sorting happens only for the mutable part of the payload, i.e. after
/// the last barrier checkpoint. Anything prior to the last barrier
/// checkpoint is considered immutable and will not be reordered.
#[derive(Debug, Clone, Default)]
pub struct OrderBy<P: Platform, S: OrderScore<P>>(PhantomData<(P, S)>);
#[derive(Debug, Clone)]
pub struct OrderBy<P: Platform, S: OrderScore<P>> {
scorer: S,
_phantom: PhantomData<P>,
}

impl<P: Platform, S: OrderScore<P>> Default for OrderBy<P, S> {
fn default() -> Self {
Self {
scorer: S::default(),
_phantom: PhantomData,
}
}
}

impl<P: Platform, S: OrderScore<P>> OrderBy<P, S> {
pub fn new(scorer: S) -> Self {
Self {
scorer,
_phantom: PhantomData,
}
}
}
impl<P: Platform, S: OrderScore<P>> Step<P> for OrderBy<P, S> {
async fn step(
self: Arc<Self>,
Expand All @@ -50,7 +82,8 @@ impl<P: Platform, S: OrderScore<P>> Step<P> for OrderBy<P, S> {
let history = payload.history_staging();

// Find the correct order of orders in the payload.
let ordered = match SortedOrders::<P, S>::try_from(&history) {
let ordered = match SortedOrders::<P, S>::try_from((&history, &self.scorer))
{
Ok(ordered) => ordered.into_iter(),
// when the step started running, the payload had no nonce conflicts and
// all orders were able to construct valid checkpoints. After reordering,
Expand Down Expand Up @@ -115,12 +148,14 @@ struct SortedOrders<'a, P: Platform, S: OrderScore<P>> {
_scoring: PhantomData<S>,
}

impl<'a, P: Platform, S: OrderScore<P>> TryFrom<&'a Span<P>>
impl<'a, P: Platform, S: OrderScore<P>> TryFrom<(&'a Span<P>, &'a S)>
for SortedOrders<'a, P, S>
{
type Error = S::Error;

fn try_from(span: &'a Span<P>) -> Result<Self, Self::Error> {
fn try_from(
(span, scorer): (&'a Span<P>, &'a S),
) -> Result<Self, Self::Error> {
let executables = span.iter().filter(|checkpoint| !checkpoint.is_barrier());
let all_orders: HashMap<B256, &'a Checkpoint<P>> = executables
.map(|checkpoint| (checkpoint.hash().unwrap(), checkpoint))
Expand All @@ -141,7 +176,7 @@ impl<'a, P: Platform, S: OrderScore<P>> TryFrom<&'a Span<P>>

let mut by_score = BTreeMap::<_, BTreeSet<_>>::default();
for (hash, checkpoint) in &all_orders {
let score = S::score(checkpoint)?;
let score = scorer.score(checkpoint)?;
by_score.entry(score).or_default().insert(*hash);
}

Expand Down
5 changes: 4 additions & 1 deletion src/steps/ordering/profit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ impl<P: Platform> OrderScore<P> for CoinbaseProfitScore<P> {
type Error = ProviderError;
type Score = U256;

fn score(checkpoint: &Checkpoint<P>) -> Result<Self::Score, Self::Error> {
fn score(
&self,
checkpoint: &Checkpoint<P>,
) -> Result<Self::Score, Self::Error> {
let fee_recipient = checkpoint.block().coinbase();
let current_balance = checkpoint.balance_of(fee_recipient)?;

Expand Down
5 changes: 4 additions & 1 deletion src/steps/ordering/tip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ impl<P: Platform> OrderScore<P> for PriorityFeeScore<P> {
type Error = Infallible;
type Score = u128;

fn score(checkpoint: &Checkpoint<P>) -> Result<Self::Score, Self::Error> {
fn score(
&self,
checkpoint: &Checkpoint<P>,
) -> Result<Self::Score, Self::Error> {
Ok(checkpoint.effective_tip_per_gas())
}
}
Expand Down
Loading