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
32 changes: 20 additions & 12 deletions crates/driver/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ impl Competition {
.user_trades()
.map(|trade| trade.order().uid)
.collect();
let has_haircut = solution.has_haircut();
observe::encoding(&id);
let settlement = solution
.encode(
Expand All @@ -248,10 +249,10 @@ impl Competition {
self.solver.solver_native_token(),
)
.await;
(id, orders, settlement)
(id, orders, has_haircut, settlement)
})
.collect::<FuturesUnordered<_>>()
.filter_map(|(id, orders, result)| async move {
.filter_map(|(id, orders, has_haircut, result)| async move {
match result {
Ok(solution) => {
self.risk_detector.encoding_succeeded(&orders);
Expand All @@ -261,8 +262,11 @@ impl Competition {
Err(_err) if id.solutions().len() > 1 => None,
Err(err) => {
self.risk_detector.encoding_failed(&orders);
observe::encoding_failed(self.solver.name(), &id, &err);
notify::encoding_failed(&self.solver, auction.id(), &id, &err);
observe::encoding_failed(self.solver.name(), &id, &err, has_haircut);
// don't notify on errors for solutions with haircut
if !has_haircut {
notify::encoding_failed(&self.solver, auction.id(), &id, &err);
}
None
}
}
Expand Down Expand Up @@ -362,26 +366,30 @@ impl Competition {
// gets picked by the procotol.
if let Ok(remaining) = deadline.remaining() {
let score_ref = &mut score;
let has_haircut = settlement.has_haircut();
let simulate_on_new_blocks = async move {
let mut stream =
ethrpc::block_stream::into_stream(self.eth.current_block().clone());
while let Some(block) = stream.next().await {
if let Err(infra::simulator::Error::Revert(err)) =
self.simulate_settlement(&settlement).await
{
observe::winner_voided(block, &err);
observe::winner_voided(self.solver.name(), block, &err, has_haircut);
*score_ref = None;
self.settlements
.lock()
.unwrap()
.retain(|s| s.solution().get() != solution_id);
notify::simulation_failed(
&self.solver,
auction.id(),
settlement.solution(),
&infra::simulator::Error::Revert(err),
true,
);
// Only notify solver if solution doesn't have haircut
if !has_haircut {
notify::simulation_failed(
&self.solver,
auction.id(),
settlement.solution(),
&infra::simulator::Error::Revert(err),
true,
);
}
return;
}
}
Expand Down
10 changes: 10 additions & 0 deletions crates/driver/src/domain/competition/solution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,16 @@ impl Solution {
)
})
}

/// Returns true if any trade in this solution has a non-zero haircut fee.
/// Used to determine if simulation failures should suppress solver
/// notifications.
pub fn has_haircut(&self) -> bool {
self.trades.iter().any(|trade| match trade {
Trade::Fulfillment(fulfillment) => !fulfillment.haircut_fee().is_zero(),
Trade::Jit(_) => false, // JIT orders don't have haircut
})
}
}

/// Given two solutions returns the factors with
Expand Down
5 changes: 5 additions & 0 deletions crates/driver/src/domain/competition/solution/settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ impl Settlement {
.map(|(token, amount)| (token, amount.into()))
.collect()
}

/// Returns true if this settlement's solution has any trades with haircut.
pub fn has_haircut(&self) -> bool {
self.solution.has_haircut()
}
}

/// Should the interactions be internalized?
Expand Down
44 changes: 39 additions & 5 deletions crates/driver/src/infra/observe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,26 @@ pub fn encoding(id: &solution::Id) {
}

/// Observe that settlement encoding failed.
pub fn encoding_failed(solver: &solver::Name, id: &solution::Id, err: &solution::Error) {
tracing::info!(?id, ?err, "discarded solution: settlement encoding");
pub fn encoding_failed(
solver: &solver::Name,
id: &solution::Id,
err: &solution::Error,
has_haircut: bool,
) {
tracing::info!(
?id,
?err,
has_haircut,
"discarded solution: settlement encoding"
);
let reason = if has_haircut {
"SettlementEncodingHaircut"
} else {
"SettlementEncoding"
};
metrics::get()
.dropped_solutions
.with_label_values(&[solver.as_str(), "SettlementEncoding"])
.with_label_values(&[solver.as_str(), reason])
.inc();
}

Expand Down Expand Up @@ -164,8 +179,27 @@ pub fn score(settlement: &Settlement, score: &eth::Ether) {

// Observe that the winning settlement started failing upon arrival of a new
// block
pub fn winner_voided(block: BlockInfo, err: &simulator::RevertError) {
tracing::warn!(block = block.number, ?err, "solution reverts on new block");
pub fn winner_voided(
solver: &solver::Name,
block: BlockInfo,
err: &simulator::RevertError,
has_haircut: bool,
) {
tracing::warn!(
block = block.number,
?err,
has_haircut,
"solution reverts on new block"
);
let reason = if has_haircut {
"SimulationRevertHaircut"
} else {
"SimulationRevert"
};
metrics::get()
.dropped_solutions
.with_label_values(&[solver.as_str(), reason])
.inc();
}

pub fn revealing() {
Expand Down
Loading