Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use {
pub type Scored = state::Scored<Score>;
pub type Ranked = state::Ranked<Score>;

/// A solver's solution paired with the driver, progressing through the winner
/// selection process.
/// A solver's auction bid, which includes solution and corresponding driver
/// data, progressing through the winner selection process.
///
/// It uses the type-state pattern to enforce correct state
/// transitions at compile time. The state parameter tracks progression through
Expand All @@ -20,13 +20,13 @@ pub type Ranked = state::Ranked<Score>;
/// 2. **Scored**: After computing surplus and fees for the solution
/// 3. **Ranked**: After winner selection determines if this is a winner
#[derive(Clone)]
pub struct Participant<State = Ranked> {
pub struct Bid<State = Ranked> {
solution: Solution,
driver: Arc<infra::Driver>,
state: State,
}

impl<T> Participant<T> {
impl<T> Bid<T> {
pub fn solution(&self) -> &Solution {
&self.solution
}
Expand All @@ -36,12 +36,12 @@ impl<T> Participant<T> {
}
}

impl<State> state::HasState for Participant<State> {
type Next<NewState> = Participant<NewState>;
impl<State> state::HasState for Bid<State> {
type Next<NewState> = Bid<NewState>;
type State = State;

fn with_state<NewState>(self, state: NewState) -> Self::Next<NewState> {
Participant {
Bid {
solution: self.solution,
driver: self.driver,
state,
Expand All @@ -53,7 +53,7 @@ impl<State> state::HasState for Participant<State> {
}
}

impl Participant<Unscored> {
impl Bid<Unscored> {
pub fn new(solution: Solution, driver: Arc<infra::Driver>) -> Self {
Self {
solution,
Expand Down
4 changes: 2 additions & 2 deletions crates/autopilot/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use {
std::collections::HashMap,
};

mod participant;
mod bid;
mod participation_guard;
pub mod winner_selection;

pub use {
participant::{Participant, RankType, Ranked, Scored, Unscored},
bid::{Bid, RankType, Ranked, Scored, Unscored},
participation_guard::SolverParticipationGuard,
};

Expand Down
74 changes: 34 additions & 40 deletions crates/autopilot/src/domain/competition/winner_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use {
crate::domain::{
self,
auction::order,
competition::{Participant, RankType, Ranked, Score, Solution, TradedOrder, Unscored},
competition::{Bid, RankType, Ranked, Score, Solution, TradedOrder, Unscored},
eth::{self, WrappedNativeToken},
fee,
},
Expand All @@ -45,7 +45,7 @@ pub struct Arbitrator(winsel::Arbitrator);
/// 3. compute reference scores
///
/// The functions assume the `Arbitrator` is the only one
/// changing the ordering or the `participants`.
/// changing the ordering or the `bids`.
impl Arbitrator {
pub fn new(max_winners: usize, wrapped_native_token: WrappedNativeToken) -> Self {
let token: eth::TokenAddress = wrapped_native_token.into();
Expand All @@ -56,19 +56,15 @@ impl Arbitrator {
}

/// Runs the entire auction mechanism on the passed in solutions.
pub fn arbitrate(
&self,
participants: Vec<Participant<Unscored>>,
auction: &domain::Auction,
) -> Ranking {
pub fn arbitrate(&self, bids: Vec<Bid<Unscored>>, auction: &domain::Auction) -> Ranking {
let context = auction.into();
let mut participant_by_key = HashMap::with_capacity(participants.len());
let mut solutions = Vec::with_capacity(participants.len());
let mut bid_by_key = HashMap::with_capacity(bids.len());
let mut solutions = Vec::with_capacity(bids.len());

for participant in participants {
let key = SolutionKey::from(participant.solution());
let solution = participant.solution().into();
participant_by_key.insert(key, participant);
for bid in bids {
let key = SolutionKey::from(bid.solution());
let solution = bid.solution().into();
bid_by_key.insert(key, bid);
solutions.push(solution);
}

Expand All @@ -85,27 +81,25 @@ impl Arbitrator {
let mut filtered_out = Vec::with_capacity(ws_ranking.filtered_out.len());
for ws_solution in ws_ranking.filtered_out {
let key = SolutionKey::from(&ws_solution);
let participant = participant_by_key
let bid = bid_by_key
.remove(&key)
.expect("every ranked solution has a matching participant");
.expect("every ranked solution has a matching bid");
let score = ws_solution.score();
filtered_out.push(
participant
.with_score(Score(eth::Ether(score)))
bid.with_score(Score(eth::Ether(score)))
.with_rank(RankType::FilteredOut),
);
}

let mut ranked = Vec::with_capacity(ws_ranking.ranked.len());
for ranked_solution in ws_ranking.ranked {
let key = SolutionKey::from(&ranked_solution);
let participant = participant_by_key
let bid = bid_by_key
.remove(&key)
.expect("every ranked solution has a matching participant");
.expect("every ranked solution has a matching bid");
let score = ranked_solution.score();
ranked.push(
participant
.with_score(Score(eth::Ether(score)))
bid.with_score(Score(eth::Ether(score)))
.with_rank(ranked_solution.state().rank_type),
);
}
Expand Down Expand Up @@ -242,34 +236,34 @@ impl<S> From<&winsel::Solution<S>> for SolutionKey {
pub struct Ranking {
/// Solutions that were discarded because they were malformed
/// in some way or deemed unfair by the selection mechanism.
filtered_out: Vec<Participant<Ranked>>,
filtered_out: Vec<Bid<Ranked>>,
/// Final ranking of the solutions that passed the fairness
/// check. Winners come before non-winners and higher total
/// scores come before lower scores.
ranked: Vec<Participant<Ranked>>,
ranked: Vec<Bid<Ranked>>,
/// Reference scores for each winning solver, used to compute rewards.
reference_scores: HashMap<eth::Address, Score>,
}

impl Ranking {
/// All solutions including the ones that got filtered out.
pub fn all(&self) -> impl Iterator<Item = &Participant<Ranked>> {
pub fn all(&self) -> impl Iterator<Item = &Bid<Ranked>> {
self.ranked.iter().chain(&self.filtered_out)
}

/// Enumerates all solutions. The index is used as solution UID.
pub fn enumerated(&self) -> impl Iterator<Item = (usize, &Participant<Ranked>)> {
pub fn enumerated(&self) -> impl Iterator<Item = (usize, &Bid<Ranked>)> {
self.all().enumerate()
}

/// All solutions that won the right to get executed.
pub fn winners(&self) -> impl Iterator<Item = &Participant<Ranked>> {
self.ranked.iter().filter(|p| p.is_winner())
pub fn winners(&self) -> impl Iterator<Item = &Bid<Ranked>> {
self.ranked.iter().filter(|b| b.is_winner())
}

/// All solutions that were not filtered out but also did not win.
pub fn non_winners(&self) -> impl Iterator<Item = &Participant<Ranked>> {
self.ranked.iter().filter(|p| !p.is_winner())
pub fn non_winners(&self) -> impl Iterator<Item = &Bid<Ranked>> {
self.ranked.iter().filter(|b| !b.is_winner())
}

/// Reference scores for each winning solver, used to compute rewards.
Expand All @@ -278,7 +272,7 @@ impl Ranking {
}

/// All solutions that passed the filtering step.
pub fn ranked(&self) -> impl Iterator<Item = &Participant<Ranked>> {
pub fn ranked(&self) -> impl Iterator<Item = &Bid<Ranked>> {
self.ranked.iter()
}
}
Expand All @@ -295,7 +289,7 @@ mod tests {
Price,
order::{self, AppDataHash},
},
competition::{Participant, Solution, TradedOrder, Unscored},
competition::{Bid, Solution, TradedOrder, Unscored},
eth::{self, TokenAddress},
},
infra::Driver,
Expand Down Expand Up @@ -998,7 +992,7 @@ mod tests {
// map (solver id -> solver address) for later reference during the test
let mut solver_map = HashMap::new();

// map (solution id -> participant) for later reference during the test
// map (solution id -> bid) for later reference during the test
let mut solution_map = HashMap::new();
for (solution_id, solution) in &self.solutions {
// generate solver address deterministically from the id
Expand All @@ -1021,13 +1015,13 @@ mod tests {
let solution_uid = hash(solution_id);
solution_map.insert(
solution_id,
create_solution(solution_uid, solver_address, trades, None).await,
create_bid(solution_uid, solver_address, trades, None).await,
);
}

// filter solutions
let participants = solution_map.values().cloned().collect();
let ranking = arbitrator.arbitrate(participants, &auction);
let bids = solution_map.values().cloned().collect();
let ranking = arbitrator.arbitrate(bids, &auction);
assert_eq!(ranking.ranked.len(), self.expected_fair_solutions.len());
for solution_id in &self.expected_fair_solutions {
let solution_uid = solution_map.get(&solution_id).unwrap().solution().id;
Expand Down Expand Up @@ -1199,12 +1193,12 @@ mod tests {
}
}

async fn create_solution(
async fn create_bid(
solution_id: u64,
solver_address: Address,
trades: Vec<(OrderUid, TradedOrder)>,
prices: Option<HashMap<TokenAddress, Price>>,
) -> Participant<Unscored> {
) -> Bid<Unscored> {
// The prices of the tokens do not affect the result but they keys must exist
// for every token of every trade
let prices = prices.unwrap_or({
Expand All @@ -1230,7 +1224,7 @@ mod tests {
.await
.unwrap();

Participant::new(solution, std::sync::Arc::new(driver))
Bid::new(solution, std::sync::Arc::new(driver))
}

fn amount(value: u128) -> String {
Expand All @@ -1242,8 +1236,8 @@ mod tests {
value * 10u128.pow(15)
}

fn filter_winners(solutions: &[Participant]) -> Vec<&Participant> {
solutions.iter().filter(|s| s.is_winner()).collect()
fn filter_winners(bids: &[Bid]) -> Vec<&Bid> {
bids.iter().filter(|b| b.is_winner()).collect()
}

// Used to generate deterministic identifiers (e.g., UIDs, addresses) from
Expand Down
20 changes: 10 additions & 10 deletions crates/autopilot/src/infra/persistence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl Persistence {
pub async fn save_solutions(
&self,
auction_id: domain::auction::Id,
solutions: impl Iterator<Item = &domain::competition::Participant>,
solutions: impl Iterator<Item = &domain::competition::Bid>,
) -> Result<(), DatabaseError> {
let _timer = Metrics::get()
.database_queries
Expand All @@ -221,17 +221,17 @@ impl Persistence {
auction_id,
&solutions
.enumerate()
.map(|(uid, participant)| {
.map(|(uid, bid)| {
let solution = Solution {
uid: uid.try_into().context("uid overflow")?,
id: u256_to_big_decimal(&participant.solution().id().into()),
solver: ByteArray(participant.solution().solver().0.0),
is_winner: participant.is_winner(),
filtered_out: participant.is_filtered_out(),
id: u256_to_big_decimal(&bid.solution().id().into()),
solver: ByteArray(bid.solution().solver().0.0),
is_winner: bid.is_winner(),
filtered_out: bid.is_filtered_out(),
score: number::conversions::alloy::u256_to_big_decimal(
&participant.score().get().0,
&bid.score().get().0,
),
orders: participant
orders: bid
.solution()
.orders()
.iter()
Expand All @@ -254,13 +254,13 @@ impl Persistence {
side: order.side.into(),
})
.collect(),
price_tokens: participant
price_tokens: bid
.solution()
.prices()
.keys()
.map(|token| ByteArray(token.0.0.0))
.collect(),
price_values: participant
price_values: bid
.solution()
.prices()
.values()
Expand Down
Loading
Loading