Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add block number to simulation error #2054

Merged
merged 5 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions crates/driver/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ impl Competition {
tracing::warn!(block = block.number, ?err, "solution reverts on new block");
*score_ref = None;
*self.settlement.lock().unwrap() = None;
if let Some(id) = settlement.notify_id() {
Copy link
Contributor Author

@sunce86 sunce86 Nov 14, 2023

Choose a reason for hiding this comment

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

Here we have IF because we decided to not send the notifications for merged solutions. While this is okkish now since external solvers are sending a single solution anyway, it may become problematic in the future with combinatorial auctions...

notify::simulation_failed(&self.solver, auction.id(), id, &err);
}
return;
}
}
Expand Down
21 changes: 18 additions & 3 deletions crates/driver/src/infra/notify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ pub fn encoding_failed(
}
solution::Error::Blockchain(_) => return,
solution::Error::Boundary(_) => return,
solution::Error::Simulation(simulator::Error::WithTx(error)) => {
notification::Kind::SimulationFailed(error.tx.clone())
solution::Error::Simulation(error) => {
simulation_failed(solver, auction_id, solution_id, error);
return;
}
solution::Error::Simulation(simulator::Error::Basic(_)) => return,
solution::Error::AssetFlow(missmatch) => notification::Kind::AssetFlow(missmatch.clone()),
solution::Error::Execution(_) => return,
solution::Error::FailingInternalization => return,
Expand All @@ -86,6 +86,21 @@ pub fn encoding_failed(
solver.notify(auction_id, Some(solution_id), notification);
}

pub fn simulation_failed(
solver: &Solver,
auction_id: Option<auction::Id>,
solution_id: solution::Id,
err: &simulator::Error,
) {
if let simulator::Error::Revert(error) = err {
solver.notify(
auction_id,
Some(solution_id),
notification::Kind::SimulationFailed(error.block, error.tx.clone()),
);
}
}

pub fn executed(
solver: &Solver,
auction_id: auction::Id,
Expand Down
2 changes: 1 addition & 1 deletion crates/driver/src/infra/notify/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub enum Kind {
/// Solution received from solver engine don't have unique id.
DuplicatedSolutionId,
/// Failed simulation during competition.
SimulationFailed(Transaction),
SimulationFailed(eth::BlockNo, Transaction),
/// No valid score could be computed for the solution.
ScoringFailed(ScoreKind),
/// Solution aimed to internalize tokens that are not considered safe to
Expand Down
50 changes: 30 additions & 20 deletions crates/driver/src/infra/simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod tenderly;
#[derive(Debug, Clone)]
pub struct Simulator {
inner: Inner,
eth: Ethereum,
disable_access_lists: bool,
/// If this is [`Some`], every gas estimate will return this fixed
/// gas value.
Expand All @@ -29,9 +30,10 @@ pub enum Config {

impl Simulator {
/// Simulate transactions on [Tenderly](https://tenderly.co/).
pub fn tenderly(config: tenderly::Config, network_id: eth::NetworkId) -> Self {
pub fn tenderly(config: tenderly::Config, network_id: eth::NetworkId, eth: Ethereum) -> Self {
Self {
inner: Inner::Tenderly(tenderly::Tenderly::new(config, network_id)),
eth,
disable_access_lists: false,
disable_gas: None,
}
Expand All @@ -40,7 +42,8 @@ impl Simulator {
/// Simulate transactions using the Ethereum RPC API.
pub fn ethereum(eth: Ethereum) -> Self {
Self {
inner: Inner::Ethereum(eth),
inner: Inner::Ethereum,
eth,
disable_access_lists: false,
disable_gas: None,
}
Expand All @@ -50,7 +53,8 @@ impl Simulator {
/// Uses Ethereum RPC API to generate access lists.
pub fn enso(config: enso::Config, eth: Ethereum) -> Self {
Self {
inner: Inner::Enso(enso::Enso::new(config, eth.network().chain), eth),
inner: Inner::Enso(enso::Enso::new(config, eth.network().chain)),
eth,
disable_access_lists: false,
disable_gas: None,
}
Expand All @@ -75,22 +79,25 @@ impl Simulator {
if self.disable_access_lists {
return Ok(tx.access_list);
}
let block: eth::BlockNo = self.eth.current_block().borrow().number.into();
let access_list = match &self.inner {
Inner::Tenderly(tenderly) => {
tenderly
.simulate(tx.clone(), tenderly::GenerateAccessList::Yes)
.await
.map_err(with_tx(tx.clone()))?
.map_err(with(tx.clone(), block))?
.access_list
}
Inner::Ethereum(ethereum) => ethereum
Inner::Ethereum => self
.eth
.create_access_list(tx.clone())
.await
.map_err(with_tx(tx.clone()))?,
Inner::Enso(_, ethereum) => ethereum
.map_err(with(tx.clone(), block))?,
Inner::Enso(_) => self
.eth
.create_access_list(tx.clone())
.await
.map_err(with_tx(tx.clone()))?,
.map_err(with(tx.clone(), block))?,
};
Ok(tx.access_list.merge(access_list))
}
Expand All @@ -100,33 +107,35 @@ impl Simulator {
if let Some(gas) = self.disable_gas {
return Ok(gas);
}
let block: eth::BlockNo = self.eth.current_block().borrow().number.into();
Ok(match &self.inner {
Inner::Tenderly(tenderly) => {
tenderly
.simulate(tx.clone(), tenderly::GenerateAccessList::No)
.measure("tenderly_simulate_gas")
.await
.map_err(with_tx(tx))?
.map_err(with(tx, block))?
.gas
}
Inner::Ethereum(ethereum) => ethereum
Inner::Ethereum => self
.eth
.estimate_gas(tx.clone())
.await
.map_err(with_tx(tx))?,
Inner::Enso(enso, _) => enso
.map_err(with(tx, block))?,
Inner::Enso(enso) => enso
.simulate(tx.clone())
.measure("enso_simulate_gas")
.await
.map_err(with_tx(tx))?,
.map_err(with(tx, block))?,
})
}
}

#[derive(Debug, Clone)]
enum Inner {
Tenderly(tenderly::Tenderly),
Ethereum(Ethereum),
Enso(enso::Enso, Ethereum),
Ethereum,
Enso(enso::Enso),
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -140,10 +149,11 @@ pub enum SimulatorError {
}

#[derive(Debug, thiserror::Error)]
#[error("err: {err:?}, tx: {tx:?}")]
pub struct WithTxError {
#[error("block: {block:?}, err: {err:?}, tx: {tx:?}")]
pub struct RevertError {
pub err: SimulatorError,
pub tx: eth::Tx,
pub block: eth::BlockNo,
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -153,10 +163,10 @@ pub enum Error {
/// If a transaction reverted, forward that transaction together with the
/// error.
#[error(transparent)]
WithTx(#[from] WithTxError),
Revert(#[from] RevertError),
}

fn with_tx<E>(tx: eth::Tx) -> impl FnOnce(E) -> Error
fn with<E>(tx: eth::Tx, block: eth::BlockNo) -> impl FnOnce(E) -> Error
where
E: Into<SimulatorError>,
{
Expand Down Expand Up @@ -185,7 +195,7 @@ where
SimulatorError::Enso(enso::Error::Revert(_)) => Some(tx),
};
match tx {
Some(tx) => Error::WithTx(WithTxError { err, tx }),
Some(tx) => Error::Revert(RevertError { err, tx, block }),
None => Error::Basic(err),
}
}
Expand Down
21 changes: 13 additions & 8 deletions crates/driver/src/infra/solver/dto/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ impl Notification {
kind: match kind {
notify::Kind::Timeout => Kind::Timeout,
notify::Kind::EmptySolution => Kind::EmptySolution,
notify::Kind::SimulationFailed(tx) => Kind::SimulationFailed(Tx {
from: tx.from.into(),
to: tx.to.into(),
input: tx.input.into(),
value: tx.value.into(),
access_list: tx.access_list.into(),
}),
notify::Kind::SimulationFailed(block, tx) => Kind::SimulationFailed(
block.0,
Tx {
from: tx.from.into(),
to: tx.to.into(),
input: tx.input.into(),
value: tx.value.into(),
access_list: tx.access_list.into(),
},
),
notify::Kind::ScoringFailed(notify::ScoreKind::ZeroScore) => {
Kind::ScoringFailed(ScoreKind::ZeroScore)
}
Expand Down Expand Up @@ -100,7 +103,7 @@ pub enum Kind {
Timeout,
EmptySolution,
DuplicatedSolutionId,
SimulationFailed(Tx),
SimulationFailed(BlockNo, Tx),
ScoringFailed(ScoreKind),
NonBufferableTokensUsed {
tokens: BTreeSet<eth::H160>,
Expand All @@ -115,6 +118,8 @@ pub enum Kind {
Settled(Settlement),
}

type BlockNo = u64;

#[serde_as]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
1 change: 1 addition & 0 deletions crates/driver/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ fn simulator(config: &infra::Config, eth: &Ethereum) -> Simulator {
save_if_fails: tenderly.save_if_fails,
},
eth.network().id.clone(),
eth.to_owned(),
),
Some(infra::simulator::Config::Enso(enso)) => Simulator::enso(
simulator::enso::Config {
Expand Down
21 changes: 13 additions & 8 deletions crates/solvers/src/api/routes/notify/dto/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ impl Notification {
kind: match &self.kind {
Kind::Timeout => notification::Kind::Timeout,
Kind::EmptySolution => notification::Kind::EmptySolution,
Kind::SimulationFailed(tx) => notification::Kind::SimulationFailed(eth::Tx {
from: tx.from.into(),
to: tx.to.into(),
input: tx.input.clone().into(),
value: tx.value.into(),
access_list: tx.access_list.clone(),
}),
Kind::SimulationFailed(block, tx) => notification::Kind::SimulationFailed(
*block,
eth::Tx {
from: tx.from.into(),
to: tx.to.into(),
input: tx.input.clone().into(),
value: tx.value.into(),
access_list: tx.access_list.clone(),
},
),
Kind::ScoringFailed(ScoreKind::ObjectiveValueNonPositive { quality, gas_cost }) => {
notification::Kind::ScoringFailed(
notification::ScoreKind::ObjectiveValueNonPositive(
Expand Down Expand Up @@ -113,7 +116,7 @@ pub enum Kind {
Timeout,
EmptySolution,
DuplicatedSolutionId,
SimulationFailed(Tx),
SimulationFailed(BlockNo, Tx),
ScoringFailed(ScoreKind),
NonBufferableTokensUsed {
tokens: BTreeSet<H160>,
Expand All @@ -128,6 +131,8 @@ pub enum Kind {
Settled(Settlement),
}

type BlockNo = u64;

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
Expand Down
6 changes: 2 additions & 4 deletions crates/solvers/src/boundary/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,8 +588,6 @@ fn to_domain_solution(
})
}

const UNKNOWN_BLOCK_NUMBER: u64 = 0;

fn to_boundary_auction_result(notification: &notification::Notification) -> (i64, AuctionResult) {
let auction_id = match notification.auction_id {
auction::Id::Solve(id) => id,
Expand All @@ -601,15 +599,15 @@ fn to_boundary_auction_result(notification: &notification::Notification) -> (i64
AuctionResult::Rejected(SolverRejectionReason::RunError(SolverRunError::Timeout))
}
Kind::EmptySolution => AuctionResult::Rejected(SolverRejectionReason::NoUserOrders),
Kind::SimulationFailed(tx) => AuctionResult::Rejected(
Kind::SimulationFailed(block_number, tx) => AuctionResult::Rejected(
SolverRejectionReason::SimulationFailure(TransactionWithError {
error: "".to_string(),
transaction: SimulatedTransaction {
from: tx.from.into(),
to: tx.to.into(),
data: tx.input.clone().into(),
internalization: InternalizationStrategy::Unknown,
block_number: UNKNOWN_BLOCK_NUMBER, // todo #2018
block_number: *block_number,
tx_index: Default::default(),
access_list: Default::default(),
max_fee_per_gas: Default::default(),
Expand Down
3 changes: 2 additions & 1 deletion crates/solvers/src/domain/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type RequiredEther = Ether;
type TokensUsed = BTreeSet<TokenAddress>;
type TransactionHash = eth::H256;
type Transaction = eth::Tx;
type BlockNo = u64;
type Missmatches = HashMap<eth::TokenAddress, num::BigInt>;

/// The notification about important events happened in driver, that solvers
Expand All @@ -28,7 +29,7 @@ pub enum Kind {
Timeout,
EmptySolution,
DuplicatedSolutionId,
SimulationFailed(Transaction),
SimulationFailed(BlockNo, Transaction),
ScoringFailed(ScoreKind),
NonBufferableTokensUsed(TokensUsed),
SolverAccountInsufficientBalance(RequiredEther),
Expand Down
Loading